Skip to content

userIssa/heapsentinel

Repository files navigation

⬡ HeapSentinel

Runtime heap exploitation primitive detector for Linux binaries.

HeapSentinel instruments a target process with Frida and classifies heap bugs — use-after-free, double-free, heap overflow, and tcache poisoning — as they happen, mapping each finding to the exploitation technique it enables. A second static analysis layer (angr) covers code paths that never execute at runtime.

$ python3 main.py -t ./targets/vuln_heap -m both -T 10

  [FINDING] Use-After-Free  @ 0x1ffe27d0  →  Tcache Poisoning     High
  [FINDING] Double-Free     @ 0x1ffe27d0  →  Tcache Dup           High
  [FINDING] Heap-Overflow   @ 0x1ffe2840  →  Chunk Overlap        High

Features

  • Dynamic analysis — Frida agent hooks malloc, free, memset, memcpy, strcpy and monitors heap state in real time
  • Static analysis — angr CFG analysis finds double-free and UAF paths in code that doesn't execute
  • Technique mapping — each primitive is mapped to the exploitation technique it enables (tcache poisoning, fastbin dup, house of force, etc.)
  • Two output layers — live terminal table + HTML/JSON reports with call stacks
  • Crash-resistant — per-event flushing means findings survive process crashes before return
  • Zero false positives on the how2heap benchmark (100% precision, dynamic layer)

Detection coverage

Primitive Detection method Example technique
Use-After-Free Write to freed chunk via libc hook Tcache Poisoning
Double-Free (A→A) Consecutive frees, no malloc between Tcache Dup
Double-Free (A→B→A) Fastbin bypass pattern Fastbin Dup
Heap Overflow Write past chunk boundary Chunk Overlap via Size Corruption
Overlap malloc New allocation overlaps live chunk overlapping_chunks technique
Suspicious malloc malloc returns untracked address Tcache Poisoning (crash-before-return)
Non-heap free (static) Stack pointer passed to free() House of Spirit

Evaluation

Tested against the how2heap benchmark (glibc 2.31 + 2.35) and three CTF-style validation targets.

Mode Precision Recall F1
Dynamic only 100% 68.4% 81.2%
Combined (dynamic + static) 94.1% 84.2% 88.9%

Static analysis contributes a +15.8% recall improvement by catching primitives on crash-before-return paths and code that is never reached at runtime.


Installation

Requirements: Linux x86-64, Python 3.10+, root or ptrace capability for Frida.

git clone https://github.com/userIssa/heapsentinel
cd heapsentinel

python3 -m venv .venv
source .venv/bin/activate

# Core (dynamic analysis)
pip install frida frida-tools rich click jinja2

# Optional (static analysis)
pip install angr

angr is optional — --mode dynamic works without it. Static analysis adds significant install size (~500MB) but pushes recall from 68% to 84%.


Quick start

# Compile the included test target
gcc -o targets/vuln_heap targets/vuln_heap.c -no-pie -fno-stack-protector -g

# Run dynamic analysis (10 second timeout)
python3 main.py -t targets/vuln_heap -m dynamic -T 10

# Run static analysis only (no Frida, no root needed)
python3 main.py -t targets/vuln_heap -m static

# Run both layers together
python3 main.py -t targets/vuln_heap -m both -T 10

# Attach to a running process
python3 main.py -p <PID> -m dynamic -T 30

# Verbose — see every malloc/free/write event live
python3 main.py -t targets/vuln_heap -m dynamic -v

# No binary? Try the built-in demo
python3 main.py --demo

Usage

python3 main.py [OPTIONS]

Options:
  -t, --target TEXT                 Path to target binary
  -p, --pid INTEGER                 Attach to running process by PID
  -a, --args TEXT                   Arguments for target binary
  -m, --mode [dynamic|static|both]  Analysis mode  [default: dynamic]
  -T, --timeout INTEGER             Monitor timeout in seconds  [default: 30]
  -o, --output TEXT                 Report output name  [default: output]
  -v, --verbose                     Print every heap event as it arrives
      --demo                        Run built-in demo, no binary needed
  -h, --help                        Show this message and exit

