去年也參加過 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.6
libchook.so
system()
被 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.json
1
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 |
如果你覺得這篇文章很棒,請你不吝點讚 (゚∀゚)