home
navigate_next
Blog
navigate_next

Dependency Confusion

Dependency Confusion

Discord Channel

If you want to learn more or talk about hacking then you should join my Discord channel. Im on there 24/7 and often drop exclusive content that I don't share anywhere else.
Join Discord

Pentest Interview Playbook

I wrote a book on passing pentest interviews from resume to offer.

I’ve worked as a pentester, launched my own firm, and interviewed and hired pentesters myself.

This book breaks down every stage of the interview process with a step by step playbook to help you land a real pentesting job.
Buy Now
Dependency Confusion is a sneaky supply chain vulnerability that turns a misconfigured package manager into a remote code execution vector. In this post, we’ll walk through how it works and how to exploit it.
Dependency Confusion

Introduction

When you write code you typically leverage third party libraries or packages instead of writing every single line yourself. There is no need to reinvent the wheel so importing code from third parties is standard practice. To utilize the code from third parties you will have to install it to your computer, if you accidently download a malicious package it will be able to execute shell commands thus resulting in remote code execution(RCE). 

Package Manager

Package managers are tools that help developers easily add and manage the software libraries their projects need. Instead of downloading files manually, a package manager does the work of finding the right libraries, installing them, and keeping everything up to date. This makes it faster and easier to build and maintain software without worrying about setup issues or missing pieces. Some common package managers are npm for JavaScript, pip for Python, Maven for Java, and Cargo for Rust.

While most package managers default to pulling packages from public registries such as npmjs.com for javascript or PyPI for python many organizations also use private or internal repositories to host proprietary code, internal tooling, or customized forks of open-source libraries. Package managers can be configured to fetch packages from both public and internal sources, but this flexibility requires careful setup to avoid misconfiguration and ensure the right packages are installed in the correct context.

For this blog the only thing you need to know is that package managers will download and execute code. If an attacker can control the code downloaded by the package manager then they can execute commands on that machine. 

Dependency Confusion

Dependency Confusion is a supply chain attack where an attacker exploits naming overlaps to deceive package managers into installing a malicious public package instead of a trusted internal or private one. This is how we will get the package manager to download and execute our code. 

Testing for this vulnerability is relatively easy if you have access to the list of dependencies the application uses. Tools like nuclei often scan for exposed “package.json” files, most people will report that as an information severity bug but you should be checking the dependencies for dependency confusion.  As shown in the image above there is one dependency called “ghostlulz-dependency”.

As shown in the image above it returns a 404 not found. If this happens you probably have dependency confusion. Our package manager will try to install a package that does not exist. All we have to do is register the package name on the public NPM site and they will download our package.

This can happen due to typosquating but this normally happens due to a company having their own internal NPM repository. A large fortune 500 company might have internal packages with custom code for developers to use. If there is a misconfiguration their package manager might check the public repo before their private repo allowing us to sneak in our malicious library. 

In the case of our “ghostlulz-dependency” library we can assume the target has an internal NPM repo with this. However, as we saw previously there is no public repo, also the package.json file isnt properly scoped to only use the internal repo so there is a good chance it will try to check the public repo to see if that package exists. 

As shown in the image above if we run npm install it will try to download the repo from the public repository but there is nothing there. All we have to do is create it and it will download and execute our code.

First create an account at: https://www.npmjs.com/ . After that create the package you want to hijack. In my case it was the ghostlulz-dependency package.  You can follow the steps below:

  • Make Folder
    • mkdir ghostlulz-dependency; cd ghostlulz-dependency
  • Initialize project
    • npm init -y
  • Open package.json
    • nano package.json
  • Edit package.json
    • Add - "scripts": { "install": "sh -c \"echo 'ghostlulz-dependency pwned you' > pwned.txt\"" }
  • Login to NPM
    • Npm login
  • Publish package
    • npm publish --access public

After doing that your package should be live on NPM. As shown in the image below we were able to upload our package.

You should also be able to see the command I am running. For this test I am just creating a file but you could do all kinds of things such as downloading and running your back door. 

As shown in the image above our file was successfully created after running npm install. 

Dev Ops

This isn't important to exploiting the vulnerability but it is important for targeted attacks of the exploit.  If you find this vulnerable on Netflix , Google, Amazon, or any other company you probably want to run code on their servers, not some random machine that's running their code.

This will normally happen to do CI/CD pipelines. Most large organizations automate the entire deployment process.  Your goal is to get code execution on one of these servers, not some random computer. To do so you just have to wait for their servers to rebuild the application, when it does you should see your code being executed. To verify this you would typically code a small backdoor that beacons back to you , within the backdoors response you can place information such as the IP used, server name, and other identifying information. This should allow you to see which server your code is running on so you can attribute it back to a specific company. This part is very important if you are doing a targeted attack against a bug bounty scope. You will need to prove your code ran on servers that are in scope.

Conclusion

This is a sneaky bug that can easily find its way into an organization's code. We saw this in NPM but this can easily happen for python , ruby, Go, or any other language.

All it takes is a typo or a package manager that checks the public repo before your private one. If that happens you have an easy remote code execution(RCE) exploit. 

arrow_back
Back to blog