GitHub - b0nsu/CTF-Rat: Self-contained pwn/rev CTF solving kit for Claude and Codex — tools, doctrine, knowledge, and automated analysis workflows. · GitHub
Skip to content
b0nsuPublic

About

Self-contained pwn/rev CTF solving kit for Claude and Codex — tools, doctrine, knowledge, and automated analysis workflows.

Topics

Resources

Contributing

Stars

5 stars

Watchers

0 watching

Forks

Repository files navigation

ctf-rat — route · verify · reuse

CTF-Rat

route → verify → reuse

Verify fast on a small context, and stop re-deriving what the cache already knows.

focus agents selfcontained tests

python git angr pwntools ghidra


A pwn/rev-focused, environment-agnostic, self-contained CTF solving kit. Tools, doctrine, knowledge, and reference data all live in one repo — set it up once on any Linux environment (native / VM / WSL2 / container) and Claude · Codex can start solving challenges right here.

📑 Table of Contents

⚡ Quick Start

# 0. One-time environment setup (venv+angr+pwntools, Ghidra, glibc-fetch)  →  SETUP.md
# 1. Lock the target engagement (allowlist / active lock)
ctfguard begin <challenge> [target]
# 2. Route — project evidence dimensions and bounded candidate probes
rat route <bin>
# 3. Run one bounded discriminator; use full recon/revq only when evidence says it is needed
rat query pwn <bin>              # example pwn capability query
rat query func <bin> <function>  # example rev function card

🔄 Flow

flowchart LR
    A["📥 newchal / ctfpull<br/>collect artifact"] --> B["🧭 rat route<br/>signals · dimensions · candidate probes"]
    B --> D["🎯 run one bounded discriminator<br/>recommend only on clear dominance"]
    B --> E["🔍 preserve coexisting leads and unresolved premises"]
    D --> F["🧪 rat-verify / concrete-verify"]
    E --> F
    F --> G{"PASS?"}
    G -->|no, DEEP condition| H["🔬 lazy-load doctrine<br/>inherit STATE · cache · current evidence"]
    H -->|next discriminator| D
    G -->|yes| I["📦 pkshare → HANDOFF.md<br/>knowledge/learned/"]

    style A fill:#264653,color:#fff
    style B fill:#2a9d8f,color:#fff
    style D fill:#e76f51,color:#fff
    style E fill:#e76f51,color:#fff
    style F fill:#e9c46a,color:#111
    style H fill:#6d597a,color:#fff
    style I fill:#43aa8b,color:#fff
Loading

🧭 Entry Points & Rules

Doc Role
SETUP.md Environment-agnostic one-time setup
CLAUDE.md (= AGENTS.md) Agent entry point. Auto-loads the FAST hot-path (7-rule ROE · tool map) every session; full doctrine is lazy-loaded only on DEEP escalation (M1 slim entrypoint)
operator skill skills/<route>/SKILL.md — load exactly one per route. SIGNALS / FIRST ACTION / PIVOT / ESCALATE / VERIFY
knowledge/GROUNDING_INDEX.md Knowledge router → knowledge/ctf-skills/

Note

DEEP-only doctrine — SOLVING (FAST evidence handoff + bounded convergence loop) · SOLVABILITY · PRIMITIVE_GATE · FINALS

Benchmark corpus, ablations, and design-review docs live on the dev branch. main ships only the operational tools needed to actually solve and verify challenges.

📂 Layout

CLAUDE.md / AGENTS.md      agent entry point (symlink)
SETUP.md                   environment-agnostic initial setup
doctrine/                  SOLVING · SOLVABILITY · PRIMITIVE_GATE · REFUSAL · FINALS
docs/                       calibration (로컬 누적 로그, 릴리스는 빈 템플릿 — 오염 상수는 doctrine 밖)
knowledge/                 vendored pwn/rev knowledge + repo-owned learned/ + writeup pipeline
reference/                 libc-offsets/ · glibc/(list · SOURCES · glibc-fetch)
bin/                       all tools (+ghidra_scripts/)
solve/_template/rev/       symsolve · vmlift
kernel/                    kernel pwn extensions
tests/                     e2e_mock.py(ctfpull) · e2e_rev.sh(rev loop)

🛠️ Core Tools

rat — the single front-door dispatcher

