Early May 2nd, the npm security team received and responded to reports of a package that masqueraded as a cookie parsing library but contained a malicious backdoor. The result of the investigation concluded with three packages and three versions of a fourth package being unpublished from the npm Registry.

No packages published to the npm Registry used the malicious modules in a way that would have allowed the backdoor to be triggered. Applications not published to the registry that directly required the malicious modules might have been vulnerable, but are out of the scope of our analysis.

Initial report

Initial information from the community reported that the package getcookies contained a potential backdoor, that express-cookies and http-fetch-cookies depended upon getcookies, and that a popular package, mailparser , depended upon http-fetch-cookies.

Triage

Upon receiving the report, npm’s security team started triage. The goal of triage was determining whether the reported package did in fact contain malicious code, and, if so, how this impacted the community.

We won’t disclose the entire backdoor here, but we’ll give a few highlights that we noted as it was reviewed.

The backdoor worked by parsing the user-supplied HTTP request.headers , looking for specifically formatted data that provides three different commands to the backdoor.

JSON.stringify(req.headers).replace(/g([a-f0-9]{4})h((?:[a-f0-9]{2})+)i/gi, (o, p, v) => {})

We can see here that the headers are stringified and the result searched for values in the format of: gCOMMANDhDATAi

The control flow codes available were:

0xfffe—reset the code buffer

0xfffa—execute the code located in the buffer by calling vm.runInThisContext , providing module.exports , require , req , res , and next as arguments.

, providing , , , , and as arguments. default—load the remote code into memory for execution

These control codes allowed for an attacker to input arbitrary code into a running server and execute it.

Beyond the backdoor code, other aspects of these modules and the account also stood out:

Based on a reverse image search, the profile image on the user who published getcookies appeared to be a stock photo.

The GitHub user linked from these npm packages was created in March.

Download counts spiked a few weeks ago for getcookies, express-cookies, and http-fetch-cookies. This seems to correlate to when a version of mailparser was published that depended upon http-fetch-cookie.

mailparser └── http-fetch-cookies └── express-cookies └──getcookies

Dependency relationship of the reported modules

Despite being deprecated, mailparser still receives about 64,000 weekly downloads. We searched for how users of this module might be impacted. We determined the published versions of mailparser that depended on http-fetch-cookies did not use the module in any way, eliminating any risk the backdoor posed. We speculate that mailparser’s requiring http-fetch-cookies was to execute an attack in the future or to inflate download counts of express-cookies to add to its legitimacy.

Ultimately, mailparser users weren’t impacted. Only users that directly required and used the express-cookies or getcookies packages would be impacted.

Actions we took

removed the dustin87 user and unpublished getcookies, express-cookies, and http-fetch-cookies;

user and unpublished getcookies, express-cookies, and http-fetch-cookies; removed the 3 versions of mailparser that depended on the http-fetch-cookies module;

that depended on the module; reset npm tokens for the author of mailparser to prevent further unauthorized publishes.

Timeline

All times in Pacific Daylight Time (UTC–7)

05:23—Initial community report

05:41—Security team started triage

07:01—Unpublished getcookies, express-cookies, http-fetch-cookies

07:02—Revoked tokens for mailparser maintainer

07:26—Unpublished mailparser versions 2.2.3, 2.2.2, and 2.2.1