On Nov. 20, 2018, it was discovered that EventStream, a highly popular JavaScript library, was compromised with the addition of a third-party dependency, flatmap-stream, containing encrypted malicious code. The attack targeted other Node.js libraries used in cryptocurrency wallets. Basically, if your application used both EventStream and a cryptocurrency-related library called copay-dash, then the malicious code from flatmap-stream would try to steal Bitcoins from your cryptocurrency wallets.
EventStream is a very popular library with almost 2 million downloads per week. Even so, the original owner, Dominic Tarr, has not maintained it since 2012. So, in September 2018, Tarr transferred project ownership to a volunteer to maintain it.
The new owner, a user called Right9ctrl, turned out to be a bad actor. Soon after taking ownership of EventStream, Right9ctrl added a dependency to flatmap-stream. Until then, flatmap-stream was a little-known library that had no downloads on NPM. Right after that, someone (it’s not clear who) made a change to flatmap-stream that included the malicious code. Now the EventStream library was pulling in a malicious dependency.
Three days later, Right9ctrl removed the dependency on flatmap-stream from the EventStream library (perhaps to hide his or her tracks). However, people who included the EventStream package in their projects during those three days pulled in the malicious code. And if they haven’t updated their projects since then, it may still be there.
The malicious code, which targets the copay-dash library, looks for cryptocurrency wallet profiles. It maps the wallet IDs with the public keys of profiles with balances over 100 BTC (Bitcoin) or 1,000 BCH (Bitcoin Cash). Then it sends the identified wallets to a server in Kuala Lumpur.
Then the code captures the passwords to these wallets and sends them to the same server. The malicious code can obtain these passwords by overwriting the Credentials.getKeys function in one of the Bitcoin-related libraries using the ability to redefine functions through JavaScript prototypes. Thus, the attackers can obtain both Bitcoin wallet IDs and passwords.
What makes the EventStream incident so unusual is the attack approach. A common method of distributing malware is typosquatting, where an attacker publishes a malicious package with a similar name as a popular package. But in this case, the new owner/attacker gained ownership of the original package through a legitimate channel.
“This represents a scary social-engineering vector for malware,” Cory Doctorow said on the Boing Boing blog. “A malicious person volunteers to help maintain the project, makes some small, positive contributions, gets commit access to the project, and releases a malicious patch, infecting millions of users and apps.”
What’s not unusual is for owners of popular packages to give them away. As Tarr points out, “There are likely to be many other modules in your dependency trees that are now a burden to their authors” and thus may end up in the hands of new owners.
Interestingly enough, after someone alerted Tarr of the vulnerability in EventStream, he could not update the code on NPM. He had already transferred the rights to the library to Right9ctrl. Thus, he had to contact NPM to remove the malicious package from the repository.
NPM has now pulled flatmap-stream, so applications trying to download it will return an error. If your projects include any of these packages, update all dependencies to the latest recommended versions.
So what does this story tell us? Dependency management is hard. NPM verifies the immediate dependencies that you add to your package when you install them (as long as you don’t ignore the notifications). But those dependencies may become compromised later if someone adds another dependency that has malicious code. Even if a library contains malicious code for only a few days, for a popular library, that’s enough to affect thousands or millions of users. If these users don’t update their applications, the malicious code will still be there. And the malicious dependency may not necessarily target the original application. Instead, it might go after peer dependencies used in the same or other applications.
Take these steps to make sure your code is not using any malicious or outdated dependencies: