feat(chain,skills): close benchmark misses — CRLF-on-Location, second-order precondition; condense BENCHMARK

The 13-target benchmark left 3 misses. Root-caused and fixed the two that were
coverage gaps (the third was single-run variance, already handled by the
session-limit fix):

- CRLF header injection (web_crlf_header_go): the agent confirmed the open
  redirect on /go?url= and stopped; the CRLF payload was never generated. The
  open_redirect skill now tests %0d%0a header injection on the SAME param, and
  CHAIN_DOCTRINE says a param landing in a Location header must also be tested
  for response splitting. chain.rs: CWE-113/93/644 now provide capabilities;
  attack_graph maps their kill-chain stage.
- Second-order SQLi (web_sqli_second_order): the sink was behind /admin, which
  the customer account could not reach. CHAIN_DOCTRINE now teaches the
  precondition pattern (store the payload, trigger from every identity, escalate
  first if the trigger page needs a role you lack, else report as a chained
  lead). chain.rs: CWE-564 requires PrivilegedContext so it chains after privesc.

BENCHMARK.md: added the TypeSafe calibrated-adjudication row; dropped the
"genuinely ahead" prose (the table is the summary); condensed the rest
188 -> 89 lines; refreshed scale (27 validators, 47 modules, 383 tests).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
CyberSecurityUPandClaude Opus 5 committed 2026-09-20 12:42:36 -03:00
1 parent dff2e3c0f0
commit fce86522ca
5 files changed
+38 -119

No files matched your search

@@ -45,6 +45,9 @@ fn map_cwe(cwe: &str) -> (&'static str, &'static str, &'static str) {
// Session fixation.
384 => ("A07:2021-Auth-Failures", "T1539", "credential-access"),
601 => ("A01:2021-Broken-Access-Control", "T1566", "initial-access"),
113 | 93 => ("A03:2021-Injection", "T1557", "initial-access"),
644 => ("A03:2021-Injection", "T1557", "initial-access"),
564 => ("A03:2021-Injection", "T1190", "execution"),
352 => ("A01:2021-Broken-Access-Control", "T1189", "execution"),
434 => ("A04:2021-Insecure-Design", "T1505.003", "execution"),
1321 | 915 => ("A08:2021-Software-Data-Integrity", "T1059", "execution"),
@@ -90,6 +90,9 @@ pub fn provides(f: &Finding) -> Vec<Capability> {
}
}
209 | 532 | 538 | 540 | 548 | 693 | 1021 => vec![Capability::InternalKnowledge],
// CRLF / response splitting / host-header: header control feeds cache
// poisoning and redirect abuse downstream.
113 | 93 | 644 => vec![Capability::InternalKnowledge, Capability::SessionMaterial],
// Missing throttling turns any guess into an unlimited one.
307 | 770 | 799 | 400 => vec![Capability::UnlimitedAttempts],
// Password policy.
@@ -130,6 +133,9 @@ pub fn requires(f: &Finding) -> Vec<Capability> {
614 | 1004 | 1275 => vec![Capability::SessionMaterial],
// Escalation needs a foothold.
269 | 250 | 668 => vec![Capability::PrivilegedContext],
// Second-order SQLi: the stored payload only fires on the (often
// privileged) trigger page, so it needs that context to be reached.
564 => vec![Capability::PrivilegedContext],
_ => vec![],
}
}
@@ -582,6 +582,8 @@ const CHAIN_DOCTRINE: &str = "CHAIN THE FOOTHOLD (pivot to deeper, provable impa
· XXE → SSRF/file read → creds; deserialization/SSTI → RCE via a gadget/template sink; prove exec with a marker.\n\
· IDOR/BOLA/mass-assignment → account/tenant takeover or role escalation (`role=admin`); open-redirect/XSS/CORS → token/session theft → ATO.\n\
· Exposed `.git`/backup/`.env`/secrets → reconstruct source & keys → auth to internal APIs, cloud, DB; default/leaked creds → domain/service compromise.\n\
· A param that lands in a redirect/`Location` header → ALSO test CRLF/header injection on the SAME param (`%0d%0aX-Injected: pwned`, `%0d%0aSet-Cookie:`): an open redirect and response splitting share the sink, so never stop at the redirect.\n\
- Second-order & preconditions: a payload you STORE (profile/bio/name/review/filename) may only fire on a DIFFERENT page, often a privileged one (e.g. an admin search). Plant the payload, then TRIGGER it from every identity you hold; if the trigger page needs a role you lack, FIRST look for a privesc/IDOR/mass-assign to reach it, and if none exists, report the second-order as a CHAINED lead (payload stored + trigger located, blocked only by authorization) rather than dropping it.\n\
- Reuse loot relentlessly: every credential/JWT/cookie/API key/host you obtain is input to the next step — carry it forward across modules and try it everywhere it might be accepted.\n\
- Understand the BUSINESS & LOGIC: reason about what the app is FOR (payments, orders, tenancy, KYC, entitlements) and chain toward business impact — payment/price/coupon abuse, cross-tenant data access, entitlement/limit bypass, workflow/state-machine skips (skip approval/verification steps), race conditions on balance/stock. These compound: each finding updates your model of the app for the next probe.\n\
- Stop at proof: demonstrate the impact with the SMALLEST safe step and report the CHAIN end-to-end; never destroy, overwrite, encrypt, mass-exfiltrate, or DoS to 'prove' it.\n\n";