mcpField
E
exploiting-memory-corruption

exploiting-memory-corruption

github.com/trilwuSecurity17747 installsUpdated 12w ago
Repository

About exploiting-memory-corruption

A native memory-corruption exploitation skill for authorized binary-security work, CTFs, and labs. It connects bug classes to exploit primitives and techniques, with methods chosen according to the target's mitigations and allocator behavior.

Key features

  1. 01Enumerate binary mitigations
  2. 02Map bug classes to primitives
  3. 03Build ROP and code-reuse chains
  4. 04Apply glibc heap techniques
  5. 05Develop exploits incrementally with pwntools
  6. 06Test within authorized lab scope

Use cases

  1. 01Assessing a crash in an ELF or PE binary
  2. 02Developing a CTF pwn challenge exploit
  3. 03Testing ROP against enabled mitigations
  4. 04Investigating heap corruption on glibc

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/trilwu/secskills --skill exploiting-memory-corruption

After installing, type/exploiting-memory-corruptionin Claude Code to confirm it loaded.

Details

From the project README

Exploiting Memory Corruption

Reverse engineering tells you a binary is buggy. Exploitation is the separate craft of turning that bug into a controlled primitive and then into code execution, against the mitigations the target actually ships. The work is a chain of conversions — bug to primitive, primitive to leak, leak to control — and each link is chosen by what the binary's protections allow, not by rote.

When to Use

  • You have a crash or a known bug class in an ELF/PE and need to assess exploitability and build a working exploit
  • Constructing a ROP/JOP chain, a ret2libc, or a heap-grooming sequence
  • Defeating a specific mitigation set (ASLR, NX/DEP, canary, PIE, RELRO) on a lab or CTF target
  • Working an authorized binary-exploitation task or a CTF pwn challenge

When NOT to Use

  • Recovering what the binary does — disassembly, decompilation, algorithm recovery — is analyzing-binaries; do that first if the bug is not yet understood.
  • Unpacking or de-virtualizing a protected binary before you can even see the code — use unpacking-protected-binaries.
  • Web, deserialization, or injection bugs — those are their own skills (testing-web-applications, exploiting-deserialization); this skill is native memory corruption only.
  • Kernel/driver exploitation specifically — the primitives differ enough (no libc, SMEP/SMAP/KASLR, syscall context) that it is out of scope here; the userland method still informs it.
  • Fuzzing to find the bug — that is a discovery step; this skill starts once you have a crash.

Enumerate the Mitigations First

