ret2syscall

Set register values => call "int 0x80" (x86) or "syscall" (x86_64)

Theory

If the binary itself contains int 0x80 (x86) or syscall (x86_64) as well as necessary ROP gadgets, you might want to try ret2syscall to get a shell. To find such gadgets, use ROPgadget or Ropper. With this method, you simply build an ROP chain to set all relevant registers to some proper state and call int 0x80 (x86) or syscall (x86_64) when everything is ready. This idea is the same as an execve() shellcode.

Below is a quick cheat sheet. This cheat sheet provides a general idea of how ret2syscall is exploited, but it is definitely NOT the only way of doing it. If any gadget is missing from the binary, use your creativity to build equivalent ROP chains.

32-bit ret2syscall

Calling Convention

  • syscall number: $eax

  • 1st parameter: $ebx

  • 2nd parameter: $ecx

  • 3rd parameter: $edx

  • 4th parameter: $esi

  • 5th parameter: $edi

  • 6th parameter: $ebp

  • call int 0x80 when everything is ready.

Calling execve("/bin/sh", 0, 0)

  1. Set $eax to 0xb

    • ROPgadget --binary vuln --only "pop|ret" | grep eax

  2. Set $ebx to the address of the string "/bin/sh"

    • ROPgadget --binary vuln --only "pop|ret" | grep ebx

    • ROPgadget --binary vuln --string "/bin/sh"

  3. Set $ecx to 0

    • ROPgadget --binary vuln --only "pop|ret" | grep ecx

  4. Set $edx to 0

    • ROPgadget --binary vuln --only "pop|ret" | grep edx

  5. Call int 0x80 (opcode for syscall on x86)

    • ROPgadget --binary vuln --only "int"

64-bit ret2syscall

Calling Convention

  • syscall number: $rax

  • 1st parameter: $rdi

  • 2nd parameter: $rsi

  • 3rd parameter: $rdx

  • 4th parameter: $r10

  • 5th parameter: $r8

  • 6th parameter: $r9

  • call syscall when everything is ready.

Note: If there exists more arguments, the extra ones will be stored on the stack.

Calling execve("/bin/sh", 0, 0)

  1. Set $rax to 0x3b

    • ROPgadget --binary vuln --only "pop|ret" | grep rax

  2. Set $rdi to the address of the string "/bin/sh"

    • ROPgadget --binary vuln --only "pop|ret" | grep rdi

    • ROPgadget --binary vuln --string "/bin/sh"

  3. Set $rsi to 0

    • ROPgadget --binary vuln --only "pop|ret" | grep rsi

  4. Set $rdx to 0

    • ROPgadget --binary vuln --only "pop|ret" | grep rdx

  5. Find syscall

    • ROPgadget --binary vuln --only "syscall"

Nuance

Oftentimes, the binary does not contain the string "/bin/sh". If that is the case, we should pass "/bin/sh" to the .bss section before exploiting ret2syscall. The address of .bss can be easily found using Pwntools: bss = elf.bss(). The binary may contain some input function, such as gets(). We can utilize input functions such as gets() for initializing a STDIN session and then input the string "/bin/sh" in this STDIN session.

Last updated