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/basic-stack-binary-exploitation-methodology/elf-tricks.md
+15-3Lines changed: 15 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -218,6 +218,11 @@ Each symbol entry contains:
218
218
-**Value** (address sin memory)
219
219
-**Size**
220
220
221
+
#### GNU IFUNC (indirect functions)
222
+
223
+
- GCC can emit `STT_GNU_IFUNC` symbols with the `__attribute__((ifunc("resolver")))` extension. The dynamic loader calls the resolver at load time to select the concrete implementation (commonly CPU dispatch).
#### GNU Symbol Versioning (dynsym/dynstr/gnu.version)
222
227
223
228
Modern glibc uses symbol versions. You will see entries in `.gnu.version` and `.gnu.version_r` and symbol names like `strlen@GLIBC_2.17`. The dynamic linker can require a specific version when resolving a symbol. When crafting manual relocations (e.g. ret2dlresolve) you must supply the correct version index, otherwise resolution fails.
- Modern linkers can emit compact **relative** relocations with `-z pack-relative-relocs`. This adds `DT_RELR`, `DT_RELRSZ`, and `DT_RELRENT` entries to the dynamic section for PIEs/shared libraries (it is ignored for non-PIE executables).
If the **program is loaded in a place different** from the preferred address (usually 0x400000) because the address is already used or because of **ASLR** or any other reason, a static relocation **corrects pointers** that had values expecting the binary to be loaded in the preferred address.
@@ -420,11 +430,11 @@ Note that these global variables are located in `.data` or `.bss` but in the lis
420
430
From C code it's possible to obtain the same result using the GNU extensions :
421
431
422
432
```c
423
-
__attributte__((constructor)) //Add a constructor to execute before
424
-
__attributte__((destructor)) //Add to the destructor list
433
+
__attribute__((constructor)) //Add a constructor to execute before
434
+
__attribute__((destructor)) //Add to the destructor list
425
435
```
426
436
427
-
From a compiler perspective, to execute these actions before and after the `main` function is executed, it's possible to create a `init` function and a `fini` function which would be referenced in the dynamic section as **`INIT`** and **`FIN`**. and are placed in the `init` and `fini` sections of the ELF.
437
+
From a compiler perspective, to execute these actions before and after the `main` function is executed, it's possible to create a `init` function and a `fini` function which would be referenced in the dynamic section as **`INIT`** and **`FINI`**. and are placed in the `init` and `fini` sections of the ELF.
428
438
429
439
The other option, as mentioned, is to reference the lists **`__CTOR_LIST__`** and **`__DTOR_LIST__`** in the **`INIT_ARRAY`** and **`FINI_ARRAY`** entries in the dynamic section and the length of these are indicated by **`INIT_ARRAYSZ`** and **`FINI_ARRAYSZ`**. Each entry is a function pointer that will be called without arguments.
430
440
@@ -491,6 +501,8 @@ Leaking `AT_RANDOM` gives you the canary value if you can dereference that point
491
501
492
502
## References
493
503
504
+
- GCC Common Function Attributes (ifunc / STT_GNU_IFUNC): https://gcc.gnu.org/onlinedocs/gcc-14.3.0/gcc/Common-Function-Attributes.html
505
+
- GNU ld `-z pack-relative-relocs` / `DT_RELR` docs: https://sourceware.org/binutils/docs/ld.html
Copy file name to clipboardExpand all lines: src/binary-exploitation/libc-heap/house-of-roman.md
+26-15Lines changed: 26 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,13 @@
4
4
5
5
## Basic Information
6
6
7
-
This was a very interesting technique that allowed for RCE without leaks via fake fastbins, the unsorted_bin attack and relative overwrites. However it has ben [**patched**](https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c).
7
+
This was a very interesting technique that allowed for RCE without leaks via fake fastbins, the unsorted_bin attack and relative overwrites. However it has been [**patched**](https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c).
8
+
9
+
### Applicability in 2026
10
+
11
+
-**glibc window:** Works reliably on **2.23–2.28**. On **2.29** the additional `unsorted_chunks` integrity checks make the unsorted‑bin write unreliable, so success drops sharply. From **2.34** onward `__malloc_hook/__free_hook` were removed, making the original target unavailable. Use it only on old libc’s (or custom builds that keep the hooks) or for CTF challenges that ship an old libc.
12
+
-**Tcache era (≥2.26):** Tcache will eat your 0x70 allocations and stop the fastbin/unsorted primitives. Disable it (`setenv("GLIBC_TUNABLES","glibc.malloc.tcache_count=0",1);`) **before** any allocation or fill each 0x70 tcache bin with 7 frees to drain it.
13
+
-**Safe-linking:** It applies to tcache/fastbin in ≥2.32, but House of Roman only needs **partial pointer overwrite of a libc address already present in fd/bk**, so safe-linking does not help the defender here (the attacker never forges a fresh pointer). The real stopper is the hook removal and the unsorted-bin checks.
8
14
9
15
### Code
10
16
@@ -46,24 +52,24 @@ Current heap layout:
46
52
0x190: relative_offset_heap - size 0x70
47
53
48
54
bin layout:
49
-
fastbin: fastbin_victim -> relative_offset_heap
50
-
unsorted: leftover_main
55
+
fastbin: fastbin_victim -> relative_offset_heap
56
+
unsorted: leftover_main
51
57
*/
52
58
```
53
59
54
60
-`fastbin_victim` has a `fd` pointing to `relative_offset_heap`
55
61
-`relative_offset_heap` is an offset of distance from `fake_libc_chunk`, which contains a pointer to `main_arena + 0x68`
56
-
-Just changing the last byte of `fastbin_victim.fd`it's possible to make `fastbin_victim points`to `main_arena + 0x68`
62
+
-Changing the last byte of `fastbin_victim.fd`makes `fastbin_victim` point to `main_arena + 0x68`.
57
63
58
64
For the previous actions, the attacker needs to be capable of modifying the fd pointer of `fastbin_victim`.
59
65
60
-
Then, `main_arena + 0x68` is not that interesting, so lets modify it so the pointer points to **`__malloc_hook`**.
66
+
Then, `main_arena + 0x68` is not that interesting, so let's modify it so the pointer points to **`__malloc_hook`**.
61
67
62
-
Note that `__memalign_hook` usually starts with `0x7f` and zeros before it, then it's possible to fake it as a value in the `0x70` fast bin. Because the last 4 bits of the address are **random** there are `2^4=16` possibilities for the value to end pointing where are interested. So a BF attack is performed here so the chunk ends like: **`0x70: fastbin_victim -> fake_libc_chunk -> (__malloc_hook - 0x23)`.**
68
+
Note that `__memalign_hook` usually starts with `0x7f` and zeros before it, then it's possible to fake it as a value in the `0x70` fast bin. Because the last 4 bits of the address are **random** there are `2^4=16` possibilities for the value to end pointing where we are interested. So a BF attack is performed here so the chunk ends like: **`0x70: fastbin_victim -> fake_libc_chunk -> (__malloc_hook - 0x23)`.**
63
69
64
-
(For more info about the rest of the bytes check the explanation in the [how2heap](https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_roman.c)[ example](https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_roman.c)). If the BF don't work the program just crashes (so start gain until it works).
70
+
(For more info about the rest of the bytes check the explanation in the [how2heap](https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_roman.c)[ example](https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_roman.c)). If the brute force fails the program just crashes (restart until it works).
65
71
66
-
Then, 2 mallocs are performed to remove the 2 initial fast bin chunks and the a third one is alloced to get a chunk in the **`__malloc_hook:`**
72
+
Then, 2 mallocs are performed to remove the 2 initial fast bin chunks and a third one is allocated to get a chunk in **`__malloc_hook`**.
But basically it allows to write `main_arena + 0x68` to any location by specified in `chunk->bk`. And for the attack we choose `__malloc_hook`. Then, after overwriting it we will use a relative overwrite) to point to a `one_gadget`.
88
+
But basically it allows to write `main_arena + 0x68` to any location specified in `chunk->bk`. For the attack we choose `__malloc_hook`. Then, after overwriting it we will use a relative overwrite to point to a `one_gadget`.
84
89
85
90
For this we start getting a chunk and putting it into the **unsorted bin**:
86
91
@@ -102,20 +107,26 @@ So, to trigger the write of `main_arena + 0x68` in `__malloc_hook` we perform af
102
107
103
108
### Step 3: Set \_\_malloc_hook to system
104
109
105
-
In the step one we ended controlling a chunk containing `__malloc_hook` (in the variable `malloc_hook_chunk`) and in the second step we managed to write `main_arena + 0x68`in here.
110
+
In step one we controlled a chunk containing `__malloc_hook` (in the variable `malloc_hook_chunk`) and in the second step we managed to write `main_arena + 0x68`there.
106
111
107
-
Now, we abuse a partial overwrite in `malloc_hook_chunk` to use the libc address we wrote there(`main_arena + 0x68`) to **point a `one_gadget` address**.
112
+
Now, we abuse a partial overwrite in `malloc_hook_chunk` to use the libc address we wrote there(`main_arena + 0x68`) to **point to a `one_gadget` address**.
108
113
109
114
Here is where it's needed to **bruteforce 12 bits of randomness** (more info in the [how2heap](https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_roman.c)[ example](https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_roman.c)).
110
115
111
-
Finally, one the correct address is overwritten, **call `malloc` and trigger the `one_gadget`**.
116
+
Finally, once the correct address is overwritten, **call `malloc` and trigger the `one_gadget`**.
117
+
118
+
## Modern tips & variants
119
+
120
+
-**Unsorted-bin check in 2.29+:** If you must run on 2.29–2.33, corrupt both `fd`**and**`bk` to satisfy the integrity check before triggering the write; otherwise `_int_malloc` aborts. Success rate is very low and usually only viable in brute-force CTF settings.
121
+
-**Hook removal (2.34+):** With `__malloc_hook` gone, adapt the primitive to land on any writable GOT/global you can later reuse (e.g., overwrite `exit@GOT` in non-PIE binaries) or pivot to a **House of Pie** style top‑chunk hijack to control `top` instead of a hook.
122
+
-**Any‑address fastbin alloc (2024 gist):** A recent writeup shows reusing the same grooming to fastbin‑allocate over `__free_hook` or other globals by first landing a libc pointer in fastbin and then re‑pointing it before the fixup. This works on 2.24–2.28 but still dies on 2.29 integrity checks.
-j request.json --function system -p "bash -c 'id'"
76
76
```
77
77
78
78
The tool parses the captured snapshot, injects the gadget tuples, recomputes the checksum, and prints a ready-to-send `/livewire/update` payload.
79
79
80
80
## CVE-2025-54068 – RCE without `APP_KEY`
81
81
82
+
According to the vendor advisory, the issue affects Livewire v3 (>= 3.0.0-beta.1 and < 3.6.3) and is unique to v3.
83
+
82
84
`updates` are merged into component state **after** the snapshot checksum is validated. If a property inside the snapshot is (or becomes) a synthetic tuple, Livewire reuses its meta while hydrating the attacker-controlled update value:
83
85
84
86
```php
@@ -95,6 +97,21 @@ Exploit recipe:
95
97
96
98
1. Find a Livewire component with an untyped public property (e.g., `public $count;`).
97
99
2. Send an update that sets that property to `[]`. The next snapshot now stores it as `[[], {"s": "arr"}]`.
100
+
101
+
A minimal type-juggling flow looks like this:
102
+
103
+
```http
104
+
POST /livewire/update
105
+
...
106
+
"updates": {"count": []}
107
+
```
108
+
109
+
Then the next snapshot stores a tuple that keeps the `arr` synthesizer metadata:
110
+
111
+
```json
112
+
"count": [[], {"s": "arr"}]
113
+
```
114
+
98
115
3. Craft another `updates` payload where that property contains a deeply nested array embedding tuples such as `[ <payload>, {"s":"clctn","class":"GuzzleHttp\\Psr7\\FnStream"} ]`.
99
116
4. During recursion, `hydrate()` evaluates each nested child independently, so attacker-chosen synth keys/classes are honoured even though the outer tuple and checksum never changed.
100
117
5. Reuse the same `CollectionSynth`/`FormObjectSynth` primitives to instantiate a Queueable gadget whose `$chained[0]` contains the phpggc payload. Livewire processes the forged updates, invokes `dispatchNextJobInChain()`, and reaches `system(<cmd>)` without knowing `APP_KEY`.
@@ -112,6 +129,7 @@ Key reasons this works:
112
129
- Fingerprints the deployed Livewire version by parsing `<script src="/livewire/livewire.js?id=HASH">` and mapping the hash to vulnerable releases.
113
130
- Collects baseline snapshots by replaying benign actions and extracting `components[].snapshot`.
114
131
- Generates either an `updates`-only payload (CVE-2025-54068) or a forged snapshot (known APP_KEY) embedding the phpggc chain.
132
+
- If no object-typed parameter is found in a snapshot, Livepyre falls back to brute-forcing candidate params to reach a coercible property.
0 commit comments