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/common-binary-protections-and-bypasses/pie/bypassing-canary-and-pie.md
+68-49Lines changed: 68 additions & 49 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,56 +18,61 @@ For example, if a binary is protected using both a **canary** and **PIE**, you c
18
18
> [!TIP]
19
19
> It's supposed that the return address inside the stack belongs to the main binary code, which, if the vulnerability is located in the binary code, will usually be the case.
20
20
21
-
To brute-force the RBP and the RIP from the binary you can figure out that a valid guessed byte is correct if the program output something or it just doesn't crash. The **same function** as the provided for brute-forcing the canary can be used to brute-force the RBP and the RIP:
21
+
This technique is specially useful when **each failed probe only kills the current worker but does not rerandomize the parent state** (for example, a `fork()`-per-connection server or a service that respawns workers without `execve()`). If you first need to brute-force the canary in that scenario, check [BF Forked & Threaded Stack Canaries](../stack-canaries/bf-forked-stack-canaries.md).
22
+
23
+
To brute-force the RBP and the RIP from the binary you can figure out that a valid guessed byte is correct if the program outputs something or it just doesn't crash. The **same primitive** used to brute-force the canary can be reused to leak the saved `RBP` and the saved `RIP`:
24
+
25
+
<details>
26
+
<summary>Python3 helper to brute-force the canary, saved RBP and saved RIP</summary>
If the target reads with `gets`/`fgets`-style functions, remember to remove terminators such as `\n` from the candidate alphabet. With `read`/`recv`, brute-forcing all byte values is usually fine.
75
+
71
76
The last thing you need to defeat the PIE is to calculate **useful addresses from the leaked** addresses: the **RBP** and the **RIP**.
72
77
73
78
From the **RBP** you can calculate **where are you writing your shell in the stack**. This can be very useful to know where are you going to write the string _"/bin/sh\x00"_ inside the stack. To calculate the distance between the leaked RBP and your shellcode you can just put a **breakpoint after leaking the RBP** an check **where is your shellcode located**, then, you can calculate the distance between the shellcode and the RBP:
@@ -77,23 +82,37 @@ INI_SHELLCODE = RBP - 1152
77
82
```
78
83
79
84
From the **RIP** you can calculate the **base address of the PIE binary** which is what you are going to need to create a **valid ROP chain**.\
80
-
To calculate the base address just do `objdump -d vunbinary` and check the disassemble latest addresses:
85
+
To calculate the base address, disassemble the binary and identify the **exact static offset of the return site** pointed to by the saved `RIP` (`objdump -d`, `r2 -A`, `gef`, `pwndbg`, etc.):
81
86
82
87
.png>)
83
88
84
-
In that example you can see that only **1 Byte and a half is needed**to locate all the code, then, the base address in this situation will be the **leaked RIP but finishing on "000"**. For example if you leaked `0x562002970ecf` the base address is `0x562002970000`
89
+
The **reliable** calculation is to subtract that static offset from the leaked runtime address:
85
90
86
91
```python
87
-
elf.address =RIP- (RIP&0xfff)
92
+
RET_OFFSET=0x13cf# example: instruction after the call to the vulnerable function
93
+
elf.address =RIP-RET_OFFSET
94
+
assert elf.address &0xfff==0
88
95
```
89
96
90
-
## Improvements
97
+
If the leaked `RIP` is known to belong to the **first executable page** of a small binary, page-aligning it can still be enough as a quick shortcut or sanity check. For example, if you leak `0x562002970ecf`, then the page containing that instruction starts at `0x562002970000`:
91
98
92
-
According to [**some observation from this post**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#extended-brute-force-leaking), it's possible that when leaking RBP and RIP values, the server won't crash with some values which aren't the correct ones and the BF script will think he got the good ones. This is because it's possible that **some addresses just won't break it even if there aren't exactly the correct ones**.
99
+
```python
100
+
page_base =RIP- (RIP&0xfff)
101
+
```
93
102
94
-
According to that blog post it's recommended to add a short delay between requests to the server is introduced.
Blindly treating **"no crash"** as **"correct byte"** is fragile for saved `RBP` and saved `RIP` values. In practice, the following tweaks make this attack much more reliable:
97
106
107
+
-**Use timeouts for saved `RBP` guesses**: a wrong value used by `leave; ret` may survive longer than a bad canary or a bad return address, so remote targets usually need a larger timeout than local tests.
108
+
-**Introduce a short delay between probes**: sending requests too quickly can leave many workers/processes around, fill memory, or accumulate `TIME_WAIT` sockets, creating false positives unrelated to the guessed byte.
109
+
-**Do not brute-force bytes you already know**: if disassembly shows that the target return site must end in a fixed tail such as `...e06`, brute-force only the randomized byte or nibble(s). On amd64, the low 12 bits inside the page are constant for a given return site.
110
+
-**Validate candidates more than once**: a wrong `RIP` can still return into valid code and print output. Requiring the same candidate to succeed several times, or validating it with a known stop gadget as in [BROP](../../rop-return-oriented-programing/brop-blind-return-oriented-programming.md), reduces false positives.
111
+
-**Re-check the stack delta after leaking `RBP`**: the distance from the leaked frame pointer to your controlled buffer can change with stack alignment, so measure that delta for the leaked frame layout instead of assuming a single constant.
0 commit comments