rat route <bin> projects observations into independent dimensions and returns evidence-linked bounded candidate probes (a thin composition of rat-doctor + rat-profile + revq, no new analysis). Static routing leaves skill=null; after an active derived/direct STATE observation confirms the method, rat route <bin> --skill <name> --skill-evidence <obs_id> --skill-reason <reason> records the committed Skill in the existing route-assessment note. It emits a recommendation only when one probe clearly dominates the alternatives on deterministic evidence/cost criteria, and rat query {graph,func,oracle,pwn,pattern,slice} · rat dyn|verify · rat state compact · rat cache stats expose everything through one entry point. Existing CLIs (revq / recon / etc.) still work standalone.

Each routed probe has a stable ID bound to the binary digest and expected evidence. rat query func, rat query oracle, and rat query pwn save hash-checked result artifacts with active STATE observations. A later rat route consumes matching results: a complete checker card with comparison sites and both oracle branches retires that probe, while partial or invalidated evidence keeps it pending with an explicit reason and suspends automatic re-query. After rat route BIN → rat query pwn BIN → rat route BIN, a stack candidate advances to a local pwncrash control measurement; format and heap candidates advance to one bounded decomp BIN CALLER inspection. A caller named by revq is selected when available; otherwise the placeholder in next.target must be replaced with a recovered caller. Independent leads can leave decision=null; choose one of the listed experiments. Import-only PWN cards never prove a safe or vulnerable callsite. An active route.probe.refutation observation can refute an exact probe; for bounded read/fgets input callsites the current validator has no independent exhaustive inventory, so it does not accept a safe-callsite refutation.

rat query pattern <bin> returns at most three source-bound hypothesis aids for coexisting Router v2 leads. rat dyn session optionally keeps one local GDB process for sequential observations; see GDB session usage.

ctfpull — pull CTFd challenges into a newchal scaffold

Never auto-submits flags (ToS + honest-mode).

ctfpull ctfd --list [--category pwn]
ctfpull ctfd --id 42 [--dest DIR]        # download → extract → detect ELF → run.json → newchal

기본 수집 경로는 solve/<name>/artifact/이며, 생성된 풀이 파일과 제공 원본이 solve/<name>/ 한 곳에 모인다. --dest DIR을 명시하면 기존처럼 DIR/<name>/에 스테이징한다.

Config precedence: CLI > env vars (CTFD_URL / CTFD_TOKEN) > dotenv (--env, default ./.ctfd.env).

🔍 rev loop
revq <bin>                     # summary + INTERESTING (check-routine candidates) + EVASION (anti-debug/packing)
revq <bin> --func <name>       # single-function neighborhood card (saves context before decompiling)
decomp <bin> <func>             # Bash executable: Ghidra headless decompile cache
mod2solve system.json           # solve matrix @ x = target modulo 2**bits; verify against original inputs
symsolve.py <bin> --find-str Correct --stdin 16 --printable   # recover input, re-run the real binary to verify
vmlift.py --disasm|--run|--solve [blob]                       # custom VM lifter

[!TIP] revq addresses = angr's load base (PIE 0x400000) → feed them straight into symsolve --find <addr>.

decomp is a Bash script: invoke bin/decomp <bin> <func> directly or with bash bin/decomp <bin> <func>. Python tools such as revq accept python3 bin/revq ...; python3 bin/decomp ... does not. Bad-instruction output from a cached function is marked partial with a bounded objdump diagnostic; compare revq's mapped address with the ELF VMA before disassembly.

For WebAssembly core modules, rat route and revq expose a bounded section inventory. Use rat-wasm inspect BIN --func INDEX for one function body digest and rat-wasm verify BIN --stdin-file INPUT --expect-stdout LITERAL for an independent Wasmtime execution (optional setup in SETUP §9). This does not promote a solve or primitive. Use rat-capture --limit N -- TOOL ... instead of piping tool output through head when the original exit code matters.

For Linux boot images, newchal skips generic ELF recon and rat route reports the kernel identity and the prerequisite to extract ELF vmlinux using Linux scripts/extract-vmlinux. Symbol-bearing kernels use bounded ELF metadata in revq (including executable NOTYPE symbols), without a whole-kernel CFG. This establishes artifact identity only; boot environment and primitives still require evidence. Ghidra imports of kernels or files at least 16 MiB require decomp --allow-full-analysis --timeout SEC BIN [FUNC]; cached function reads remain available. Initial import analyzes and exports the whole program.

