muaddib-scanner 2.10.5 → 2.10.7
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/README.md +11 -11
- package/package.json +1 -1
- package/src/response/playbooks.js +5 -0
- package/src/rules/index.js +9 -0
- package/src/sandbox/index.js +15 -2
- package/src/scanner/ast-detectors.js +1 -1
- package/src/scanner/shell.js +3 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
npm and PyPI supply-chain attacks are exploding. Shai-Hulud compromised 25K+ repos in 2025. Existing tools detect threats but don't help you respond.
|
|
32
32
|
|
|
33
|
-
MUAD'DIB combines **14 parallel scanners** (
|
|
33
|
+
MUAD'DIB combines **14 parallel scanners** (162 detection rules), a **deobfuscation engine**, **inter-module dataflow analysis**, **compound scoring**, **ML classifiers** (XGBoost), and Docker sandbox to detect known threats and suspicious behavioral patterns in npm and PyPI packages.
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
|
@@ -136,7 +136,7 @@ Ultra-strict detection with lower tolerance. Detects any network access, subproc
|
|
|
136
136
|
muaddib scan . --webhook "https://discord.com/api/webhooks/..."
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
-
Strict filtering (v2.1.2): alerts only for IOC matches, sandbox-confirmed threats, or canary token exfiltration.
|
|
139
|
+
Strict filtering (v2.1.2): alerts only for IOC matches, sandbox-confirmed threats, or canary token exfiltration. Priority triage (v2.10.5): P1 (red, IOC/sandbox/canary), P2 (orange, high-score/compounds), P3 (yellow, rest).
|
|
140
140
|
|
|
141
141
|
### Behavioral anomaly detection (v2.0)
|
|
142
142
|
|
|
@@ -195,9 +195,9 @@ muaddib replay # Ground truth validation (46/49 TPR)
|
|
|
195
195
|
| GitHub Actions | Shai-Hulud backdoor detection |
|
|
196
196
|
| Hash Scanner | Known malicious file hashes |
|
|
197
197
|
|
|
198
|
-
###
|
|
198
|
+
### 162 detection rules
|
|
199
199
|
|
|
200
|
-
All rules are mapped to MITRE ATT&CK techniques. See [SECURITY.md](SECURITY.md#detection-rules-
|
|
200
|
+
All rules are mapped to MITRE ATT&CK techniques. See [SECURITY.md](SECURITY.md#detection-rules-v2105) for the complete rules reference.
|
|
201
201
|
|
|
202
202
|
### Detected campaigns
|
|
203
203
|
|
|
@@ -282,13 +282,13 @@ repos:
|
|
|
282
282
|
|
|
283
283
|
| Metric | Result | Details |
|
|
284
284
|
|--------|--------|---------|
|
|
285
|
-
| **Wild TPR** (Datadog 17K) | **92.
|
|
285
|
+
| **Wild TPR** (Datadog 17K) | **92.8%** (13,538/14,587 in-scope) | 17,922 packages. 3,335 skipped (no JS). By category: compromised_lib 97.8%, malicious_intent 92.1% |
|
|
286
286
|
| **TPR** (Ground Truth) | **93.9%** (46/49) | 51 real attacks. 3 out-of-scope: browser-only |
|
|
287
|
-
| **FPR** (Benign curated) | **
|
|
287
|
+
| **FPR** (Benign curated) | **11.0%** (58/529) | 529 npm packages, real source via `npm pack` |
|
|
288
288
|
| **FPR** (Benign random) | **7.5%** (15/200) | 200 random npm packages, stratified sampling |
|
|
289
289
|
| **ADR** (Adversarial + Holdout) | **96.3%** (103/107) | 67 adversarial + 40 holdout (107 available on disk), global threshold=20 |
|
|
290
290
|
|
|
291
|
-
**
|
|
291
|
+
**2643 tests** across 57 files. **162 rules** (157 RULES + 5 PARANOID).
|
|
292
292
|
|
|
293
293
|
> **Methodology caveats:**
|
|
294
294
|
> - TPR measured on 49 Node.js attack samples (3 browser-only excluded from 51 total)
|
|
@@ -329,11 +329,11 @@ npm test
|
|
|
329
329
|
|
|
330
330
|
### Testing
|
|
331
331
|
|
|
332
|
-
- **
|
|
332
|
+
- **2643 tests** across 57 modular test files
|
|
333
333
|
- **56 fuzz tests** - Malformed inputs, ReDoS, unicode, binary
|
|
334
|
-
- **Datadog 17K benchmark** -
|
|
334
|
+
- **Datadog 17K benchmark** - 14,587 confirmed malware samples (in-scope)
|
|
335
335
|
- **Ground truth validation** - 51 real-world attacks (93.9% TPR)
|
|
336
|
-
- **False positive validation** -
|
|
336
|
+
- **False positive validation** - 11.0% FPR on 529 curated npm packages, 7.5% on 200 random
|
|
337
337
|
|
|
338
338
|
---
|
|
339
339
|
|
|
@@ -351,7 +351,7 @@ npm test
|
|
|
351
351
|
- [Evaluation Methodology](docs/EVALUATION_METHODOLOGY.md) - Experimental protocol, holdout scores
|
|
352
352
|
- [Threat Model](docs/threat-model.md) - What MUAD'DIB detects and doesn't detect
|
|
353
353
|
- [Adversarial Evaluation](ADVERSARIAL.md) - Red team samples and ADR results
|
|
354
|
-
- [Security Policy](SECURITY.md) - Detection rules reference (
|
|
354
|
+
- [Security Policy](SECURITY.md) - Detection rules reference (162 rules)
|
|
355
355
|
- [Security Audit](docs/SECURITY_AUDIT.md) - Bypass validation report
|
|
356
356
|
- [FP Analysis](docs/EVALUATION.md) - Historical false positive analysis
|
|
357
357
|
|
package/package.json
CHANGED
|
@@ -183,6 +183,11 @@ const PLAYBOOKS = {
|
|
|
183
183
|
sh_c_curl_exec:
|
|
184
184
|
'sh -c wrapping autour de curl detecte. Technique d\'evasion pour masquer l\'execution de commandes distantes. Analyser le contenu telecharge.',
|
|
185
185
|
|
|
186
|
+
python_time_delay_exec:
|
|
187
|
+
'ELEVE: Execution Python avec delai time.sleep() important detectee. ' +
|
|
188
|
+
'Technique d\'evasion sandbox (T1497.003) : le malware attend l\'expiration du timeout sandbox avant d\'executer le payload. ' +
|
|
189
|
+
'Verifier le code Python execute et les connexions reseau post-delai.',
|
|
190
|
+
|
|
186
191
|
shai_hulud_backdoor:
|
|
187
192
|
'CRITIQUE: Backdoor Shai-Hulud dans GitHub Actions. Supprimer le workflow et auditer les runs precedents.',
|
|
188
193
|
|
package/src/rules/index.js
CHANGED
|
@@ -1519,6 +1519,15 @@ const RULES = {
|
|
|
1519
1519
|
references: ['https://attack.mitre.org/techniques/T1059/004/'],
|
|
1520
1520
|
mitre: 'T1059.004'
|
|
1521
1521
|
},
|
|
1522
|
+
python_time_delay_exec: {
|
|
1523
|
+
id: 'MUADDIB-SHELL-019',
|
|
1524
|
+
name: 'Python Time Delay Execution',
|
|
1525
|
+
severity: 'HIGH',
|
|
1526
|
+
confidence: 0.80,
|
|
1527
|
+
description: 'Execution Python avec delai time.sleep() >= 100s via child process. Technique d\'evasion sandbox (T1497.003) : le malware attend que la sandbox expire avant d\'executer le payload.',
|
|
1528
|
+
references: ['https://attack.mitre.org/techniques/T1497/003/'],
|
|
1529
|
+
mitre: 'T1497.003'
|
|
1530
|
+
},
|
|
1522
1531
|
|
|
1523
1532
|
// Intent Graph rules (v2.6.0)
|
|
1524
1533
|
detached_credential_exfil: {
|
package/src/sandbox/index.js
CHANGED
|
@@ -186,8 +186,21 @@ async function runSingleSandbox(packageName, options = {}) {
|
|
|
186
186
|
dockerArgs.push('-e', `CANARY_GITCONFIG=${createCanaryGitconfig().replace(/\r?\n/g, '\\n')}`);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
// Inject time offset
|
|
190
|
-
|
|
189
|
+
// Inject time offset — libfaketime-aware (v2.10.7)
|
|
190
|
+
// Run 1 (offset=0): no libfaketime, preload.js handles JS-level only
|
|
191
|
+
// Runs 2+ (offset>0): libfaketime handles C-level time shift for ALL processes
|
|
192
|
+
// (Node, Python, bash), preload.js TIME_OFFSET=0 to avoid double acceleration
|
|
193
|
+
const useFaketime = timeOffset > 0;
|
|
194
|
+
dockerArgs.push('-e', `NODE_TIMING_OFFSET=${useFaketime ? 0 : timeOffset}`);
|
|
195
|
+
|
|
196
|
+
if (useFaketime) {
|
|
197
|
+
const hours = Math.floor(timeOffset / 3600000);
|
|
198
|
+
const faketimeStr = hours >= 24
|
|
199
|
+
? `+${Math.floor(hours / 24)}d x1000`
|
|
200
|
+
: `+${hours}h x1000`;
|
|
201
|
+
dockerArgs.push('-e', `MUADDIB_FAKETIME=${faketimeStr}`);
|
|
202
|
+
dockerArgs.push('-e', 'MUADDIB_FAKETIME_ACTIVE=1');
|
|
203
|
+
}
|
|
191
204
|
|
|
192
205
|
// Both modes need NET_RAW for tcpdump (runs as root in entrypoint).
|
|
193
206
|
// Strict mode also needs NET_ADMIN for iptables network blocking.
|
|
@@ -1929,7 +1929,7 @@ function handleCallExpression(node, ctx) {
|
|
|
1929
1929
|
}
|
|
1930
1930
|
}
|
|
1931
1931
|
|
|
1932
|
-
// Module._load() — internal module loader bypass (
|
|
1932
|
+
// Module._load() — internal module loader bypass (security audit v2)
|
|
1933
1933
|
if (propName === '_load') {
|
|
1934
1934
|
const calleeObj = node.callee.object;
|
|
1935
1935
|
const isModuleIdentifier = calleeObj.type === 'Identifier' &&
|
package/src/scanner/shell.js
CHANGED
|
@@ -26,7 +26,9 @@ const MALICIOUS_PATTERNS = [
|
|
|
26
26
|
{ pattern: /eval\s+.*\$\(curl/m, name: 'eval_curl_subshell', severity: 'CRITICAL' },
|
|
27
27
|
{ pattern: /sh\s+-c\s+['"].*curl/m, name: 'sh_c_curl_exec', severity: 'HIGH' },
|
|
28
28
|
// Bun runtime evasion (v2.8.9 — Shai-Hulud 2.0)
|
|
29
|
-
{ pattern: /\bbun\s+run\b/m, name: 'bun_runtime_evasion', severity: 'HIGH' }
|
|
29
|
+
{ pattern: /\bbun\s+run\b/m, name: 'bun_runtime_evasion', severity: 'HIGH' },
|
|
30
|
+
// Python time.sleep sandbox evasion (v2.10.7 — CanisterWorm T1497.003)
|
|
31
|
+
{ pattern: /python[23]?\s+-c\s*['"].*time\.sleep\s*\(\s*[1-9]\d{2,}/m, name: 'python_time_delay_exec', severity: 'HIGH' }
|
|
30
32
|
];
|
|
31
33
|
|
|
32
34
|
const SHEBANG_RE = /^#!.*\b(?:ba)?sh\b/;
|