HKCERT CTF 2022: Writeup on ★☆☆☆☆ (I)
===
## 美麗都大廈 / spyce (Web)
1. 打開網站
2. 隨便點一個頁面,例如 "Hello, world"
3. 該頁面顯示 "Hello from Spyce version 2.1!",下面有個連結顯示 "Source for this page",按它。
4. 網址的 query string 部份顯示 `path=%2Fhome%2Fspwnce%2Fwww%2Fdocs%2Fexamples%2Fhello.spy`
5. 將此 query string 部份改成 `path=/flag1`,然後瀏覽此網址
6. 拿到旗幟了!
## 海山樓 / Flawed ElGamal (Crypto)
### 題目大意
當玩家連線到伺服器時,他們都會收到一個被加密的旗幟 $(c_1, c_2)$。以下是用作加密的公鑰:
```python
p = 1444779821068309665607966047026245709114363505560724292470220924533941341173119282750461450104319554545087521581252757303050671443847680075401505584975539
g = 2
h = 679175474187312157096793918495021788380347146757928688295980599009809870413272456661249570962293053504169610388075260415234004679602069004959459298631976
```
根據原始碼,密文是由一個有缺陷的 ElGamal 算法加密而成的。它會根據以下步驟算出密文 $(c_1, c_2)$:
1. 產生一個隨機的 $y \in [1, p-1]$,
2. 計算 $s := h^y \ \text{mod}\ p$,
3. 計算 $c_1 := g^y\ \text{mod}\ p$,
4. 計算 $c_2 := m \cdot s$。
我們的目標是解密密文,並取得旗幟 $m$。
### 題解
在我們的 ElGamal 算法裡面, $c_2 := m \cdot s$ 並沒有取模數。因此 $c_2$ 將會是 $m$ 的倍數。如果我們多次連線到伺服器的話,我們將可得到數個密文 $(c_1, c_2)$、$(c_1', c_2')$ 等等。由於 $c_2, c_2', ...$ 是 $m$ 的倍數,所以
$$m = \gcd(c_2, c_2', ...).$$
我們可以用歐幾里德算法來計算兩個數字的最大公倍數,其 Python 程式碼如下:
```python
def gcd(a, b):
while b != 0:
a, b = b, a % b
return a
```
我們亦可使用下列關係來算出 $a, b, ..., c$ 之最大公倍數:
$$\gcd(a, b, ..., c) = \gcd(...\gcd(\gcd(a, b), ...), c).$$
得出 $m$ 後,我們要把它變成一個字串。由 `chall.py` 得知,旗幟是以 `m = int.from_bytes(flag, 'big')` 這一行來轉換成一個數字的。你可以研究一下怎麼使用 Python 的 [`int.to_bytes`](https://docs.python.org/3/library/stdtypes.html#int.to_bytes) 來把數字變回來。
:::danger
**🏁 我把 $m$ 變成一個字串了,可是旗幟呢?** 那代表你所得到的 `m` 不正確。這很大機會是因為你所得到的數字還不等於 $m$,而是 $m$ 的一個(很小的)倍數。你可以獲取更多的 $c_2$ 來計算最大公倍數,從而算出一個正確的 $m$。
:::
## 沙田大會堂 / echo (Pwn)
1. 理解源碼
- 程式會讀取使用者的輸入然後打印出來。
- 不斷重複直至使用者輸入"--"而且全局變數`can_leave`不是0。 (但看完源碼以後總覺得`can_leave`一定是0?)
2. 尋找漏洞
- `scanf("%s", buf)`不會去限制使用者輸入的字數,令程式弱於`Buffer overflow attack`。
- `printf(buf)`看上去很奇怪,因為通常在c語言中打印字串會是`printf("%s", buf)`。
- 如果使用者能夠控制printf函數的第一個參數的內容,使用者可以用`Format string attack`攻擊程式。
- `Format string attack`可以達到`任意內存讀取`和`任意內存寫入`的效果.
3. 檢查程式的保護
- 在gef裡面運行`Checksec`以檢查程式開啟的保護。
- 由於PIE(Position Independent Executable)開啟了,`get_shell`函數的內存地址是隨機的。
- 由於Canary開啟了,除非能知道canary的值,否則`Buffer overflow attack`是行不通的。
4. 攻擊
- 利用`Format string attack`去洩露一個PIE地址(指一個受PIE影響到地址),然後計算出`get_shell`函數的內存地址。 (洩露出來的地址跟`get_shell`函數地址之間的差是固定的)
- 利用`Format string attack`去洩露canary的值。
- 利用`Format string attack`去覆蓋`can_leave`的值,因此程式可以離開while循環以及main函數 (以觸發`Buffer overflow attack`)。
- 利用`Buffer overflow attack`去覆蓋main函數‘返回地址’的值到`get_shell`函數的地址。
5. 漏洞利用
- 寫個python腳本解題。安裝pwntools能給你很多解pwn題時有用的函數。 (pwntools相關的用法可以參考這年/去年底hkcert ctf賽前工作坊)
- 理解第4步描述的攻擊,寫出有效的攻擊載荷(payloads)
- 目標是調用`get_shell`函數去獲取shell和cat旗幟。
- 這是給你起手用的解題腳本的骨架:
```python
from pwn import *
host = ??
port_number = ??
io = remote('<host>', port_nubmer)
elf = ELF('./chall')
# leak canary
payload0 = ??
io.sendlineafter(b'\nInput:\n', payload0)
# now the canary is leak on screen, script to get the canary value
canary = ??
# leak address
payload1 = ??
io.sendlineafter(b'\nInput:\n', payload1)
# now the address is leak on screen, script to get the leaked address
leaked_address = ??
pie_base = leaked_address - fixed_offset
canLeave = pie_base + elf.symbols['can_leave']
getShell = pie_base + elf.symbols['get_shell']
# overwrite canLeave to non-Zero
payload2 = ??
io.sendlineafter(b'\nInput:\n', payload2)
# Calling system (in get_shell function) requires stack be aligned.
# Add ret_gadget (to add rsp by 8) before calling get_shell to align the stack.
ret_gadget = getShell + 25
len_of_input_before_canary = ??
payload3 = b'a' * len_of_input_before_canary + p64(canary) + p64(0), + p64(ret_gadget), p64(getShell)
io.sendlineafter(b':\n', payload3)
io.sendlineafter(b':\n', b'--')
io.interactive()
```
## 米埔自然護理區 / Fiddler Crab (Reverse)
可以用好多方法黎做呢條題目, 但係最簡單就應該係睇隻game既 network traffic黎感覺下佢入面到底係做緊咩.
我地可以setup 一個proxy 黎偷睇中間既所有traffic, 甚至可以用黎改入面既內容.
件事係, 你setup 一個係game server 問埋中間既proxy server, game client 就連去個proxy, proxy就連過去game server. 咁個proxy就可以睇曬兩邊傳送出黎既data, 又或者send 俾對面之前改左個內容佢.
你可以用求其一個proxy/mitm工具黎做, 如果係HTTP request 既話, 你可以用`mitmproxy`, 但係依家用TCP proxy會好d (因為`mitmproxy` 會好難setup non TLS traffic)
其中一個TCP proxy 會係 [tcpproxy](https://github.com/ickerwx/tcpproxy).
setup 類似係咁:
```
python tcpproxy.py -ti <ip> -tp <port> -lp 8000 -im textdump -om textdump
```
咁你就可以直接用chess client 黎connect去個proxy:
```
./chess_client 127.0.0.1:8000
```
係proxy 果邊就會睇到啲traffic data, 你會睇到個client send 啲咩俾server, server 又回應啲咩俾個client.
你都可以用`tcpproxy`黎改啲traffic data, 好似咁
```
python tcpproxy.py -ti <ip> -tp <port> -lp 8000 -im replace:search=<SEARCH>:replace=<REPLACE> -om textdump
```
想知點config 就上github睇下佢地啲source code
你試下改唔同既野就可能會發現唔係下下改咩都會得. 要點改先會唔炒? (可能關於頭果幾古怪bytes?)
依家你應該可以用改野黎出到千. 祝你好運!
## 珍寶海鮮舫 / SD Card (Forensics)
### 題解
下載 `sdcard.zip`, 解壓縮並找到 `sdcard.dd`. 把它放入數位鑑識工具當中。
在以下的 writeup 中,我將使用 [Autopsy](https://www.autopsy.com/download/) 作爲示範。你應找到在 `$CarvedFiles` 之中找到 `f0000000.png`。
![](https://i.imgur.com/J4ZYqND.png)
Autopsy 亦提供副檔名的搜尋功能。使用此方法則會找到 `_lag.png`。
![](https://i.imgur.com/Nsz17Lb.png)
`f0000000.png` 與 `_lag.png` 爲同一照片。我匯出 `f0000000.png` 並繼續進行調查。
:::info
若你不打算使用 Autopsy 並使用其他工具,照片名稱可能會有差異。
:::
我們直接打開照片但沒有看到什麼。
![](https://i.imgur.com/ltd47JX.png)
同時可交換圖檔格式中的資料亦與檔案大小不符。
![](https://i.imgur.com/21rgcK9.png)
我們可以在以下方向繼續調查:
* 隱寫術
* 修復損壞照片
由於檔案副檔名爲 PNG, 我們決定以修復損壞照片作爲調查方向。 你可以在下方閱讀更多有關 PNG 數據塊結構的資料:
![](https://raw.githubusercontent.com/corkami/pics/master/binary/PNG.png)
[PNG](https://github.com/corkami/pics/blob/master/binary/PNG.png) © corkami, CC BY-NC-SA 2.0
我們決定將 `f0000000.png` 放入 [PNG file chunk inspector](https://www.nayuki.io/page/png-file-chunk-inspector)。發現有多個 IHDR 數據塊結構。
![](https://i.imgur.com/vFhItMf.png)
可是一個正常的 PNG 檔案只有一個 IHDR 數據塊。我們可以使用任意的十六進位編輯器修正照片 `f0000000.png`。
我使用 [HexEd.it](https://hexed.it/) 修正照片。你亦可使用 HxD 修正。
![](https://i.imgur.com/2nJUn3Y.png)
在 PNG file chunk inspector 中我們得知我們可以移除以下其中之一的 IHDR 數據塊:
* 位元 8 - 32 (0x00000008 - 0x00000020)
* 位元 33 - 57 (0x00000021 - 0x00000039)
第一個 IHDR 數據塊 (位元 8 - 32) 顯示照片只有 1 像素。這個數據塊令人生疑,所以我們先嘗試把它去除。一個 IHDR 數據塊應爲 25 位元。
![](https://i.imgur.com/8634bH8.png)
當你祓除第一個 IHDR 數據塊後,儲存照片並嘗試把它打開。你現在應該可以正常地瀏覽 PNG 照片。
![](https://i.imgur.com/ARHFdtr.png)
以下只是旗幟的一部分。現在嘗試自行解一次題吧!
![](https://i.imgur.com/fr5OuKt.png)