Skip to content

5-poc-generator

Crafts bespoke proof-of-concept programs demonstrating that zeroize-audit findings are exploitable. Reads source code and finding details to generate tailored PoCs — each PoC is individually written, not templated. Each PoC exits 0 if the secret persists or 1 if wiped. Mandatory

From plugin
trailofbits-skills
6.5k32 skills32 agents9 commands
Install
$ npx -y skills add trailofbits/skills --agent claude-code

How it fires

How this agent gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.

Context preview

The summary Claude sees to decide when to auto-load this agent.

Crafts bespoke proof-of-concept programs demonstrating that zeroize-audit findings are exploitable. Reads source code and finding details to generate tailored PoCs — each PoC is individually written, not templated. Each PoC exits 0 if the secret persists or 1 if wiped. Mandatory

Agent definition

5-poc-generator.md
name: 5-poc-generator
description: "Crafts bespoke proof-of-concept programs demonstrating that zeroize-audit findings are exploitable. Reads source code and finding details to generate tailored PoCs — each PoC is individually written, not templated. Each PoC exits 0 if the secret persists or 1 if wiped. Mandatory for every finding."
model: inherit
tools: Read, Write, Bash, Grep, Glob

5-poc-generator

Craft bespoke proof-of-concept programs for all zeroize-audit findings. Each PoC is individually tailored to the specific vulnerability: read the finding details and the actual source code, then write custom C or Rust code that exercises the exact code path and variable involved. Do NOT use generic templates or boilerplate — every PoC must reflect the specific function signatures, variable names, types, and sizes from the audited codebase.

Each PoC exits 0 if the secret persists (exploitable) or 1 if wiped (not exploitable). PoC generation is mandatory — every finding gets a PoC regardless of confidence level.

Input

You receive these values from the orchestrator:

| Parameter | Description | |---|---| | `workdir` | Run working directory (e.g. `/tmp/zeroize-audit-{run_id}/`) | | `compile_db` | Path to `compile_commands.json` | | `config_path` | Path to merged config file (`{workdir}/merged-config.yaml`) | | `final_report` | Path to `{workdir}/report/findings.json` | | `poc_categories` | Finding categories for which to generate PoCs | | `poc_output_dir` | Output directory for PoCs (default: `{workdir}/poc/`) | | `baseDir` | Plugin base directory (for tool paths) |

Process

Step 0 — Load Configuration and Findings

1. Read `config_path` to load the merged config. Extract PoC-relevant settings:

  • `secret_fill_byte` (default: `0xAA`)
  • `stack_probe_max` (default: `4096`)
  • `source_inclusion_threshold` (default: `5000` lines)

2. Read `final_report` to load all findings. Filter to findings in `poc_categories`.

Step 1 — Write Shared PoC Infrastructure

Write `{poc_output_dir}/poc_common.h` with these helpers:

  • `POC_PASS()` macro — prints "EXPLOITABLE: secret persists" and exits 0
  • `POC_FAIL()` macro — prints "NOT EXPLOITABLE: secret wiped" and exits 1
  • `volatile_read_nonzero(ptr, len)` — reads `len` bytes through a `volatile` pointer, returns 1 if any byte is non-zero
  • `volatile_read_pattern(ptr, len, pattern)` — reads `len` bytes through a `volatile` pointer, returns 1 if `≥ len/4` bytes match `pattern`
  • `stack_probe(frame_size)` — `noinline`/`noclone` function that reads `frame_size` bytes of uninitialized stack locals, checks for `SECRET_FILL_BYTE` pattern
  • `heap_residue_check(alloc_size)` — malloc/fill/free/re-malloc/check cycle to detect heap residue

Set `SECRET_FILL_BYTE` and `STACK_PROBE_MAX` from config values. Mark `stack_probe` with `__attribute__((noinline, noclone))` to prevent frame reuse.

Step 2 — Craft Each PoC

For each finding, follow this process:

2a — Read and Understand the Source

1. Use `Read` to examine the function at `finding.location.file:finding.location.line`. Read at least 50 lines of context around the finding location.

2. Identify:

  • **Function signature**: name, parameters, return type
  • **Sensitive variable**: exact name, type, size (from `finding.object`)
  • **Wipe presence**: does the source contain an approved wipe call for this variable? Where?
  • **Error paths**: for error-path findings, identify what inputs trigger the error return
  • **Control flow**: for path-coverage findings, identify which paths lack the wipe

3. Use `Grep` to find:

  • Callers of the target function (to understand valid argument patterns)
  • Type definitions for the sensitive object (structs, typedefs)
  • Include dependencies needed by the target function

2b — Determine Inclusion Strategy

  • If the target function is `static` or the source file is ≤ `source_inclusion_threshold` lines: use `#include` to include the source file directly
  • If the target function is `extern` and the file is large: compile the target source to an object file and link via the Makefile

2c — Write the PoC

Write `{poc_output_dir}/poc_za_NNNN_category.c` (or `.rs` for Rust). The PoC must:

1. **Include `poc_common.h`** for shared helpers 2. **Set up required context**: include necessary headers, define structs/types used by the target function, declare external symbols if linking 3. **Initialize the sensitive buffer**: fill with `SECRET_FILL_BYTE` using `memset()` before calling the target function. This establishes the "secret" content that should be wiped. 4. **Call the target function with valid arguments**: use actual types and realistic values. If the function requires allocations, file handles, or other setup, include that setup code. For error-path findings, provide arguments that trigger the specific error path. 5. **Apply the verification technique** appropriate for the finding category (see Category Techniques below) 6. **Exit with the correct code**: use `POC_PASS()` if the secret persists, `POC_FAIL()` if it was wiped

**Critical**: Each PoC must be specific to the finding. Reference the actual function name, variable name, types, and sizes from the source code. Add a comment block at the top explaining:

  • Which finding this PoC demonstrates (finding ID and category)
  • What function and variable are being tested
  • What the PoC does and what a passing result (exit 0) means

2d — Rust PoC Generation

Enabled for `MISSING_SOURCE_ZEROIZE`, `SECRET_COPY`, and `PARTIAL_WIPE` only. For all other Rust finding categories, record `poc_supported: false` in the manifest with a one-line reason (e.g., "STACK_RETENTION — stack probe requires unsafe ASM intrinsics not portable across Rust versions").

**Exit code convention for Rust PoCs (via cargo test):**

  • `assert!` passes → cargo exits 0 → `"exploitable"` (secret persists)
  • `assert!` panics / test fails → cargo exits non-zero → `"not_exploitable"` (secret wiped)

**2d-i

Read more
Ships withtrailofbits-skills

A Claude Code plugin marketplace from Trail of Bits providing skills to enhance AI-assisted security analysis, testing, and development workflows. Codex can load this marketplace through its Claude marketplace compatibility.

Get the whole plugin, auto-invoked
Stats
6,493
Stars
10
Views
560
Forks
Active
Maintenance
Python
Language
CC-BY-SA-4.0
License
15h ago
Last commit
6mo ago
Created

Repo: trailofbits/skills