📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
---
|
||||
name: "cloudflare-security-audit"
|
||||
description: "Audit authorized codebases for exploitable vulnerabilities using scoped reconnaissance, adversarial review, validation, and structured reporting."
|
||||
risk: "offensive"
|
||||
source: "community"
|
||||
source_repo: "cloudflare/security-audit-skill"
|
||||
source_type: "community"
|
||||
date_added: 2026-07-13
|
||||
author: "community"
|
||||
tags: []
|
||||
tools: []
|
||||
---
|
||||
|
||||
|
||||
# Security Audit
|
||||
|
||||
> [!WARNING]
|
||||
> **Authorized Use Only.** Audit only code and systems the user owns or is explicitly authorized to assess. Keep testing inside the approved scope and avoid destructive exploitation.
|
||||
|
||||
## Example
|
||||
|
||||
```text
|
||||
User: Audit this repository for authorization bypasses and injection paths. Keep testing local and non-destructive.
|
||||
Agent: I will confirm the repository scope, map trust boundaries, validate each candidate, and report only reproducible findings.
|
||||
```
|
||||
|
||||
You are a security auditor. Your job is to find **exploitable vulnerabilities with real impact**.
|
||||
|
||||
## When to Use
|
||||
|
||||
Use this skill when asked to perform a security audit, find security bugs, do a security review, audit for vulnerabilities, or pen-test a codebase. Activate it for web apps, APIs, services, CLI tools, libraries, daemons, and more.
|
||||
|
||||
## Platform terminology
|
||||
|
||||
This skill is agent-neutral. In the methodology:
|
||||
|
||||
- **Task tool** means the coding agent's delegation or sub-agent mechanism.
|
||||
- **`research` agent** means a delegated agent optimized for focused codebase exploration and factual verification.
|
||||
- **`general` agent** means a delegated agent that can investigate broadly and spawn focused research agents.
|
||||
- **`subagent_type`** means the equivalent delegated-agent role supported by the current platform.
|
||||
|
||||
Use the platform's equivalent capabilities while preserving the specified roles, parallelism, prompts, and independence boundaries.
|
||||
|
||||
## Setup
|
||||
|
||||
Before starting, establish two paths:
|
||||
- **Target**: the codebase to audit (from the user's request or the current working directory)
|
||||
- **Output directory**: where all audit artifacts go. Ask the user if not specified, or default to `~/security-audit-skill/<repo-name>/run-<N>` where `<N>` is the next unused integer (check what exists with `ls`). Create it if it doesn't exist. This ensures multiple runs against the same repo produce separate results.
|
||||
|
||||
All files written during the audit go in the output directory:
|
||||
- `architecture.md` — Phase 1 output, fed into Phase 2 agent prompts
|
||||
- `REPORT.md` — human-readable report (Phase 4)
|
||||
- `FINDINGS-DETAIL.md` — detailed data flows for MEDIUM+ findings (Phase 4)
|
||||
- `findings.json` — machine-readable structured output (Phase 5)
|
||||
|
||||
Subagents (Phases 1, 2, 3, 6) do NOT write files — they return results to you via the Task tool. You are responsible for writing all files to the output directory.
|
||||
|
||||
### Coverage and prior runs
|
||||
|
||||
Each audit run explores different code paths depending on which agents find what and where they dig. No single run finds everything. Testing shows the best single run finds roughly half the total vulnerabilities across multiple runs.
|
||||
|
||||
**If prior runs exist** for the same repo (check `~/security-audit-skill/<repo-name>/`), read their `findings.json` files before starting Phase 2. Use them to:
|
||||
1. **Skip known findings** — don't waste agents re-discovering the same status bypass. Mention prior findings in the report but focus hunting effort on new ground.
|
||||
2. **Target gaps** — if prior runs focused heavily on injection and auth, weight this run toward business logic, creative attacks, and the wildcard agent. If prior runs missed public endpoints, focus there.
|
||||
3. **Resolve disagreements** — if prior runs gave conflicting verdicts on the same finding, validate it definitively.
|
||||
|
||||
Include a brief summary of prior runs in the architecture summary so Phase 2 agents know what's already been found.
|
||||
|
||||
**If no prior runs exist**, note in the report that coverage improves with additional runs and recommend the user run the audit again to catch findings this run may have missed.
|
||||
|
||||
## Core Principles
|
||||
|
||||
### Only report what you can exploit
|
||||
|
||||
Every finding must have a concrete attack scenario: who is the attacker, what do they do, and what do they get? "An attacker could theoretically..." is not a finding. "Send this request, get this result" is.
|
||||
|
||||
### Confirm dynamically when you can
|
||||
|
||||
This is a source-first audit, but a claim you can execute beats one you can only argue. Where the target is locally buildable — a parser, a library, a CLI, a native component — build and run it: reproduce the crash, run the payload, diff the two parsers on the same bytes. Better still, **extract the suspect code into a minimal standalone harness** and test the hypothesis in isolation — fuzz the one function, feed it the crafted input, watch what it does. Where confirmation needs infrastructure you don't have — a proxy chain, a live cache, production auth — you cannot confirm from source alone: mark it "requires deployment testing" and do not report it as confirmed. Dynamic evidence is what resolves the memory-safety and request-framing classes that static reading leaves ambiguous.
|
||||
|
||||
### Determine the baseline dynamically
|
||||
|
||||
In Phase 1, identify what this application is and what comparable applications exist. Use those comparables to calibrate -- not to dismiss findings, but to focus effort. If the comparable has the same pattern and it's been exploited there, that's a STRONGER finding, not a weaker one. If the comparable has the same pattern and nobody's ever exploited it in 20 years, you should understand why before reporting it.
|
||||
|
||||
Do NOT hardcode a specific comparable. A CMS gets compared to other CMSes. An API gateway gets compared to other API gateways. A novel application may have no meaningful comparable.
|
||||
|
||||
### Defense-in-depth gaps are not vulnerabilities
|
||||
|
||||
If Layer A prevents the attack, the absence of Layer B is a hardening note, not a finding. Report it separately if you want, but do not inflate its severity.
|
||||
|
||||
### Severity requires impact
|
||||
|
||||
Severity is the combination of **likelihood** (how easy to exploit, what access is needed) and **impact** (what damage is achieved). Use both axes:
|
||||
|
||||
- **CRITICAL**: Unauthenticated RCE, full database dump, admin account takeover without credentials
|
||||
- **HIGH**: Authenticated RCE, SQL injection with data exfiltration, stored XSS that fires for all users, auth bypass. Also: any finding where the RBAC/permission model is *completely* defeated for an action — e.g., a user can perform an action that the system explicitly gates behind a higher role, and the action has real consequences (publishing content, deleting resources, modifying other users' data).
|
||||
- **MEDIUM**: Targeted XSS requiring specific conditions, CSRF with meaningful state change, information disclosure of secrets/credentials. Also: business logic bypasses with real but limited consequences — e.g., the action is possible but requires authentication, or the impact is confined to the attacker's own data, or the bypass requires uncommon conditions.
|
||||
- **LOW**: Information disclosure of non-secret data, DoS requiring sustained effort
|
||||
- **INFORMATIONAL**: A confirmed but minimal-impact observation with no standalone exploit — useful mainly as a building block for another finding. Pure defense-in-depth gaps belong in hardening notes, not here.
|
||||
|
||||
The key distinction between HIGH and MEDIUM for business logic findings: **does the finding defeat an explicit security boundary?** Defeating one — acting past a role the system explicitly enforces — is HIGH; a data inconsistency, a finding that requires privileged access to exploit, or one with limited blast radius is MEDIUM.
|
||||
|
||||
If you cannot describe the concrete damage an attacker achieves, the severity is probably lower than you think.
|
||||
|
||||
These principles are enforced operationally by the **validation rules in [HUNTING.md](references/HUNTING.md)** — the canonical bar every hunter applies before reporting a finding, and that Phase 3 re-applies adversarially. The domain companion files add domain-specific checks on top of that bar; they do not replace it.
|
||||
|
||||
## Workflow overview
|
||||
|
||||
Follow all six phases in order:
|
||||
|
||||
1. **Recon** — Run Phase 1 from [RECONNAISSANCE.md](references/RECONNAISSANCE.md) to map the application's architecture, trust boundaries, and input surfaces.
|
||||
2. **Hunt** — Use [HUNTING.md](references/HUNTING.md) for Phase 2 orchestration, methodology, and validation rules; select scopes from [ATTACK-CLASSES.md](references/ATTACK-CLASSES.md), which routes native, AI/LLM, HTTP-protocol/auth, and client-side targets to specialized companion files ([MEMORY-SAFETY-AND-BINARY.md](references/MEMORY-SAFETY-AND-BINARY.md), [AI-AND-LLM.md](references/AI-AND-LLM.md), [WEB-PROTOCOL-AND-AUTH.md](references/WEB-PROTOCOL-AND-AUTH.md), [CLIENT-SIDE.md](references/CLIENT-SIDE.md)).
|
||||
3. **Validate** — Use Phase 3 in [VALIDATION-AND-REPORTING.md](references/VALIDATION-AND-REPORTING.md) to consolidate duplicates and independently try to disprove every finding.
|
||||
4. **Report** — Use Phase 4 in [VALIDATION-AND-REPORTING.md](references/VALIDATION-AND-REPORTING.md) to write `REPORT.md` and `FINDINGS-DETAIL.md`.
|
||||
5. **Structured output** — Use Phase 5 in [VALIDATION-AND-REPORTING.md](references/VALIDATION-AND-REPORTING.md) and `resources/report-schema.json` to write `findings.json`, then validate it with a trusted JSON Schema validator already available in the user's environment.
|
||||
6. **Independent verification** — Use Phase 6 in [VALIDATION-AND-REPORTING.md](references/VALIDATION-AND-REPORTING.md) to verify every factual claim and reconcile all outputs.
|
||||
|
||||
## Limitations
|
||||
|
||||
- Requires a coding agent with a model that supports tool use and parallel sub-agents
|
||||
- A trusted JSON Schema validator is required for structural validation in Phase 5
|
||||
- Multiple runs are needed for full coverage — a single run typically finds roughly half of the total vulnerabilities
|
||||
- The skill does not replace manual penetration testing or automated SAST/DAST tools
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
These are the mistakes that make security audits useless:
|
||||
|
||||
1. **Listing everything that deviates from OWASP as a finding.** OWASP is a checklist, not a bug list. Every real application makes tradeoffs.
|
||||
2. **Rating defense-in-depth gaps as HIGH/CRITICAL.** "Missing validateIdentifier where the query builder already quotes identifiers" is not HIGH severity.
|
||||
3. **Ignoring the deployment model.** Rate limiting at the CDN layer is a valid architecture. Not every app needs application-level rate limiting.
|
||||
4. **Treating designed behavior as a bug.** Understand the trust model before auditing. If the design says admins are fully trusted, admin-does-admin-things is not a finding.
|
||||
5. **Padding the report with LOW findings to look thorough.** Ten LOWs don't make a useful report. Three MEDIUMs do.
|
||||
6. **"Potential" findings without proof.** Either you can exploit it or you can't. If you need the word "potentially" or "theoretically", you haven't done enough research.
|
||||
7. **Ignoring what the codebase does well.** If auth is solid, say so. It builds trust in the findings you DO report and helps the team prioritize.
|
||||
8. **Constructing exploits from incorrect parser/runtime assumptions.** The most convincing false positives come from reasoning "the parser/runtime will interpret this as..." without verifying. If your exploit depends on parser or runtime behavior, cite the spec or test it. Don't assume.
|
||||
9. **Skipping business logic and creative attacks.** The standard vulnerability classes (SQLi, XSS, SSRF) are what every scanner checks. The value of a manual audit is finding the things scanners can't: logic errors, state machine violations, chained attacks, implicit trust assumptions.
|
||||
10. **Giving up too easily.** "The codebase uses parameterized queries so there's no SQL injection" is a lazy conclusion. Check EVERY use of sql.raw(). Check dynamic identifiers. Check search/FTS. Check if there's a code path that bypasses the query builder. Push.
|
||||
@@ -0,0 +1,67 @@
|
||||
# AI, LLM, and Agent Hunting
|
||||
|
||||
#### When to use this file
|
||||
|
||||
Reach for this file when the target embeds a language model in a trust-sensitive path: chatbots and assistants, RAG pipelines, agent/tool-calling loops, MCP servers and clients, code that builds prompts from untrusted input, or code that consumes model output and acts on it. These targets fail differently from ordinary web apps — the dangerous data flow is *untrusted text → model → capability or sink*, and the model is a confused deputy that will faithfully carry attacker instructions across a trust boundary the developer assumed the model would respect. It won't.
|
||||
|
||||
Use this alongside `ATTACK-CLASSES.md`, not instead of it: the transport is still HTTP, the tools still hit SQL/shell/filesystem sinks, and access control still applies. This file covers the model-specific layer on top.
|
||||
|
||||
Pick the relevant classes based on Phase 1. Split per subsystem (retrieval, tool dispatch, output rendering) for large targets.
|
||||
|
||||
## Core discipline (include in every agent prompt for this domain)
|
||||
|
||||
```
|
||||
- "The model can be prompt-injected" is NOT a finding on its own. Prompt injection that only affects the attacker's own session and their own output is a party trick. A finding requires the injection to CROSS A BOUNDARY: reach a victim's context, invoke a capability the requester lacks, exfiltrate data the requester can't see, or drive a downstream sink the attacker couldn't otherwise reach (server-side SQL/shell/SSRF beyond their own session). Name the boundary crossed.
|
||||
- The bug is in the CODE, not the model. The finding is the missing code-level gate between attacker-influenceable input and a dangerous capability or sink — point at the line that grants the capability, trusts the output, or feeds the context, not the model's mood. Non-determinism is not a defense: the model's probability of complying is an exploitability detail, never a reporting blocker. If the code makes the output harmless (output that never reaches a sink), there is no finding regardless of what the model can be talked into saying.
|
||||
- Model output is untrusted input. Trace it to its sink with the same rigor as any user input. "It came from our model" is the exact assumption being attacked.
|
||||
- A guardrail prompt ("never reveal the system prompt", "refuse harmful requests") is not a security control. Do not credit it as a mitigation. If the only thing standing between the attacker and impact is instructions in the prompt, the boundary is undefended.
|
||||
```
|
||||
|
||||
## Prompt-injection attack classes (subagent_type: `general`)
|
||||
|
||||
**Indirect injection via retrieved / ingested content**
|
||||
The high-value class. Attacker plants instructions in data the model later ingests in *someone else's* session: a RAG document, an indexed web page, a file upload, an email, an issue/PR body, a tool's response, a filename. Trace every source that reaches the prompt context and ask "who can write this, and whose session does it fire in?" Find the ingestion path; confirm the content reaches the context window unfiltered; confirm that context has a capability worth hijacking.
|
||||
|
||||
**Tool-argument injection (model output → sink)**
|
||||
The model emits a tool call and the code executes it with model-generated arguments. Those arguments hit a real sink — SQL (`query(args.filter)`), shell (`exec(args.cmd)`), file path (`readFile(args.path)`), HTTP (`fetch(args.url)` → SSRF), or another API. The code trusts the arguments because "the model produced structured output." Trace each tool handler's parameters to their sink and validate them at the handler like any request body.
|
||||
|
||||
**Direct injection into a privileged capability**
|
||||
Direct (same-session) injection only matters when the model can do something the *user* is not authorized to do directly. If the assistant runs tools under a service identity, or has a system prompt containing secrets, or can reach internal endpoints, then a user talking the model into using those crosses a privilege boundary even in their own session. If the model can only do what the user could already do via the UI, direct injection is not a finding. Hunt step: enumerate every capability the assistant holds that its users don't, then check whether same-session user text can steer the model into each.
|
||||
|
||||
**Prompt-template / delimiter injection**
|
||||
Untrusted input concatenated into the prompt without fencing or role separation, so the attacker forges structure the orchestrator trusts: a fake system turn, a fabricated prior conversation turn, or a counterfeit tool result. The finding is the assembly code — the concatenation that lets user bytes impersonate a trusted role — not the model obeying them. Find where the prompt is built and whether untrusted spans are delimited or escaped from control text.
|
||||
|
||||
## Agent and tool-calling attack classes (subagent_type: `general`)
|
||||
|
||||
**Excessive agency / confused-deputy authority**
|
||||
The agent executes tools under *its own* identity (service account, broad API key, DB superuser) rather than the requesting user's. Every tool call is then a privilege-escalation vector: the user asks, the agent acts with more authority than the user has. Check whether tool execution re-checks the *user's* permission on the *specific resource*, or just that "the agent is allowed to call this tool." The same gap at the parameter level is IDOR through tools: `get_document(id)` / `read_file(path)` with the ID filled from user text and no check that *this* user may reach *that* resource — endpoint IDOR reached by asking. Common false positive: a shared service credential that runs every query *scoped to the authenticated user's ID* is normal, safe architecture — not a confused deputy.
|
||||
|
||||
**Unbounded action loops / cost and side-effect abuse**
|
||||
Agent loops that call tools until a goal is met: can an attacker drive an expensive or irreversible loop (spend, send, delete, external API calls) through a single crafted request? Look for tool calls with side effects inside a model-controlled iteration with no per-action authorization or budget. The impact that makes this a finding crosses out of the attacker's own session — it hits the operator's bill, a shared rate/quota limit, or other tenants' availability (denial-of-wallet), so it survives the "capability they already have" test even when the attacker only touches their own request.
|
||||
|
||||
**Sub-agent / MCP trust inheritance**
|
||||
When an agent spawns sub-agents or connects to MCP servers, what identity and context do they inherit? A sub-agent or tool server that receives the full session, credentials, or a broader capability set than the task needs is a lateral-movement primitive. A malicious or compromised MCP server is an attacker that speaks directly into the model's context — treat its responses as indirect injection.
|
||||
|
||||
## Output-handling and disclosure attack classes (subagent_type: `general`)
|
||||
|
||||
**Insecure output rendering (XSS / injection via model output)**
|
||||
Model output rendered as HTML/Markdown without sanitization → stored/reflected XSS. Markdown image/link rendering is the classic exfiltration channel: the model emits `` and the client fetches it, leaking context to the attacker's server. Check where model output is displayed and whether it's treated as trusted HTML. The image-exfil channel only fires if the render surface auto-loads remote resources and no CSP `img-src` restricts the destination — if the rendering client is out of scope or unknown (native app, terminal, CSP-locked web UI), the sink is unconfirmed: treat it as unverifiable, not a finding.
|
||||
|
||||
**System-prompt / context extraction to a real secret**
|
||||
Extraction is only a finding if the context actually contains something sensitive — API keys, other users' data, internal URLs, hidden business rules that gate access. Confirm the secret is really in the context (read the prompt-assembly code) before reporting. A leaked generic "you are a helpful assistant" prompt is not a finding.
|
||||
|
||||
**Cross-session / multi-tenant context bleed**
|
||||
Conversation history, embeddings, or the KV/prompt cache keyed too broadly, so one user's context appears in another's session. Trace the cache/session key: is it scoped per user, or is there a path where a shared key mixes tenants? Related: retrieval (vector or keyword search) that lacks a per-tenant metadata/ACL filter at query time pulls another tenant's chunks into context — IDOR at the retrieval layer; confirm the query itself applies the tenant filter, not just that documents carry a tenant field. These are code bugs (bad cache key, shared buffer, unfiltered query), not model behavior — verify them in the storage/retrieval layer.
|
||||
|
||||
## Universal moves (apply across the above)
|
||||
|
||||
- **Draw the boundary before hunting.** Enumerate: what identity do tools run as, what's in the context window, who can write to each context source, where does output go. Most AI findings fall out of a correct map of these four; most AI false positives come from not drawing it.
|
||||
- **Find the capability, then find who can reach it.** Start from the most dangerous tool (delete, spend, exec, fetch-internal) and work backwards to whether untrusted text can reach its arguments. Power × reachability, same as any privileged interface.
|
||||
|
||||
## Validation rules (apply before reporting ANY finding here)
|
||||
|
||||
1. **Name the boundary crossed.** State exactly who the attacker is, whose session/identity the payload executes in, and what they get that they couldn't get directly. If attacker and victim are the same principal and the capability is one they already have, it is not a finding.
|
||||
2. **For confused-deputy / excessive-agency claims, prove both halves.** Show (a) the tool performs no per-resource check scoped to the requesting user, AND (b) the action is one the user could not perform through a normal authenticated request. A shared service credential with per-user query scoping fails both tests and is not a finding.
|
||||
3. **Cite the trusting line and prove the taint reaches it.** For tool-argument and output findings, show the concrete sink (the `exec`/`query`/`fetch`/`innerHTML`) with model-influenced data reaching it unvalidated; for extraction/disclosure findings, cite the prompt-assembly code and confirm the secret or cross-tenant data is really in the context. If you can't cite the code, you have a black-box observation, not a finding.
|
||||
4. **Don't assert capabilities you can't see in source.** Claims that depend on deployment facts not in the repo — whether an "internal-only" endpoint is actually unreachable by the user, what a tool's target really exposes, which client renders the output — are unverifiable from source. If the user could reach the same thing directly (flat network, same origin), it is not a privilege crossing. Confirm the capability and the boundary in code, or mark it unverifiable rather than reporting it.
|
||||
5. **Return ONLY confirmed findings** with the boundary crossed, the trusting code path, and the observable result — or "No exploitable AI/LLM issues found" if that's honest.
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
# Attack Classes
|
||||
|
||||
#### Attack classes — choose and split based on Phase 1
|
||||
|
||||
Select attack classes relevant to the application type. Not every class applies to every codebase. The list below is a starting point — add application-specific ones based on Phase 1. For large codebases, split classes per subsystem.
|
||||
|
||||
> **Native / binary / kernel targets** (C/C++/Rust-unsafe, kernel modules, parsers and decoders, reverse-engineering tooling, runtimes/JITs, firmware): the web-oriented classes below fit poorly. Use the memory-safety, binary, and kernel classes in [MEMORY-SAFETY-AND-BINARY.md](MEMORY-SAFETY-AND-BINARY.md) instead of or alongside them.
|
||||
>
|
||||
> **AI / LLM / agent targets** (chatbots, RAG pipelines, tool-calling agents, MCP servers/clients, anything that builds prompts from untrusted input or acts on model output): use the prompt-injection, agency, and output-handling classes in [AI-AND-LLM.md](AI-AND-LLM.md) alongside the classes below.
|
||||
>
|
||||
> **HTTP-protocol and auth targets** (reverse proxies, CDNs, API gateways, custom HTTP parsers, and anything implementing sessions, JWT, OAuth/OIDC, or SAML): use the request-framing, cache, and auth-protocol classes in [WEB-PROTOCOL-AND-AUTH.md](WEB-PROTOCOL-AND-AUTH.md) alongside the classes below.
|
||||
>
|
||||
> **Client-side / browser targets** (SPAs, browser extensions, embedded webviews, anything using `postMessage`, CORS, or WebSockets, or that renders untrusted content in the DOM): use the DOM-injection, messaging-trust, and UI-redress classes in [CLIENT-SIDE.md](CLIENT-SIDE.md) alongside the classes below.
|
||||
|
||||
**Injection** (subagent_type: `general`)
|
||||
Trace untrusted input from entry point to dangerous sink. What counts as a "dangerous sink" depends on the application:
|
||||
- Web apps: SQL queries, HTML output, shell commands, template engines, file paths, HTTP redirects, deserialization
|
||||
- Libraries: any function that processes caller-supplied data without validation — buffer operations, parsers, format strings
|
||||
- CLI tools: shell command construction, file path handling, environment variable interpolation
|
||||
- Services: query construction, message serialization, log injection, LDAP/XPATH queries
|
||||
- Client-side (browser/JS): DOM XSS, prototype pollution, `postMessage`/origin trust, and other browser-side classes — see [CLIENT-SIDE.md](CLIENT-SIDE.md)
|
||||
|
||||
Don't just check the obvious direct paths. Look for indirect injection: data stored safely, then retrieved and used in a dangerous context by different code. Look for injection through field names, keys, headers, and metadata — not just values. Look for injection into secondary systems (logs, caches, search indexes, analytics).
|
||||
|
||||
**Access control** (subagent_type: `general`)
|
||||
Can a caller do something they shouldn't? Go beyond checking whether permission checks exist — verify they check the *right* permission for the *right* resource via the *right* mechanism:
|
||||
- Is there a path to the same state change that checks a different (weaker) permission?
|
||||
- Can a field in the request body override what the permission system intended to restrict?
|
||||
- Are there endpoints that gate on authentication but forget authorization?
|
||||
- Does the same resource have multiple access paths with inconsistent checks?
|
||||
- What about bulk/batch/export/import operations — do they enforce per-item permissions?
|
||||
|
||||
For complex access models, split into separate agents for auth bypass vs authorization logic.
|
||||
|
||||
**Resource and file handling** (subagent_type: `general`)
|
||||
- Path traversal (reading/writing outside intended directories) — including through symlinks, encoded sequences, and null bytes
|
||||
- SSRF (making the application fetch attacker-controlled URLs) — including through redirects, DNS rebinding, and URL parser differentials
|
||||
- Unsafe deserialization, archive extraction (zip slip), temp file handling
|
||||
- Memory safety (if applicable): buffer overflows, use-after-free, integer overflow
|
||||
- Race conditions on file operations (TOCTOU between check and use)
|
||||
|
||||
**Cryptography and secrets** (subagent_type: `general`)
|
||||
- Weak randomness for security-critical values (tokens, keys, nonces)
|
||||
- Hardcoded secrets, secrets in logs, error messages, URLs, or client-visible responses
|
||||
- Broken key derivation, missing HMAC verification, nonce reuse
|
||||
- Timing side-channels on secret comparison
|
||||
- Misuse of crypto primitives (ECB mode, unauthenticated encryption, static IVs, etc.)
|
||||
- What happens when crypto operations fail? Does the error path fall back to no-crypto?
|
||||
|
||||
**Business logic** (subagent_type: `general`)
|
||||
This is where the real bugs hide. Standard scanners can't find logic errors. For each major workflow:
|
||||
- **State machine violations**: Can you skip steps? Go backwards? Reach an invalid state? What happens if you replay a completed flow? What about partial failure — if step 2 of 3 fails, is step 1 rolled back?
|
||||
- **Race conditions with business impact**: Concurrent operations that produce invalid states (double-spend, double-approve, lost updates). Focus on operations that check-then-act non-atomically.
|
||||
- **Numeric/quantity manipulation**: Negative values, zero values, overflow, precision loss, type coercion between string and number.
|
||||
- **Access boundary violations**: Not "does the permission check exist" but "is it the right check for the business rule?" Can input to one operation bypass a restriction enforced on a different operation for the same effect?
|
||||
- **Implicit trust assumptions**: Data from storage, config, other components, or plugins assumed safe because "we validated it on the way in." What if a different code path wrote it?
|
||||
- **Time-based logic**: Expiry checks, scheduling, rate windows, clock skew. What happens at exact boundary moments? What about timezone differences between components?
|
||||
- **Default and fallback behavior**: What's the security posture when config is missing? When a feature flag is off? When a dependency is unavailable? When the system is mid-migration?
|
||||
|
||||
**Feature abuse and data leakage** (subagent_type: `general`)
|
||||
Legitimate features used for unintended purposes. Don't look for bugs in the code — look for bugs in the design:
|
||||
- **Export/backup as exfiltration**: Can a low-privilege user trigger an export, snapshot, or backup that includes data above their access level? Can they export other users' data? Does the export include deleted/draft/private content? Revision history that was supposed to be pruned?
|
||||
- **Import/restore as injection**: Can import overwrite existing data? Can it create records that bypass normal validation? Can it inject content into collections the user doesn't have write access to? Does it respect the same permission model as the UI?
|
||||
- **Search/filter/sort as oracle**: Can search queries reveal whether content exists that the user can't directly access? Do filter parameters let users probe statuses, roles, or fields they shouldn't know about? Does sorting by a hidden field reveal its values through result ordering?
|
||||
- **Enumeration through side effects**: Do error messages differ between "doesn't exist" and "you don't have access"? Do response times differ? Response sizes? HTTP status codes? Can you enumerate users through password reset, invite, or registration flows?
|
||||
- **Preview/draft/staging leakage**: Are preview tokens scoped to one item or do they unlock broader access? Can draft content be discovered through search, RSS feeds, sitemaps, or API listing endpoints? Can cache headers cause a CDN to serve private content publicly?
|
||||
- **Notification/webhook as SSRF**: Can a user set a notification URL, webhook URL, or callback URL that the server fetches? Is it validated against internal networks? What about after a redirect?
|
||||
|
||||
**Chained attacks and trust boundaries** (subagent_type: `general`)
|
||||
Individual safe behaviors that become dangerous in combination. Think about the full system:
|
||||
- **Multi-step chains**: Map out what a low-privilege user CAN do, then look for combinations. Info disclosure (learning a resource ID) + IDOR (accessing it directly) + missing rate limit (brute-forcing the ID space). Open redirect + OAuth callback = token theft. Benign XSS in a low-value context + CSRF to escalate it.
|
||||
- **Cross-component trust gaps**: Component A validates input and passes it to component B. Does B re-validate or trust A? What if A's validation is subtly different from what B needs (e.g., A allows 255 chars but B truncates at 128, creating a different string)? What about plugin/extension trust — can third-party code manipulate core state, bypass permission hooks, or access storage directly?
|
||||
- **Second-order attacks**: Data safe when stored but dangerous when used in a different context. A field name safe in SQL becomes a key in a JSON path expression. A slug safe in a URL becomes part of a file path. Content stored HTML-escaped gets double-escaped or rendered in a context that expects raw text. Config values stored as strings get parsed as URLs, regexes, or templates.
|
||||
- **Scope and capability escalation**: Tokens, API keys, or OAuth scopes that grant broader access than their name implies. A `read` scope that also allows listing draft content. Session cookies that survive a role downgrade. Plugin capabilities that provide a stepping stone to higher access. MCP or AI tool integrations that inherit the user's full session.
|
||||
- **Timing and ordering**: Can you use a feature before setup/migration is complete? Act on a resource between soft-delete and hard-delete? Use a token between revocation and cache expiry? Exploit the gap between two non-atomic operations (check-then-act, read-then-write, validate-then-use)?
|
||||
- **Rollback and recovery abuse**: What happens when an operation is undone? Undelete, restore from backup, revert a revision, cancel a pending action. Does the rollback restore more than intended? Does it bypass current permissions? Can you restore a resource into a state that's no longer valid?
|
||||
|
||||
**Wildcard** (subagent_type: `general`)
|
||||
You are not given a category. You are given the codebase and told to break it.
|
||||
|
||||
Ignore the standard vulnerability classes — other agents are covering those. Your job is to find the thing nobody thought to look for. Read code that looks boring. Follow functions that seem unrelated to security. Get curious about the weird stuff.
|
||||
|
||||
Some starting points, but don't limit yourself to these:
|
||||
- What's the strangest code in the codebase? Why does it exist? What happens if it's abused?
|
||||
- Are there any features that feel half-finished, experimental, or bolted on? Those have the weakest security because they got the least review.
|
||||
- What happens if you use the API in a way the frontend never would? The UI constrains users, but the API doesn't. What API calls are possible but never made by the client?
|
||||
- Are there any hidden or undocumented endpoints, parameters, headers, or features? Look at route registrations, middleware, and config for things that aren't in the docs.
|
||||
- What happens when you mix features that weren't designed to work together? Localization + preview + caching. Import + plugins + webhooks. OAuth + impersonation + API keys.
|
||||
- Is there anything interesting in the git history? Reverted security fixes, commented-out auth checks, secrets that were committed then removed (still in history).
|
||||
- What would you do if you had a valid account but wanted to cause maximum damage without being detected? Not escalation — sabotage. Corrupting data, poisoning caches, exhausting resources, creating confusing state.
|
||||
- Are there any operations that are irreversible? What if you trick an admin into performing one?
|
||||
- What assumptions does the code make about the environment? That the database is local, that the clock is accurate, that DNS is trustworthy, that the filesystem is case-sensitive?
|
||||
- Look at the test files — what are they NOT testing? What edge cases did the developer think about (tests exist) vs. what they didn't (no tests)?
|
||||
|
||||
Follow rabbit holes. If something looks weird, dig. If a function has a comment explaining why it's safe, verify the explanation. If a variable is named `temp` or `hack` or `legacy`, read every line of it.
|
||||
|
||||
**Obvious things** (subagent_type: `general`)
|
||||
The other agents are hunting for subtle bugs. This agent checks the dumb stuff that's easy to overlook because everyone assumes someone else already checked it:
|
||||
- Are there any hardcoded passwords, API keys, tokens, or secrets in the source? (grep for `password`, `secret`, `apikey`, `token`, `Bearer`, `-----BEGIN`, common default passwords)
|
||||
- Are there any TODO/FIXME/HACK/XXX comments that reference security? (`TODO: add auth`, `FIXME: validate input`, `HACK: skip permission check`)
|
||||
- Is debug mode / dev mode properly gated? Can it be enabled in production via environment variable, query parameter, or header?
|
||||
- Are there test/example/seed credentials that work in production?
|
||||
- Is there a `/debug`, `/admin`, `/test`, `/status`, `/health`, `/metrics`, `/env`, `/.env`, `/config` endpoint that's unprotected?
|
||||
- Are there any `.env`, `.env.local`, `credentials.json`, `*.pem`, `*.key` files checked into the repo?
|
||||
- Does the `.gitignore` actually cover secrets, uploads, and local config?
|
||||
- Are dependencies pinned? Are there known CVEs in the dependency tree? (check lockfiles)
|
||||
- Are there any `eval()`, `exec()`, `child_process`, `Function()`, `vm.runInContext`, `import()` with dynamic input?
|
||||
- Are CORS headers set to `*` or overly permissive? Is `Access-Control-Allow-Credentials` combined with a wildcard origin?
|
||||
- Are cookies missing `HttpOnly`, `Secure`, or `SameSite` attributes?
|
||||
- Are there any open redirects? (parameters named `redirect`, `return`, `next`, `url`, `goto`, `continue` that feed into redirects without validation)
|
||||
- Is TLS enforced? Are there any HTTP-only endpoints?
|
||||
- Are error responses in production returning stack traces, internal paths, or SQL errors?
|
||||
|
||||
This agent doesn't need to be creative. It needs to be thorough and literal. Check every item. Report what it finds.
|
||||
|
||||
IMPORTANT: For any finding this agent reports, it must verify the full code path, not just surface appearance. If a cookie is missing `HttpOnly`, check whether the cookie contains security-sensitive data and whether JS needs to read it by design. If an error message contains a field name, check whether the field is ever actually populated with sensitive data. A flag is not a finding — trace the impact before reporting.
|
||||
@@ -0,0 +1,67 @@
|
||||
# Client-Side and Browser Hunting
|
||||
|
||||
#### When to use this file
|
||||
|
||||
Reach for this file when meaningful trust decisions or untrusted rendering happen in the browser: single-page apps, browser extensions, embedded webviews, and anything that renders attacker-influenceable content into the DOM, receives cross-window messages, opens WebSockets, or serves credentialed cross-origin responses. These bugs live in code the server never executes — the fragment after `#`, `window.name`, a `postMessage` payload — so server-side escaping and the classes in `ATTACK-CLASSES.md` don't cover them.
|
||||
|
||||
Use alongside `ATTACK-CLASSES.md`. The injection class there covers server-side sinks; this file covers the browser-side source→sink paths, cross-origin trust, and UI-redress classes that only exist client-side.
|
||||
|
||||
Pick the relevant classes based on Phase 1. Split per surface (DOM rendering, message/WebSocket handlers, auth-carrying endpoints) for large front-ends.
|
||||
|
||||
## Core discipline (include in every agent prompt for this domain)
|
||||
|
||||
```
|
||||
- Client-side taint needs a controllable SOURCE and an executing SINK on the client path. A source with no sink, or a sink fed only server-rendered trusted data, is not a finding. Name both and show untrusted data reaching the sink unsanitized.
|
||||
- The impact must cross to a victim or cross an origin. XSS in the attacker's own DOM, or a "leak" of the attacker's own data, is not a finding. State whose session executes it or whose cross-origin data it steals.
|
||||
- Framework auto-escaping is a real mitigation. React/Vue/Angular escape interpolation by default — the finding is where the code opts OUT (`dangerouslySetInnerHTML`, `v-html`, `bypassSecurityTrust*`, `$sce.trustAs*`). Do not report escaped interpolation.
|
||||
- A missing header or attribute (X-Frame-Options, frame-ancestors, rel=noopener, SameSite) is only a finding with a concrete sensitive action behind it. A bare missing flag with no state-changing action or credentialed cross-origin read is a hardening note.
|
||||
```
|
||||
|
||||
## DOM-based injection attack classes (subagent_type: `general`)
|
||||
|
||||
**DOM-based XSS**
|
||||
Trace client-side sources — `location.hash`/`search`/`href`/`pathname`, `document.referrer`, `window.name`, `postMessage` data, `document.cookie` — into execution sinks: `innerHTML`/`outerHTML`, `document.write`, `eval`, `Function`, `setTimeout`/`setInterval` with a string argument, `element.src`/`href` set to a `javascript:` URI, jQuery `$(...)`/`.html()`, or framework escape hatches (`dangerouslySetInnerHTML`, `v-html`, `bypassSecurityTrustHtml`). The bug is source→sink with no sanitization *on the client path*; server-side escaping never sees fragment or `window.name` data.
|
||||
|
||||
**DOM clobbering**
|
||||
Attacker-injected `id`/`name` attributes — surviving an HTML sanitizer that strips script but allows attributes — that shadow a global the script later reads (`window.config`, a `form.action`, a flag checked before initialization). Look for code reading `window.X`/`document.X` that an injected element named `X` can override. Requires a markup-injection sink that permits `id`/`name`.
|
||||
|
||||
## Client-side trust and messaging attack classes (subagent_type: `general`)
|
||||
|
||||
**postMessage origin trust**
|
||||
A `message` handler that acts on `event.data` (writes the DOM, calls a privileged function, stores a token) without checking `event.origin` against an allowlist, or with a weak check (`indexOf`, `startsWith`, unanchored regex, `endsWith` on the host). Also the send side: `postMessage(data, '*')` leaking data to any embedder. Confirm the handler does something security-relevant with the data.
|
||||
|
||||
**Cross-site WebSocket hijacking (CSWSH)**
|
||||
A WebSocket handshake authenticated only by ambient cookies, with no `Origin` check and no per-session CSRF token — an attacker page opens a socket in the victim's authenticated context and reads/writes their data. Find the upgrade handler; check whether it validates `Origin` and binds to a token, not just the cookie.
|
||||
|
||||
**CORS with credentials**
|
||||
A server that reflects the request `Origin` into `Access-Control-Allow-Origin` while sending `Access-Control-Allow-Credentials: true`, or allowlists `null` or a weak suffix match — any origin then reads authenticated responses. The finding is reflection or weak-match *with credentials*, not a wildcard alone (`*` with credentials is rejected by browsers).
|
||||
|
||||
## UI-redress and navigation attack classes (subagent_type: `general`)
|
||||
|
||||
**Clickjacking**
|
||||
A state-changing action (transfer, delete, grant, confirm) reachable in a framed page with no `X-Frame-Options: DENY`/`SAMEORIGIN` and no `frame-ancestors` CSP and no UI framebusting. A missing frame guard on a read-only page with no sensitive action is not a finding — require the action.
|
||||
|
||||
**Reverse tabnabbing**
|
||||
A link whose target is attacker-influenceable, opened with `target="_blank"`, letting the opened page rewrite `window.opener.location` to a phishing origin. Modern browsers imply `noopener` for `target="_blank"`, so this is a finding only where the code sets `rel="opener"` explicitly, uses `window.open` without `noopener`, or the threat model includes older browsers — check before reporting.
|
||||
|
||||
**Client-side open redirect / navigation**
|
||||
A navigation built from a client source (`location = params.get('next')`, `location.hash` fed into `location.href`, a router redirect) with no allowlist — including `javascript:`/`data:` schemes that promote the redirect into XSS. Distinct from a server open-redirect: the sink is in JS, so the server never sees it.
|
||||
|
||||
## Prototype pollution attack classes (subagent_type: `general`)
|
||||
|
||||
**Prototype pollution and gadget chain**
|
||||
An attacker-controlled key (`__proto__`, `constructor.prototype`) reaching a *nested/recursive* write — a deep merge, `lodash.set`-style path assignment, `obj[a][b]=v` with an attacker-controlled segment, or a query-string parser that builds nested objects — that lands on `Object.prototype`. A plain `JSON.parse` or shallow `Object.assign` does NOT pollute. Require the recursive sink AND a gadget that reads the polluted property (an options object checked with `opts.isAdmin`, a template reading a config default, a sink that concatenates a polluted `src`). Pollution with no reachable gadget is not exploitable; the gadget is what turns it into XSS, auth bypass, or (in Node) RCE.
|
||||
|
||||
## Universal moves (apply across the above)
|
||||
|
||||
- **Start from the sink and walk back to a client source.** Grep the execution sinks (`innerHTML`, `eval`, `document.write`, `dangerouslySetInnerHTML`, `postMessage`, `new WebSocket`) and trace each argument back to `location`/`name`/`referrer`/message data. A sink fed only server-rendered trusted data is not a finding.
|
||||
- **Server escaping ends where the fragment begins.** Data after `#`, plus `window.name` and cross-window messages, never reaches the server — so server-side filters can't see it. That blind spot is the DOM-XSS goldmine.
|
||||
- **Enumerate the escape hatches.** In an auto-escaping framework, the candidate list *is* every `dangerouslySetInnerHTML`/`v-html`/`bypassSecurityTrust*`/`$sce.trustAs*` call. Start there.
|
||||
|
||||
## Validation rules (apply before reporting ANY finding here)
|
||||
|
||||
1. **Confirm a controllable source AND an executing sink on the client path.** Cite the source (`location.hash`, `event.data`, `window.name`) and the sink (`innerHTML`, `eval`, navigation), and show untrusted data reaching the sink without sanitization. A source with no sink, or a sink fed only trusted data, is not a finding.
|
||||
2. **For prototype pollution, prove the recursive write AND a gadget.** Show the nested/recursive assignment that reaches `Object.prototype`, then the code that later reads the polluted property to a security-relevant effect. Pollution with no reachable gadget is not exploitable.
|
||||
3. **For messaging / CORS / WebSocket, show the origin check is absent or weak.** Cite the handler and the missing or `indexOf`/`startsWith`/unanchored-regex origin check, and that the data drives a security-relevant action or a credentialed cross-origin read. Reflection plus credentials, not a bare wildcard.
|
||||
4. **For UI-redress, require the sensitive action behind the missing guard.** Name the state-changing action that gets framed (clickjacking) or the attacker-controlled `_blank` link (tabnabbing). A missing `X-Frame-Options`/`rel=noopener` with nothing sensitive behind it is a hardening note — and framebusting, `frame-ancestors`, or the browser's `noopener` default may already defeat it. Check before reporting.
|
||||
5. **Return ONLY confirmed findings** with the client source→sink path and whose session it fires in — or "No exploitable client-side issues found" if that's honest.
|
||||
@@ -0,0 +1,110 @@
|
||||
# Vulnerability Hunting
|
||||
|
||||
### Phase 2: Hunt for vulnerabilities
|
||||
|
||||
Launch **multiple `general` agents in parallel** via the Task tool. Use `general`, not `research` — general agents can spawn their own sub-agents via the Task tool, so when a hunter finds a rabbit hole that needs deeper investigation (e.g., tracing injection into an auth subsystem it doesn't fully understand), it can spin up a focused `research` sub-agent rather than trying to do everything in one context window.
|
||||
|
||||
Each agent gets the architecture summary from Phase 1 injected into its prompt plus the hunting methodology and validation rules. Launch them in a single message so they run concurrently.
|
||||
|
||||
**How many agents?** Use Phase 1 to decide. More focused agents produce better results than broad ones that run out of context. For a small library, 3-4 agents may suffice. For a large application with distinct subsystems, launch 8-12+ — split by attack class AND by subsystem. If Phase 1 revealed an auth system, a plugin system, a media pipeline, and a comment engine, each of those could warrant its own injection agent, its own logic agent, etc.
|
||||
|
||||
Every agent prompt MUST include:
|
||||
1. The architecture summary from Phase 1 (copy it in verbatim)
|
||||
2. The specific attack class and scope to investigate
|
||||
3. Relevant file paths from Phase 1 as starting points
|
||||
4. The hunting methodology (below)
|
||||
5. The validation rules (below)
|
||||
|
||||
#### Hunting methodology — include in every Phase 2 agent prompt
|
||||
|
||||
Tell each agent to think like an attacker, not a code reviewer:
|
||||
|
||||
```
|
||||
## How to hunt
|
||||
|
||||
Don't just check if defenses exist. Try to break them.
|
||||
|
||||
READ THE CODE AT DEPTH. Don't stop at the first function. Follow the data through
|
||||
every layer — from the entry point through validation, transformation, storage, retrieval,
|
||||
and output. Bugs live in the gaps between layers.
|
||||
|
||||
Think about these angles:
|
||||
|
||||
1. THE HAPPY PATH IS DEFENDED. ATTACK THE SAD PATH.
|
||||
Error handlers, fallback branches, catch blocks, default cases, timeout paths,
|
||||
retry logic, cleanup routines. What happens when things fail? Are errors handled
|
||||
with the same rigor as success? Does a failed validation leave state half-modified?
|
||||
|
||||
2. WHAT HAPPENS AT BOUNDARIES?
|
||||
Empty input. Maximum-length input. Null vs undefined vs missing. Zero. Negative numbers.
|
||||
Unicode edge cases. The first item and the last item. One more than the maximum. Exactly
|
||||
at the rate limit. The moment a token expires.
|
||||
|
||||
3. WHAT DO COMPONENTS ASSUME ABOUT EACH OTHER?
|
||||
Does the database layer assume the API layer validated input? Does the renderer assume
|
||||
content was sanitized on write? Does the auth middleware assume routes register themselves
|
||||
correctly? Find where trust is implicit and test whether it's justified.
|
||||
|
||||
4. WHAT IF OPERATIONS HAPPEN IN THE WRONG ORDER?
|
||||
Call step 3 before step 1. Call delete during create. Send the callback before the request.
|
||||
Hit the confirmation endpoint without starting the flow. Replay a completed flow.
|
||||
|
||||
5. WHAT IF TWO THINGS HAPPEN AT ONCE?
|
||||
Two requests to the same resource. Modify while reading. Delete while iterating.
|
||||
Publish while someone else is editing. Two users claiming the same unique resource.
|
||||
|
||||
6. WHERE DO TWO PARSERS OR VALIDATORS DISAGREE?
|
||||
Input accepted by the schema but rejected by the database. URL parsed differently by
|
||||
the router vs the application code. Content-type header says one thing, body is another.
|
||||
Filename extension vs MIME type vs magic bytes.
|
||||
|
||||
7. WHAT SURVIVES A ROUND TRIP?
|
||||
Data stored then retrieved — is it the same? Does encoding change? Does escaping
|
||||
double-up? Is a relative path resolved differently on read vs write? Does serialization
|
||||
lose type information?
|
||||
|
||||
8. WHAT DOES THE CONFIGURATION CONTROL?
|
||||
What happens when config is missing or default? Can an environment variable override a
|
||||
security control? Does a feature flag disable validation? What's the security posture
|
||||
during setup/first-run before config is complete?
|
||||
|
||||
9. FOLLOW THE MONEY (OR THE PRIVILEGE).
|
||||
For every operation that changes state, ask: who authorized this? Trace back to the
|
||||
permission check. Is it checking the right permission? Is it checking against the right
|
||||
resource? Is there a parallel path to the same state change that checks differently
|
||||
or not at all?
|
||||
|
||||
10. LOOK FOR LEAKED CONTEXT.
|
||||
Error messages that reveal internal paths. Stack traces in production. Timing differences
|
||||
that reveal whether a record exists. Response size differences. HTTP headers that
|
||||
disclose versions. Debug endpoints that survived into production.
|
||||
|
||||
11. WHAT PARAMETERS OVERRIDE SECURITY-RELEVANT DEFAULTS?
|
||||
Where a default is safe but a user-supplied parameter can change it. Look for
|
||||
every input that overrides a security-relevant default and check if the override
|
||||
is gated by appropriate permissions.
|
||||
|
||||
12. WHERE DO UNVERIFIED CLAIMS DRIVE TRUST DECISIONS?
|
||||
Anywhere self-declared identity, capability, or metadata influences an access
|
||||
or trust decision without independent verification.
|
||||
|
||||
GO DEEP, AND PROVE IT. You can spawn sub-agents: if evaluating a candidate finding needs
|
||||
deep understanding of a subsystem, use the Task tool to launch a research agent instead of
|
||||
holding everything in one context. And where the code is locally runnable, don't just reason
|
||||
about it — extract the suspect function into a minimal harness (or build and run the target)
|
||||
and test the hypothesis directly. A reproduced result beats an argued one.
|
||||
|
||||
YOUR SCOPE IS YOUR PRIMARY FOCUS, NOT A BOUNDARY.
|
||||
If while investigating your assigned area you notice something wrong in a different
|
||||
category — a permission issue while tracing injection, a race condition while reviewing
|
||||
auth — report it. Don't ignore a bug because it's "not your area." Attackers don't
|
||||
respect category boundaries.
|
||||
|
||||
## Validation rules — apply before reporting ANY finding
|
||||
1. You MUST construct a concrete attack (exact inputs, requests, or action sequence)
|
||||
2. The attack MUST achieve meaningful impact (not just "learn field names" or "cause an error")
|
||||
3. Check if another layer already prevents exploitation — if so, it's a hardening note, not a finding
|
||||
4. If the baseline comparable has the same pattern, note whether it's been exploited there
|
||||
5. If your exploit depends on parser/runtime behavior, verify against the relevant spec or implementation — do not reason from intuition.
|
||||
6. Return ONLY confirmed findings with concrete attacks, or "No exploitable vulnerabilities found" if that's honest.
|
||||
```
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
# Memory Safety, Binary, and Kernel Hunting
|
||||
|
||||
#### When to use this file
|
||||
|
||||
The attack classes in `ATTACK-CLASSES.md` are tuned for web apps, APIs, and services. Reach for *this* file when the target processes untrusted bytes in a memory-unsafe context: C/C++/Objective-C, Rust `unsafe`, kernel modules and drivers, parsers and decoders (image/video/font/archive/PDB), reverse-engineering and dev tooling, network daemons, firmware, and language runtimes/JITs. These targets fail differently from web apps — the bug is a memory corruption or a logic error in privileged code, not an injection or an access-control gap — so the hunt needs a different lens.
|
||||
|
||||
Pick the relevant classes based on Phase 1. Split per subsystem for large targets.
|
||||
|
||||
## Core discipline (include in every agent prompt for this domain)
|
||||
|
||||
```
|
||||
- A buffer sized for the common case can still overflow on adversarial input. Verify every "this length is bounded" claim against the WORST case, not the happy path.
|
||||
- "Huge count = guaranteed crash" is FALSE. An oversized copy length is size- and libc-dependent: it often faults, but the copy primitive can also wrap or land a short, scattered write first. Determine the actual write behavior before downgrading to DoS-only.
|
||||
- Static offsets are a guess; the crash dump is truth. An unreproduced bug is not a bug — if you claim exploitability, say exactly which input reaches which sink and what the observable result is.
|
||||
- Sanitizer silence ≠ safety where the deref is outside instrumented code (hand-written asm, JIT-emitted, intra-allocation). Don't trust a clean ASan run for those.
|
||||
```
|
||||
|
||||
## Memory-safety attack classes (subagent_type: `general`)
|
||||
|
||||
**Spatial: out-of-bounds read/write**
|
||||
- **Length subtraction underflow** — a copy/loop bound is `a - b` (`uri.len - prefix`, `total - consumed`) where the attacker can make `b > a`. Negative → casts to ~SIZE_MAX. Map which bytes land where; don't assume "just a crash."
|
||||
- **Operator-precedence / multi-term length errors** — an unparenthesized `+`/`-` length chain (`endp - begin + consume`) that silently over-adds when one term is attacker-sized. Audit each CALLER's value of the variable term — the common caller is often correct-by-accident on the zero path and survives testing.
|
||||
- **`sizeof(*p)` vs `sizeof(element)` pointer-depth confusion** — an allocation/copy size computed one indirection too deep (`gid_t **` → `sizeof(*p)`=8 not 4). The bounds check passes because it uses the same wrong unit. Compiled tell: `shl $0x3` where `shl $0x2` was meant.
|
||||
- **Wire-length into fixed stack buffer** — a function rebuilds a network/user blob into a fixed array using an attacker length field, with the bounds check missing/late or computed on the wrong headroom (a header pre-written into the buffer). Re-derive true headroom (size minus fixed prefix); confirm no guard precedes the copy.
|
||||
|
||||
**Temporal: use-after-free / lifetime**
|
||||
- **Embedded waiter-anchor freed without draining** — a struct embeds a list head (`selinfo`/`knlist`/timer/knote) reachable by unprivileged poll/select/kqueue, and a free path destroys it but skips the drain a wakeup path does. For every `selrecord(&obj->x)`, require a matching drain on EACH path that can free `obj`.
|
||||
- **Cached raw pointer + reallocating owner** — a view caches `base+offset`, a grow/realloc path moves the backing store, and the invalidation walks only the *current* wrapper's view set while grow *replaces* the wrapper. The original view dangles.
|
||||
|
||||
**Type confusion**
|
||||
- **Read-and-write confusion → addrof/fakeobj** — a confusion that reads a pointer slot as a scalar (addrof) and writes a scalar into a pointer slot (fakeobj). The standard pivot of runtime/JIT exploitation; the prior art is about the PROBLEM CLASS (NaN-boxing, cached typed-array data pointer), not the specific target.
|
||||
- **Hierarchical-walker leaf check skipped** — a page-table / nested / B-tree / extent walker checks the valid bit but not the leaf/size bit at level N, then descends treating an attacker-owned leaf as an interior node.
|
||||
|
||||
**Value: uninitialized & oracle**
|
||||
- **Uninitialized worst-case buffer + observable compare = read oracle** — a buffer sized to a MAX constant is partially written, then compared against attacker bytes with an attacker-controlled compare length where match/no-match is observable. No memory-disclosure bug needed; the gap between actual output and MAX-size is the leak window. Brute one byte/connection, hint the structural bits, parallelize.
|
||||
|
||||
## Kernel & privileged-interface attack classes (subagent_type: `general`)
|
||||
|
||||
- **User-copy bounds + double-fetch (TOCTOU)** — a syscall/ioctl/Mach-trap entry whose user-copy primitive (`copyin` / `copy_from_user`) brings attacker memory in, then re-reads the SAME user address after a check. Any fact derived from concurrently-mutable user memory and trusted on a later pass is a double-fetch even when each op is individually correct.
|
||||
- **Object lifecycle / UAF (IOKit/OSObject and friends)** — unbalanced retain/release on an externally-reachable object; a method that releases on one path but a sibling dispatch (compat/fallback/ptrace) forgot it. Diff the duplicated dispatch paths.
|
||||
- **Unchecked downcast / type confusion** — `OSDynamicCast` (or any tagged-union cast) whose result is used without a null check, or a selector/index into a dispatch table without a bounds check.
|
||||
- **World-writable / under-permissioned powerful interface** — a device node, admin socket, or mgmt API exposed more broadly than its power, that validates the request SHAPE (index in range) but never the requester's AUTHORITY over the named resource. Danger = power × reachability; enumerate the surface reachable from the *actual* untrusted context first.
|
||||
- **Validate-then-act-on-stale-state** — a fast path and a compat/ptrace/fallback path to the same operation where one copy forgot a guard the other performs.
|
||||
|
||||
## Universal moves (apply across the above)
|
||||
|
||||
- **Audit the incomplete fix.** A targeted patch is a high-signal pointer to a dangerous sink with the analysis already done. Read the diff → find the exact sink it hardened → scan the same function, parallel paths, and alternate callers for the SAME tainted-data-to-sink shape the patch missed. Incomplete fixes are their own bug class.
|
||||
- **Trust asymmetry between two ends of a protocol.** A filter/verification/size-cap installed on one side of a connection but missing on the symmetric call on the other. Find the protective call → grep its mirror on the opposite role → if absent, the earliest unprotected pre-auth parse is the prize. A malicious server/MITM is a real attacker.
|
||||
- **Chain a weak primitive.** A blocked path means you haven't found the right pivot, not that it's unexploitable. Always ask "what does this actually let me do, and what runs automatically once I can put bytes on disk?" (plugin dirs, autoload, `.git/hooks`, `conftest.py`).
|
||||
- **Hunt where the crowd isn't.** The tools researchers themselves trust — debuggers, disassemblers, scanners, dev tooling — are under-audited and high-impact. Old code and obscure formats are gold.
|
||||
|
||||
## Validation rules (apply before reporting ANY finding here)
|
||||
|
||||
1. **Build a debuggable target first.** Wire in crash dumps + a debugger before you claim exploitability. You can't iterate on what you can't observe.
|
||||
2. **Read the offset from the crash, not the disassembly.** Send a cyclic (De Bruijn) pattern; the faulting register values give the exact offset. A variable-length prefix (handle, optional field, padding) shifts the geometry off the static prediction.
|
||||
3. **Prove a UAF by reclaim-and-compare** when the sanitizer is blind (asm/JIT/intra-allocation): trigger the dangling view, reclaim the freed region with a size-matched content-controlled allocation, write through the dangler, read the reclaimer back — aliasing either way proves it.
|
||||
4. **Distinguish crash from exploitable.** For an OOB write, map which bytes land where and whether a security-relevant field is reachable; for a "huge count," prove the bounded-write case before calling it DoS-only.
|
||||
5. **Return ONLY confirmed findings** with the exact input → sink path and the observable result, or "No exploitable memory-safety issues found" if that's honest.
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
# Reconnaissance
|
||||
|
||||
### Phase 1: Understand the application
|
||||
|
||||
Before looking for bugs, understand what you're auditing. This requires depth, not just a directory listing. Launch **multiple `research` agents in parallel** to map different aspects of the codebase:
|
||||
|
||||
**Agent 1a: Overview, tech stack, and comparable baseline**
|
||||
```
|
||||
Explore the codebase at <path>. Answer:
|
||||
1. What is this application? What kind of software? (web app, API, CLI tool, library, daemon, desktop app, mobile backend, etc.)
|
||||
2. Who uses it and how? (end users, developers, operators, other services)
|
||||
3. What's the tech stack? (languages, frameworks, databases, runtime, deployment model)
|
||||
4. What comparable mainstream software exists? What security tradeoffs does the comparable accept?
|
||||
5. What's the high-level directory structure?
|
||||
Return specific file paths for key entry points.
|
||||
```
|
||||
|
||||
**Agent 1b: Trust boundaries and access control**
|
||||
```
|
||||
Explore the codebase at <path>. Find and read ALL code related to:
|
||||
1. Trust boundaries — where does untrusted input enter the system? (HTTP requests, CLI args, file reads, IPC, message queues, environment variables, config files, etc.)
|
||||
2. Authentication — how do callers prove identity? (sessions, tokens, API keys, mTLS, Unix sockets, etc.) If there's no authentication, note that.
|
||||
3. Authorization — how are permissions enforced? (middleware, decorators, capability checks, file permissions, etc.) If there's no authorization model, note that.
|
||||
4. Privilege separation — does the code run as root? Drop privileges? Use sandboxing? Fork workers?
|
||||
5. Any bypass mechanisms (dev-only modes, test helpers, setup flows, debug flags)
|
||||
Return the trust model: who are the actors, what can each do by design, and which code enforces it. Include specific file paths and line numbers.
|
||||
```
|
||||
|
||||
**Agent 1c: Input surface inventory**
|
||||
```
|
||||
Explore the codebase at <path>. Produce a complete inventory of where external input enters the system:
|
||||
1. Network-facing surfaces (HTTP endpoints, gRPC services, WebSocket handlers, TCP/UDP listeners, etc.) — list each with method/verb and purpose
|
||||
2. File-based input (file uploads, config file parsing, log ingestion, import/export, etc.)
|
||||
3. IPC and inter-service input (message queues, shared memory, Unix sockets, environment variables, CLI arguments)
|
||||
4. User-generated content surfaces (anywhere users provide content that is stored and later rendered, served, or processed)
|
||||
5. External integrations (OAuth, webhooks, third-party APIs, plugin loading, dynamic code execution)
|
||||
6. All places where input reaches dangerous sinks (SQL/query builders, HTML/template output, file paths, shell commands, deserialization, eval, dynamic imports)
|
||||
Return specific file paths. Be exhaustive.
|
||||
```
|
||||
|
||||
Collect all three agents' outputs and synthesize them into `<output-dir>/architecture.md`:
|
||||
- 1-2 page structured summary covering application type, tech stack, trust model, input surfaces, and baseline comparable
|
||||
- Include the key file paths from all agents — these become the starting points for Phase 2
|
||||
- This document is injected verbatim into every Phase 2 agent prompt
|
||||
|
||||
If Phase 1 agents reveal the codebase is larger or more complex than expected (e.g., plugin system, multi-tenant architecture, complex auth chains, multiple deployment targets), launch additional `research` agents to map those areas before proceeding. The quality of Phase 2 depends entirely on the quality of Phase 1.
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
# Validation, Reporting, and Verification
|
||||
|
||||
### Phase 3: Validate findings
|
||||
|
||||
Collect all findings from Phase 2 agents and **consolidate duplicates first**. Phase 2 deliberately overlaps agent scopes, so the same issue is frequently reported by more than one hunter — merge findings that share a root cause before validating, or you'll validate and report the same bug multiple times. For each remaining finding, launch a **separate `research` validation agent** that tries to disprove it. The hunting agents are biased toward finding things; the validation agents are biased toward killing false positives. This adversarial step is critical.
|
||||
|
||||
For findings from the same attack surface, batch them into one validation agent. Launch validation agents in parallel where they cover independent areas.
|
||||
|
||||
Each validation agent prompt should:
|
||||
1. State the specific finding being validated (title, claimed attack, claimed impact)
|
||||
2. Ask the agent to read the exact code paths and verify each step of the trace
|
||||
3. Ask it to apply these tests (the adversarial, Phase 3 form of the canonical validation rules in [HUNTING.md](HUNTING.md) — here a separate agent tries to make each one fail):
|
||||
|
||||
**Validation tests:**
|
||||
1. **Exploitation test**: Read the actual code at each step of the trace. Does the data flow work as claimed? Can you construct the exact input (HTTP request, CLI invocation, API call, crafted file, etc.) that triggers this?
|
||||
2. **Impact test**: What does the attacker actually get? If the answer is "they learn field names" or "they cause an error", that's not meaningful impact — not a finding on its own (at most a building block for a chain).
|
||||
3. **Baseline test**: Does the identified comparable have the same pattern? If yes, has it been exploited? If never exploited in years of production use, understand why before reporting.
|
||||
4. **Mitigation test**: Is there another layer that prevents exploitation? Check middleware, database constraints, framework defaults.
|
||||
5. **Parser/runtime behavior test**: If the exploit depends on how a parser or runtime handles specific input, verify against the actual spec or implementation — do not reason from intuition.
|
||||
|
||||
Tell each validation agent:
|
||||
|
||||
```
|
||||
Your job is to DISPROVE this finding. Read the actual source code at every step. If you cannot disprove it, confirm it with the exact code that makes it exploitable. Return one of:
|
||||
- "CONFIRMED: [explanation of why it's real, with code evidence]"
|
||||
- "REJECTED: [explanation of what the finding got wrong, with code evidence]"
|
||||
```
|
||||
|
||||
**Kill false positives aggressively, but don't kill real findings.** A short report with 3 real findings is worth more than a long report with 30 theoretical ones. An honest "nothing found" is valid — but push hard before reaching that conclusion.
|
||||
|
||||
### Phase 4: Report
|
||||
|
||||
Write the report to the output directory established in Setup.
|
||||
|
||||
**Output files:**
|
||||
|
||||
1. `REPORT.md` -- Main report with:
|
||||
- One-paragraph executive summary (honest assessment of security posture)
|
||||
- Identified baseline and how this application compares
|
||||
- Findings table (severity, title, one-line description)
|
||||
- Each finding with: file path, concrete attack scenario, impact, recommended fix
|
||||
- Hardening notes section (defense-in-depth suggestions, NOT findings)
|
||||
- Positive patterns section (what the codebase does well -- this calibrates trust in the audit)
|
||||
|
||||
2. `FINDINGS-DETAIL.md` -- For each finding rated MEDIUM or above:
|
||||
- Complete data flow from input to sink with file:line references
|
||||
- Exact HTTP request(s) to trigger
|
||||
- What the attacker gets
|
||||
- How the baseline comparable handles the same scenario
|
||||
|
||||
Keep it short. If the report is longer than the codebase deserves, you're padding.
|
||||
|
||||
### Phase 5: Structured output and schema check
|
||||
|
||||
For every finding that survived Phase 3 validation, produce a structured JSON object conforming to the schema defined in `../resources/report-schema.json` (read it via the Read tool before writing output). Write the result to `<output-dir>/findings.json`.
|
||||
|
||||
The schema supports two verdict types via `oneOf`:
|
||||
- **`confirmed`** — a validated vulnerability with full trace, execution, and remediation
|
||||
- **`rejected`** — a finding that was investigated and determined to be factually incorrect
|
||||
|
||||
**Before writing `findings.json`:**
|
||||
|
||||
1. Read `../resources/report-schema.json`. Follow it exactly — `additionalProperties: false` is enforced, so extra fields will make the output invalid.
|
||||
2. For each finding, populate every required field. If you cannot fill `trace` with real file paths and line numbers verified against the source, the finding is not sufficiently verified — go back and verify it or reject it. Mind the required fields that aren't self-evident: `intended_behavior` (what the code is *supposed* to do, so the defect is legible), `confidence` (`low`/`medium`/`high`, with a reason), and the `severity` object (`likelihood`/`impact`/`overall_severity`). All `severity` scores use the schema's **lowercase** enum — `informational`/`low`/`medium`/`high`/`critical`; the UPPERCASE tiers in SKILL.md and REPORT.md are prose labels, not valid JSON values.
|
||||
3. Validate `<output-dir>/findings.json` against `../resources/report-schema.json` with a trusted JSON Schema validator already available in the user's environment. Do not install or execute a new validator without approval. This is a structural check only — it confirms the JSON conforms to the schema, not that the findings are correct. Factual verification is Phase 6's job. Fix any failures before proceeding.
|
||||
|
||||
### Phase 6: Independent verification
|
||||
|
||||
The structured output from Phase 5 forces self-validation, but the same agent that wrote the finding also wrote the JSON — it won't catch its own blind spots. This phase uses a fresh agent to independently verify every claim in `findings.json`.
|
||||
|
||||
Launch **one `research` agent per confirmed finding** via the Task tool, all in parallel. Each agent gets exactly one finding from `findings.json` and verifies it independently. Give each agent the JSON object for its finding and this prompt:
|
||||
|
||||
```
|
||||
You are an independent verifier. You did NOT write this finding. Your job is to read the actual source code and verify that every factual claim is correct.
|
||||
|
||||
1. Read the file and line number cited in EVERY trace step. Verify:
|
||||
- The file exists at that path
|
||||
- The line number matches the described code
|
||||
- The scope (function name) is correct
|
||||
- The description accurately reflects what the code does
|
||||
|
||||
2. Verify the root_cause statement by reading the cited file and confirming the described defect exists.
|
||||
|
||||
3. Verify the execution payloads would actually work, in terms that fit the target:
|
||||
- Does the entry point exist as claimed — the endpoint/URL, CLI command, exported function, syscall/ioctl, message handler, or tool the attacker invokes?
|
||||
- Does the invocation match — HTTP method, argument shape, call signature, or message format?
|
||||
- Would the input survive validation and parsing on the real code path?
|
||||
- Would the relevant authentication, authorization, or ownership check pass as described?
|
||||
|
||||
4. Verify conditions are complete — are there prerequisites the finding missed?
|
||||
|
||||
5. Check the remediation code_changes — would the fix actually prevent the attack without breaking normal functionality?
|
||||
|
||||
6. Verify `intended_behavior` accurately states what the code should do, and that `confidence` matches the strength of the evidence — don't leave `high` on a claim you couldn't fully trace.
|
||||
|
||||
Return one of:
|
||||
- "VERIFIED" — all claims checked out against the source
|
||||
- "CORRECTED: [field]: [what was wrong] → [what it should be]" — factual error in a specific field
|
||||
- "REJECTED: [reason]" — the finding is fundamentally wrong
|
||||
```
|
||||
|
||||
Apply the agent's corrections:
|
||||
- **VERIFIED** findings: no changes needed
|
||||
- **CORRECTED** findings: update the specific fields in `findings.json`, re-run the schema validation script
|
||||
- **REJECTED** findings: change their `verdict` to `"rejected"` with the agent's reason, or remove them entirely
|
||||
|
||||
After applying corrections, reconcile the prose deliverables: update `REPORT.md` and `FINDINGS-DETAIL.md` so they match the final `findings.json`. Remove or amend any finding the verification gate rejected or corrected — the human-readable report and the machine-readable output must not disagree.
|
||||
|
||||
This is the final quality gate. Do not skip it.
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
# HTTP-Protocol and Authentication Hunting
|
||||
|
||||
#### When to use this file
|
||||
|
||||
Reach for this file when the target speaks HTTP at a layer where parsing, caching, or identity decisions happen: reverse proxies, CDNs, API gateways, load balancers, custom HTTP servers and parsers, and any app that builds responses or URLs from request metadata — and whenever it implements or consumes an auth protocol (sessions, JWTs, OAuth/OIDC, SAML, or password-reset flows). These are the classes `ATTACK-CLASSES.md` treats only in passing: the injection class covers *content*, but not the request/response framing or the identity-token machinery, which have their own specific, high-hit-rate bug patterns.
|
||||
|
||||
Use alongside `ATTACK-CLASSES.md`. Access control there answers "is the check present and correct"; this file answers "can the attacker forge, replay, or confuse the identity the check runs on, or desync the request the check applies to."
|
||||
|
||||
Pick the relevant classes based on Phase 1; split per subsystem (framing/proxy layer, token verification, session store) for large targets. A pure single-server app behind a managed CDN has little smuggling surface; a custom proxy or a service that trusts `X-Forwarded-*` has a lot.
|
||||
|
||||
## Core discipline (include in every agent prompt for this domain)
|
||||
|
||||
```
|
||||
- Framing bugs live in DISAGREEMENT, not in one parser. Request smuggling and cache poisoning exist because two components interpret the same bytes differently. Find the two components and the byte they disagree on; a single correct parser in isolation is not the finding.
|
||||
- A signature you don't verify is decoration. For every token (JWT, SAML, cookie), find the exact line that verifies the signature AND the claims that apply to that token type (for JWT/OIDC: exp, aud, iss, nonce) — and that the algorithm is pinned server-side, not read from the token header. "It's signed" means nothing if nothing checks the signature with the right key and algorithm.
|
||||
- Every use of Host, X-Forwarded-*, Forwarded, or a request-derived URL is a trust decision. Trace it to what it controls: a reset link, a cache key, a redirect, an access check.
|
||||
- Reflected input in a security-relevant response field (Set-Cookie, Location, cache key, an absolute URL sent to a victim) — trace it to a cross-user impact (poisoned cache entry, redirect or token sent to a victim, cookie set in another context) even when it isn't classic XSS.
|
||||
```
|
||||
|
||||
## HTTP request-framing attack classes (subagent_type: `general`)
|
||||
|
||||
**Request smuggling / desync**
|
||||
A discrepancy in how two components (front proxy vs back-end, or HTTP/2 front vs HTTP/1.1 back) resolve message length. Classic forms: CL.TE, TE.CL, TE.TE (obfuscated `Transfer-Encoding`), and H2 downgrade (H2.CL / H2.TE) where an HTTP/2 front-end forwards to an HTTP/1.1 back-end and the injected `Content-Length`/`Transfer-Encoding` or CRLF in a header value survives. Audit angle: any component that parses HTTP messages itself, forwards requests, or normalizes headers. Look for lenient length handling (accepting both CL and TE, tolerating whitespace/casing/duplicates in `Transfer-Encoding`), and CRLF-in-header-value passthrough on the HTTP/2→1.1 hop. The prize is a request prefix that gets glued onto the *next* user's request.
|
||||
|
||||
**Web cache poisoning (unkeyed input)**
|
||||
An input influences the response but is not part of the cache key, so the attacker's response is stored and served to others. Find the cache key construction, then find every input that changes the response body/headers but is absent from that key — `X-Forwarded-Host`, `X-Forwarded-Scheme`, custom headers, cookies stripped from the key, or a query param the key normalizes away. Reflected unkeyed input that lands in the cached body (a poisoned script src, an `<base href>` from `X-Forwarded-Host`) is stored XSS against every cache consumer.
|
||||
|
||||
**Cache deception**
|
||||
Path/extension confusion that makes a dynamic, per-user page get cached as if it were a static asset (`/account/profile.css`, `/api/me;.js`, path-parameter tricks). The back-end serves the user's private page; the cache stores it under a path the attacker can then request. Trace how the cache decides "is this cacheable" versus how the app routes the path — the gap is the bug.
|
||||
|
||||
**Host-header and forwarded-header trust**
|
||||
`Host` / `X-Forwarded-Host` used to build absolute URLs, routing, or cache keys. The highest-impact sink is password-reset / verification link construction: attacker sets the header, the victim receives a link to the attacker's domain, clicks, and leaks the token. Also: authentication or routing decisions keyed on a spoofable forwarded header.
|
||||
|
||||
**CRLF / response header injection**
|
||||
User input reflected into a response header (`Location`, `Set-Cookie`, custom headers) with unescaped CR/LF, letting the attacker inject headers or split the response. Trace user input into any header-setting call; confirm the framework doesn't already strip CR/LF (many do — verify first, see #5).
|
||||
|
||||
## Authentication-protocol attack classes (subagent_type: `general`)
|
||||
|
||||
First establish which role the target plays — it determines whose duty each control is. `redirect_uri` allowlisting, PKCE enforcement, authorization-code issuance, and assertion signing belong to the **authorization server / IdP**; a **relying-party client** legitimately sends its own `redirect_uri` and consumes tokens, so do not report "no `redirect_uri` allowlist" or "issues codes without PKCE" against a client. Token *verification* defects (below) apply to whichever side validates the token.
|
||||
|
||||
**JWT verification defects**
|
||||
The densest source of auth bypasses. Check, in the verification code:
|
||||
- **`alg` confusion** — `alg: none` accepted, or RS256→HS256 where the server verifies an attacker-forged HS256 token using the *public* key as the HMAC secret. Find where the algorithm is chosen: is it taken from the token header (attacker-controlled) or pinned server-side?
|
||||
- **Decode without verify** — code that reads claims from a decoded token but never calls the verify function, or ignores its return/exception.
|
||||
- **Missing claim checks** — `exp` (expiry), `nbf`, `aud` (audience — token for service A replayed at service B), `iss` (issuer). A signature check without claim checks is half a check.
|
||||
- **Key-selection injection** — `kid`, `jku`, or `x5u` header taken from the token: `kid` used in a file path (traversal) or SQL (injection) to fetch the key, or `jku` pointing at an attacker-hosted JWK Set / `x5u` at an attacker-hosted X.509 cert chain. Attacker names the key that verifies their own forgery.
|
||||
- **Weak/shared secret** — HMAC secret that's a guessable string or shared across trust domains.
|
||||
|
||||
**OAuth / OIDC flow defects**
|
||||
- **`redirect_uri` validation** — substring/prefix matching, open-redirect on an allowlisted host, or `redirect_uri` not bound to the client. Leaks the authorization code to the attacker.
|
||||
- **Missing/weak `state`** — no CSRF token on the callback → login CSRF / forced-login / session fixation of the OAuth flow. (`state` is a session-binding/CSRF control; authorization-code injection is prevented by PKCE and the OIDC `nonce`, not by `state` — don't conflate them.) Confirm `state` is generated, bound to the session, and verified on return.
|
||||
- **PKCE** — missing on public clients, or `code_verifier` not actually checked against `code_challenge`.
|
||||
- **`id_token` validation** — audience, issuer, signature, and `nonce` all verified? A token minted for another client accepted here is account takeover.
|
||||
- **Mix-up / IdP confusion** — multi-IdP flows where the response isn't bound to the IdP the request went to.
|
||||
|
||||
**SAML assertion defects**
|
||||
- **Signature wrapping (XSW)** — a signed assertion plus an injected unsigned one; the verifier checks the signature on one element but reads identity from another. Find the gap between "what is signature-verified" and "what is read as the authenticated identity."
|
||||
- **Signature exclusion** — unsigned assertions accepted, or signature verification skippable via a flag/empty-signature path.
|
||||
- **XXE / DTD** in the XML parser processing assertions.
|
||||
- **Comment truncation** — a comment inserted into the signed NameID (`admin@company.com<!---->.attacker.com`) that canonicalization strips before the signature check (so it still validates) but that truncates identity extraction to the pre-comment text (`admin@company.com`, the victim). Same root as XSW: the bytes the signature covers ≠ the bytes read as identity.
|
||||
- **Missing replay / binding checks** — even with a valid signature, is the assertion bound and fresh? Check `NotBefore`/`NotOnOrAfter` (validity window), `Recipient`/`Audience` (assertion minted for *this* SP, not replayed from another), `InResponseTo` (bound to a real outstanding request — blocks unsolicited-response injection), and one-time-use (a replayed assertion rejected). The signature checks above prove the assertion wasn't forged; these prove it wasn't stolen and replayed.
|
||||
|
||||
**Session-management defects**
|
||||
- **Fixation** — session identifier not rotated on privilege change (login, step-up auth). Attacker fixes a known ID, victim authenticates into it.
|
||||
- **Weak invalidation** — session/token still valid after logout, password change, or revocation; server-side state not cleared (especially stateless JWT sessions with no revocation list).
|
||||
- **Predictable identifiers** (non-CSPRNG session IDs an attacker can guess/derive), or an overly broad cookie `Domain` that leaks the session cookie to an attacker-controlled sibling subdomain. (Bare "cookie could be shorter-lived" with no leakage path is a hardening note, not a finding.)
|
||||
|
||||
**Password-reset / account-recovery defects**
|
||||
- Token not cryptographically bound to the user (reset A's token, use it on B), predictable/short token, no single-use or expiry, token leaked via `Host` header (see above) or `Referer`, or a race that mints multiple valid tokens. Recovery flows are frequently the weakest path to the strongest impact (account takeover).
|
||||
|
||||
## Universal moves (apply across the above)
|
||||
|
||||
- **Diff duplicated request paths side by side.** Where the code has more than one thing that parses or forwards HTTP (a middleware plus the framework, a normalizer plus the router, a legacy API version plus the current one), read them together and feed each the same ambiguous bytes on paper. Divergence is the smuggling/desync bug.
|
||||
- **Walk the whole token lifecycle.** Issue → store → transmit → verify → refresh → revoke. The bugs live in the transitions the happy path skips: a session still valid after logout, a refresh that never re-checks revocation, a reset token that survives a password change.
|
||||
- **Enumerate every door to the same identity.** SSO, password login, API key, password reset, impersonation — each is a parallel path that mints a session. The weakest one sets the account's real security; a hardened login means nothing if reset is trivial.
|
||||
- **Audit the compat/fallback path.** A legacy endpoint version, a deprecated header, or a "for old clients" branch that skips a guard the main path added. Old auth code is where the reverted or forgotten check hides.
|
||||
|
||||
## Validation rules (apply before reporting ANY finding here)
|
||||
|
||||
1. **Source-visibility gate — this domain lives partly outside the repo.** Framing bugs (proxy chain), cache poisoning/deception (cache-key config), secret strength, and token entropy frequently depend on components, config, or values NOT in the audited tree. If confirming the bug requires a component/config/secret you cannot read, it is **unverifiable from source: flag it "requires deployment testing" and do NOT report it as a confirmed finding.** "Downgrade" is not enough — an unconfirmable HIGH reported as a MEDIUM is still a false positive.
|
||||
2. **For framing/cache findings, name both components and the divergent parse.** "The Go net/http back-end accepts a bare-LF `Transfer-Encoding` that the front proxy treats as CL" — not "smuggling may be possible." A single server with no proxy in front has no smuggling surface. If you've confirmed only the in-repo half (the back-end genuinely mishandles a specific ambiguous input — bare-LF `Transfer-Encoding`, duplicate CL), record it as a lead with the exact bytes — "requires paired front-end testing" — a real observation, not a severity-rated finding.
|
||||
3. **For token findings, cite the verification line and what it fails to check.** Point at the `verify`/`decode` call and the missing `alg` pin / `aud` check / signature step. A forged-token claim requires showing the server would accept the forgery, not just that JWTs are in use. Establish the client-vs-server role first — don't fault a client for controls the server owns.
|
||||
4. **Prove the cross-user impact.** Show the payload reaching a victim's response (cache), request (smuggling), session (fixation), or inbox (reset link). Attacker-only effects are not findings: a `Host` header reflected into a self-referential link the victim never receives out-of-band is a hardening note; a `Host` header controlling a reset link emailed to the victim is a finding.
|
||||
5. **Verify the framework AND the library default don't already handle it.** Many stacks strip CR/LF from headers, rotate sessions on login, and key caches on `Host` by default; JWT libraries increasingly reject `alg:none` and require an explicit algorithm list — check the library and version, and if you cannot determine the default, treat it as unverifiable rather than assuming it's vulnerable. Only report secret/RNG weakness when the code itself sets a hardcoded/short/derivable value or uses a non-CSPRNG. Confirm the specific defense is absent — do not report a gap the framework or library already closes.
|
||||
6. **Return ONLY confirmed findings** with the divergent parse or the skipped verification step and the cross-user impact — or "No exploitable protocol/auth issues found" if that's honest.
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
{
|
||||
"$comment": "Single source of truth for findings.json structure (see SKILL.md Phase 5). Validate findings directly against this schema so there is no second copy of these rules to keep in sync.",
|
||||
"output_schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"description": "Confirmed vulnerability — provide the complete, independently verified report.",
|
||||
"properties": {
|
||||
"verdict": {
|
||||
"type": "string",
|
||||
"const": "confirmed"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "A concise, standard title for the vulnerability."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Comprehensive explanation of the vulnerability. Include any reproduction details (proof-of-concept input, configuration, observed output or crash) here."
|
||||
},
|
||||
"root_cause": {
|
||||
"type": "string",
|
||||
"description": "One sentence using the template: '[function_or_component] in [file] does not [missing action], allowing [consequence]'. MUST include the function/component name and file name where the defect exists."
|
||||
},
|
||||
"intended_behavior": {
|
||||
"type": "string",
|
||||
"description": "What was the developer trying to build? Explain the intended, non-vulnerable business logic."
|
||||
},
|
||||
"trace": {
|
||||
"type": "array",
|
||||
"minItems": 2,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["entrypoint", "propagation", "sink"]
|
||||
},
|
||||
"file": {
|
||||
"type": "string",
|
||||
"description": "Exact file path relative to repository root."
|
||||
},
|
||||
"line": {
|
||||
"type": "integer"
|
||||
},
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "Bare function or method name. No parentheses, no arguments."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Factual description of the state change or data movement."
|
||||
}
|
||||
},
|
||||
"required": ["kind", "file", "line", "scope", "description"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"description": "Sequential code trace from entrypoint to sink, verified against actual source code. The first step must be kind 'entrypoint', the last must be kind 'sink', and any intermediate steps must be kind 'propagation' (enforced by the validator)."
|
||||
},
|
||||
"conditions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["authentication_level", "authorization_role", "user_interaction", "system_configuration", "network_routing", "environmental_dependency", "data_state", "timing_dependency", "third_party_dependency"]
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["kind", "description"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"description": "Factual prerequisites for exploitation. Empty array if exploitable by default."
|
||||
},
|
||||
"execution": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"attacker_perspective": {
|
||||
"type": "string",
|
||||
"description": "Who is the attacker and their starting point."
|
||||
},
|
||||
"payloads": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Specific malicious inputs, HTTP requests, or scripts."
|
||||
},
|
||||
"instructions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Linear array of all attacker actions from setup through exploitation."
|
||||
},
|
||||
"expected_result": {
|
||||
"type": "string",
|
||||
"description": "Observable outcome confirming successful exploitation."
|
||||
}
|
||||
},
|
||||
"required": ["attacker_perspective", "payloads", "instructions", "expected_result"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"remediation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"strategy": {
|
||||
"type": "string",
|
||||
"description": "High-level explanation of the fix."
|
||||
},
|
||||
"code_changes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"fixed_code": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["file_name", "fixed_code"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["strategy"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"severity": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"likelihood": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"score": {
|
||||
"type": "string",
|
||||
"enum": ["informational", "low", "medium", "high", "critical"]
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["score", "reason"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"impact": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"score": {
|
||||
"type": "string",
|
||||
"enum": ["informational", "low", "medium", "high", "critical"]
|
||||
},
|
||||
"reason": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["score", "reason"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"overall_severity": {
|
||||
"type": "string",
|
||||
"enum": ["informational", "low", "medium", "high", "critical"]
|
||||
}
|
||||
},
|
||||
"required": ["likelihood", "impact", "overall_severity"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"confidence": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"score": {
|
||||
"type": "string",
|
||||
"enum": ["low", "medium", "high"]
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "Why you scored the confidence this way. Mention any missing files, complex routing, or ambiguous data flows."
|
||||
}
|
||||
},
|
||||
"required": ["score", "reason"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["verdict", "title", "description", "root_cause", "intended_behavior", "trace", "conditions", "execution", "remediation", "severity", "confidence"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"description": "Rejected finding — the described behavior is factually incorrect or the code path does not exist.",
|
||||
"properties": {
|
||||
"verdict": {
|
||||
"type": "string",
|
||||
"const": "rejected"
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "Explain which specific claims in the finding are factually wrong (e.g., code path doesn't exist, mitigation prevents the described flow, trace is incorrect)."
|
||||
}
|
||||
},
|
||||
"required": ["verdict", "reason"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user