Where did these mysterious PrismJS npm versions come from?
In 2015, strange 9000.0.x
versions of PrismJS appeared on npm downloads, and nobody had a clue where they came from, or what purpose they served.
Roughly four years later, PrismJS 9000.0.1
and 9000.0.2
were removed from npm for the reasons described below.
But to date, no one seems to know anything more about this incident.
PrismJS is a lightweight, robust, and elegant syntax highlighting library that is based on Dabblet.
Its sheer popularity among developers is demonstrated by the 4.5 million weekly downloads PrismJS receives on the npm registry.
Prism is also the choice of the library for websites of famous tech brands like Stripe, Drupal, MySQL, React, etc.
So, who published Prism 9000.0.x?
In November 2015, Rob Loach, a developer raised concerns about strange versions 9000.0.1 and 9000.0.2 that had appeared on npm.
Versions 9000.0.x published on 12th May, 2015, stood out as, at the time, the latest version of PrismJS was 1.3.0.
Also, the very first version 0.0.1 had been published on 13th May, 2015, so how come, the timestamp data from npm show 9000.0.x versions having been published a day prior to the initial release?
{ "name": "prismjs",
"dist-tags": { "latest": "1.3.0" },
"versions": [ "0.0.1", "1.1.0", "1.2.0", "1.3.0", "9000.0.1" ],
"time":
{
"9000.0.1": "2015-05-12T23:54:40.643Z",
"9000.0.2": "2015-05-12T23:56:14.033Z",
"0.0.1": "2015-05-13T00:37:38.541Z",
"1.1.0": "2015-10-06T00:03:04.995Z",
"1.2.0": "2015-10-07T17:35:20.776Z",
"1.3.0": "2015-10-27T02:35:27.738Z" }
}
Loach raised a GitHub issue for the maintainers of PrismJS to unpublish these “broken” versions from npm, and this is where it gets interesting.
PrismJS creator and an elected W3C member, Lea Verou asked another web developer at the time, “any ideas where the 9000 came from? Is it safe to remove?”
By July 2016, Golmote, a contributor to the PrismJS project responded with:
“I don’t know where those weird versions come from. They are dated at the time of the creation of the NPM package… so I guess they may have been mistakes, or automatically created maybe?”
The chatter continued in the same thread for quite some time as access issues were being sorted—that is, maintainers trying to figure out who had access to PrismJS’ npm account.
Finally, npm was notified and began taking these 9000.0.x versions down sometime in October 2019.
A dependency confusion attack?
A particularly interesting incidence here is the choice of large version numbers itself: 9000.0.1 and 9000.0.2.
These version numbers of PrismJS were causing problems for some developers:
“Removing this would be great as it will help to keep a consistent versioning,” said software developer Harald Nezbeda at the time.
“Currently, this is causing also confusion in services… Updating to the latest version of the dependencies causes it to take 9000.0.1,” Nezbeda continued.
Another open-source CLI tool called updates, which checks for npm dependency updates, had to rewrite its version resolution logic to not pull the 9000.0.x versions.
Earlier this year, I broke news on how a researcher hacked over 35 big tech firms and earned over $130,000 in bug bounties by exploiting a novel kind of open-source supply chain weakness, called dependency confusion.
Incidentally, the said researcher, Alex Birsan, mentions using an example 9000.0.0 version in his proof-of-concept (PoC) dependency confusion demos, in his blog post released this February.
Other bug bounty hunters have used 9000.0.x versions in their copycat PoC demos as well [1, 2].
But the exact mechanics behind how conflicting dependency names and higher-numbered versions in open-source ecosystems that lack proper namespacing can be troublesome have been known to the developer community years before dependency confusion attacks made “news.”
“If I know of a package in use by a company through log analysis, bug report analysis, etc., I could potentially go register the same name in the default repo with a very high [semantic version] and know that it’s very likely that this would be picked up over the intended, internally developed module because there’s no namespace,” Sonatype CTO Brian Fox had said in his 2017 writeup.
In 2015 alone, for example, RubyGems had deleted a 9000.0.0 version of rails-assets-angular.
Why? Because it was malicious. Somebody had used it to pull a successful dependency confusion attack:
“Someone used bundler’s CVE-2013-0334 to perform [an]attack on our service and created rails-assets-angular gem with the same name as on our service.”
“Now every time someone [runs] bundle update
, the RubyGems gem is installed instead of ours,” said developer, Adam Stankiewicz, who represents the official rails-assets.org service.
What is inside 9000.0.x versions?
But, what about the PrismJS 9000.0.x versions?
Although these versions have been pulled from npm downloads, and the entirety of the internet for good, I was able to dig into Sonatype’s automated malware detection system archives to retrieve a copy of the 9000.0.1 version:
While analysis is still ongoing, so far, there is no indication of any malicious code or exploit embedded within PrismJS 9000.0.1.
Considering, nobody—including PrismJS developers, knows where these weird 9000.0.1 and 9000.0.2 versions had come from, it is plausible that an attempted supply-chain attack was caught and subverted in time before the author of these 9000.0.x versions had managed to publish an outright malicious PrismJS version.
Still, the 9000.0.x versions did make their way into development builds of at least some open-source projects, inconveniencing their maintainers.
Fast-forward to 2021, dependency confusion attacks are being actively leveraged to target software projects named after renowned brands like Amazon, Slack, Zillow, and Lyft.
Moreover, novel brandjacking and typosquatting supply chain attacks are on the rise, like the newly discovered Linux and macOS malware hidden within a counterfeit component named after “Browserify”.
As these attacks continue to grow, it is worth checking your software development builds for suspicious dependencies, malicious components, and install automation solutions that can proactively detect and block these attacks from compromising your software builds.