###### tags: `CTF Write Up` # 2019 AIS3 EOF final ## Reverse1: vault 這題考的是 webassembly 網頁利用 js call 到 assembly 的過程如下 ``` result = Module.ccall("k", "string", ["string"], [p455w0rd]) | var ret = func.apply(null, cArgs); | var func = getCFunc(ident); | Module["_k"] = Module["asm"]["e"] | Module["asm"] = asm; | asm = createWasm() ``` trace 到這邊後可以知道 Module["asm"] 是 wasm 的 handler, 所以上面的Module["asm"]["e"] 就是 call wasm 的 e 函數,並且會帶一個參數 接著 wasm 這個語言在 reverse 上麻煩的是能對他 decompile 的工具少之又少,看了一陣子[文件](https://www.pnfsoftware.com/reversing-wasm.pdf),雖然勉強看得懂但是程式量實在太大,最後終於找到超神工具 jeb 利用他的demo版本順利 decompile wasm ![](https://i.imgur.com/AOUYDZQ.png) decompile 後其實程式邏輯蠻簡單的,好幾個判斷條件,利用那些判斷條件就可以湊出 15 位的密碼 `mtQQXXqN7iUqFi1` 將密碼輸入 flag 就會噴在console log 了 ![](https://i.imgur.com/X9Paqrg.png) 最後稍微放一下看文件的筆記 ``` 常用的 wasm get_local => push set_local => pop I32.LE_S=> pops two operands, does a signed less-than-or-equals comparison, and pushes the result onto the stack ``` ## Reverse2: gift 題目是一包gz file 解開後會有一個elf file ,做的事情就是單純比對使用者輸入字串,比對成功就吐一大串payload,看頭幾個字可以發現是gz ㄑfile 解開還是elf file 也做同樣的比對 馬上猜測是類似之前在 myfirstctf 解過的 `Russian doll`,那時候想說用手動一個一個拆結果沒解出來,於是這次果斷寫腳本去 run 整體流程 1. 解開gz 得到 elf 2. 抓出特定位置的密碼 3. 執行程式並輸入密碼 4. 抓取程式output的 payload 5. 這個payload是一個 gz file ,回到1. 不斷循環 到第1003次後就會看到 flag 了 ## Reverse4: compiler 這題我沒有解出來,看看之後會不會給官方解法,所以下面講的都只是我的想法而已 先來說說我的發現,這題是 lex + yacc 所編譯出來的compiler,先看yylex 的 graph 可以發現右側那塊蠻不尋常的 #### yylex() ![](https://i.imgur.com/ZDXhQDA.png) 每個小方格都是長下面這樣,只有傳進 eax 的值會不同,跳上去的 address 是 return address 的地址 跳回 yyparse ``` -------------------------------- .text:00000000000087EF mov eax, 11Dh .text:00000000000087F4 jmp loc_8B12 -------------------------------- .text:00000000000087F9 mov eax, 11Eh .text:00000000000087FE jmp loc_8B12 -------------------------------- ``` 至於這先小方格是從這邊跳轉出去的,這個 dword_325BC 是找就寫好的一大串list,利用var_48 做位移來達到跳到不同區段 ``` .text:000000000000854A cmp [rbp+var_48], 2Ch .text:000000000000854E ja loc_8AFA .text:0000000000008554 mov eax, [rbp+var_48] .text:0000000000008557 lea rdx, ds:0[rax*4] .text:000000000000855F lea rax, dword_325BC .text:0000000000008566 mov eax, [rdx+rax] .text:0000000000008569 cdqe .text:000000000000856B lea rdx, dword_325BC .text:0000000000008572 add rax, rdx .text:0000000000008575 jmp rax ``` 這邊可以提一下,透過正規方式compile 出來的程式很少會出現 jmp 一個 暫存器位址,所以這邊相當可疑,但由於沒有特定某個小方格是跳到別的地方的所以估計後門不是在這邊 #### yyparse 一樣右側會發現有一大串不尋常的跳轉區 ![](https://i.imgur.com/g4DGi71.png) 但這次發現有個疑似後門的地方,在某一個跳轉格中出現了會 print hacked!的字樣,合理判斷是要讓 parser 能夠跳到這邊來就可以了 ![](https://i.imgur.com/IU3oVrY.png) 能跳到各個方塊的起始程式碼如下,利用 var_7C8 做偏移 ``` .text:0000000000006619 cmp [rbp+var_7C8], 74h ; 't' .text:0000000000006620 ja loc_7E76 .text:0000000000006626 mov eax, [rbp+var_7C8] .text:000000000000662C lea rdx, ds:0[rax*4] .text:0000000000006634 lea rax, dword_31B64 .text:000000000000663B mov eax, [rdx+rax] .text:000000000000663E cdqe .text:0000000000006640 lea rdx, dword_31B64 .text:0000000000006647 add rax, rdx .text:000000000000664A jmp rax ``` 經過一些追蹤反推對應需要跳到這個address,如附圖會在`0x555555585b84` 的位置,也就是說 var_7C8 要是 0x8 就會跳到後門 ![](https://i.imgur.com/IwxQMBE.png) 所以我推測的解題邏輯如下 1. 從 parser 端反推所需要匹配的規則 2. 推出規則後就可以知道要輸入哪些token 3. 再從lex 推出 這些token 對應的是哪些字串 4. 最後利用刻意構造的input 去跳到 backdoor 但跳上去後是否就會 print 出 flag 就不得而知了,這大概是我想到可能的解法了 後記: 這題沒有出題者 writeup ## Misc 3: unlucky 這是蠻有趣的一題,由於我的戰略是狂解reverse 所以沒打算看,結果一看發現用到的技術蠻像最近在玩的 runELF-on-Memory 所以就來解解看 連進 server 是一個 python 程式,程式流程如下 1. 一開始會建立一個檔案,並寫入一串亂碼最後把檔案刪除,但沒有關閉 fd 2. 利用 popen 給使用者一個輸入指令的機會 3. 讓使用者猜剛剛寫進檔案的亂碼是甚麼,猜對就有 shell 可以用 我的解題流程 - 觀察 server 不尋常物件 - get shell - 提權 與 format string ### 觀察 利用python 所提供的一次指令執行檢查server中有甚麼檔案 - readflag: 是一個 binary 並且有附上 source code,看起來是用來讀flag的,但是他只會readflag 不會顯示出來 - flag: 在根目錄下有一個 flag 檔案,也就是我們的目標,權限是只有 root 能讀 到這邊大概可以知道這題的想法,get shell 後因為還不是 root 因此還要設法通過 readflag 這隻程式去提權拿flag。 ### get shell get shell 這關最大的重點是,linux 能夠利用 memfd_create 去創立一個沒有實質存在於目錄中只存在於記憶體中的 file descripter,而此題刪除掉的 file 也是同樣道理,我們也能利用他的fd 去讀原本檔案內容 我們可以利用 `/proc/<pid>/fd/<fd number>` 去讀一個 process 所開啟的 fd,所以這邊在python 可以直接打上 `cat /proc/<pid>/fd/3` 就可以成功得到那串亂碼 ### binary format string 稍微看一下 source 就能發現他有一個設計好的 format string 可以用,故意讓你無法透過一行指令得到 flag 所以才設計的吧。透過 %8$p %9$p ...就可以leak flag 了 #### 奇異解 (別隊提供) 直接在python 輸入command 指令 `bash -c '%1$p'|&/r*` ## 官方 writeup https://github.com/BookGin/my-ctf-challenges#ais3-eof-ctf-2019-finals https://github.com/how2hack/my-ctf-challenges/tree/master/eof_finalctf-2020 https://github.com/yuawn/CTF/tree/master/2020/eof-final