In December I discovered a supply chain vulnerability that impacted 6,530 public npm package versions, at least I thought I did. Turns out that earlier in October of 2020 Security Innovation published similar research dubbing the issue Repo Jacking. This initially took the wind out of my sails but after I thought about it rediscovery is pretty cool and I was able to expand upon it a bit by focusing on abandoned S3 buckets, Google Cloud Storage bucket, expired domain names, and finding and reporting a vulnerability in GitHub to make exploitation possible in some conditions.
If you want to see if your organization is potentially at risk, here is a list of package versions that were found to be vulnerable. If you are using these particular package versions I would recommend not.
While maintainers were notified by email a lot of those emails bounced. Please do not go screaming into the issues, email, or DMs of these package maintainers… and if you do reach out please be kind and respectful of their volunteer, unpaid, time. I encourage all security practitioners to get more involved in the open source communities they complain about (I too could do a better job here)… Anyway, on to the details!
It’s typical for a Node.js based application to have many 3rd party dependencies. These npm packages can point to resources (the package) that are not hosted by the npm Registry. Typically these types of dependencies will be either a file or git repository that is available via HTTP/HTTPS or SSH.
If the dependency reference (domain, storage bucket, GitHub account) becomes abandoned and made available for an attacker to claim then an attacker can now control this resource as part of the npm install process creating an exploitable situation.
Attack Walk Through
Let’s look at an example of how this might be exploited using a GitHub repository.
The attacker registers the available GitHub username and creates a repository with the same name and the code they want to be their payload shaped like an npm package.
When the app owner runs npm install npm will run a git clone on the referenced repository. Let’s say the dependency reference looks like:
github:evilpacket/beep-boop#beta It will clone
evilpacket/beep-boop. Once the clone is complete npm will try to git checkout whatever comes after the #, in this case the
beta branch or in the case of the lock file it will checkout a particular commit hash.
What are some mitigating circumstances
There are some npm features that can change how exploitable this is from situation to situation.
package-lock.json can save you in some situations, especially for anything that directly references a file vs a repository (repos are a special). This is because it has the integrity hashes that aren’t going to match when the attacker changes the payload.
Also a package-lock.json won’t help you on initial install or if package-lock.json isn’t being used. You should be checking that file into all your projects and using npm ci for builds.
The local npm cache
If the object that is requested is in the cache, you’re going to get that object and not a public or attacker controlled object.
Another aspect that shapes exploitability here is if the vulnerable dependency is a dev (development) dependency or not. Dev dependencies aren’t going to get installed with a typical npm install pkg but could still be useful in a targeted attack against a developer of one of those packages ( but who would want to do that 😈 )
GitHub branch names & policies
GitHub (and others like gitlab) will block the creation of a branch name that is 40 hex characters (the same format as a commit hash) with an error like
remote: error: GH002: Sorry, branch or tag names consisting of 40 hex characters are not allowed., however I found a way around this security control so while I couldn’t forge a commit hash I could name a branch the same as the upstream commit hash and our payload would pass validation in the package-lock.json.
This issue was reported to GitHub security ❤️.
Additionally GitHub claims
“To prevent developers from pulling down potentially unsafe packages, we now retire the namespace of any open source project that had more than 100 clones in the week leading up to the owner’s account being renamed or deleted.” That said I did not encounter any block when testing exploitation of this issue with older versions of the popular webpack-cli package, but it’s likely all these packages fall just below the popularity watermark set by this control.
Results, Numbers, and that’s it.
While I’m never shocked to find vulnerable dependencies in the npm Registry I wasn’t expecting this many packages to be vulnerable.
You can find the package list here: https://evilpacket.net/files/vulnerable_package_versions.csv
Summary breakdown by resource type. As you can see the behavior of using GitHub repositories as dependencies and the forwarding behavior that GitHub allows creates a lot more vulnerability.
|Google Cloud Storage Buckets abandoned||2|
|AWS S3 Bucket abandoned||7|
|Domain Name expired||2|
|GitHub Repo (not found or redirect)||743|
I think the conclusion of this is that you need to scrutinize the open source you use and your supply chain a lot more than you think you do. Enjoy typing npm install.