muaddib-scanner 2.5.7 → 2.5.8
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/_test_aiweapon.js +12 -0
- package/_test_fp.js +11 -0
- package/_test_fp2.js +37 -0
- package/_test_fp3.js +19 -0
- package/_test_nodemailer.js +17 -0
- package/_test_p4_detail.js +45 -0
- package/_test_p4_detail2.js +32 -0
- package/_test_p4_quick.js +33 -0
- package/_test_regex.js +5 -0
- package/_vitest_result.json +395 -0
- package/iocs/builtin.yaml +80 -11
- package/iocs/packages.yaml +216 -12
- package/logs/alerts/2026-03-06T13-25-09-667-evil-pkg.json +20 -0
- package/logs/alerts/2026-03-06T13-25-09-668-evil-pkg.json +20 -0
- package/logs/alerts/2026-03-06T13-25-09-668-suspect-pkg.json +24 -0
- package/logs/alerts/2026-03-06T13-25-10-228-evil-pkg.json +24 -0
- package/logs/daily-reports/2026-03-06.json +61 -0
- package/package.json +1 -1
- package/src/ioc/scraper.js +1 -1
- package/src/scanner/ast-detectors.js +24 -8
- package/src/scanner/ast.js +21 -1
- package/src/scanner/typosquat.js +5 -1
- package/src/scoring.js +40 -10
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const { run } = require('./src/index.js');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
(async () => {
|
|
5
|
+
const dir = path.join('datasets/adversarial/ai-agent-weaponization');
|
|
6
|
+
const result = await run(dir, { _capture: true });
|
|
7
|
+
console.log('Score:', result.summary.riskScore);
|
|
8
|
+
console.log('MaxFile:', result.summary.maxFileScore, 'Pkg:', result.summary.packageScore);
|
|
9
|
+
for (const t of result.threats) {
|
|
10
|
+
console.log(` ${t.severity.padEnd(8)} ${t.type.padEnd(30)} ${(t.file || '').substring(0, 50)}`);
|
|
11
|
+
}
|
|
12
|
+
})();
|
package/_test_fp.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const { applyFPReductions } = require('./src/scoring.js');
|
|
2
|
+
|
|
3
|
+
const threats = [
|
|
4
|
+
{ type: 'reverse_shell', severity: 'HIGH', message: 'JS reverse shell', file: 'dist\\chunks\\cli-api.js' },
|
|
5
|
+
{ type: 'env_proxy_intercept', severity: 'HIGH', message: 'new Proxy(process.env)', file: 'dist\\module-evaluator.js' },
|
|
6
|
+
{ type: 'prototype_hook', severity: 'MEDIUM', message: 'WebSocket.prototype.addEventListener', file: 'dist\\chunks\\cli-api.js' }
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
console.log('BEFORE:', threats.map(t => `${t.type}: ${t.severity}`));
|
|
10
|
+
applyFPReductions(threats, null, null);
|
|
11
|
+
console.log('AFTER:', threats.map(t => `${t.type}: ${t.severity}`));
|
package/_test_fp2.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { applyFPReductions } = require('./src/scoring.js');
|
|
3
|
+
|
|
4
|
+
// Simulate the exact vitest threats from the scan
|
|
5
|
+
const threats = [
|
|
6
|
+
{ type: 'prototype_hook', severity: 'MEDIUM', message: 'WebSocket.prototype.addEventListener overridden — native API hooking for traffic interception.', file: 'dist\\chunks\\cli-api.B7PN_QUv.js', count: 1 },
|
|
7
|
+
{ type: 'prototype_hook', severity: 'MEDIUM', message: 'WebSocket.prototype.removeEventListener overridden — native API hooking for traffic interception.', file: 'dist\\chunks\\cli-api.B7PN_QUv.js', count: 1 },
|
|
8
|
+
{ type: 'env_access', severity: 'LOW', message: 'Dynamic access to process.env (variable key).', file: 'dist\\chunks\\cli-api.B7PN_QUv.js', count: 1 },
|
|
9
|
+
{ type: 'reverse_shell', severity: 'HIGH', message: 'JavaScript reverse shell: net.Socket + connect() + pipe to shell process stdin/stdout.', file: 'dist\\chunks\\cli-api.B7PN_QUv.js', count: 1 },
|
|
10
|
+
{ type: 'env_access', severity: 'LOW', message: 'Dynamic access to process.env (variable key).', file: 'dist\\chunks\\init.B6MLFIaN.js', count: 1 },
|
|
11
|
+
{ type: 'dynamic_import', severity: 'MEDIUM', message: 'Dynamic import() with computed argument (possible obfuscation).', file: 'dist\\chunks\\traces.CCmnQaNT.js', count: 1 },
|
|
12
|
+
{ type: 'module_compile', severity: 'LOW', message: 'module._compile() detected.', file: 'dist\\chunks\\vm.D3epNOPZ.js', count: 1 },
|
|
13
|
+
{ type: 'module_compile_dynamic', severity: 'LOW', message: 'In-memory code execution.', file: 'dist\\chunks\\vm.D3epNOPZ.js', count: 1 },
|
|
14
|
+
{ type: 'require_cache_poison', severity: 'LOW', message: 'require.cache accessed.', file: 'dist\\chunks\\vm.D3epNOPZ.js', count: 1 },
|
|
15
|
+
{ type: 'dynamic_require', severity: 'LOW', message: 'Dynamic require() with variable argument.', file: 'dist\\chunks\\vm.D3epNOPZ.js', count: 1 },
|
|
16
|
+
{ type: 'dynamic_import', severity: 'MEDIUM', message: 'Dynamic import() with computed argument (possible obfuscation).', file: 'dist\\module-evaluator.js', count: 1 },
|
|
17
|
+
{ type: 'env_access', severity: 'LOW', message: 'Dynamic access to process.env (variable key).', file: 'dist\\module-evaluator.js', count: 1 },
|
|
18
|
+
{ type: 'env_proxy_intercept', severity: 'HIGH', message: 'new Proxy(process.env) detected — intercepts all environment variable access.', file: 'dist\\module-evaluator.js', count: 1 },
|
|
19
|
+
{ type: 'suspicious_dataflow', severity: 'MEDIUM', message: 'Suspicious flow: credentials read (stuff) + network send (request)', file: 'dist\\chunks\\cli-api.B7PN_QUv.js', count: 1 },
|
|
20
|
+
{ type: 'suspicious_dataflow', severity: 'LOW', message: 'Suspicious flow: credentials read (stuff) + network send (get)', file: 'dist\\chunks\\init.B6MLFIaN.js', count: 1 },
|
|
21
|
+
{ type: 'suspicious_dataflow', severity: 'MEDIUM', message: 'Suspicious flow: credentials read (stuff) + network send (get)', file: 'dist\\module-evaluator.js', count: 1 }
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
console.log('BEFORE:');
|
|
25
|
+
threats.forEach(t => console.log(` ${t.severity.padEnd(8)} ${t.type.padEnd(25)} ${t.file}`));
|
|
26
|
+
|
|
27
|
+
applyFPReductions(threats, null, 'vitest');
|
|
28
|
+
|
|
29
|
+
console.log('\nAFTER:');
|
|
30
|
+
threats.forEach(t => console.log(` ${t.severity.padEnd(8)} ${t.type.padEnd(25)} ${t.file}`));
|
|
31
|
+
|
|
32
|
+
// Now check scoring
|
|
33
|
+
const { calculateRiskScore } = require('./src/scoring.js');
|
|
34
|
+
const result = calculateRiskScore(threats);
|
|
35
|
+
console.log('\nScore:', result.riskScore, 'Level:', result.riskLevel);
|
|
36
|
+
console.log('MaxFile:', result.maxFileScore, 'Cross:', result.crossFileBonus, 'Pkg:', result.packageScore);
|
|
37
|
+
console.log('FileScores:', result.fileScores);
|
package/_test_fp3.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const { run } = require('./src/index.js');
|
|
2
|
+
|
|
3
|
+
(async () => {
|
|
4
|
+
const result = await run(
|
|
5
|
+
'C:/Users/kposz/PyCharmMiscProject/CDA 2025 - 2026/muad-dib/.muaddib-cache/benign-tarballs/vitest/package',
|
|
6
|
+
{ _capture: true }
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
console.log('Score:', result.summary.riskScore);
|
|
10
|
+
const rs = result.threats.find(t => t.type === 'reverse_shell');
|
|
11
|
+
const ep = result.threats.find(t => t.type === 'env_proxy_intercept');
|
|
12
|
+
console.log('reverse_shell:', rs ? rs.severity : 'not found');
|
|
13
|
+
console.log('env_proxy_intercept:', ep ? ep.severity : 'not found');
|
|
14
|
+
|
|
15
|
+
// Show all threats
|
|
16
|
+
result.threats.forEach(t => {
|
|
17
|
+
console.log(` ${t.severity.padEnd(8)} ${t.type.padEnd(25)} ${t.file}`);
|
|
18
|
+
});
|
|
19
|
+
})();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const { run } = require('./src/index.js');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
(async () => {
|
|
5
|
+
const dir = path.join('.muaddib-cache/benign-tarballs/nodemailer/package');
|
|
6
|
+
const result = await run(dir, { _capture: true });
|
|
7
|
+
|
|
8
|
+
console.log('Score:', result.summary.riskScore);
|
|
9
|
+
|
|
10
|
+
// Show all suspicious_dataflow threats with full message
|
|
11
|
+
for (const t of result.threats) {
|
|
12
|
+
if (t.severity !== 'LOW') {
|
|
13
|
+
console.log(`\n${t.severity} ${t.type} [${t.file}]`);
|
|
14
|
+
console.log(` ${t.message}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
})();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const { run } = require('./src/index.js');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
const packages = [
|
|
6
|
+
'next', 'gatsby', 'sails', 'webpack', 'jasmine', 'karma', 'knex',
|
|
7
|
+
'eslint', 'lerna', 'recoil', 'mathjs', 'nodemailer', 'ses'
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
const CACHE_DIR = '.muaddib-cache/benign-tarballs';
|
|
11
|
+
const SEVERITY_WEIGHTS = { CRITICAL: 25, HIGH: 10, MEDIUM: 3, LOW: 1 };
|
|
12
|
+
|
|
13
|
+
(async () => {
|
|
14
|
+
for (const pkg of packages) {
|
|
15
|
+
const cacheName = pkg.replace(/\//g, '-').replace(/^@/, '');
|
|
16
|
+
const dir = path.join(CACHE_DIR, cacheName, 'package');
|
|
17
|
+
if (!fs.existsSync(dir)) continue;
|
|
18
|
+
try {
|
|
19
|
+
const result = await run(dir, { _capture: true });
|
|
20
|
+
const score = result.summary.riskScore;
|
|
21
|
+
if (score <= 20) continue; // Only show FPs
|
|
22
|
+
console.log(`\n=== ${pkg} (score: ${score}) ===`);
|
|
23
|
+
console.log(` maxFile: ${result.summary.maxFileScore}, crossBonus: ${result.summary.crossFileBonus || 0}, pkgScore: ${result.summary.packageScore}`);
|
|
24
|
+
console.log(` mostSuspicious: ${result.summary.mostSuspiciousFile}`);
|
|
25
|
+
|
|
26
|
+
// Group by severity
|
|
27
|
+
const byType = {};
|
|
28
|
+
for (const t of result.threats) {
|
|
29
|
+
const key = `${t.severity}:${t.type}`;
|
|
30
|
+
if (!byType[key]) byType[key] = { severity: t.severity, type: t.type, count: 0, files: new Set(), points: 0 };
|
|
31
|
+
byType[key].count++;
|
|
32
|
+
byType[key].files.add(t.file);
|
|
33
|
+
byType[key].points += SEVERITY_WEIGHTS[t.severity] || 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Sort by points descending
|
|
37
|
+
const sorted = Object.values(byType).sort((a, b) => b.points - a.points);
|
|
38
|
+
for (const entry of sorted.slice(0, 8)) {
|
|
39
|
+
console.log(` ${entry.severity.padEnd(8)} ${entry.type.padEnd(30)} x${entry.count} = ${entry.points}pts files: ${[...entry.files].slice(0, 2).join(', ')}`);
|
|
40
|
+
}
|
|
41
|
+
} catch (e) {
|
|
42
|
+
console.log(`ERR ${pkg}: ${e.message}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
})();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const { run } = require('./src/index.js');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
const packages = ['next', 'webpack', 'jasmine', 'knex', 'eslint', 'lerna', 'nodemailer'];
|
|
6
|
+
const CACHE_DIR = '.muaddib-cache/benign-tarballs';
|
|
7
|
+
|
|
8
|
+
(async () => {
|
|
9
|
+
for (const pkg of packages) {
|
|
10
|
+
const cacheName = pkg.replace(/\//g, '-').replace(/^@/, '');
|
|
11
|
+
const dir = path.join(CACHE_DIR, cacheName, 'package');
|
|
12
|
+
if (!fs.existsSync(dir)) continue;
|
|
13
|
+
try {
|
|
14
|
+
const result = await run(dir, { _capture: true });
|
|
15
|
+
const score = result.summary.riskScore;
|
|
16
|
+
console.log(`\n=== ${pkg} (score: ${score}) ===`);
|
|
17
|
+
console.log(` maxFile: ${result.summary.maxFileScore}, crossBonus: ${result.summary.crossFileBonus || '(not in summary)'}, pkgScore: ${result.summary.packageScore}`);
|
|
18
|
+
console.log(` mostSuspicious: ${result.summary.mostSuspiciousFile}`);
|
|
19
|
+
|
|
20
|
+
// Show high-value threats (MEDIUM+)
|
|
21
|
+
const highValue = result.threats.filter(t => t.severity !== 'LOW');
|
|
22
|
+
for (const t of highValue) {
|
|
23
|
+
console.log(` ${t.severity.padEnd(8)} ${t.type.padEnd(30)} ${(t.file || '').substring(0, 60)}`);
|
|
24
|
+
}
|
|
25
|
+
// Count LOWs
|
|
26
|
+
const lowCount = result.threats.filter(t => t.severity === 'LOW').length;
|
|
27
|
+
console.log(` + ${lowCount} LOW threats`);
|
|
28
|
+
} catch (e) {
|
|
29
|
+
console.log(`ERR ${pkg}: ${e.message}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
})();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const { run } = require('./src/index.js');
|
|
2
|
+
|
|
3
|
+
const packages = [
|
|
4
|
+
'next', 'gatsby', 'sails', 'stencil', 'webpack', '@swc/core',
|
|
5
|
+
'vitest', 'jasmine', 'karma', 'knex', 'eslint', 'lerna',
|
|
6
|
+
'@changesets/cli', 'recoil', 'mathjs', 'nodemailer', 'ses', 'jspdf'
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
const CACHE_DIR = '.muaddib-cache/benign-tarballs';
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
|
|
13
|
+
(async () => {
|
|
14
|
+
const results = [];
|
|
15
|
+
for (const pkg of packages) {
|
|
16
|
+
const cacheName = pkg.replace(/\//g, '-').replace(/^@/, '');
|
|
17
|
+
const dir = path.join(CACHE_DIR, cacheName, 'package');
|
|
18
|
+
if (!fs.existsSync(dir)) {
|
|
19
|
+
console.log(`SKIP ${pkg} (not cached)`);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const result = await run(dir, { _capture: true });
|
|
24
|
+
const score = result.summary.riskScore;
|
|
25
|
+
const flagged = score > 20;
|
|
26
|
+
console.log(`${flagged ? 'FP' : 'OK'} ${String(score).padStart(3)} ${pkg}`);
|
|
27
|
+
if (flagged) results.push(pkg);
|
|
28
|
+
} catch (e) {
|
|
29
|
+
console.log(`ERR ${pkg}: ${e.message}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
console.log(`\n${results.length} FPs remaining: ${results.join(', ')}`);
|
|
33
|
+
})();
|
package/_test_regex.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const DIST_FILE_RE = /(?:^|[/\\])(?:dist|build)[/\\]|\.min\.js$|\.bundle\.js$/i;
|
|
2
|
+
console.log('dist\\chunks\\file.js:', DIST_FILE_RE.test('dist\\chunks\\file.js'));
|
|
3
|
+
console.log('dist/chunks/file.js:', DIST_FILE_RE.test('dist/chunks/file.js'));
|
|
4
|
+
console.log('package\\dist\\file.js:', DIST_FILE_RE.test('package\\dist\\file.js'));
|
|
5
|
+
console.log('src\\index.js:', DIST_FILE_RE.test('src\\index.js'));
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
{
|
|
2
|
+
"target": "C:/Users/kposz/PyCharmMiscProject/CDA 2025 - 2026/muad-dib/.muaddib-cache/benign-tarballs/vitest/package",
|
|
3
|
+
"timestamp": "2026-03-06T10:35:36.370Z",
|
|
4
|
+
"threats": [
|
|
5
|
+
{
|
|
6
|
+
"type": "prototype_hook",
|
|
7
|
+
"severity": "MEDIUM",
|
|
8
|
+
"message": "WebSocket.prototype.addEventListener overridden — native API hooking for traffic interception.",
|
|
9
|
+
"file": "dist\\chunks\\cli-api.B7PN_QUv.js",
|
|
10
|
+
"count": 1,
|
|
11
|
+
"rule_id": "MUADDIB-AST-017",
|
|
12
|
+
"rule_name": "Native API Prototype Hooking",
|
|
13
|
+
"confidence": "high",
|
|
14
|
+
"references": [
|
|
15
|
+
"https://www.sygnia.co/blog/malicious-chalk-debug-npm-packages/",
|
|
16
|
+
"https://attack.mitre.org/techniques/T1557/"
|
|
17
|
+
],
|
|
18
|
+
"mitre": "T1557",
|
|
19
|
+
"playbook": "Prototype de fonction native modifie (fetch, XMLHttpRequest, http.request). Technique d'interception de trafic pour voler des donnees en transit. Supprimer le package. Auditer le trafic reseau recent.",
|
|
20
|
+
"points": 3
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"type": "prototype_hook",
|
|
24
|
+
"severity": "MEDIUM",
|
|
25
|
+
"message": "WebSocket.prototype.removeEventListener overridden — native API hooking for traffic interception.",
|
|
26
|
+
"file": "dist\\chunks\\cli-api.B7PN_QUv.js",
|
|
27
|
+
"count": 1,
|
|
28
|
+
"rule_id": "MUADDIB-AST-017",
|
|
29
|
+
"rule_name": "Native API Prototype Hooking",
|
|
30
|
+
"confidence": "high",
|
|
31
|
+
"references": [
|
|
32
|
+
"https://www.sygnia.co/blog/malicious-chalk-debug-npm-packages/",
|
|
33
|
+
"https://attack.mitre.org/techniques/T1557/"
|
|
34
|
+
],
|
|
35
|
+
"mitre": "T1557",
|
|
36
|
+
"playbook": "Prototype de fonction native modifie (fetch, XMLHttpRequest, http.request). Technique d'interception de trafic pour voler des donnees en transit. Supprimer le package. Auditer le trafic reseau recent.",
|
|
37
|
+
"points": 3
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"type": "env_access",
|
|
41
|
+
"severity": "LOW",
|
|
42
|
+
"message": "Dynamic access to process.env (variable key).",
|
|
43
|
+
"file": "dist\\chunks\\cli-api.B7PN_QUv.js",
|
|
44
|
+
"count": 3,
|
|
45
|
+
"rule_id": "MUADDIB-AST-002",
|
|
46
|
+
"rule_name": "Sensitive Environment Variable Access",
|
|
47
|
+
"confidence": "high",
|
|
48
|
+
"references": [
|
|
49
|
+
"https://blog.phylum.io/shai-hulud-npm-worm",
|
|
50
|
+
"https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions"
|
|
51
|
+
],
|
|
52
|
+
"mitre": "T1552.001",
|
|
53
|
+
"playbook": "Acces a une variable d'environnement sensible. Verifier si les donnees sont exfiltrees.",
|
|
54
|
+
"points": 1
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"type": "reverse_shell",
|
|
58
|
+
"severity": "HIGH",
|
|
59
|
+
"message": "JavaScript reverse shell: net.Socket + connect() + pipe to shell process stdin/stdout.",
|
|
60
|
+
"file": "dist\\chunks\\cli-api.B7PN_QUv.js",
|
|
61
|
+
"count": 1,
|
|
62
|
+
"rule_id": "MUADDIB-SHELL-002",
|
|
63
|
+
"rule_name": "Reverse Shell",
|
|
64
|
+
"confidence": "high",
|
|
65
|
+
"references": [
|
|
66
|
+
"https://attack.mitre.org/techniques/T1059/004/"
|
|
67
|
+
],
|
|
68
|
+
"mitre": "T1059.004",
|
|
69
|
+
"playbook": "CRITIQUE: Reverse shell detecte. Machine potentiellement compromise. Isoler immediatement.",
|
|
70
|
+
"points": 10
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"type": "env_access",
|
|
74
|
+
"severity": "LOW",
|
|
75
|
+
"message": "Dynamic access to process.env (variable key).",
|
|
76
|
+
"file": "dist\\chunks\\init.B6MLFIaN.js",
|
|
77
|
+
"count": 4,
|
|
78
|
+
"unreachable": true,
|
|
79
|
+
"rule_id": "MUADDIB-AST-002",
|
|
80
|
+
"rule_name": "Sensitive Environment Variable Access",
|
|
81
|
+
"confidence": "high",
|
|
82
|
+
"references": [
|
|
83
|
+
"https://blog.phylum.io/shai-hulud-npm-worm",
|
|
84
|
+
"https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions"
|
|
85
|
+
],
|
|
86
|
+
"mitre": "T1552.001",
|
|
87
|
+
"playbook": "Acces a une variable d'environnement sensible. Verifier si les donnees sont exfiltrees.",
|
|
88
|
+
"points": 1
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"type": "dynamic_import",
|
|
92
|
+
"severity": "MEDIUM",
|
|
93
|
+
"message": "Dynamic import() with computed argument (possible obfuscation).",
|
|
94
|
+
"file": "dist\\chunks\\traces.CCmnQaNT.js",
|
|
95
|
+
"count": 1,
|
|
96
|
+
"rule_id": "MUADDIB-AST-008",
|
|
97
|
+
"rule_name": "Dynamic import() of Dangerous Module",
|
|
98
|
+
"confidence": "high",
|
|
99
|
+
"references": [
|
|
100
|
+
"https://attack.mitre.org/techniques/T1027/"
|
|
101
|
+
],
|
|
102
|
+
"mitre": "T1027",
|
|
103
|
+
"playbook": "import() dynamique detecte. Technique d'evasion pour eviter la detection de require(). Verifier quel module est charge et son usage.",
|
|
104
|
+
"points": 3
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"type": "module_compile",
|
|
108
|
+
"severity": "LOW",
|
|
109
|
+
"message": "module._compile() detected — executes arbitrary code from string in module context (flatmap-stream pattern).",
|
|
110
|
+
"file": "dist\\chunks\\vm.D3epNOPZ.js",
|
|
111
|
+
"count": 1,
|
|
112
|
+
"unreachable": true,
|
|
113
|
+
"rule_id": "MUADDIB-AST-023",
|
|
114
|
+
"rule_name": "Module Compile Execution",
|
|
115
|
+
"confidence": "high",
|
|
116
|
+
"references": [
|
|
117
|
+
"https://blog.npmjs.org/post/180565383195/details-about-the-event-stream-incident",
|
|
118
|
+
"https://attack.mitre.org/techniques/T1059/007/"
|
|
119
|
+
],
|
|
120
|
+
"mitre": "T1059",
|
|
121
|
+
"playbook": "CRITIQUE: module._compile() detecte. Cette API Node.js interne execute du code arbitraire a partir d'une chaine dans le contexte d'un module. Utilisee dans flatmap-stream pour executer un payload dechiffre sans ecrire sur disque. Isoler immediatement. Analyser la source de la chaine compilee.",
|
|
122
|
+
"points": 1
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"type": "module_compile_dynamic",
|
|
126
|
+
"severity": "LOW",
|
|
127
|
+
"message": "In-memory code execution via Module._compile(). Common malware evasion technique.",
|
|
128
|
+
"file": "dist\\chunks\\vm.D3epNOPZ.js",
|
|
129
|
+
"count": 1,
|
|
130
|
+
"unreachable": true,
|
|
131
|
+
"rule_id": "MUADDIB-AST-025",
|
|
132
|
+
"rule_name": "Dynamic Module Compile Execution",
|
|
133
|
+
"confidence": "high",
|
|
134
|
+
"references": [
|
|
135
|
+
"https://blog.npmjs.org/post/180565383195/details-about-the-event-stream-incident",
|
|
136
|
+
"https://attack.mitre.org/techniques/T1059/007/"
|
|
137
|
+
],
|
|
138
|
+
"mitre": "T1059",
|
|
139
|
+
"playbook": "CRITIQUE: Module._compile() avec argument dynamique (variable, expression). Execution de code en memoire sans ecriture sur disque. Technique d'evasion utilisee dans flatmap-stream et SANDWORM_MODE. Isoler immediatement. Tracer la source de la chaine compilee pour extraire le payload.",
|
|
140
|
+
"points": 1
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"type": "require_cache_poison",
|
|
144
|
+
"severity": "LOW",
|
|
145
|
+
"message": "require.cache accessed — module cache poisoning to hijack or replace core Node.js modules.",
|
|
146
|
+
"file": "dist\\chunks\\vm.D3epNOPZ.js",
|
|
147
|
+
"count": 1,
|
|
148
|
+
"unreachable": true,
|
|
149
|
+
"rule_id": "MUADDIB-AST-019",
|
|
150
|
+
"rule_name": "Require Cache Poisoning",
|
|
151
|
+
"confidence": "high",
|
|
152
|
+
"references": [
|
|
153
|
+
"https://attack.mitre.org/techniques/T1574/006/"
|
|
154
|
+
],
|
|
155
|
+
"mitre": "T1574.006",
|
|
156
|
+
"playbook": "CRITIQUE: require.cache modifie pour hijacker des modules Node.js. Le code remplace les exports de modules charges (https, http, fs) pour intercepter toutes les requetes. Supprimer le package. Redemarrer le processus Node.js. Auditer le trafic reseau recent.",
|
|
157
|
+
"points": 1
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"type": "dynamic_require",
|
|
161
|
+
"severity": "LOW",
|
|
162
|
+
"message": "Dynamic require() with variable argument (module name obfuscation).",
|
|
163
|
+
"file": "dist\\chunks\\vm.D3epNOPZ.js",
|
|
164
|
+
"count": 3,
|
|
165
|
+
"unreachable": true,
|
|
166
|
+
"rule_id": "MUADDIB-AST-006",
|
|
167
|
+
"rule_name": "Dynamic Require with Concatenation",
|
|
168
|
+
"confidence": "high",
|
|
169
|
+
"references": [
|
|
170
|
+
"https://attack.mitre.org/techniques/T1027/"
|
|
171
|
+
],
|
|
172
|
+
"mitre": "T1027",
|
|
173
|
+
"playbook": "require() avec concatenation detecte. Technique d'obfuscation pour masquer le module charge. Analyser les variables concatenees.",
|
|
174
|
+
"points": 1
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"type": "dynamic_import",
|
|
178
|
+
"severity": "MEDIUM",
|
|
179
|
+
"message": "Dynamic import() with computed argument (possible obfuscation).",
|
|
180
|
+
"file": "dist\\module-evaluator.js",
|
|
181
|
+
"count": 1,
|
|
182
|
+
"rule_id": "MUADDIB-AST-008",
|
|
183
|
+
"rule_name": "Dynamic import() of Dangerous Module",
|
|
184
|
+
"confidence": "high",
|
|
185
|
+
"references": [
|
|
186
|
+
"https://attack.mitre.org/techniques/T1027/"
|
|
187
|
+
],
|
|
188
|
+
"mitre": "T1027",
|
|
189
|
+
"playbook": "import() dynamique detecte. Technique d'evasion pour eviter la detection de require(). Verifier quel module est charge et son usage.",
|
|
190
|
+
"points": 3
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"type": "env_access",
|
|
194
|
+
"severity": "LOW",
|
|
195
|
+
"message": "Dynamic access to process.env (variable key).",
|
|
196
|
+
"file": "dist\\module-evaluator.js",
|
|
197
|
+
"count": 4,
|
|
198
|
+
"rule_id": "MUADDIB-AST-002",
|
|
199
|
+
"rule_name": "Sensitive Environment Variable Access",
|
|
200
|
+
"confidence": "high",
|
|
201
|
+
"references": [
|
|
202
|
+
"https://blog.phylum.io/shai-hulud-npm-worm",
|
|
203
|
+
"https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions"
|
|
204
|
+
],
|
|
205
|
+
"mitre": "T1552.001",
|
|
206
|
+
"playbook": "Acces a une variable d'environnement sensible. Verifier si les donnees sont exfiltrees.",
|
|
207
|
+
"points": 1
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"type": "env_proxy_intercept",
|
|
211
|
+
"severity": "HIGH",
|
|
212
|
+
"message": "new Proxy(process.env) detected — intercepts all environment variable access.",
|
|
213
|
+
"file": "dist\\module-evaluator.js",
|
|
214
|
+
"count": 1,
|
|
215
|
+
"rule_id": "MUADDIB-AST-009",
|
|
216
|
+
"rule_name": "Environment Variable Proxy Interception",
|
|
217
|
+
"confidence": "high",
|
|
218
|
+
"references": [
|
|
219
|
+
"https://attack.mitre.org/techniques/T1552/001/"
|
|
220
|
+
],
|
|
221
|
+
"mitre": "T1552.001",
|
|
222
|
+
"playbook": "CRITIQUE: new Proxy(process.env) intercepte tous les acces aux variables d'environnement. Technique d'exfiltration silencieuse. Isoler la machine, regenerer tous les secrets.",
|
|
223
|
+
"points": 10
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"type": "suspicious_dataflow",
|
|
227
|
+
"severity": "MEDIUM",
|
|
228
|
+
"message": "Suspicious flow: credentials read (npm_config_user_agent, process.env[dynamic], process.env[dynamic], npm_config_VITEST_MODULE_DIRECTORIES, process.env[dynamic]) + network send (request, request, net.connect, tls.connect, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, fetch, get, get, get, get, get, get, get, get, get, get, get, get, get, get, request, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get)",
|
|
229
|
+
"file": "dist\\chunks\\cli-api.B7PN_QUv.js",
|
|
230
|
+
"count": 1,
|
|
231
|
+
"rule_id": "MUADDIB-FLOW-001",
|
|
232
|
+
"rule_name": "Suspicious Data Flow",
|
|
233
|
+
"confidence": "high",
|
|
234
|
+
"references": [
|
|
235
|
+
"https://blog.phylum.io/shai-hulud-npm-worm"
|
|
236
|
+
],
|
|
237
|
+
"mitre": "T1041",
|
|
238
|
+
"playbook": "CRITIQUE: Code lit des credentials et les envoie sur le reseau. Exfiltration probable. Isoler la machine, regenerer tous les secrets.",
|
|
239
|
+
"points": 3
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"type": "suspicious_dataflow",
|
|
243
|
+
"severity": "LOW",
|
|
244
|
+
"message": "Suspicious flow: credentials read (process.env[dynamic], process.env[dynamic], process.env[dynamic], process.env[dynamic]) + network send (get, fetch, get, post)",
|
|
245
|
+
"file": "dist\\chunks\\init.B6MLFIaN.js",
|
|
246
|
+
"count": 1,
|
|
247
|
+
"unreachable": true,
|
|
248
|
+
"rule_id": "MUADDIB-FLOW-001",
|
|
249
|
+
"rule_name": "Suspicious Data Flow",
|
|
250
|
+
"confidence": "high",
|
|
251
|
+
"references": [
|
|
252
|
+
"https://blog.phylum.io/shai-hulud-npm-worm"
|
|
253
|
+
],
|
|
254
|
+
"mitre": "T1041",
|
|
255
|
+
"playbook": "CRITIQUE: Code lit des credentials et les envoie sur le reseau. Exfiltration probable. Isoler la machine, regenerer tous les secrets.",
|
|
256
|
+
"points": 1
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"type": "suspicious_dataflow",
|
|
260
|
+
"severity": "MEDIUM",
|
|
261
|
+
"message": "Suspicious flow: credentials read (process.env[dynamic], process.env[dynamic], process.env[dynamic], process.env[dynamic]) + network send (get, get)",
|
|
262
|
+
"file": "dist\\module-evaluator.js",
|
|
263
|
+
"count": 1,
|
|
264
|
+
"rule_id": "MUADDIB-FLOW-001",
|
|
265
|
+
"rule_name": "Suspicious Data Flow",
|
|
266
|
+
"confidence": "high",
|
|
267
|
+
"references": [
|
|
268
|
+
"https://blog.phylum.io/shai-hulud-npm-worm"
|
|
269
|
+
],
|
|
270
|
+
"mitre": "T1041",
|
|
271
|
+
"playbook": "CRITIQUE: Code lit des credentials et les envoie sur le reseau. Exfiltration probable. Isoler la machine, regenerer tous les secrets.",
|
|
272
|
+
"points": 3
|
|
273
|
+
}
|
|
274
|
+
],
|
|
275
|
+
"python": null,
|
|
276
|
+
"summary": {
|
|
277
|
+
"total": 16,
|
|
278
|
+
"critical": 0,
|
|
279
|
+
"high": 2,
|
|
280
|
+
"medium": 6,
|
|
281
|
+
"low": 8,
|
|
282
|
+
"riskScore": 28,
|
|
283
|
+
"riskLevel": "MEDIUM",
|
|
284
|
+
"globalRiskScore": 46,
|
|
285
|
+
"maxFileScore": 20,
|
|
286
|
+
"packageScore": 0,
|
|
287
|
+
"mostSuspiciousFile": "dist\\chunks\\cli-api.B7PN_QUv.js",
|
|
288
|
+
"fileScores": {
|
|
289
|
+
"dist\\chunks\\cli-api.B7PN_QUv.js": 20,
|
|
290
|
+
"dist\\chunks\\init.B6MLFIaN.js": 2,
|
|
291
|
+
"dist\\chunks\\traces.CCmnQaNT.js": 3,
|
|
292
|
+
"dist\\chunks\\vm.D3epNOPZ.js": 4,
|
|
293
|
+
"dist\\module-evaluator.js": 17
|
|
294
|
+
},
|
|
295
|
+
"breakdown": [
|
|
296
|
+
{
|
|
297
|
+
"rule": "MUADDIB-SHELL-002",
|
|
298
|
+
"type": "reverse_shell",
|
|
299
|
+
"points": 10,
|
|
300
|
+
"reason": "JavaScript reverse shell: net.Socket + connect() + pipe to shell process stdin/stdout."
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
"rule": "MUADDIB-AST-009",
|
|
304
|
+
"type": "env_proxy_intercept",
|
|
305
|
+
"points": 10,
|
|
306
|
+
"reason": "new Proxy(process.env) detected — intercepts all environment variable access."
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"rule": "MUADDIB-AST-017",
|
|
310
|
+
"type": "prototype_hook",
|
|
311
|
+
"points": 3,
|
|
312
|
+
"reason": "WebSocket.prototype.addEventListener overridden — native API hooking for traffic interception."
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"rule": "MUADDIB-AST-017",
|
|
316
|
+
"type": "prototype_hook",
|
|
317
|
+
"points": 3,
|
|
318
|
+
"reason": "WebSocket.prototype.removeEventListener overridden — native API hooking for traffic interception."
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"rule": "MUADDIB-AST-008",
|
|
322
|
+
"type": "dynamic_import",
|
|
323
|
+
"points": 3,
|
|
324
|
+
"reason": "Dynamic import() with computed argument (possible obfuscation)."
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"rule": "MUADDIB-AST-008",
|
|
328
|
+
"type": "dynamic_import",
|
|
329
|
+
"points": 3,
|
|
330
|
+
"reason": "Dynamic import() with computed argument (possible obfuscation)."
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
"rule": "MUADDIB-FLOW-001",
|
|
334
|
+
"type": "suspicious_dataflow",
|
|
335
|
+
"points": 3,
|
|
336
|
+
"reason": "Suspicious flow: credentials read (npm_config_user_agent, process.env[dynamic], process.env[dynamic], npm_config_VITEST_MODULE_DIRECTORIES, process.env[dynamic]) + network send (request, request, net.connect, tls.connect, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, fetch, get, get, get, get, get, get, get, get, get, get, get, get, get, get, request, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get, get)"
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"rule": "MUADDIB-FLOW-001",
|
|
340
|
+
"type": "suspicious_dataflow",
|
|
341
|
+
"points": 3,
|
|
342
|
+
"reason": "Suspicious flow: credentials read (process.env[dynamic], process.env[dynamic], process.env[dynamic], process.env[dynamic]) + network send (get, get)"
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"rule": "MUADDIB-AST-002",
|
|
346
|
+
"type": "env_access",
|
|
347
|
+
"points": 1,
|
|
348
|
+
"reason": "Dynamic access to process.env (variable key)."
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
"rule": "MUADDIB-AST-002",
|
|
352
|
+
"type": "env_access",
|
|
353
|
+
"points": 1,
|
|
354
|
+
"reason": "Dynamic access to process.env (variable key)."
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
"rule": "MUADDIB-AST-023",
|
|
358
|
+
"type": "module_compile",
|
|
359
|
+
"points": 1,
|
|
360
|
+
"reason": "module._compile() detected — executes arbitrary code from string in module context (flatmap-stream pattern)."
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
"rule": "MUADDIB-AST-025",
|
|
364
|
+
"type": "module_compile_dynamic",
|
|
365
|
+
"points": 1,
|
|
366
|
+
"reason": "In-memory code execution via Module._compile(). Common malware evasion technique."
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"rule": "MUADDIB-AST-019",
|
|
370
|
+
"type": "require_cache_poison",
|
|
371
|
+
"points": 1,
|
|
372
|
+
"reason": "require.cache accessed — module cache poisoning to hijack or replace core Node.js modules."
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"rule": "MUADDIB-AST-006",
|
|
376
|
+
"type": "dynamic_require",
|
|
377
|
+
"points": 1,
|
|
378
|
+
"reason": "Dynamic require() with variable argument (module name obfuscation)."
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"rule": "MUADDIB-AST-002",
|
|
382
|
+
"type": "env_access",
|
|
383
|
+
"points": 1,
|
|
384
|
+
"reason": "Dynamic access to process.env (variable key)."
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"rule": "MUADDIB-FLOW-001",
|
|
388
|
+
"type": "suspicious_dataflow",
|
|
389
|
+
"points": 1,
|
|
390
|
+
"reason": "Suspicious flow: credentials read (process.env[dynamic], process.env[dynamic], process.env[dynamic], process.env[dynamic]) + network send (get, fetch, get, post)"
|
|
391
|
+
}
|
|
392
|
+
]
|
|
393
|
+
},
|
|
394
|
+
"sandbox": null
|
|
395
|
+
}
|