You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/binary-exploitation/rop-return-oriented-programing/ret2esp-ret2reg.md
+57-9Lines changed: 57 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,6 +21,32 @@ jmp rsp
21
21
22
22
And write the shellcode early in the stack.
23
23
24
+
### Finding `jmp/call esp/rsp` gadgets
25
+
26
+
On modern challenges it is common to automate this search first and only then start placing shellcode. Some practical options are:
27
+
28
+
```python
29
+
from pwn import*
30
+
31
+
elf = ELF('./vuln')
32
+
rop = ROP(elf)
33
+
34
+
print(rop.jmp_esp) # i386
35
+
print(rop.jmp_rsp) # amd64
36
+
```
37
+
38
+
`pwntools` will also discard gadgets whose **address contains badchars**, which is useful when the instruction exists but cannot be used directly from the overflow.
39
+
40
+
You can also search more aggressively with `ROPgadget` because sometimes the binary does not contain a clean disassembled `jmp rsp`, but it still contains the raw opcode bytes inside some other executable instruction stream:
This is especially useful in amd64 because the opcode for **`jmp rsp`** is just **`ff e4`**, so any executable byte sequence with those bytes can become a valid landing point.
49
+
24
50
### Example
25
51
26
52
You can find an example of this technique in [https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp](https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp) with a final exploit like:
You can see another example of this technique in [https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html](https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html). There is a buffer overflow without NX enabled, it's used a gadget to r**educe the address of `$esp`** and then a `jmp esp;` to jump to the shellcode:
74
+
You can see another example of this technique in [https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html](https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html). There is a buffer overflow without NX enabled. The exploit uses a gadget to **reduce the address of `$esp`** and then a `jmp esp;` to jump to the shellcode:
49
75
50
76
```python
51
77
# From https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html
@@ -86,33 +112,51 @@ target.interactive()
86
112
87
113
Similarly, if we know a function returns the address where the shellcode is stored, we can leverage **`call eax`** or **`jmp eax`** instructions (known as **ret2eax** technique), offering another method to execute our shellcode. Just like eax, **any other register** containing an interesting address could be used (**ret2reg**).
88
114
115
+
Typical cases are functions returning the destination buffer in the return-value register, or code paths that keep a pointer to the attacker-controlled buffer in some argument/scratch register until the vulnerable `ret`.
116
+
117
+
### Hunting the register jump
118
+
119
+
On x86/x64 you usually search for **`jmp reg`** or **`call reg`** targeting the register that points to your bytes:
On ARM64 the same idea applies, but you are normally looking for **`br xN`** or **`blr xN`** gadgets instead:
126
+
127
+
```bash
128
+
ROPgadget --binary ./vuln --only "br|blr"
129
+
```
130
+
131
+
If the binary is protected with badchar restrictions, remember that the **gadget address** matters as much as the gadget mnemonic. A perfect `jmp rax` is useless if the address cannot be injected intact.
-**`strcpy`**will be store in **`eax`** the address of the buffer where the shellcode was stored and **`eax`**isn't being overwritten, so it's possible use a `ret2eax`.
139
+
-**`strcpy`**stores in **`eax`** the address of the buffer where the shellcode was stored and **`eax`**is not overwritten, so it is possible to use a `ret2eax`.
96
140
97
141
## ARM64
98
142
99
143
### Ret2sp
100
144
101
-
In ARM64 there **aren't** instructions allowing to **jump to the SP registry**. It might be possible to find a gadget that **moves sp to a registry and then jumps to that registry**, but in the libc of my kali I couldn't find any gadget like that:
145
+
In ARM64 there **aren't** instructions allowing to **jump directly to the SP register**. It might be possible to find a gadget that **moves sp to a register and then jumps to that register**, but in the libc of my kali I couldn't find any gadget like that:
We will use that gadget to jump to it because the binary is compile **WITHOUT PIE.** Using a pattern it's possible to see that the **offset of the buffer overflow is 80**, so the exploit would be:
202
+
We will use that gadget to jump to it because the binary is compiled **WITHOUT PIE.** Using a pattern it's possible to see that the **offset of the buffer overflow is 80**, so the exploit would be:
159
203
160
204
```python
161
205
from pwn import *
@@ -178,13 +222,17 @@ p.interactive()
178
222
179
223
## Protections
180
224
181
-
-[**NX**](../common-binary-protections-and-bypasses/no-exec-nx.md): If the stack isn't executable this won't help as we need to place the shellcode in the stack and jump to execute it.
182
-
-[**ASLR**](../common-binary-protections-and-bypasses/aslr/index.html) & [**PIE**](../common-binary-protections-and-bypasses/pie/index.html): Those can make harder to find a instruction to jump to esp or any other register.
225
+
-[**NX**](../common-binary-protections-and-bypasses/no-exec-nx.md): If the target memory is not executable, **ret2esp/ret2reg only gives you control-flow redirection**, not code execution. In modern exploits this is often combined with a previous `mprotect`/`VirtualProtect`-style stage or with already executable memory.
226
+
-[**ASLR**](../common-binary-protections-and-bypasses/aslr/index.html) & [**PIE**](../common-binary-protections-and-bypasses/pie/index.html): These make it harder to know the address of the final `jmp/call <reg>` gadget. Partial overwrites may still work when the gadget is close enough to the original return address.
227
+
-[**CET / Shadow Stack**](../common-binary-protections-and-bypasses/cet-and-shadow-stack.md): On x86_64, classic ret-based entry into `jmp esp` / `jmp rsp` / `jmp reg` gadgets becomes unreliable because the corrupted return address is checked against the hardware shadow stack before the gadget is reached.
228
+
-**ARM64 PAC/BTI**: Pointer Authentication can break the classic saved-LR overwrite path, and Branch Target Identification means `br xN` jumps are expected to land on a valid BTI landing pad (`bti j` / `bti jc`). A `br xN` gadget may exist but still fault on hardened binaries if the destination bytes are not a valid indirect-branch target.
0 commit comments