The exploit you can build is dictated by what is turned on. Read them before planning anything (checksec, or pwntools' checksec/ELF):

MitigationWhat it blocksThe usual answer
NX / DEPExecuting your shellcode on the stack/heapROP / ret2libc — reuse existing executable code
ASLRHardcoding addressesAn info leak to recover a base, then compute offsets
Stack canaryNaive stack-return overwriteLeak the canary, overwrite something else, or brute a forking server
PIEHardcoding the binary's own addressesLeak a binary address; until then only relative/partial overwrites
Partial RELROLittle — GOT is still writableGOT overwrite is on the table
Full RELROGOT overwriteTarget __malloc_hook/__free_hook (pre-2.34), or a ROP-only path

The two questions that order the work: do I have an info leak (ASLR/PIE), and where can I write (RELRO). Everything else follows from those.

Bug Class to Primitive

Name what you have before reaching for a technique:

  • Stack buffer overflow → direct control of saved return address; the cleanest path to a ROP chain, canary permitting.
  • Heap overflow / off-by-one → corrupt adjacent chunk metadata or an adjacent object's pointers; primitive depends on the heap layout you can groom.
  • Use-after-free → reallocate the freed slot with attacker data so a stale pointer is used under your control; classic route to a fake vtable/object.
  • Type confusion → treat one type's fields as another's, often yielding a read/write or a controlled call directly.
  • Format string → %n for a targeted write and %p/%s for a leak; often both a leak and a write from one bug.
  • Integer overflow / OOB → usually a means to one of the above (a short-computed size producing a heap overflow).

The goal of this step is one of three primitives: control flow hijack, an arbitrary read, or an arbitrary write. Get to a primitive, then build.

ROP and Code Reuse

With NX on, you execute the program's own bytes in an order you choose:

  • Find gadgets with ROPgadget or ropper; look for the pop rdi; ret-style register controls that set up a call.
  • ret2libc — return into system("/bin/sh") once you have a libc base; the most direct chain when libc is available and leaked.
  • ret2csu to control rdx/rsi when no clean gadget exists, using __libc_csu_init's tail on pre-2.34 binaries.
  • SROP (sigreturn-oriented) when gadgets are scarce but you can stage a fake signal frame and reach a sigreturn.
  • ret2dlresolve to call a function by forcing the dynamic linker to resolve it, useful with no leak and partial RELRO.
  • one_gadget to find a single libc address that execs a shell, collapsing the final stage when its constraints hold.

Match the leaked libc to a build with libc-database so the offsets are right — a chain built against the wrong libc fails silently.

Heap Exploitation (glibc ptmalloc)

Heap work is layout work: you arrange allocations so a corruption lands on something useful. The technique depends on the glibc version, because the allocator's checks have tightened steadily.

  • tcache poisoning (2.26+) — the highest-leverage modern primitive: corrupt a freed tcache chunk's next to return an arbitrary pointer from the next malloc. On 2.32+ the pointer is mangled by safe-linking (XORed with the address it is stored at, shifted right by 12), so you need a heap leak to forge it, and 2.34 removed the __free_hook/__malloc_hook targets that used to end the chain.
  • fastbin dup — double-free into the fastbin to return the same chunk twice; watch the tcache-first behaviour on modern glibc.
  • unsorted-bin leak — a freed chunk in the unsorted bin holds a main_arena pointer; reading it leaks a libc address to defeat ASLR.
  • House of * techniques (Force, Orange, Einherjar, and newer) — each abuses a specific allocator invariant for an arbitrary write when the simple primitives are blocked; reach for them only when the layout demands it.

Drive all of this in pwndbg or GEF — heap, bins, and tcache commands show the state so you groom against reality, not a mental model.

The Loop

Build the exploit incrementally with pwntools, and verify each conversion before adding the next: reach the crash, prove control of the instruction pointer, land a leak and confirm the computed base, then the write, then the shell. When a stage misbehaves, attach with gdb.attach() at exactly that point rather than staring at the whole chain. A working local exploit that fails remotely is almost always an environment gap — libc version, stack alignment (a stray ret for movaps), or an env-dependent leak.

Scope and Authorization

Exploit development belongs on binaries you own, CTF targets, lab VMs, or software you are explicitly engaged to test — the lab_only egress mode is the default here, since a working exploit run against production is an intrusion, not research. Two specific edges:

  • A 0-day in third-party software puts you in coordinated disclosure, not publication. Build the proof, report it through the vendor's channel, and do not drop a weaponized exploit publicly ahead of a fix.
  • Never test an exploit against a system you do not control to "see if it works." A memory-corruption exploit that lands crashes the service when it does not; that is an availability impact on someone else's estate.

Rationalizations to Reject

  • "It crashes on EIP/RIP, so it's exploitable." Control of the instruction pointer is the start, not the finish. Modern mitigations mean a crash may be a dead end without a leak; assess reachability of a primitive before claiming exploitability.
  • "It works locally, ship it." Remote libc, ASLR entropy, and stack alignment differ. Match the target libc and test against the real environment before reporting it as working.
  • "Just spray shellcode on the stack." NX has been default for over a decade. If you are writing shellcode to the stack and jumping to it, confirm NX is actually off before spending time on it.
  • "one_gadget will handle the last step." Its constraints (specific registers null, specific stack state) frequently do not hold. Check them; keep a ret2libc as the fallback.
  • "The heap technique from the writeup will just work." Allocator checks are version-specific — tcache keys, count checks, safe-linking. Confirm the target glibc version and that the technique still applies to it.

ATT&CK Coverage

Generated from secskills-core/ttp-index.json — edit that file, then run python3 scripts/sync_attack.py --write. Re-verify IDs against the current ATT&CK release before citing them in a report.

Execution (TA0002)

  • T1203 Exploitation for Client Execution — see also performing-social-engineering, analyzing-malware

Privilege Escalation (TA0004)

  • T1068 Exploitation for Privilege Escalation — see also escalating-linux-privileges, escalating-windows-privileges

Lateral Movement (TA0008)

  • T1210 Exploitation of Remote Services — see also enumerating-network-services

Detection content for any of these: engineering-detections. Proactive search: hunting-threats. Post-compromise: responding-to-incidents.

References

  • analyzing-binaries — recovering the code and understanding the bug first
  • unpacking-protected-binaries — when the binary must be unpacked/de-virtualized
  • reviewing-cryptography — when the target is a crypto primitive, not memory
  • maintaining-engagement-state — egress modes and the lab-only default here

Comments

Markdown supported