Security researchers have discovered an npm timing attack that reveals the names of private packages so threat actors can release malicious clones publicly to trick developers into using them instead.
The attack relies on a small time difference in the return of a “404 Not Found” error when searching for a private compared to a non-existent package in the repository.
While the response time difference is only a few hundred milliseconds, it is enough to determine whether a private package exists to perform package impersonation attacks.
Organizations create private packages for internal projects and certain software products to minimize the risk of their development teams falling for typosquatting attacks, and to keep their code and functions secret.
Keeping private packages private is crucial for organizations using them. Otherwise, attackers can create clones or typosquatted packages that hackers could trick employees of organizations into downloading and using in software projects.
If the developers and internal software testers don’t discover the compromise, the products could reach end users, achieving a supply chain compromise.
In a report by Aqua Security’s threat research team, who shared its findings with BleepingComputer before publication, attackers are increasingly focusing on supply chain attacks, fueling a rise of 300% in associated activities in 2021.
Timing attack details
npm includes a registry API that allows users to download existing packages, check for the existence of packages, and receive information about all packages under a specific scope.
When using the npm registry to download a package that does not exist or is set to private, the website will return a 404 HTTP error code, indicating that the package could not be found.
404 Not-found response to an API request on npm (Aqua Security)
Aqua Security discovered the npm timing attack by using this API to check for the existence of private packages they created on npm and compared the response time of the 404 HTTP errors against API checks for non-existent packages.
The researchers performed their test by checking for the existence of a package name five times. They discovered there is a measurable difference in the average time required for npm to respond to the request, allowing them to discern whether a package is private or non-existent.
More specifically, the average response time when the private package exists is 648 milliseconds, while the average time when it doesn’t exist drops to just 101 milliseconds.
Time to respond to five requests (Aqua Security)
The researchers assume this is due to the caching mechanism and the architecture of npm’s API, which introduces this information-disclosure potential.
A hacker can try out a “blind” dictionary attack or look for naming patterns and combinations in the targeted organization’s public packages to derive possible private package names.
Additionally, online information contains historic package information, so the attackers could use them to determine what public packages may have been turned private later.
In the latter case, a potential compromise with a clone package could be very stealthy, as an old copy of a package that used to be public may still retain enough functionality to work as intended in the software product that uses it.
GitHub will not fix the issue
Aqua Security disclosed the bug to GitHub on March 8th, 2022, but was told on March 25th that it would not be fixed due to architectural limitations.
“Because of these architectural limitations, we cannot prevent timing attacks from determining whether a specific private package exists on npm,” GitHub told Aqua Security.
The researchers say that organizations can take preventive action by frequently searching npm for suspicious packages that spoof their private packages with duplicate or similar names.
Additionally, organizations can create public packages that spoof their private packages as placeholders since npm doesn’t allow uploading same-name packages on public repositories.