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,271 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __0secCreateRequire } from "node:module";
|
|
3
|
+
const require = __0secCreateRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
// packages/core/dist/kernel/exploit/recipes.js
|
|
6
|
+
var RECIPE_LIBRARY = [
|
|
7
|
+
{
|
|
8
|
+
id: "dirtyCred",
|
|
9
|
+
title: "DirtyCred \u2014 privileged-object swap via heap reuse (data-only)",
|
|
10
|
+
// A UAF (or a refcount bug collapsing to one) on a file/cred object is the
|
|
11
|
+
// canonical DirtyCred entry; a page-UAF can also be funnelled here.
|
|
12
|
+
appliesTo: ["uaf", "refcount"],
|
|
13
|
+
ceiling: "root",
|
|
14
|
+
preconditions: [
|
|
15
|
+
"UAF (or refcount-to-UAF) on a `struct file` or `struct cred` \u2014 or any freeable object that can be made to alias one via same-cache reuse",
|
|
16
|
+
"ability to free the victim object while a dangling reference is still usable (the swap window)",
|
|
17
|
+
"an unprivileged path that allocates the privileged replacement (e.g. open(/etc/passwd) for a write-capable file, or a setuid exec for a privileged cred)"
|
|
18
|
+
],
|
|
19
|
+
sprayObjects: ["file", "cred"],
|
|
20
|
+
steps: [
|
|
21
|
+
"free the unprivileged victim object through the bug, keeping the dangling reference",
|
|
22
|
+
"spray the privileged counterpart into the freed slot via same-cache reuse (open a write-protected file / trigger a privileged cred alloc)",
|
|
23
|
+
"use the dangling reference to operate on the now-privileged object (write through the privileged file / run with the swapped cred)"
|
|
24
|
+
],
|
|
25
|
+
finalStep: "file-swap",
|
|
26
|
+
reliability: "high",
|
|
27
|
+
// Data-only: no leak, no KASLR/SMEP/SMAP dependence — the whole point.
|
|
28
|
+
caveats: [
|
|
29
|
+
"defeated by cred/file slab isolation (vendor DirtyCred mitigation, SLAB_VIRTUAL-era dedicated caches) \u2014 verify the victim shares a cache with the privileged object",
|
|
30
|
+
"needs a same-cache privileged allocation primitive reachable unprivileged"
|
|
31
|
+
],
|
|
32
|
+
citation: "Lin, Wu, Xing \u2014 DirtyCred (CCS 2022 / Black Hat USA 2022); CVE-2021-4154, CVE-2022-2588"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "dirtyPagetable",
|
|
36
|
+
title: "Dirty Pagetable \u2014 page-UAF \u2192 PTE poison \u2192 arbitrary physical R/W",
|
|
37
|
+
appliesTo: ["page-uaf", "uaf"],
|
|
38
|
+
ceiling: "root",
|
|
39
|
+
preconditions: [
|
|
40
|
+
"page-granularity UAF: a freed page (order-0+) returned to the page allocator and reusable across caches",
|
|
41
|
+
"a dangling write into the freed page after it is reclaimed",
|
|
42
|
+
"ability to fault in a fresh page-table page (mmap + touch) so the freed page is reused as a PTE-bearing page table"
|
|
43
|
+
],
|
|
44
|
+
sprayObjects: ["pte"],
|
|
45
|
+
steps: [
|
|
46
|
+
"free the victim page through the bug (cross-cache it back to the buddy allocator if the bug is slab-granularity \u2014 see crossCacheSlubStick)",
|
|
47
|
+
"fault in a user page table so the kernel reclaims the freed page as a page-table page",
|
|
48
|
+
"use the dangling write to forge a PTE pointing at an attacker-chosen physical frame \u2192 arbitrary physical read/write",
|
|
49
|
+
"locate kernel physmap and overwrite `core_pattern` (or `modprobe_path`) via the physical R/W"
|
|
50
|
+
],
|
|
51
|
+
finalStep: "core_pattern",
|
|
52
|
+
reliability: "high",
|
|
53
|
+
caveats: [
|
|
54
|
+
"needs page-granularity reuse \u2014 a pure slab UAF must be widened with a cross-cache free first (crossCacheSlubStick)",
|
|
55
|
+
"physmap base discovery still wants a coarse KASLR defeat (EntryBleed prefetch in kctf.h) unless KASLR is off"
|
|
56
|
+
],
|
|
57
|
+
citation: "Nicolas Wu \u2014 Dirty Pagetable writeup; kernelCTF LTS page-UAF winners"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: "crossCacheSlubStick",
|
|
61
|
+
title: "Cross-cache SLUB stick \u2014 slab-UAF \u2192 page reuse bridge",
|
|
62
|
+
// The bridge: turns a slab-granularity bug into page-granularity reuse so a
|
|
63
|
+
// downstream recipe (Dirty Pagetable, or an unlike-cache reclaim) applies.
|
|
64
|
+
appliesTo: ["uaf", "oob-write", "page-uaf"],
|
|
65
|
+
ceiling: "arb-write",
|
|
66
|
+
preconditions: [
|
|
67
|
+
"a slab-cache UAF/OOB whose victim slab can be drained so the whole slab page is freed back to the buddy allocator",
|
|
68
|
+
"control over allocation/free timing to empty the victim slab"
|
|
69
|
+
],
|
|
70
|
+
sprayObjects: ["msg_msg", "pipe_buffer"],
|
|
71
|
+
steps: [
|
|
72
|
+
"groom the victim slab so the bug's object is the last live one",
|
|
73
|
+
"free the remaining objects so the empty slab page returns to the buddy allocator (the cross-cache free)",
|
|
74
|
+
"immediately allocate the target page in a DIFFERENT cache (or a page-table page) so it reclaims the freed slab page",
|
|
75
|
+
"drive the dangling slab-relative write into the reclaimed cross-cache object \u2014 hand off to dirtyPagetable / dirtyCred for the root tail"
|
|
76
|
+
],
|
|
77
|
+
finalStep: "modprobe_path",
|
|
78
|
+
reliability: "medium",
|
|
79
|
+
caveats: [
|
|
80
|
+
"RANDOM_KMALLOC_CACHES and freelist randomization raise the grooming cost (more sprays, lower landing rate)",
|
|
81
|
+
"a bridge, not a complete chain \u2014 pair it with a root-tail recipe"
|
|
82
|
+
],
|
|
83
|
+
citation: "kernelCTF LTS cross-cache (SLUB) technique; kctf.h sprays"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: "msgMsgArbRW",
|
|
87
|
+
title: "msg_msg / pipe_buffer spray \u2192 arbitrary R/W \u2192 modprobe_path",
|
|
88
|
+
appliesTo: ["uaf", "oob-write"],
|
|
89
|
+
ceiling: "root",
|
|
90
|
+
preconditions: [
|
|
91
|
+
"UAF or OOB-write in a generic `kmalloc-N` bucket reachable unprivileged",
|
|
92
|
+
"the victim cache is not bucket-isolated from the spray (SLAB_BUCKETS off, or an accounted spray for a `-cg-` victim)"
|
|
93
|
+
],
|
|
94
|
+
sprayObjects: ["msg_msg", "pipe_buffer", "setxattr"],
|
|
95
|
+
steps: [
|
|
96
|
+
"spray msg_msg (or pipe_buffer) to reclaim the freed/adjacent slot",
|
|
97
|
+
"corrupt the reclaiming object's length/next field to widen into an out-of-bounds read (leak) and then an arbitrary write",
|
|
98
|
+
"overwrite `modprobe_path` (or `core_pattern`) and trigger it for root"
|
|
99
|
+
],
|
|
100
|
+
finalStep: "modprobe_path",
|
|
101
|
+
reliability: "medium",
|
|
102
|
+
caveats: [
|
|
103
|
+
"SLAB_BUCKETS isolates msg_msg from a plain victim \u2014 see spray-selector.ts",
|
|
104
|
+
"setxattr is a transient (race) reclaim window; prefer the persistent sprays"
|
|
105
|
+
],
|
|
106
|
+
citation: "kernelCTF LTS msg_msg/pipe_buffer winners; kctf.h sprays"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "fdTableSpray",
|
|
110
|
+
title: "fd-table spray \u2014 open()-flood grooms `struct file *` fd-arrays",
|
|
111
|
+
// The bridge/grooming vector: an open() flood expands a task's fdtable so
|
|
112
|
+
// the kernel allocates fresh `struct file *[]` arrays in a target kmalloc
|
|
113
|
+
// bucket — a controllable, persistent reclaim object for a slab UAF/OOB, and
|
|
114
|
+
// the layout target that feeds the fileOpsVtableConfusion tail.
|
|
115
|
+
appliesTo: ["uaf", "oob-write"],
|
|
116
|
+
ceiling: "arb-write",
|
|
117
|
+
preconditions: [
|
|
118
|
+
"a slab UAF/OOB in a `kmalloc-N` bucket the `struct fdtable` fd-array (`struct file *[]`) can be steered to reclaim",
|
|
119
|
+
"an unprivileged path to open() many descriptors so `files_struct` grows its `fdtable` and reallocates the fd-array into the victim bucket"
|
|
120
|
+
],
|
|
121
|
+
sprayObjects: ["fdtable", "files_struct", "file"],
|
|
122
|
+
steps: [
|
|
123
|
+
"open() a flood of descriptors so the task's `files_struct` expands its `fdtable` and allocates a fresh `struct file *[]` fd-array in the target kmalloc bucket (dup2/dup to size the array precisely)",
|
|
124
|
+
"free the victim object through the bug, then trigger another fdtable grow so the `struct file *` fd-array reclaims the freed slot",
|
|
125
|
+
"drive the dangling/OOB write to corrupt a `struct file *` entry in the reclaimed fd-array so a live fd aliases an attacker-shaped `struct file`",
|
|
126
|
+
"operate through the confused fd \u2014 hand off to fileOpsVtableConfusion for the control-flow / privileged-object tail"
|
|
127
|
+
],
|
|
128
|
+
finalStep: "file-swap",
|
|
129
|
+
reliability: "medium",
|
|
130
|
+
caveats: [
|
|
131
|
+
"the fd-array shares generic `kmalloc-N` \u2014 SLAB_BUCKETS / RANDOM_KMALLOC_CACHES isolate it from a plain victim (groom same-call-site instead)",
|
|
132
|
+
"a bridge, not a complete chain \u2014 pair it with fileOpsVtableConfusion or a DirtyCred file swap for the root tail"
|
|
133
|
+
],
|
|
134
|
+
citation: "kernelCTF fd-array (files_struct/fdtable) grooming; classic `struct file *` reclaim vector"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: "fakeCredOneShot",
|
|
138
|
+
title: "fake-cred one-shot \u2014 timer/ops UAF \u2192 forged cred + commit_creds hijack (data-only root)",
|
|
139
|
+
// A UAF (or a refcount bug collapsing to one) on an object that owns an
|
|
140
|
+
// embedded `timer_list` / callback the kernel re-arms and fires after free.
|
|
141
|
+
appliesTo: ["uaf", "refcount"],
|
|
142
|
+
ceiling: "root",
|
|
143
|
+
preconditions: [
|
|
144
|
+
"UAF (or refcount-to-UAF) on an object that owns an embedded `timer_list` (or a work/tasklet/ops callback) which the kernel re-arms (mod_timer) and fires AFTER the free",
|
|
145
|
+
"a same-cache spray that lands in the freed object's kmalloc bucket with fully attacker-controlled bytes (msg_msg via msgsnd is the canonical one \u2014 persists until msgrcv, header 48B)",
|
|
146
|
+
"the freed object's layout overlaps a forgeable `struct cred`: the callback pointer aliases `cred.euid/egid` (== the indirect-call target) and the object's slack covers cred caps + user/user_ns/ucounts/group_info",
|
|
147
|
+
"an unprivileged child that can busy-spin so the timer softirq fires in ITS task context (commit_creds escalates `current`)"
|
|
148
|
+
],
|
|
149
|
+
sprayObjects: ["msg_msg"],
|
|
150
|
+
steps: [
|
|
151
|
+
"free the victim object through the bug while its timer is still re-armable",
|
|
152
|
+
"spray a forged `msg_msg` into the freed slot: inject ONLY the `timer_list.function = commit_creds` (leave entry.next/pprev/expires zeroed so detach is safe and timer_pending()==false), and lay a forged cred in the slack (caps=~0, user/user_ns/ucounts/group_info=&init_*)",
|
|
153
|
+
"let the kernel re-arm the timer (mod_timer on the reclaimed object) \u2014 the re-arm leaves the timer base/flags intact and takes .function from the forged bytes",
|
|
154
|
+
"the busy-spinning unprivileged child receives the timer softirq: call_timer_fn runs commit_creds(&obj) \u2192 the child is root; normalize euid/fsuid via CAP_SETUID from the forged cap_effective and drop a shell"
|
|
155
|
+
],
|
|
156
|
+
finalStep: "cred-forge",
|
|
157
|
+
reliability: "high",
|
|
158
|
+
caveats: [
|
|
159
|
+
"CFI_CLANG / FineIBT rejects an indirect call to commit_creds from call_timer_fn \u2014 the forward-edge check requires a type-matching target; this one-shot assumes CFI is OFF (as on the proven hid-sony target)",
|
|
160
|
+
"hardened / KASLR-ON root needs a companion kernel-TEXT infoleak: the physmap leak (MODE_LEAK) recovers page_offset_base (defeats RANDOMIZE_MEMORY) but NOT .text, so commit_creds's slid address is still unknown without a paired text leak",
|
|
161
|
+
"needs the spray to share a cache with the victim (MEMCG off, or an accounted spray for a -cg- victim) \u2014 verify the bucket match"
|
|
162
|
+
],
|
|
163
|
+
citation: "0sec hid-sony ghl_poke_timer UAF \u2192 LPE (fake-cred one-shot, 9/9 root); DirtyCred-family credential forge without arbitrary R/W"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: "workqueueBounceRoot",
|
|
167
|
+
title: "timer-UAF \u2192 forge-pwq \u2192 kworker process-context bounce \u2192 WWW \u2192 modprobe_path",
|
|
168
|
+
// The GuitarZero full clean chain (0sec#1215): the poison-tolerant, no-arb-
|
|
169
|
+
// read sibling of fakeCredOneShot. Where the one-shot fires commit_creds in a
|
|
170
|
+
// softirq (fatal-fault on any miss), this bounces into PROCESS context via a
|
|
171
|
+
// real kworker, so a survivable WWW gadget can overwrite modprobe_path and a
|
|
172
|
+
// miss leaves the box ALIVE. See guitarzero-primitives.ts for the primitives.
|
|
173
|
+
appliesTo: ["uaf", "refcount"],
|
|
174
|
+
ceiling: "root",
|
|
175
|
+
preconditions: [
|
|
176
|
+
"UAF on an object owning an embedded `timer_list` the kernel re-arms after free (as fakeCredOneShot), reclaimed via a self-address-leakable object (user_key_payload \u2014 keyctl_read exposes the reclaimed bytes)",
|
|
177
|
+
"a self-address leak (N-object same-bucket timer linkage \u2192 chunk = leaked\u2212344) and the leaked timer-bucket per-CPU pointer",
|
|
178
|
+
"a kernel-text base (EntryBleed unprivileged, or a companion leak on a virtualized-TSC box) to slide cpu_worker_pools/delayed_work_timer_fn/gadget",
|
|
179
|
+
"CFI off: the forged work->func indirect call is not forward-edge-checked"
|
|
180
|
+
],
|
|
181
|
+
sprayObjects: ["user_key", "msg_msg"],
|
|
182
|
+
steps: [
|
|
183
|
+
"free the timer-owning victim; reclaim with a user_key_payload carrying the forged timer/work layout (leak-then-reoccupy bakes the self-address in)",
|
|
184
|
+
"leak the chunk self-address + timer-bucket pointer; recover __per_cpu_offset[0] from the bucket + read-back expires",
|
|
185
|
+
"forge workqueue_struct+pool_workqueue at chunk+256 (256-aligned; get_work_pwq masks the low 8 bits) and point pwq->pool at the computed real cpu_worker_pools[0][0] \u2014 no arbitrary read",
|
|
186
|
+
"timer.function=delayed_work_timer_fn \u2192 __queue_work \u2192 process_one_work runs work->func in a real kworker (PROCESS context); the self-address-baked work->entry passes the workqueue.c list_empty gate",
|
|
187
|
+
'work->func = perf_swevent_del (survivable unlink-WWW) writes *(modprobe_path)="/tmp/x"; socket() over an unregistered family fires request_module \u2192 /tmp/x runs as root'
|
|
188
|
+
],
|
|
189
|
+
finalStep: "modprobe_path",
|
|
190
|
+
reliability: "high",
|
|
191
|
+
caveats: [
|
|
192
|
+
"CFI_CLANG / FineIBT forward-edge-checks the work->func indirect call \u2014 the WWW gadget must be a type-matching signed target; the proven chain assumes CFI OFF (kCFI \u21D2 a data-only tail is needed)",
|
|
193
|
+
"EntryBleed (CVE-2022-4543) supplies the text base bare-metal; it is marginal on a virtualized-TSC (KVM) box (rdtscp buries the prefetch delta) \u2014 supply the base via a companion leak there",
|
|
194
|
+
"one probabilistic reoccupy race per boot (~2/3; ~96% within 3 boots); a miss bakes clockevents_handle_noop (endbr64;ret) so the box stays ALIVE for retry"
|
|
195
|
+
],
|
|
196
|
+
citation: "0sec hid-sony ghl_poke_timer UAF \u2192 clean unprivileged LPE (/root/ghl-clean/ghl_lpe_clean.c, uid=0 5/7 boots; /root/ghl-full FULL-ROOT); EntryBleed CVE-2022-4543"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: "fileOpsVtableConfusion",
|
|
200
|
+
title: "pipe_buffer/file \u2192 fake file_operations vtable \u2192 fileops hijack (control transfer)",
|
|
201
|
+
// Type-confusion tail: with an arb-write or UAF over a `struct file` (or a
|
|
202
|
+
// `pipe_buffer` aliased onto one), overwrite `f_op` / the fd-array's
|
|
203
|
+
// `fde_file[0]` to point at an attacker-shaped `file_operations`, then invoke
|
|
204
|
+
// a fileop (->unlocked_ioctl / ->release) to hijack the slot and transfer
|
|
205
|
+
// control (commit_creds(prepare_kernel_cred(0)) or a stack pivot).
|
|
206
|
+
appliesTo: ["uaf", "oob-write"],
|
|
207
|
+
ceiling: "root",
|
|
208
|
+
preconditions: [
|
|
209
|
+
"an arbitrary write, or a UAF/OOB, over a `struct file` (or a `pipe_buffer` that can be aliased onto one \u2014 see fdTableSpray)",
|
|
210
|
+
"a kernel-readable location holding an attacker-shaped `file_operations` (sprayed pipe_buffer/msg_msg payload, or a known writable global)",
|
|
211
|
+
"an unprivileged path to invoke a fileop on the confused fd (ioctl/read/close) so the hijacked slot is called"
|
|
212
|
+
],
|
|
213
|
+
sprayObjects: ["file", "pipe_buffer", "fdtable"],
|
|
214
|
+
steps: [
|
|
215
|
+
"overwrite the victim `struct file`'s `f_op` (or the fd-array's `fde_file[0]` \u2192 its `f_op`) with a pointer to a fake `file_operations`",
|
|
216
|
+
"populate the fake `file_operations` so the targeted slot (`->unlocked_ioctl` / `->release`) points at the control-transfer gadget",
|
|
217
|
+
"invoke the fileop on the confused fd (ioctl / close) to call the hijacked slot \u2014 transfer control to the gadget",
|
|
218
|
+
"run `commit_creds(prepare_kernel_cred(0))` (or pivot) from the hijacked fileop to overwrite the current task's creds \u2192 root"
|
|
219
|
+
],
|
|
220
|
+
finalStep: "cred-overwrite",
|
|
221
|
+
reliability: "medium",
|
|
222
|
+
caveats: [
|
|
223
|
+
"CFI / FineIBT rejects an arbitrary indirect call target \u2014 the fake `->ioctl` must land on a type-matching signed gadget, not raw shellcode",
|
|
224
|
+
"KASLR requires a text/heap leak first to place the fake vtable + gadget (pair with a leak primitive in library.ts)",
|
|
225
|
+
"read-only `struct file.f_op` in some builds \u2014 confuse via a controllable `pipe_buffer`/fd-array alias rather than an in-place `f_op` write"
|
|
226
|
+
],
|
|
227
|
+
citation: "classic fake-file_operations / vtable hijack; pipe_buffer & fd-array type-confusion (kernelCTF fileops slot hijack)"
|
|
228
|
+
}
|
|
229
|
+
];
|
|
230
|
+
function getRecipe(id) {
|
|
231
|
+
return RECIPE_LIBRARY.find((r) => r.id === id);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// packages/core/dist/kernel/exploit/strategy.js
|
|
235
|
+
var ESCALATION_LADDER = [
|
|
236
|
+
"none",
|
|
237
|
+
"attempted",
|
|
238
|
+
"triggered",
|
|
239
|
+
"reclaim",
|
|
240
|
+
"arb-read",
|
|
241
|
+
"arb-write",
|
|
242
|
+
"root"
|
|
243
|
+
];
|
|
244
|
+
function rungRank(rung) {
|
|
245
|
+
const rank = ESCALATION_LADDER.indexOf(rung);
|
|
246
|
+
return rank < 0 ? 0 : rank;
|
|
247
|
+
}
|
|
248
|
+
function maxRung(a, b) {
|
|
249
|
+
return rungRank(a) >= rungRank(b) ? a : b;
|
|
250
|
+
}
|
|
251
|
+
function ratchet(current, observed) {
|
|
252
|
+
return maxRung(current, observed);
|
|
253
|
+
}
|
|
254
|
+
function rungAtLeast(rung, floor) {
|
|
255
|
+
return rungRank(rung) >= rungRank(floor);
|
|
256
|
+
}
|
|
257
|
+
function ladderUpTo(ceiling) {
|
|
258
|
+
const ceil = rungRank(ceiling);
|
|
259
|
+
return ESCALATION_LADDER.filter((rung) => rung !== "none" && rungRank(rung) <= ceil);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export {
|
|
263
|
+
ESCALATION_LADDER,
|
|
264
|
+
rungRank,
|
|
265
|
+
maxRung,
|
|
266
|
+
ratchet,
|
|
267
|
+
rungAtLeast,
|
|
268
|
+
ladderUpTo,
|
|
269
|
+
RECIPE_LIBRARY,
|
|
270
|
+
getRecipe
|
|
271
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __0secCreateRequire } from "node:module";
|
|
3
|
+
const require = __0secCreateRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
// packages/core/dist/agent/output-truncation.js
|
|
6
|
+
var BYTES_PER_TOKEN = 4;
|
|
7
|
+
var DEFAULT_TOOL_OUTPUT_TOKENS = 1e4;
|
|
8
|
+
function utf8Size(codePoint) {
|
|
9
|
+
if (codePoint < 128)
|
|
10
|
+
return 1;
|
|
11
|
+
if (codePoint < 2048)
|
|
12
|
+
return 2;
|
|
13
|
+
if (codePoint < 65536)
|
|
14
|
+
return 3;
|
|
15
|
+
return 4;
|
|
16
|
+
}
|
|
17
|
+
function headSlice(text, byteBudget) {
|
|
18
|
+
let bytes = 0;
|
|
19
|
+
let i = 0;
|
|
20
|
+
while (i < text.length) {
|
|
21
|
+
const cp = text.codePointAt(i);
|
|
22
|
+
const size = utf8Size(cp);
|
|
23
|
+
if (bytes + size > byteBudget)
|
|
24
|
+
break;
|
|
25
|
+
bytes += size;
|
|
26
|
+
i += cp > 65535 ? 2 : 1;
|
|
27
|
+
}
|
|
28
|
+
return text.slice(0, i);
|
|
29
|
+
}
|
|
30
|
+
function tailSlice(text, byteBudget) {
|
|
31
|
+
let bytes = 0;
|
|
32
|
+
let end = text.length;
|
|
33
|
+
while (end > 0) {
|
|
34
|
+
let start = end - 1;
|
|
35
|
+
const unit = text.charCodeAt(start);
|
|
36
|
+
if (unit >= 56320 && unit <= 57343 && start > 0) {
|
|
37
|
+
const prev = text.charCodeAt(start - 1);
|
|
38
|
+
if (prev >= 55296 && prev <= 56319)
|
|
39
|
+
start -= 1;
|
|
40
|
+
}
|
|
41
|
+
const size = utf8Size(text.codePointAt(start));
|
|
42
|
+
if (bytes + size > byteBudget)
|
|
43
|
+
break;
|
|
44
|
+
bytes += size;
|
|
45
|
+
end = start;
|
|
46
|
+
}
|
|
47
|
+
return text.slice(end);
|
|
48
|
+
}
|
|
49
|
+
function truncateMiddle(text, options = {}) {
|
|
50
|
+
const limit = options.limit ?? DEFAULT_TOOL_OUTPUT_TOKENS;
|
|
51
|
+
const mode = options.mode ?? "tokens";
|
|
52
|
+
const byteBudget = Math.max(0, mode === "tokens" ? limit * BYTES_PER_TOKEN : limit);
|
|
53
|
+
const totalBytes = Buffer.byteLength(text, "utf8");
|
|
54
|
+
const originalTokens = Math.ceil(totalBytes / BYTES_PER_TOKEN);
|
|
55
|
+
const originalLines = text.length === 0 ? 0 : text.split("\n").length;
|
|
56
|
+
if (totalBytes <= byteBudget) {
|
|
57
|
+
return { text, truncated: false, originalTokens, originalLines, truncatedTokens: 0 };
|
|
58
|
+
}
|
|
59
|
+
const headBudget = Math.floor(byteBudget / 2);
|
|
60
|
+
const head = headSlice(text, headBudget);
|
|
61
|
+
const tail = tailSlice(text, byteBudget - headBudget);
|
|
62
|
+
const keptBytes = Buffer.byteLength(head, "utf8") + Buffer.byteLength(tail, "utf8");
|
|
63
|
+
const truncatedTokens = Math.ceil((totalBytes - keptBytes) / BYTES_PER_TOKEN);
|
|
64
|
+
return {
|
|
65
|
+
text: `${head}
|
|
66
|
+
\u2026${truncatedTokens} tokens truncated\u2026
|
|
67
|
+
${tail}`,
|
|
68
|
+
truncated: true,
|
|
69
|
+
originalTokens,
|
|
70
|
+
originalLines,
|
|
71
|
+
truncatedTokens
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function formatTruncated(text, policy = {}) {
|
|
75
|
+
const initial = truncateMiddle(text, policy);
|
|
76
|
+
if (!initial.truncated)
|
|
77
|
+
return initial.text;
|
|
78
|
+
const limit = policy.limit ?? DEFAULT_TOOL_OUTPUT_TOKENS;
|
|
79
|
+
const mode = policy.mode ?? "tokens";
|
|
80
|
+
const byteBudget = Math.max(0, mode === "tokens" ? limit * BYTES_PER_TOKEN : limit);
|
|
81
|
+
const header = [
|
|
82
|
+
`Warning: truncated output (original token count: ${initial.originalTokens})`,
|
|
83
|
+
`Total output lines: ${initial.originalLines}`,
|
|
84
|
+
""
|
|
85
|
+
].join("\n");
|
|
86
|
+
const headerBytes = Buffer.byteLength(header, "utf8");
|
|
87
|
+
if (headerBytes >= byteBudget)
|
|
88
|
+
return headSlice(header, byteBudget);
|
|
89
|
+
let bodyBudget = byteBudget - headerBytes;
|
|
90
|
+
let body = truncateMiddle(text, { mode: "bytes", limit: bodyBudget });
|
|
91
|
+
let rendered = `${header}
|
|
92
|
+
${body.text}`;
|
|
93
|
+
while (Buffer.byteLength(rendered, "utf8") > byteBudget && bodyBudget > 0) {
|
|
94
|
+
bodyBudget = Math.max(0, bodyBudget - (Buffer.byteLength(rendered, "utf8") - byteBudget));
|
|
95
|
+
body = truncateMiddle(text, { mode: "bytes", limit: bodyBudget });
|
|
96
|
+
rendered = `${header}
|
|
97
|
+
${body.text}`;
|
|
98
|
+
}
|
|
99
|
+
return Buffer.byteLength(rendered, "utf8") <= byteBudget ? rendered : headSlice(rendered, byteBudget);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
truncateMiddle,
|
|
104
|
+
formatTruncated
|
|
105
|
+
};
|