Hacktricks-skills binary-exploitation-dtors-fini-array
Exploit arbitrary write vulnerabilities using .dtors and .fini_array sections to execute shellcode at program exit. Use this skill whenever the user mentions binary exploitation, arbitrary write vulnerabilities, .dtors, .fini_array, destructor sections, or needs to execute code after main() returns. Also use when the user has write access to a binary's memory and wants to hijack program termination.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/binary-exploitation/arbitrary-write-2-exec/www2exec-.dtors-and-.fini_array/SKILL.MDBinary Exploitation: .dtors & .fini_array Exploitation
This skill helps you exploit arbitrary write vulnerabilities by hijacking the
.dtors and .fini_array sections to execute shellcode when a program terminates.
When to Use This Technique
Use this approach when:
- You have an arbitrary write vulnerability (can write to any address)
- You need to execute shellcode but don't have a direct code execution primitive
- The binary has writable .dtors or .fini_array sections (check with
orobjdump
)rabin2 - You want to execute code after main() returns
Understanding the Sections
.dtors Section
The
.dtors section contains destructor function addresses that execute before the program finishes (after main() returns).
Key points:
- Addresses are stored in the
section.dtors - Overwrite
with your shellcode address to execute it__DTOR_END__ - Look for markers between
and0xffffffff0x00000000 - If you only see these markers, no functions are registered - overwrite the
entry0x00000000
.fini_array Section
The
.fini_array section is similar to .dtors - it contains function addresses called before program termination.
Key points:
- Functions are called in reverse order (last entry first)
- Each entry executes only once (prevents eternal loops by default)
- Start writing from the last entry to control execution order
- With Full RELRO or newer Partial RELRO, this section is read-only (check first!)
Step-by-Step Exploitation Flow
Step 1: Inspect the Binary
First, check if the binary has exploitable sections:
# Check .dtors section objdump -s -j .dtors ./binary rabin2 -s ./binary | grep "__DTOR" # Check .fini_array section objdump -s -j .fini_array ./binary rabin2 -s ./binary | grep "__fini"
What to look for:
: Values between.dtors
and0xffffffff
(the0x00000000
is writable)0x00000000
: Function addresses you can overwrite.fini_array- RELRO status: Check if sections are read-only (not exploitable if they are)
Step 2: Find a Shellcode Location
You need a writable and executable location for your shellcode:
# Check memory layout readelf -l ./binary | grep -A 5 "GNU_STACK" # Look for writable sections objdump -h ./binary | grep -E "(writable|RW)" # Common locations: # - .data section # - .bss section # - Heap (if you can control it) # - Environment variables (if NX is disabled)
Step 3: Craft the Exploit
For .dtors:
- Find the address of
(the__DTOR_END__
marker)0x00000000 - Place your shellcode in a writable/executable location
- Use the arbitrary write vulnerability to overwrite
with your shellcode address__DTOR_END__ - When the program exits, your shellcode executes
For .fini_array:
- Find the address of the last entry in
.fini_array - Place your shellcode in a writable/executable location
- Use the arbitrary write vulnerability to overwrite the entry with your shellcode address
- When the program exits, your shellcode executes
Step 4: Handle RELRO Protections
Check RELRO status:
readelf -l ./binary | grep -i relro
If Full RELRO or Partial RELRO:
is likely read-only - this technique won't work.fini_array- Consider alternative techniques:
- Return-oriented programming (ROP)
- Format string vulnerabilities
- Other memory corruption primitives
Eternal Loop Technique (Advanced)
If you have at least 2 entries in .fini_array, you can create an eternal loop:
- First write: Call the vulnerable arbitrary write function again
- Second write: Calculate the return address stored by
(the function calling__libc_csu_fini
functions) and overwrite it with.fini_array
's address__libc_csu_fini - This makes
call itself again, executing__libc_csu_fini
functions repeatedly.fini_array - You get multiple arbitrary writes to chain exploits
Reference: See guyinatuxedo's insomnihack18 writeup for a detailed example.
Common Pitfalls
- Shellcode location: Make sure your shellcode is in a writable AND executable location
- RELRO: Check if sections are read-only before attempting
- Entry order:
entries execute in reverse order.fini_array - Single execution: Each
entry executes only once (unless you use the eternal loop technique).fini_array - Address calculation: Ensure you're writing to the correct address (the section address, not the function address)
Example Exploit Structure
from pwn import * # Connect to target p = process('./binary') # Find addresses binary = ELF('./binary') dtors_end = binary.symbols['__DTOR_END__'] fini_array = binary.section('.fini_array').address # Prepare shellcode shellcode = asm(shellcraft.sh()) # Find writable location (example: .data section) data_addr = binary.section('.data').address # Write shellcode to memory p.send(shellcode) # Overwrite .dtors or .fini_array with shellcode address # This depends on your arbitrary write vulnerability # Example: write to dtors_end exploit_payload = p64(shellcode_addr) p.sendline(exploit_payload) # Wait for program to exit and shellcode to execute p.interactive()
Debugging Tips
# GDB debugging gdb ./binary (gdb) break main (gdb) continue (gdb) info sections | grep -E "(dtors|fini)" (gdb) x/20wx __DTOR_END__ (gdb) x/20wx __fini_array_end__ # Check if sections are writable (gdb) info proc mappings
When NOT to Use This Technique
- Binary has Full RELRO or Partial RELRO with read-only
.fini_array - No arbitrary write vulnerability available
- NX/DEP is enabled and you can't find a writable+executable location
- You need immediate execution (this technique only works at program exit)
Related Techniques
- ROP chains: When you can't write shellcode but can chain existing code
- Format string exploits: When you have format string vulnerabilities
- Heap exploitation: When you have heap corruption primitives
- Return address overwrites: Classic stack buffer overflow technique