skills/binary-exploitation/basic-stack-binary-exploitation-methodology/elf-tricks/SKILL.md
Analyze ELF binary files for reverse engineering, security research, and exploitation. Use this skill whenever the user needs to understand ELF structure, analyze program headers, section headers, symbols, relocations, GOT/PLT, or identify binary protections like RELRO, stack canaries, and PIE. Trigger on any request involving ELF files, binary analysis, readelf output interpretation, or exploitation reconnaissance.
npx skillsauth add abelrguezr/hacktricks-skills elf-binary-analysisInstall 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.
A comprehensive guide for analyzing ELF (Executable and Linkable Format) binaries for reverse engineering, security research, and exploitation.
When analyzing an ELF binary, start with this reconnaissance sequence:
# Basic file info
file <binary>
# Program headers (memory layout, protections)
readelf -lW <binary>
# Section headers (detailed structure)
objdump -h <binary>
# Symbols (functions, variables)
readelf -sW <binary>
# Dynamic section (dependencies, relocations)
readelf -d <binary>
# Relocations (GOT/PLT entries)
readelf -r <binary>
Program headers tell the loader how to map the binary into memory. Use readelf -lW to view them.
| Type | Purpose | Exploitation Relevance |
|------|---------|------------------------|
| PHDR | Program header table itself | Contains metadata about the binary structure |
| INTERP | Dynamic loader path | Missing in static binaries; affects ret2dlresolve feasibility |
| LOAD | Memory segments to load | Shows memory layout, permissions (R/W/X), and sizes |
| DYNAMIC | Dynamic linking info | Contains NEEDED libraries, relocations, flags |
| NOTE | Metadata (build-id, properties) | May contain CPU features like CET (IBT/SHSTK) |
| GNU_EH_FRAME | Stack unwind tables | Used by debuggers, C++ exceptions |
| GNU_STACK | Stack execution flag | RW = executable stack (rare), R = non-executable |
| GNU_RELRO | Relocation read-only | Partial vs Full RELRO affects GOT overwrite attacks |
| TLS | Thread-local storage | Per-thread variable storage |
Each LOAD segment specifies:
Example interpretation:
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x003f7c 0x003f7c R E 0x10000
This segment loads 0x3f7c bytes from file offset 0 to virtual address 0x0 with read+execute permissions.
Check if the stack is executable:
readelf -l ./binary | grep GNU_STACK
RW = executable stack (vulnerable to shellcode on stack)R = non-executable stack (NX/DEP enabled)Toggle for testing:
execstack -c ./binary # Clear executable flag
execstack -s ./binary # Set executable flag
RELRO marks certain memory regions as read-only after dynamic linking:
.plt.got remains writable (GOT overwrite possible)Check RELRO status:
readelf -l ./binary | grep GNU_RELRO
readelf -d ./binary | grep FLAGS
Look for BIND_NOW or NOW flag for Full RELRO.
Section headers provide detailed information about binary contents. Use objdump -h or readelf -S.
| Section | Contents | Exploitation Relevance | |---------|----------|------------------------| | .text | Executable code | ROP gadget hunting, shellcode placement | | .data | Initialized globals | Data structures, potential targets | | .bss | Uninitialized globals | Zero-initialized, writable | | .rodata | Read-only data | Strings, constants | | .got | Global Offset Table | Function addresses (writable if Partial RELRO) | | .got.plt | PLT GOT entries | Lazy binding targets | | .plt | Procedure Linkage Table | Lazy function call stubs | | .init/.fini | Init/fini functions | Early/late execution hooks | | .init_array/.fini_array | Constructor/destructor arrays | Hijackable under Partial RELRO | | .dynamic | Dynamic linking info | NEEDED libs, relocations, flags | | .tdata/.tbss | Thread-local data | Per-thread variables |
Symbols are named locations (functions, variables) in the binary. Use readelf -sW.
Num: Value Size Type Bind Vis Ndx Name
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strlen@GLIBC_2.17
| Field | Meaning | |-------|--------| | Num | Symbol index | | Value | Address (or 0 for undefined) | | Size | Symbol size in bytes | | Type | FUNC, OBJECT, SECTION, NOTYPE, TLS, GNU_IFUNC | | Bind | LOCAL, GLOBAL, WEAK | | Vis | DEFAULT, HIDDEN, PROTECTED | | Ndx | Section index (UND = undefined/external) | | Name | Symbol name (may include version) |
IFUNC symbols have resolvers that select implementations at load time:
readelf -sW ./binary | rg -i "IFUNC"
Common for CPU dispatch (e.g., optimized string functions).
Modern glibc uses versioned symbols: strlen@GLIBC_2.17
For manual relocations (ret2dlresolve), you must supply the correct version index.
The dynamic section contains information for the dynamic linker. Use readelf -d.
| Tag | Purpose | |-----|--------| | NEEDED | Required shared libraries | | INIT/FINI | Init/fini function addresses | | INIT_ARRAY/FINI_ARRAY | Constructor/destructor arrays | | INIT_ARRAYSZ/FINI_ARRAYSZ | Array sizes | | PLTGOT | GOT/PLT base address | | PLTRELSZ/PLTREL/JMPREL | PLT relocation info | | RELA/RELASZ/RELAENT | Relocation table info | | FLAGS | BIND_NOW, NOW (Full RELRO) | | VERNEED/VERNEEDNUM/VERSYM | Symbol versioning | | RPATH/RUNPATH | Library search paths |
The dynamic linker searches for libraries in this order:
LD_LIBRARY_PATH (ignored for setuid/secure-execution)DT_RPATH (only if DT_RUNPATH absent)DT_RUNPATHld.so.cache/lib64, /usr/lib64, etc.)Check RPATH/RUNPATH:
readelf -d ./binary | egrep -i 'r(path|unpath)'
Test library resolution:
LD_DEBUG=libs ./binary 2>&1 | grep -i find
Relocations adjust addresses after the binary is loaded. Use readelf -r.
| Type | Purpose | |------|--------| | RELATIVE | Adjust addresses based on load bias | | GLOB_DAT | Global data symbol (write address to GOT) | | JUMP_SLOT | Function symbol (PLT lazy binding) | | IRELATIVE | Runtime relocation with resolver |
The GOT stores addresses of external functions and variables. Key points:
The PLT enables lazy function binding:
func@plt → jumps to PLT stub| Flag | Effect |
|------|--------|
| -z now | Full RELRO, disables lazy binding |
| -fno-plt | Direct GOT calls instead of PLT stubs |
| -z pack-relative-relocs | Compact relative relocations (DT_RELR) |
Check for packed relocations:
readelf -d ./binary | egrep -i "DT_RELR|RELRSZ|RELRENT"
The program doesn't always start at main. Initialization order:
.data and zero .bssPREINIT_ARRAY functions (if present)INIT_ARRAY functionsINIT function (if present)main for programs)__attribute__((constructor)) // Runs before main
__attribute__((destructor)) // Runs after main
These functions are added to INIT_ARRAY and FINI_ARRAY.
Under Partial RELRO, INIT_ARRAY and FINI_ARRAY are writable before ld.so marks them read-only. You can hijack control flow by overwriting entries with your function addresses.
TLS variables have per-thread storage:
__thread int my_var; // C
__thread_local int my_var; // C++
.tdata (initialized) and .tbss (uninitialized)__TLS_MODULE_BASE points to the TLS base addressThe kernel passes an auxiliary vector with runtime information:
| Entry | Purpose | |-------|--------| | AT_RANDOM | 16 random bytes (stack canary seed) | | AT_SYSINFO_EHDR | vDSO base address | | AT_EXECFN | Executable path | | AT_BASE | Dynamic linker base | | AT_PAGESZ | System page size |
Access from code:
#include <sys/auxv.h>
printf("AT_RANDOM=%p\n", (void*)getauxval(AT_RANDOM));
printf("AT_SYSINFO_EHDR=%p\n", (void*)getauxval(AT_SYSINFO_EHDR));
From /proc:
cat /proc/$(pidof target)/auxv | xxd
# File type and architecture
file ./binary
# Check protections
checksec --file=./binary # If available
# Program headers
readelf -lW ./binary
# Section headers
objdump -h ./binary
readelf -S ./binary
# Symbols
readelf -sW ./binary
nm ./binary
# Dynamic section
readelf -d ./binary
# Relocations
readelf -r ./binary
# Disassembly
objdump -d ./binary
# PIE
readelf -h ./binary | grep Type
# DYN = PIE, EXEC = non-PIE
# Stack executable
readelf -l ./binary | grep GNU_STACK
# RELRO
readelf -l ./binary | grep GNU_RELRO
readelf -d ./binary | grep -E "BIND_NOW|NOW"
# Canary (check for __stack_chk_fail)
readelf -sW ./binary | grep stack_chk
# FORTIFY_SOURCE (check for _chk functions)
readelf -sW ./binary | grep _chk
# CET (Control-flow Enforcement Technology)
readelf -n ./binary | grep -i "ibt\|shstk"
# System call functions
readelf -sW ./binary | grep -E "sys_|__kernel_"
# Memory functions
readelf -sW ./binary | grep -E "malloc|free|memcpy|strcpy"
# String functions
readelf -sW ./binary | grep -E "str|printf|scanf"
# File I/O
readelf -sW ./binary | grep -E "open|read|write|close"
# Process functions
readelf -sW ./binary | grep -E "fork|exec|exit"
AT_SYSINFO_EHDR provides a stable base for gadgets| Type | Characteristics | |------|----------------| | Dynamic (ET_DYN + INTERP) | Uses loader, has PLT/GOT, standard exploitation | | Static-PIE (ET_DYN, no INTERP) | No loader, relocations applied by kernel, no PLT resolution | | Static (ET_EXEC) | Fixed addresses, no ASLR, no dynamic linking |
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.