This document provides high-signal context for OpenCode agents working in the angband repository. It focuses on non-obvious setup requirements, expected workflows, and critical safety rules.
The docs/ directory contains detailed reference material. Start with docs/index.md for the complete knowledge map, or consult individual docs directly:
| Document | Description |
|---|---|
docs/index.md |
START HERE — Complete knowledge map organized by pipeline stage |
docs/bug-class-taxonomy.md |
11 bug classes, PaX attack paradigms, SLUBStick, technique selection matrix |
docs/heap-exploitation.md |
SLUB internals, 6 spray methods, 5 escalation patterns, cross-cache mitigations |
docs/novel-techniques.md |
13 cutting-edge techniques (LL_ATK, Kernel One Gadget, SLUBStick, CARDSHARK, etc.) |
docs/mitigations-defense.md |
VED/LKRG/AUTOSLAB mitigation taxonomy (consolidated in vkb.md) |
docs/manual_build.md |
Step-by-step guest VM exploitation walkthrough |
docs/vkb.md |
Ring 0 exploitation reference index — all external sources (papers, tools, CVEs) with URLs |
KERNEL_MITIGATIONS.md |
Verified kernel addresses, struct offsets, CVE patch status, sysctl requirements |
ARCHITECTURE.md |
Full architecture with data flow diagrams, strategy map, template docs |
TESTING.md |
End-to-end testing guide: prerequisites, demo/CVE test steps, success criteria, troubleshooting |
| Task | Start with |
|---|---|
| Analyzing a new CVE | docs/index.md → docs/bug-class-taxonomy.md |
| Choosing a spray/groom method | docs/index.md → docs/heap-exploitation.md → Spray Selection Guide |
| Selecting a technique for a blocker | docs/index.md → docs/novel-techniques.md → Technique Selection Matrix |
| Understanding a specific CVE | docs/CVE-*-analysis.md for that CVE |
| Getting kernel addresses for a target | KERNEL_MITIGATIONS.md → Verified Kernel Addresses |
| Understanding the template codegen | ARCHITECTURE.md → Jinja2 Templates section |
| Looking up an external paper/tool/CVE reference | docs/vkb.md → Quick Lookup |
| Understanding mitigations | docs/vkb.md → Defense & Mitigation Research |
| Running end-to-end tests | TESTING.md → Step-by-step instructions |
- Purpose: Angband is an automated kernel exploit generation framework. Its goal is to produce full-chain kernel exploits from CVE identifiers to accelerate severity analysis. Currently, it generates staged demo payloads (simulation-only), runs them in an isolated QEMU guest, and verifies kernel-side stage evidence through the synthetic
vuln_drillmodule. Real exploit generation is the next milestone. - Entrypoint: The main CLI tool is the installed
angbandcommand. Do not invoke repo-local scripts directly when the package entrypoint is available; install in editable mode and useangband. - Structure:
- Python package logic:
angband/. - C exploit reference primitives:
primitives/. - Generated runtime output:
mordor_run/current/exploit.yaml,mordor_run/current/exploit.c,mordor_run/current/exploit. - Synthetic testing module:
module/vuln_drill/.
- Python package logic:
Agents should establish a clean Python environment from scratch to ensure predictable behavior, rather than assuming external dependencies exist.
- Create and activate a virtual environment:
python3 -m venv venv source venv/bin/activate - Install the project in editable mode:
This creates the
pip install -e .angbandentrypoint which correctly resolves module paths (e.g.,angband.core.engine).
CRITICAL: The generated payload is currently non-operational (simulation-only), but the project goal is full-chain exploit generation. Always treat mordor_run/current/exploit as guest-only output. Never run it on the host machine.
When testing exploits in the QEMU VM, certain kernel mitigations must be disabled. See KERNEL_MITIGATIONS.md for the complete list and commands.
Key sysctls to disable before exploit testing:
sudo sysctl -w kernel.perf_event_paranoid=-1
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
sudo sysctl -w kernel.kptr_restrict=0- Demo Execution Workflow:
- The standard flow is to use
run_and_verify.sh, which handles generation, QEMU interaction via SSH, and log extraction. - In
angband init demomode, it also builds and loadsmodule/vuln_drill/vuln_drill.koin the guest. - In
angband init <CVE>mode, the CVE is metadata only andrun_and_verify.shskips guest kernel-module setup.
# Assuming venv is active and QEMU harness is running in background ./run_and_verify.sh - The standard flow is to use
- QEMU Harness Execution:
- The harness is located in
harness/setup.sh,harness/launch.sh, andharness/stop.sh. It requiresqemu-system-x86andcloud-image-utils(sudo apt install qemu-system-x86 cloud-image-utils). - Initialize the VM:
cd harness && ./setup.sh - Launch the VM:
./launch.sh - Connect to the Serial Console:
./console.sh(useful if kernel panics and SSH drops) - Stop the VM:
./stop.sh - The harness creates a 9p mount point, making the host's
angbanddirectory available inside the VM at/mnt/angband.
- The harness is located in
- Generating a Demo Configuration:
Examples:
angband init <cve_or_commit> --target <target-name>
angband init demo --target ubuntu-24.04-x86_64angband init CVE-2024-1086 --target ubuntu-24.04-x86_64
- Generating the C Payload:
Note: This reads
angband generate
mordor_run/current/exploit.yaml, generatesmordor_run/current/exploit.cusing Jinja2 templates, and compilesmordor_run/current/exploit.
- When editing Python code, test changes by regenerating the payload with
angband generate. - When modifying C primitives (
primitives/*.c), ensure theangband generatecompilation step succeeds. - Demo verification artifacts are written under
mordor_run/current/:exploit_run.logdmesg_tail.logvuln_drill_status.login demo mode
| Factor | Score | Assessment |
|---|---|---|
| Write reaches function pointer | ★★★ | Direct code execution |
| Write reaches arbitrary address | ★★ | Needs 2-step (corrupt pointer → exec) |
| Write to fixed offset + controllable value | ★ | May be chainable |
| Write to fixed offset + fixed value | ✗ | Not exploitable alone |
| Document | Purpose |
|---|---|
docs/bug-class-taxonomy.md |
Bug class classification, exploitation approaches, capability requirements, chain potential |
docs/heap-exploitation.md |
SLUB internals, 6 spray methods, 5 escalation patterns, naive vs SLUBStick cross-cache |
docs/novel-techniques.md |
13 techniques: LL_ATK, Kernel One Gadget, SLUBStick, CARDSHARK, ExpRace, signalfd, etc. |
Freed object → msg_msg reclaim (mtext@48) → controlled func ptr → kernel calls func(rdi=our_data)
│
┌───────────┴───────────┐
commit_creds (ROP chain) modprobe_path (write + trigger)
For advanced technique alternatives (LL_ATK, Kernel One Gadget, signalfd credential overwrite), see docs/novel-techniques.md.
For spray method selection and slab internals, see docs/heap-exploitation.md.
The most versatile heap spray in Linux kernel exploitation. Used by ALL angband CVEs.
Controllable size (48 to PAGE_SIZE), mtext overlays freed objects, corruptible fields
(m_ts/next/security) enable arbitrary read/free. See docs/bug-class-taxonomy.md for the full
PaX attack paradigm classification and SLUB allocator internals.
| Technique | Where Used | CVE |
|---|---|---|
| msg_msg reclaim → func ptr hijack | primitive stage | 35555, 44269, 33289 |
| wake_up_locked_poll exec | timerfd trigger | 35555 |
| pcpu_stats → modprobe_path write | macvlan primitive | 23209 |
| dirty_pagetable page reclaim | macvlan full | 23209 |
| commit_creds ROP chain | escalate stage (commit_creds path) | All |
| modprobe_path trigger | escalate stage (modprobe_path path) | All |
| KASLR side-channel bypass | leak stage | All |
| Mitigation | Effect on angband Exploits |
|---|---|
| CONFIG_RANDOM_KMALLOC_CACHES (v6.6) | Must slab drain + pattern spray (as done in macvlan) |
| Separate accounted caches (v5.14) | msg_msg and target object must match cache type |
| PaX AUTOSLAB | Same-type spray only; prefer dirty_pagetable for cross-cache |
| VED msg_msg integrity | Match object sizes exactly to bypass OOB check |
| kCFI/IBT | Data-only attacks preferred over ROP |
| CPU pinning restriction | Reduces spray reliability ~20-30% |
Before writing any exploit code, the exploit writer MUST read the actual kernel source of the vulnerable subsystem. This rule exists because every iteration of CVE-2026-31533 that skipped this step wasted days of effort on assumptions that were wrong:
- Wrong trigger condition (flood never reached the -EBUSY threshold)
- Wrong struct offsets (estimated rather than verified)
- Wrong UAF write target (callback only wrote to data fields, not function pointers)
- Wrong slab cache size (used kmalloc-256 spray when tls_rec is kmalloc-512+)
All of these were answerable from the kernel source in under 30 minutes.
| # | Question | Where to find the answer |
|---|---|---|
| 1 | Exact function and file where the bug triggers | Fix commit diff, CVE description |
| 2 | Exact syscall sequence to reach the vulnerable path | Source of the vulnerable function |
| 3 | Exact kernel state that causes the bug (threshold, race window, etc.) | Source + fix diff |
| 4 | Exact field(s) written during UAF/OOB — offset >= 48 for msg_msg exploit? | struct definition + pahole |
| 5 | Exact struct size → which kmalloc cache | sizeof() from struct definition |
| 6 | Which runtime code path is active in this kernel build | ethtool/sysctl/lsmod in VM |
# Fetch the exact file for kernel v6.8:
curl -s https://raw.githubusercontent.com/torvalds/linux/v6.8/<path/to/file.c>
# Read the fix commit diff (shows exactly what invariant was violated):
curl -s https://github.com/torvalds/linux/commit/<hash>.patch | head -300
# Get struct offsets from the loaded kernel module in the VM:
ssh -i mordor_run/ssh/id_ed25519 -p 2222 ubuntu@localhost \
"unzstd /lib/modules/\$(uname -r)/kernel/<path>.ko.zst -o /tmp/m.ko && \
pahole -C <struct_name> /tmp/m.ko 2>/dev/null || echo 'no DWARF — use source'"After reading source, write a minimal Python/C reproducer that puts the kernel in the pre-trigger state. Run it in the VM and check the serial log and dmesg for any evidence the right code path was hit (KASAN report, Oops, unexpected errno, stat counter change). Only proceed to full exploit implementation once the trigger is confirmed reachable with the expected behavior.
If the trigger cannot be confirmed in the VM, the exploit is unverified regardless of how well the code compiles.
The following ASCII diagram shows the complete, verified exploit loop pipeline with all actors, data flows, handoffs, and feedback loops. This is the authoritative reference for how the loop runs.
╔══════════════════════════════════════════════════════════════════════════════╗
║ ANGBAND EXPLOIT LOOP PIPELINE ║
║ (Bug CVE → Serial-log Crash / uid=0 Shell) ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ ║
║ ┌────────────┐ ║
║ │ BOOKKEEPER │ Reads cve-list.md, picks next pending CVE, ║
║ │ (Agent) │ sets row to in_progress, opens iteration-log/CVE-X/ ║
║ └─────┬──────┘ ║
║ │ CVE-ID + metadata ║
║ ▼ ║
║ ┌─────────────────────────────────────────────────────────────────────┐ ║
║ │ EXPLOIT WRITER (Agent) │ ║
║ │ │ ║
║ │ Step 1: Read source (fix diff + vulnerable function + structs) │ ║
║ │ Step 1b: Answer 6 source questions before touching exploit.c │ ║
║ │ Step 1c: Study existing exploits; label bad-code patterns │ ║
║ │ Step 1d: Plan escalation path from bug class + trigger evidence │ ║
║ │ Step 2: Feasibility assessment (config, hardware, privilege) │ ║
║ │ Step 3: 7-stage chain design (prep/groom/trigger/…/escalate) │ ║
║ │ Step 4: Write docs/cve-analysis/CVE-X-analysis.md │ ║
║ │ Step 5: angband init + generate → fill placeholders → compile │ ║
║ │ Step 5d: Iterate in VM (verify-cve.sh) until serial log fires │ ║
║ │ Step 1d: Post-trigger: push escalation (OOPS→panic→uid=0) │ ║
║ │ Step 6: Write exploit-loop/iteration-log/CVE-X/writer_summary.md │ ║
║ └─────┬───────────────────────────────────────────────────────────────┘ ║
║ │ exploit binary + writer_summary.md + CVE analysis doc ║
║ ▼ ║
║ ┌──────────────────────────────────────────────────────────────────┐ ║
║ │ VERIFY-CVE.SH (Script) │ ║
║ │ │ ║
║ │ 1. Copy exploit → CVE-named binary (cve-XXXX-XXXXX) │ ║
║ │ 2. Check VM state; launch harness if down │ ║
║ │ 3. Apply sysctls: kptr_restrict=0, userns=0, perf_paranoid=-1 │ ║
║ │ 4. Mount 9p host dir at /mnt/angband inside VM │ ║
║ │ 5. Run binary as ubuntu (no sudo) inside VM │ ║
║ │ 6. Health check: SSH alive after exploit? → PANIC if dead │ ║
║ │ 7. Read serial.log delta → classify UBSAN/KASAN/Call Trace/RIP │ ║
║ │ 8. Classify outcome: │ ║
║ │ exit 0 → ESCALATED (uid=0 confirmed) │ ║
║ │ exit 1 → PANIC (VM SSH dead / "Kernel panic" line) │ ║
║ │ exit 2 → OOPS (Call Trace/BUG/UBSAN in serial) │ ║
║ │ exit 3 → UNVERIFIED (no kernel output captured) │ ║
║ └─────┬────────────────────────────────────────────────────────────┘ ║
║ │ exit code + exploit_run.log + serial_snippet.log ║
║ ▼ ║
║ ┌─────────────────────────────────────────────────────────────────────┐ ║
║ │ REVIEWER (Agent) │ ║
║ │ │ ║
║ │ Reads: writer_summary.md + exploit_run.log + serial_snippet.log │ ║
║ │ Checks: all 6 source questions answered? YAML consistent? │ ║
║ │ trigger actually ran (not simulation_only)? │ ║
║ │ serial log checked (not just dmesg)? │ ║
║ │ post-trigger escalation push attempted? │ ║
║ │ │ ║
║ │ Verdict: │ ║
║ │ PASS → Bookkeeper marks CVE finished / dos-oops / escalated│ ║
║ │ ISSUES → Writer gets rerun with specific feedback appended │ ║
║ └─────┬───────────────────────────────────────────────────────────────┘ ║
║ │ ║
║ ├──── PASS ──────────────────────────────────────────────────────► ║
║ │ BOOKKEEPER║
║ │ updates ║
║ │ cve-list ║
║ │ status ║
║ │ ║
║ └──── ISSUES ──────────────────────────────────────────────────────► ║
║ feedback appended
║ to EXPLOIT_WRITER_PROMPT
║ → next rerun ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ INFRASTRUCTURE ║
║ ║
║ Host filesystem ──9p──► /mnt/angband (inside QEMU VM) ║
║ harness/launch.sh ──► QEMU VM (KVM, -smep,-smap, 2 vCPU, 4 GB RAM) ║
║ serial.log ◄────────── ttyS0 (kernel panic / Oops / UBSAN output here) ║
║ SSH :2222 ◄────────── VM (health check + exploit execution) ║
╚══════════════════════════════════════════════════════════════════════════════╝
The following pipeline defects have been identified, fixed, and must not recur:
| Bug | Symptom | Fix Applied |
|---|---|---|
| Serial log not checked | "0 new lines" but kernel crashed | verify-cve.sh reads serial_snippet.log; UBSAN/KASAN patterns now match OOPS |
| UBSAN suppresses duplicates | 2nd verify run shows 0 lines even though bug fires | Restart VM between verify runs; UBSAN only reports each location once |
| verify-cve.sh OOPS pattern too narrow | "UBSAN:" in serial not matched | Added UBSAN:|KASAN:|slab-out-of-bounds|use-after-free to grep pattern |
| Placeholders not filled | trigger prints "simulation_only" | Step 5b mandatory: fill every placeholder before verify |
| Wrong stage ordering | leak AFTER trigger; addresses stale | KASLR resolve BEFORE trigger (CVE-2026-31533 lesson) |
| Spray queues full on re-spray | 0 objects sprayed in rounds 2+ | Drain queues (msgrcv IPC_NOWAIT) before each re-fill |
| NLM_F_ACK missing | nl.recv() blocks forever |
Always set NLM_F_ACK on RTM_NEWROUTE netlink messages |
| Wrong enum constants | -ERANGE on every attribute size | Read kernel uapi header; enumerate constants, don't assume values |
# 1. VM is alive and SSH works
ssh -i mordor_run/ssh/id_ed25519 -p 2222 -o ConnectTimeout=3 ubuntu@localhost whoami
# 2. Serial log is being written (size > 0)
wc -c mordor_run/harness/serial.log
# 3. Exploit binary exists and is not a demo placeholder
ls -la mordor_run/current/exploit
grep -c 'simulation_only\|implementation pending' mordor_run/current/exploit.c || echo 'no placeholders'
# 4. YAML is consistent (escalate.method != dirty_pagetable unless you have the primitive)
cat mordor_run/current/exploit.yaml | grep -E 'escalate|groom.*cache|trigger.*method'- CVE-A provides a capability (e.g., info leak, CAP_SYS_TIME, KASLR bypass)
- CVE-B provides a primitive (e.g., limited write, heap control)
- CVE-C converts the primitive to privilege escalation
Each exploit stage provides and requires capabilities:
| Capability | Provider | Consumer |
|---|---|---|
kaslr_bypass |
CVE-A leak stage | All CVEs that need kernel addresses |
cap_net_admin |
Namespace creation | macvlan netlink operations |
kernel_write_primitive |
CVE with pcpu_stats/msg_msg corruption | escalate stage |
kernel_read_primitive |
CVE with info leak | KASLR bypass, heap address leak |
heap_address |
Info leak from UAF residual data | Groom stage (precise reclaim) |
kallsyms_access |
kptr_restrict=0 or namespace bypass | Symbol resolution |
arbitrary_free |
Double-free CVE | Heap massage / reclaim |
┌─────────────────────────────────────────────────────────────┐
│ exploit_chain.yaml │
│ chain: │
│ - cve: CVE-2026-XXXX # capability provider │
│ provides: [cap_sys_time] │
│ output: { settime_capability: true } │
│ - cve: CVE-2026-YYYY # escalation (or built-in) │
│ requires: [kernel_write_primitive] │
│ escalate: modprobe_path │
└─────────────────────────────────────────────────────────────┘
Stage(output) ──data──→ NextStage(input)
Example: CVE-A leak → CVE-B primitive → CVE-C escalate
kaslr_bypass output: { kernel_base, modprobe_path_addr }
↓
primitive input: { kernel_base, modprobe_path_addr }
primitive output: { corrupted_object_offset, write_value }
↓
escalate input: { modprobe_path_addr, write_value }
escalate output: { root_shell }
Add requires and provides to the exploit.yaml stages:
The generator composes: groom(timerfd) + trigger(timerfd) + leak(kallsyms) + primitive(timerfd) + escalate(modprobe)
Can the exploit achieve escalation alone?
├── YES → Single CVE exploit (e.g., CVE-2026-23209)
└── NO → What is missing?
├── Missing capability (e.g., CAP_SYS_TIME)?
│ └── Find CVE that provides it → CHAIN
├── Missing write primitive?
│ └── Find CVE with OOB/UAF write → CHAIN
├── Missing info leak?
│ └── Find CVE with infoleak → CHAIN
└── Fixed-offset/fixed-value writes only?
└── Can we corrupt something that later yields exec?
├── YES → Deferred exploitation (complex chain)
└── NO → NOT exploitable with current CVEs
| Priority | Item | Effort |
|---|---|---|
| P0 | Add requires/provides to YAML schema |
Low |
| P0 | Capability checker module | Medium |
| P1 | Pipeline orchestrator for multi-CVE chains | High |
| P1 | Template fragmentation into per-stage files | High |
| P2 | Shared context data passing between CVEs | Medium |
| P2 | Fallback/retry logic for failed stages | Medium |
| P3 | Automated CVE pairing (find complementary CVEs) | Very High |
| P3 | Chain verification in QEMU (multi-CVE execution) | Very High |