去年也參加過 AIS3 pre-exam 見去年心得,這似乎是變成每年這個時間的定番了呢(笑),
今年課程部份依舊是上課一星期,最後有 Group Project 發表的形式,而不同的是今年因為疫情的影響,原訂辦在交大,後來改至台科大舉辦。

官方解法
- Pwn: https://github.com/ss8651twtw/ais3-pre-exam-2020
- Web: https://github.com/djosix/AIS3-2020-Pre-Exam#turtle-crypto
- Rev: http://blog.terrynini.tw/tw/2020-AIS3-%E5%89%8D%E6%B8%AC%E5%AE%98%E6%96%B9%E8%A7%A3/
- Crypto: https://maojui.me/
- Misc: https://github.com/frozenkp/CTF/tree/master/2020/AIS3_pre-exam
Pwn
BOF
簡單的 bof ,要注意的是 movaps 的指令要求 stack 要對齊 0x10 byte,就找個 ret 的 gadget 掉過去就會 -8 byte
1 | #!/usr/bin/env python3 |
nonsense
程式可以輸入兩個字串,之後會檢查第二個字串,接著程式會 call 它(很明顯就是要塞 shellcode),但沒那麼簡單。

check() 會檢查 wubbalubbadubdub 是否為 yours 的子字串(只檢查到第一個 match 的)
而且會先檢查該字元 your[i] 是否 <= 0x1f,但只要出現了 wubbalubbadubdub 後頭就不會檢查
也就是說後頭可以塞正常的 shellcode

想法: 開頭塞個 ascii printable shellcode 跳轉到後頭的 shellcode,中間就塞 wubbalubbadubdub
1 | /-------------------\ |
1 | #!/usr/bin/env python3 |
Portal Gun
- 本題有三個檔案
portal_gun執行檔libc.so.6libchook.sosystem()被 hook 掉了
在 portal_gun 中送你一個 system()

但是實際跳過去執行時,會發現 system() 被 hook 掉了

但是有 puts() 可以利用,思路是:用 puts() leak 出某個 libc function 的 address (就 leak puts())
接著可以算出真正 system() 的位置;或是直接跳 one_gadget 也可以。
1 | #!/usr/bin/env python3 |
不過這題我遇到一點問題是:讓執行檔載入題目給訂的 libc,在本地即使成功運行 exploit,shell 也不會出來,但是在 remote 是能成功的
我猜是因為載入題目給訂的 libc 的關係,不過我還沒找到解法,如果有人能提供解法,我會很感謝的 :)
Reverse
TsaiBro
Flag 的每個字元會被 TsaiBro 轉成兩個 ...... 的字串,. 的數量是看字元 in[i] == table[j] 時,輸出 $j / 8 + 1$ 個點及 $j % 8 + 1$ 個點


所以將密文的第一行去除後,每兩行為一組去推回原本的 flag,處理後的密文
1 |
|
Fallen Beat
這題是 SDVX 欸www,怎麼 I’m so happy 不能玩QQ
這題是用 java 寫的音G,可以用 jadx-gui 反編譯 jar
patch
使用 java bytecode editor
- 有人知道其他好用的工具拜託跟我講一下XD
patch
GameControl.run()中的this.pe.setValue- 往
setValue()裡頭追發現 flag 相關的邏輯在這
1 | if (t == mc) { |
- 而
t跟mc相對應到傳入的參數是this.total與this.comboMax- 所以上面的條件是:如果最高 combo 是 note 的總數的話,就會印出 flag
- 所以就 patch 掉這裡就好
1 | # Extract |

static
此題還可以分析程式碼,可以發現 flag 最後是用 this.cache 做 XOR
1 | // Inside public void setValue(int t, int c2, int e, int l, int m, int mc, String info, ArrayList<Integer> cache) |
繼續追,可以發現 this.cache 是在 Control.GameControl:131 被新增元素的
1 | // from Control.GameControl:131 |
最後發現 cache 是譜面的數字
1 | // from Control.GameControl:94 |
根據剛剛分析的邏輯後可以寫個程式把 flag 轉回來
1 |
|
Stand up! Brain

這題要輸入一個字串,然後底下的邏輯長得就像一個 brainfuck 的 interpreter
去 .rodata 找到該 bf code:
1 | -------------------------------------------------------------------[>[-]<[-]]>[>--------------------------------------------------------[>[-]<[-]]>[>-------------------------------------------------------[>[-]<[-]]>[>------------------------------------------------------[>[-]<[-]]>[>---------------------------------------------------[>[-]<[-]]>[>---------------------------------[>[-]<[-]]>[>>----[---->+<]>++.++++++++.++++++++++.>-[----->+<]>.+[--->++<]>+++.>-[--->+<]>-.[---->+++++<]>-.[-->+<]>---.[--->++<]>---.++[->+++<]>.+[-->+<]>+.[--->++<]>---.++[->+++<]>.+++.[--->+<]>----.[-->+<]>-----.[->++<]>+.-[---->+++<]>.--------.>-[--->+<]>.-[----->+<]>-.++++++++.--[----->+++<]>.+++.[--->+<]>-.-[-->+<]>---.++[--->+++++<]>.++++++++++++++.+++[->+++++<]>.[----->+<]>++.>-[----->+<]>.---[->++<]>-.++++++.[--->+<]>+++.+++.[-]]]]]]] |
上面的 brainfuck 如果用正常的 brainfuck interpreter 應該會是無窮迴圈,但在 google 通靈之下,找到一個迴圈爛掉的 brainfuck 實作就印出 flag ㄌ
Web
Squirrel
打開網頁發現有很多松鼠,很符合題目的名字www
檢視網頁原始碼後發現 api.php 的個 endpoint,看起來有 LFI

可以用 api.php?get=path 讀出 api.php 的原始碼,可以發現 $output 存在 command injection
可以用 '; command ;' 執行任意指令

1 | // api.php |
可以在根目錄找到 5qu1rr3l_15_4_k1nd_0f_b16_r47.txt 讀出來就是 flag


- 題外話
- postman 可以直接 import curl 的指令



Elephant
這題一開始畫面有個輸入框,隨意輸入後可以發現上頭有個隱藏的小字

嘗試了一些常見的目錄後可以發現存在 .git 目錄,並且可以讀取目錄內的檔案


瀏覽一下原始碼後發現,它會把輸入的名稱用來建構 User 然後序列化後再 base64 放在 cookie 裏

如果 $user->canReadFlag() 是 true 的話會印出 flag

而 User 要在 strcmp($flag, $this->token) == 0 時才是 true

想法:strcmp() 再與空物件比較會是 == 0,Example
所以可以把 token 變成是空物件,這樣就可以通過 canReadFlag() 了
1 |
|
修改 cookie 可以用 EditThisCookie

Shark
題目開宗明義說在同個內網其他 server 上頭有 flag

題目有 LFI 但是濾掉了 ../ 但是 file:// 可以使用


而且有 RFI (所以可以讀同個內網底下的 ip)

題目原始碼

可以讀 /proc/net/arp 看同個內網底下的其他主機之 ip,掃過一遍就有 flag 了


Snake
python pickle 反序列化
1 | from flask import Flask, Response, request |
- 細節我沒有研究,在賽中翻到一篇投影片讓我解出這題
- Security Issues in Python Pickle
- 這題沒有過濾任何字元,所以可以用最簡單的 exp 過
1 | import requests, base64, pickle |
Owl

題目有個登入框,上頭有個小字寫要猜密碼,admin/admin 可以登入

登入後看HTML 原始碼,發現它送你 php 原始碼,在 /?source
完整原始碼
看到 sql 就知道這題是 SQLi ㄌ

看到 login 的邏輯部分,發現它用黑名單過濾,並且只用 str_ireplace 來取代兩遍字串
這種字串取代的過濾方法總是有方法可以 bypass,這裡的解法是:把 replace 的字插在原本字串的中間,過濾幾次就插幾次
- 例如
selselselectectect->select- 第一次
str_ireplace():selsel[select]ectect=selselectect
- 第二次
str_ireplace():sel[select]ect=select
- 第一次
可以寫個簡單的腳本自動轉換,方便寫 exp
1 |
|
而且他的 DB 是 SQLite

用 union select 測出總欄位數量及回顯欄位:' or 1=1 union select 1,2,3 limit 0,1///***
要注意的是 LIMIT 0,1 有時候你的 result 不會在第一個 row ,回顯結果會是 root,賽中害我卡超久幹

去 sql_master 撈所有的 Table Schema,得到所有的表名及欄位名稱:select group_concat(sql) from sqlite_master where type='table'

花點時間找後就會發現 flag 在 garbage 裡頭:select group_concat(value) from garbage

Rhino

觀察 robots.txt 發現我們要的 flag.txt 就在網站的根目錄
直接讀 flag.txt 發現會被擋下來


題目是用 express.js ,可以讀得到 package.json,並可以發現 chill.js 的存在
package.json1
2
3
4
5
6
7
8
9
10
11
12
13
14
15{
"name": "app",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "node chill.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "djosix",
"license": "ISC",
"dependencies": {
"cookie-session": "^1.4.0",
"express": "^4.17.1"
}
}chill.js
1 | const express = require('express'); |
想法:路由flag.txt 會檢查 cookie-session 內的 magic 是否通過 if (n && (n + 420) === 420),而加密此 cookie 的 key 也送你了
所以可以在本地建一個測試站,並想辦法 bypass if (n && (n + 420) === 420) 之後複製 cookie 給真的題目就好
如何在本地 setup 測試環境?
- 將
package.json與chill.js存下來- 記得修改 chill.js 中的
port
- 記得修改 chill.js 中的
npm install下載所需 librariesnpm start啟動伺服器
- 將
如何 bypass
if (n && (n + 420) === 420)- float 精度

- float 精度
最後複製 express:sess 與 express:sess.sig 給 rhino.ais3.org 後存取 rhino.ais3.org/flag.txt,成功拿到 flag

這題我是賽後才解出來,賽中一直卡在一個地方,直到賽後別人跟我說要怎麼 bypass if(n && (n + 420) == 420) 那邊QQ
一開始我還一直往 object 那邊想QQ
Misc
Karuego
zip 已知明文攻擊
題目是一張圖片,果斷用 binwalk 看1
2
3
4
5
6
7
8
9
10$ binwalk Karuego_0d9f4a9262326e0150272debfd4418aaa600ffe4.png
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 PNG image, 2880 x 1492, 8-bit/color RGBA, non-interlaced
41 0x29 Zlib compressed data, compressed
2059568 0x1F6D30 Zip archive data, at least v1.0 to extract, name: files/
2059632 0x1F6D70 Zip archive data, encrypted at least v2.0 to extract, compressed size: 113020, uncompressed size: 113110, name: files/3a66fa5887bcb740438f1fb49f78569cb56e9233_hq.jpg
2172779 0x21276B Zip archive data, encrypted at least v2.0 to extract, compressed size: 1087747, uncompressed size: 1092860, name: files/Demon.png
3260899 0x31C1E3 End of Zip archive, footer length: 22
一個正常的 png 長這樣,跟上頭比較可以發現上頭的檔案後面多了 zip
1 | DECIMAL HEXADECIMAL DESCRIPTION |
裡頭的 png 可以在 google 找到:
把 zip 從 jpg 中拆開:
1 | # 使用 dd |
把剛剛下載的 3a66fa5887bcb740438f1fb49f78569cb56e9233_hq.jpg 放到路徑 files/3a66fa5887bcb740438f1fb49f78569cb56e9233_hq.jpg 並壓縮起來 (plain.zip)
1 | mkdir files && mv 3a66fa5887bcb740438f1fb49f78569cb56e9233_hq.jpg files |
- 使用 pkcrack 進行已知明文攻擊
1
2
3
4
5
6
7
8pkcrack -C ./target.zip -c files/3a66fa5887bcb740438f1fb49f78569cb56e9233_hq.jpg -P ./plain.zip -p files/3a66fa5887bcb740438f1fb49f78569cb56e9233_hq.jpg -d ok.zip
# ...
# Ta-daaaaa! key0=9237ea20, key1=cf7dddf2, key2=dec3715e
# Probabilistic test succeeded for 18584 bytes.
# Stage 2 completed. Starting zipdecrypt on Wed Jun 24 20:59:25 2020
# Decrypting files/3a66fa5887bcb740438f1fb49f78569cb56e9233_hq.jpg (92bf2f95a9d174734b346f21)... OK!
# Decrypting files/Demon.png (e2b00708877d5ef45c82e286)... OK!
# Finished on Wed Jun 24 20:59:25 2020
得到 flag:
Shichirou
這題可以上傳一個檔案,上傳後的檔案會用 tar 解開並放在一層目錄下,之後會用 sha1 檢驗解壓出來的 guess.txt 看有沒有跟 flag.txt 一樣
1 | #!/usr/bin/env python3 |
想法:sha1sum 可以接 symlink 的檔案,所以可以讓 guess.txt 指向上一層的 flag.txt
1 | $ ls -al |
然後把 guess.txt 用 tar 包起來就好
1 | #!/usr/bin/env python3 |
Soy

這題就給一個有損壞的 QR Code,用工具還原
- 這邊有幾篇不錯的資源
Saburo
這題是給你一個伺服器位置,連上去後會叫你輸入 flag 的字元,如果字元正確的話數字會比較大,錯誤則會比較小,且數字會浮動,所以要寫腳本
1 | #!/usr/bin/env python3 |
- p.s.
- pwntools 的腳本用指令列執行加上
SILENT=1可以關掉 logger- e.g.
./exp.py SILENT=1
- e.g.
- pwntools 的腳本用指令列執行加上
Crypto
Brontosaurus
跟去年的解法一樣
T-Rex
這題題目是一張表,很明顯可以看出講下方那串依照上面這張表就可以對應出 flag
1 | ! @ # $ % & |
簡單寫個腳本就能得到 flag
Octopus
這題給了一個 py 檔,裡頭實作簡單模擬了 BB84 量子密鑰分發協定,但是 key_exchange 的部分被挖掉了
想法:看懂 BB84 ,並實作就可拿到 flag
1 | #!/usr/bin/env python3 |
如果你覺得這篇文章很棒,請你不吝點讚 (゚∀゚)