Seal’s Pwn Zero2Hero Stack Challenges - SROP

2 minute read

The 10th challenge from Seals PWN Zero2Hero


Source was provided.

; nasm vuln.nasm -f elf64
; ld vuln.o -o vuln -z noexecstack

global _start

section .text
_start:
    ;char buf[0x100]
    enter 0x100, 0

    call timer

    ; write(1, message, len)
    mov rax, 1
    mov rdi, 1
    mov rsi, message
    mov rdx, len
    syscall

    ; read(0, buf, 0x1000)
    mov rax, 0
    mov rdi, 0
    mov rsi, rsp
    mov rdx, 0x1000
    syscall

    ; epilogue
    leave
    ret

timer:
    ;alarm
    mov rdi, 0xf
    mov rax, 0x25
    syscall
    ret

section .data
    message: db "To pass this challenge you need to get a shell using /bin/sh"
    len: equ $-message

Gist is that we have some assembly code that prints To pass this challenge you need to get a shell using /bin/sh using the write syscall and reads in input using the read syscall

Vulnerability

The read syscall reads in 0x1000 bytes while our buffer is only 0x100. That makes this is a standard buffer overflow.

Exploitation

We’ll need to use the SROP (Sigreturn-Oriented Programming) technique to solve this challenge. To do that we’ll need:

  • A pop RAX gadget - will be used to set the syscall to 0xf (sigreturn)
  • A pointer to the /bin/sh string
  • A gadget for syscall

Once we find all those we can start building out our exploit.

The first issue we run into though, is that there is no pop RAX gadget. To mitigate this we could use the read syscall and make it read 0xf bytes. Unfortunately, we end up overwritting the return address with the second read call and the final new line that gets sent causes sigbus errors. So Instead, we can use the alarm syscall because the return value is whatever the number of seconds remaining was for a previously set alarm. Otherwise, the return value is zero if there isn’t another alarm. We can use alarm syscall to set RAX because return values are stored in RAX for x64 architectures. To do this we’ll just jump to the alarm syscall initially to set RAX and then continue.

/bin/sh can be found using next(elf.search(b'/bin/sh') pretty much the same way as finding it in libc.

For the syscall gadget we can grab it using ROPGadget.

Now we just need the offset for controlling the instruction pointer which is 264.

dfce2e63b15bf800f3656bb86c993e35.png

Now that we have that, we need to contruct our sigreturn frame which will contain our execve syscall.

da9144a60de70403e815aff2681bcd48.png

Next we can start contructing our final exploit. We’ll be jumping to alarm first, calling it twice, and then call the sigreturn syscall

Solution

Running the script gets the flag.

a65050994fdd0e2cbbfe5babb710192f.png

69a6a539d014d051a34b08a9bf7ce057.png