Reports are written to reports/<name>.html and reports/<name>.json.


How it works

Target binary
      │
      ▼
Frida JS agent          ← hooks malloc/free/memset/memcpy/strcpy
      │  event_batch
      ▼
FridaController         ← ingests events, maintains ordering
      │
      ▼
HeapStateTracker        ← shadow heap map, chunk lifecycle
      │
      ▼
PrimitiveClassifier     ← UAF / DF / overflow / overlap detection
      │
      ▼
TechniqueMapper         ← maps primitive → exploitation technique
      │
      ▼
HTML + JSON report


angr static layer (parallel)
      │
      ▼
CFGFast + callgraph     ← function discovery via nm + angr
      │
      ▼
Reachability analysis   ← double-free paths, UAF paths, non-heap free
      │
      ▼
Static findings merged into report

Running the evaluation

# Compile how2heap targets (glibc 2.35 recommended)
cd /path/to/how2heap
for f in glibc_2.35/fastbin_dup.c glibc_2.35/overlapping_chunks.c \
          glibc_2.35/house_of_botcake.c glibc_2.35/tcache_poisoning.c \
          glibc_2.35/house_of_spirit.c glibc_2.35/house_of_lore.c \
          glibc_2.35/large_bin_attack.c glibc_2.35/poison_null_byte.c; do
    name=g2.35_$(basename $f .c)
    gcc -o $name $f -g 2>/dev/null && echo "$name"
done

# Run evaluation harnesses
cd /path/to/heapsentinel
bash run_eval.sh /path/to/how2heap
bash run_eval_combined.sh /path/to/how2heap

Project structure

heapsentinel/
├── main.py                          # CLI entry point
├── core/
│   ├── heap_state.py                # Shadow heap map + event log
│   ├── primitive_classifier.py      # UAF / DF / overflow detectors
│   └── technique_mapper.py          # Primitive → technique mapping
├── instrumentation/
│   ├── frida_agent.js               # JS agent injected into target
│   └── frida_controller.py          # Python Frida session manager
├── analysis/
│   └── static_analyser.py           # angr CFG-based static analysis
├── reporting/
│   └── report_generator.py          # HTML + JSON report generation
├── targets/
│   ├── vuln_heap.c                  # Deliberately vulnerable test binary
│   ├── ctf_uaf_vtable.c             # CTF-style UAF validation target
│   ├── ctf_heap_feng_shui.c         # CTF-style overflow validation target
│   └── ctf_tcache_chain.c           # CTF-style tcache validation target
├── tests/
│   └── run_tests.py                 # 27-test stdlib-only test suite
├── run_eval.sh                      # Dynamic-only evaluation harness
└── run_eval_combined.sh             # Combined dynamic+static evaluation

Known limitations

  • Direct memory writes — raw pointer stores (*(ptr) = val) bypass all libc hooks. Techniques like unsafe_unlink that corrupt fd/bk directly require CPU-level instrumentation (Frida Stalker or hardware watchpoints) to detect dynamically.
  • Pointer provenance — UAF via a stale pointer used after reallocation to the same address is not detected, as the memory is live at that point. Full coverage requires taint tracking.
  • Static FP rate — the static layer trades some precision for recall. Double-free paths flagged statically may involve different pointers that alias to the same address.
  • glibc only — the Frida agent targets glibc's malloc implementation. musl, jemalloc, and tcmalloc are not supported.

Tested on

  • Kali Linux (glibc 2.35 + 2.31)
  • Python 3.12
  • Frida 16.x
  • angr 9.2.x

License

MIT

About

Runtime heap exploitation primitive detector for Linux — Frida dynamic analysis + angr static analysis

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors