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)