rat-adapt --root names the artifact store, normally <challenge>/.rat. Omitting it uses ./.rat. A challenge root containing .rat is rejected before execution with the corrected store path, so evidence cannot silently land in a different store from STATE.

mod2solve is a Python executable with no solver dependency. Its JSON input is {"bits":8,"matrix":[[3,2],[0,1]],"target":[19,5]}. Run python3 bin/mod2solve system.json; it returns a solution vector, solution_hex for byte-sized moduli, the original inputs and file digest, and an independent matrix @ solution check. Status distinguishes unique, multiple, and inconsistent; exit codes are 0 for a solution, 1 for no solution, and 2 for invalid input. Even pivots and rectangular systems are supported (up to 256 rows/columns and 64 modulus bits). For a non-unique system, one representative is returned; byte/printable constraints are not imposed. This verifies the equation only: validate the recovered input with the challenge's concrete verifier before recording solve evidence.

🎯 pwn

pwnkit / pwnstage / primitives · pwncalc / pwnleak / pwnpayload / pwnropcheck / pwncrash / pwnscope / pwnclean / pkflag

pkflag checks format only; REAL is not proof of executable-oracle success. By default pkflag uses solve/<active-challenge>/FLAG_FORMAT (from ctfguard's ACTIVE.json, or the current solve/<challenge> directory). A stale repository-global FLAG_FORMAT is not used implicitly. With no challenge format, a generic NAME{...} heuristic applies.

ctfguard begin example
pkflag --set 'CTF\{[A-Za-z0-9_]+\}' # set this challenge's format
pkflag --show                          # show this challenge's format
pkflag 'CTF{example_candidate}'        # format-only check
pkflag --global --show                 # explicitly inspect legacy global format

Use --chal example to select a challenge explicitly. --global --set is available only for deliberate legacy-global configuration; pkselftest no longer writes the repository setting.

➕ and more
  • Environment / execution plan — rat-doctor <bin> --format json shows which paths (native/GDB/angr/Ghidra/QEMU/Qiling/Wine) actually work for this artifact and why others are blocked. Regression testing is handled separately by pkselftest.
  • Reproducible scenarios — rat-scenario init|validate|show normalizes the input/argv/env/oracle shared by rat-dyn / rat-runtime / rat-verify. Binary stdin is preserved via --stdin-file.
  • State bus — state (record confirmed / ruled-out / next) · kernel — k_* (kernel/).
  • Internal schema test helper — ratlib.schema_docs extracts validator field contracts for tests/test_schema_docs.py; it is not part of runtime STATE validation.
  • Handoff & knowledge — pkshare → HANDOFF.md; writeupcheck quality gate → reviewed lessons land in knowledge/learned/. Typed STATE v2 takes precedence; legacy PASS entries are shown only as candidates. Completion docs require an operator attestation linked to an evidence digest.

✅ Tests (after modifying tools)

python3 bin/revq selftest
python3 bin/rat selftest
python3 solve/_template/rev/symsolve.py selftest
python3 solve/_template/rev/vmlift.py selftest
python3 bin/ctfpull selftest && python3 tests/e2e_mock.py
python3 -m unittest tests.test_writeup_pipeline
bash tests/e2e_rev.sh        # real crackme e2e if angr is installed, selftest only otherwise
bash tests/e2e_rev.sh --require-engine  # release/CI gate: missing native angr is failure

Release verification requires --require-engine and ALL GREEN ✅; the optional selftest-only run is reported as partial coverage.

📐 Operating Principles

Important

One active challenge at a time · fan-out only inside a challenge · delegate large reads to a subagent and pull back conclusions only · typed .rat/events/STATE.v2.jsonl is the canonical state stream (STATE.jsonl is legacy inspection/import only) · no reinventing what an existing tool already does.


Made for the CTF grind · route → verify → reuse

About

Self-contained pwn/rev CTF solving kit for Claude and Codex — tools, doctrine, knowledge, and automated analysis workflows.

Topics

Resources

Contributing

Stars

5 stars

Watchers

0 watching

Forks

Contributors

Languages