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,381 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __0secCreateRequire } from "node:module";
|
|
3
|
+
const require = __0secCreateRequire(import.meta.url);
|
|
4
|
+
import {
|
|
5
|
+
severityRank
|
|
6
|
+
} from "./chunk-KKGE5RQE.js";
|
|
7
|
+
|
|
8
|
+
// packages/core/dist/triage/kernel-primitive.js
|
|
9
|
+
function maxSeverity(a, b) {
|
|
10
|
+
return severityRank(a) >= severityRank(b) ? a : b;
|
|
11
|
+
}
|
|
12
|
+
var WRITE_PRIMITIVE_KINDS = /* @__PURE__ */ new Set([
|
|
13
|
+
"use-after-free",
|
|
14
|
+
"out-of-bounds-write"
|
|
15
|
+
]);
|
|
16
|
+
var CONTROL_FLOW_CALL_SITES = /^(call_timer_fn|run_timer_softirq|__run_timers?|__run_timer_base|expire_timers|__hrtimer_run_queues|__run_hrtimer|hrtimer_interrupt|__do_softirq|tasklet_action|tasklet_hi_action|process_one_work)$/;
|
|
17
|
+
function isControlFlowCallSite(faultingPc) {
|
|
18
|
+
if (!faultingPc)
|
|
19
|
+
return false;
|
|
20
|
+
const sym = faultingPc.match(/^([A-Za-z_][\w.$]*)/)?.[1];
|
|
21
|
+
if (!sym)
|
|
22
|
+
return false;
|
|
23
|
+
if (CONTROL_FLOW_CALL_SITES.test(sym))
|
|
24
|
+
return true;
|
|
25
|
+
return /(?:_timer_fn|_timeout|_callback|_cb)$/.test(sym);
|
|
26
|
+
}
|
|
27
|
+
function classifyKernelPrimitive(report) {
|
|
28
|
+
const rationale = [];
|
|
29
|
+
const hasAlloc = Boolean(report.allocSite);
|
|
30
|
+
const hasFree = Boolean(report.freeSite);
|
|
31
|
+
const isWrite = report.accessType === "write";
|
|
32
|
+
const isRead = report.accessType === "read";
|
|
33
|
+
let kind;
|
|
34
|
+
let control;
|
|
35
|
+
let exploitability;
|
|
36
|
+
let confidence;
|
|
37
|
+
switch (report.crashType) {
|
|
38
|
+
case "kasan-uaf":
|
|
39
|
+
case "kasan-wild": {
|
|
40
|
+
kind = "use-after-free";
|
|
41
|
+
control = isWrite ? "write" : isRead ? "read" : "write";
|
|
42
|
+
exploitability = control === "write" ? 0.85 : 0.55;
|
|
43
|
+
confidence = report.crashType === "kasan-wild" ? 0.5 : 0.8;
|
|
44
|
+
rationale.push(`Use-after-free grants an attacker a dangling reference; a ${control} after free is the lever.`);
|
|
45
|
+
if (hasAlloc && hasFree) {
|
|
46
|
+
exploitability = Math.min(1, exploitability + 0.05);
|
|
47
|
+
confidence = Math.min(1, confidence + 0.1);
|
|
48
|
+
rationale.push("Both KASAN allocation and free sites are known \u2014 the object lifecycle is pinned, so a reclaim window can be targeted.");
|
|
49
|
+
}
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case "kasan-oob": {
|
|
53
|
+
kind = isWrite ? "out-of-bounds-write" : "out-of-bounds-read";
|
|
54
|
+
control = isWrite ? "write" : "read";
|
|
55
|
+
exploitability = isWrite ? 0.8 : 0.45;
|
|
56
|
+
confidence = 0.8;
|
|
57
|
+
rationale.push(isWrite ? "Heap out-of-bounds write corrupts an adjacent allocation \u2014 a write-what-where candidate." : "Heap out-of-bounds read leaks adjacent allocation contents \u2014 an info-leak candidate.");
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case "kasan-stack-oob": {
|
|
61
|
+
kind = isWrite ? "out-of-bounds-write" : "out-of-bounds-read";
|
|
62
|
+
control = isWrite ? "write" : "read";
|
|
63
|
+
exploitability = isWrite ? 0.7 : 0.4;
|
|
64
|
+
confidence = 0.75;
|
|
65
|
+
rationale.push("Stack out-of-bounds access; write variants can target saved registers / return addresses subject to canary mitigation.");
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
case "kasan-double-free": {
|
|
69
|
+
kind = "double-free";
|
|
70
|
+
control = "free";
|
|
71
|
+
exploitability = 0.6;
|
|
72
|
+
confidence = 0.8;
|
|
73
|
+
rationale.push("Double-free confuses the slab allocator freelist, enabling cache poisoning toward an overlapping allocation.");
|
|
74
|
+
if (hasFree) {
|
|
75
|
+
confidence = Math.min(1, confidence + 0.1);
|
|
76
|
+
rationale.push("KASAN free site is known \u2014 the double-free path is pinned.");
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
case "kasan-invalid-free": {
|
|
81
|
+
kind = "invalid-free";
|
|
82
|
+
control = "free";
|
|
83
|
+
exploitability = 0.5;
|
|
84
|
+
confidence = 0.75;
|
|
85
|
+
rationale.push("Invalid-free passes an attacker-influenced pointer to the freer, a potential arbitrary-free primitive.");
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
case "kasan-null":
|
|
89
|
+
case "kernel-oops":
|
|
90
|
+
case "kernel-panic":
|
|
91
|
+
case "kernel-bug":
|
|
92
|
+
case "general-protection": {
|
|
93
|
+
kind = "null-deref";
|
|
94
|
+
control = "none";
|
|
95
|
+
exploitability = 0.1;
|
|
96
|
+
confidence = 0.6;
|
|
97
|
+
rationale.push("Null / faulting dereference is typically a denial-of-service unless low-address mmap is permitted (mmap_min_addr=0).");
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
case "ubsan":
|
|
101
|
+
case "ubsan-shift":
|
|
102
|
+
case "ubsan-overflow":
|
|
103
|
+
case "ubsan-bounds":
|
|
104
|
+
case "ubsan-alignment": {
|
|
105
|
+
kind = "uninitialized-access";
|
|
106
|
+
control = "none";
|
|
107
|
+
exploitability = 0.15;
|
|
108
|
+
confidence = 0.5;
|
|
109
|
+
rationale.push("UBSAN undefined behaviour rarely yields a direct memory primitive on its own; treat as a building block pending further analysis.");
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
default: {
|
|
113
|
+
kind = "unknown";
|
|
114
|
+
control = "none";
|
|
115
|
+
exploitability = 0.1;
|
|
116
|
+
confidence = 0.3;
|
|
117
|
+
rationale.push(`Crash type ${report.crashType} does not map to a known memory primitive.`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (report.accessSize && (control === "write" || control === "read")) {
|
|
121
|
+
rationale.push(`Access width ${report.accessSize} byte(s) bounds the per-step control.`);
|
|
122
|
+
if (report.accessSize >= 8) {
|
|
123
|
+
exploitability = Math.min(1, exploitability + 0.05);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const objectHint = report.allocSite ?? report.freeSite ?? void 0;
|
|
127
|
+
const faultingPc = report.faultingPc ?? parseFaultingPc(report.rawText);
|
|
128
|
+
const slabCache = report.slabCache ?? parseSlabCache(report.rawText);
|
|
129
|
+
if ((kind === "use-after-free" || kind === "double-free") && isControlFlowCallSite(faultingPc)) {
|
|
130
|
+
control = "call";
|
|
131
|
+
exploitability = Math.max(exploitability, 0.9);
|
|
132
|
+
confidence = Math.min(1, confidence + 0.05);
|
|
133
|
+
rationale.push("Faulting site is a timer/ops indirect-call dispatch frame: the freed object owns a function pointer the kernel calls after the free, so reclaiming it with a forged `.function` hijacks control flow (fake-cred one-shot: `.function=commit_creds`, `rdi=&obj`).");
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
kind,
|
|
137
|
+
control,
|
|
138
|
+
exploitability: Number(exploitability.toFixed(2)),
|
|
139
|
+
confidence: Number(confidence.toFixed(2)),
|
|
140
|
+
controlDemo: buildControlDemo(kind, control, report),
|
|
141
|
+
rationale,
|
|
142
|
+
objectHint,
|
|
143
|
+
...faultingPc ? { faultingPc } : {},
|
|
144
|
+
...slabCache ? { slabCache } : {}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function parseFaultingPc(dmesg) {
|
|
148
|
+
if (!dmesg)
|
|
149
|
+
return void 0;
|
|
150
|
+
const m = dmesg.match(/BUG:\s*KASAN:[^\n]*?\bin\s+([A-Za-z_][\w.$]*\+0x[0-9a-fA-F]+(?:\/0x[0-9a-fA-F]+)?)/);
|
|
151
|
+
return m ? m[1] : void 0;
|
|
152
|
+
}
|
|
153
|
+
function parseSlabCache(dmesg) {
|
|
154
|
+
if (!dmesg)
|
|
155
|
+
return void 0;
|
|
156
|
+
const cacheTok = dmesg.match(/\bcache\s+([A-Za-z][\w-]*-\d+|[A-Za-z][\w-]*)\b/);
|
|
157
|
+
if (cacheTok && /\d/.test(cacheTok[1]))
|
|
158
|
+
return cacheTok[1];
|
|
159
|
+
const kmallocTok = dmesg.match(/\b((?:dma-|cg-)?kmalloc(?:-cg)?-\d+)\b/);
|
|
160
|
+
if (kmallocTok)
|
|
161
|
+
return kmallocTok[1];
|
|
162
|
+
return cacheTok ? cacheTok[1] : void 0;
|
|
163
|
+
}
|
|
164
|
+
function buildControlDemo(kind, control, report) {
|
|
165
|
+
const sizeHint = report.accessSize ? `${report.accessSize}-byte` : "controlled-width";
|
|
166
|
+
const objectHint = report.allocSite ?? report.faultingFunction;
|
|
167
|
+
if (control === "call") {
|
|
168
|
+
return {
|
|
169
|
+
kind: "object-overwrite",
|
|
170
|
+
description: `After the free in ${report.freeSite ?? "the freed path"}, spray the victim cache to reclaim the freed object with a forged embedded callback (timer_list.function / ops slot), then let the kernel's timer/softirq dispatch (${objectHint}) fire and confirm the indirect call lands on the attacker-chosen target.`,
|
|
171
|
+
controlledInput: "reclaiming-object bytes at the callback-pointer offset",
|
|
172
|
+
demonstrated: false
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
switch (kind) {
|
|
176
|
+
case "use-after-free":
|
|
177
|
+
if (control === "write") {
|
|
178
|
+
return {
|
|
179
|
+
kind: "object-overwrite",
|
|
180
|
+
description: `After the free in ${report.freeSite ?? "the freed path"}, spray heap objects of the victim cache to reclaim the freed slot, then trigger the dangling ${sizeHint} write and confirm attacker bytes land in the reclaimed object (${objectHint}).`,
|
|
181
|
+
controlledInput: "heap-spray contents reclaiming the freed object",
|
|
182
|
+
demonstrated: false
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
kind: "oob-read-leak",
|
|
187
|
+
description: `Reclaim the freed object (${objectHint}) with a marker payload, then trigger the dangling read and confirm the marker bytes leak back to userspace.`,
|
|
188
|
+
controlledInput: "reclaiming object marker bytes",
|
|
189
|
+
demonstrated: false
|
|
190
|
+
};
|
|
191
|
+
case "out-of-bounds-write":
|
|
192
|
+
return {
|
|
193
|
+
kind: "write-what-where",
|
|
194
|
+
description: `Place a target object immediately after the overflowed allocation (${objectHint}) via heap grooming, then drive the ${sizeHint} OOB write and confirm a controlled field of the neighbour is overwritten.`,
|
|
195
|
+
controlledInput: "overflow payload + neighbouring object layout",
|
|
196
|
+
demonstrated: false
|
|
197
|
+
};
|
|
198
|
+
case "out-of-bounds-read":
|
|
199
|
+
return {
|
|
200
|
+
kind: "oob-read-leak",
|
|
201
|
+
description: `Groom a secret-bearing object after the under-sized allocation (${objectHint}), trigger the ${sizeHint} OOB read, and confirm adjacent bytes are returned to userspace.`,
|
|
202
|
+
controlledInput: "adjacent object placement",
|
|
203
|
+
demonstrated: false
|
|
204
|
+
};
|
|
205
|
+
case "double-free":
|
|
206
|
+
return {
|
|
207
|
+
kind: "free-confusion",
|
|
208
|
+
description: `Allocate a victim object between the two frees of ${objectHint} so the second free poisons the freelist, then confirm two live allocations alias the same slot.`,
|
|
209
|
+
controlledInput: "intervening allocation timing",
|
|
210
|
+
demonstrated: false
|
|
211
|
+
};
|
|
212
|
+
case "invalid-free":
|
|
213
|
+
return {
|
|
214
|
+
kind: "free-confusion",
|
|
215
|
+
description: `Influence the pointer passed to the freer in ${report.faultingFunction} and confirm a non-allocated / attacker-chosen address is handed to the slab allocator.`,
|
|
216
|
+
controlledInput: "the freed pointer value",
|
|
217
|
+
demonstrated: false
|
|
218
|
+
};
|
|
219
|
+
default:
|
|
220
|
+
return {
|
|
221
|
+
kind: "none",
|
|
222
|
+
description: "No bounded control demonstration is proposed for this primitive; it reads as denial-of-service or an undetermined building block.",
|
|
223
|
+
demonstrated: false
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
async function attemptControlDemo(primitive, probe) {
|
|
228
|
+
if (primitive.controlDemo.kind === "none")
|
|
229
|
+
return primitive;
|
|
230
|
+
const result = await probe(primitive.controlDemo);
|
|
231
|
+
if (!result.controlled) {
|
|
232
|
+
return {
|
|
233
|
+
...primitive,
|
|
234
|
+
controlDemo: {
|
|
235
|
+
...primitive.controlDemo,
|
|
236
|
+
demonstrated: false,
|
|
237
|
+
evidence: result.evidence
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
...primitive,
|
|
243
|
+
// A demonstrated control is hard exploitability evidence.
|
|
244
|
+
exploitability: Math.min(1, primitive.exploitability + 0.1),
|
|
245
|
+
controlDemo: {
|
|
246
|
+
...primitive.controlDemo,
|
|
247
|
+
demonstrated: true,
|
|
248
|
+
evidence: result.evidence
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function exploitabilityAdjustedSeverity(baseSeverity, primitive) {
|
|
253
|
+
let floor = "info";
|
|
254
|
+
if (primitive.controlDemo.demonstrated && WRITE_PRIMITIVE_KINDS.has(primitive.kind)) {
|
|
255
|
+
floor = "critical";
|
|
256
|
+
} else if (primitive.exploitability >= 0.8 && primitive.control === "write") {
|
|
257
|
+
floor = "critical";
|
|
258
|
+
} else if (primitive.control === "call" && primitive.exploitability >= 0.8) {
|
|
259
|
+
floor = "critical";
|
|
260
|
+
} else if (primitive.exploitability >= 0.6) {
|
|
261
|
+
floor = "high";
|
|
262
|
+
} else if (primitive.exploitability >= 0.4) {
|
|
263
|
+
floor = "medium";
|
|
264
|
+
}
|
|
265
|
+
return maxSeverity(baseSeverity, floor);
|
|
266
|
+
}
|
|
267
|
+
function describeKernelPrimitive(primitive) {
|
|
268
|
+
const lines = [
|
|
269
|
+
`Primitive: ${primitive.kind} (control=${primitive.control})`,
|
|
270
|
+
`Exploitability: ${primitive.exploitability.toFixed(2)} (classifier confidence=${primitive.confidence.toFixed(2)})`,
|
|
271
|
+
`Control demo [${primitive.controlDemo.kind}${primitive.controlDemo.demonstrated ? ", demonstrated" : ", candidate"}]: ${primitive.controlDemo.description}`
|
|
272
|
+
];
|
|
273
|
+
if (primitive.controlDemo.evidence) {
|
|
274
|
+
lines.push(`Control demo evidence: ${primitive.controlDemo.evidence}`);
|
|
275
|
+
}
|
|
276
|
+
if (primitive.objectHint) {
|
|
277
|
+
lines.push(`Object hint: ${primitive.objectHint}`);
|
|
278
|
+
}
|
|
279
|
+
for (const r of primitive.rationale)
|
|
280
|
+
lines.push(`- ${r}`);
|
|
281
|
+
return lines;
|
|
282
|
+
}
|
|
283
|
+
function classifyPrimitiveFromDmesg(dmesg, detectedCrashType) {
|
|
284
|
+
const crashType = detectedCrashType ?? sniffCrashType(dmesg) ?? "unknown";
|
|
285
|
+
const accessMatch = dmesg.match(/(Read|Write)\s+of\s+size\s+(\d+)/i);
|
|
286
|
+
const report = {
|
|
287
|
+
rawText: dmesg,
|
|
288
|
+
crashType,
|
|
289
|
+
faultingFunction: "unknown",
|
|
290
|
+
callStack: [],
|
|
291
|
+
subsystem: "unknown",
|
|
292
|
+
accessType: accessMatch ? accessMatch[1].toLowerCase() : void 0,
|
|
293
|
+
accessSize: accessMatch ? parseInt(accessMatch[2], 10) : void 0,
|
|
294
|
+
allocSite: /Allocated by task/i.test(dmesg) ? "alloc" : void 0,
|
|
295
|
+
freeSite: /Freed by task/i.test(dmesg) ? "free" : void 0
|
|
296
|
+
};
|
|
297
|
+
return classifyKernelPrimitive(report);
|
|
298
|
+
}
|
|
299
|
+
function sniffCrashType(dmesg) {
|
|
300
|
+
if (/use-after-free/i.test(dmesg))
|
|
301
|
+
return "kasan-uaf";
|
|
302
|
+
if (/out-of-bounds/i.test(dmesg))
|
|
303
|
+
return "kasan-oob";
|
|
304
|
+
if (/double-free/i.test(dmesg))
|
|
305
|
+
return "kasan-double-free";
|
|
306
|
+
if (/invalid-free/i.test(dmesg))
|
|
307
|
+
return "kasan-invalid-free";
|
|
308
|
+
if (/null pointer|null-ptr-deref/i.test(dmesg))
|
|
309
|
+
return "kasan-null";
|
|
310
|
+
return void 0;
|
|
311
|
+
}
|
|
312
|
+
var DANGER_LADDER = [
|
|
313
|
+
"none",
|
|
314
|
+
"warn",
|
|
315
|
+
"race",
|
|
316
|
+
"oob-read",
|
|
317
|
+
"info-leak",
|
|
318
|
+
"gpf",
|
|
319
|
+
"double-free",
|
|
320
|
+
"oob-write",
|
|
321
|
+
"uaf-write",
|
|
322
|
+
"control-flow"
|
|
323
|
+
];
|
|
324
|
+
var BENIGN_MAX_CLASS = "info-leak";
|
|
325
|
+
var UPGRADE_MIN_CLASS = "gpf";
|
|
326
|
+
function rankDanger(cls) {
|
|
327
|
+
const i = DANGER_LADDER.indexOf(cls);
|
|
328
|
+
return i < 0 ? 0 : i;
|
|
329
|
+
}
|
|
330
|
+
function maxDangerClass(a, b) {
|
|
331
|
+
return rankDanger(a) >= rankDanger(b) ? a : b;
|
|
332
|
+
}
|
|
333
|
+
function isDangerUpgrade(baseline, observed) {
|
|
334
|
+
return rankDanger(baseline) <= rankDanger(BENIGN_MAX_CLASS) && rankDanger(observed) >= rankDanger(UPGRADE_MIN_CLASS);
|
|
335
|
+
}
|
|
336
|
+
function classifyDmesgDanger(dmesg) {
|
|
337
|
+
if (!dmesg)
|
|
338
|
+
return "none";
|
|
339
|
+
const hasWrite = /\bWrite of size\s+\d+/i.test(dmesg);
|
|
340
|
+
const hasRead = /\bRead of size\s+\d+/i.test(dmesg);
|
|
341
|
+
const isKasan = /KASAN:/i.test(dmesg);
|
|
342
|
+
if (/KASAN.*double-free|double free|Object already free/i.test(dmesg))
|
|
343
|
+
return "double-free";
|
|
344
|
+
if (/Control flow|CFI failure|kCFI|forward-edge/i.test(dmesg))
|
|
345
|
+
return "control-flow";
|
|
346
|
+
if (hasWrite && /use-after-free/i.test(dmesg))
|
|
347
|
+
return "uaf-write";
|
|
348
|
+
if (hasWrite && /out-of-bounds|slab-out-of-bounds|global-out-of-bounds/i.test(dmesg))
|
|
349
|
+
return "oob-write";
|
|
350
|
+
if (hasWrite && isKasan)
|
|
351
|
+
return "oob-write";
|
|
352
|
+
if (/general protection fault/i.test(dmesg))
|
|
353
|
+
return "gpf";
|
|
354
|
+
if (hasRead || isKasan)
|
|
355
|
+
return "oob-read";
|
|
356
|
+
if (/KCSAN:|data-race/i.test(dmesg))
|
|
357
|
+
return "race";
|
|
358
|
+
if (/WARNING:|\bWARN\b|------------\[ cut here \]/i.test(dmesg))
|
|
359
|
+
return "warn";
|
|
360
|
+
return "none";
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export {
|
|
364
|
+
maxSeverity,
|
|
365
|
+
isControlFlowCallSite,
|
|
366
|
+
classifyKernelPrimitive,
|
|
367
|
+
parseFaultingPc,
|
|
368
|
+
parseSlabCache,
|
|
369
|
+
buildControlDemo,
|
|
370
|
+
attemptControlDemo,
|
|
371
|
+
exploitabilityAdjustedSeverity,
|
|
372
|
+
describeKernelPrimitive,
|
|
373
|
+
classifyPrimitiveFromDmesg,
|
|
374
|
+
sniffCrashType,
|
|
375
|
+
BENIGN_MAX_CLASS,
|
|
376
|
+
UPGRADE_MIN_CLASS,
|
|
377
|
+
rankDanger,
|
|
378
|
+
maxDangerClass,
|
|
379
|
+
isDangerUpgrade,
|
|
380
|
+
classifyDmesgDanger
|
|
381
|
+
};
|