Background
In July 2019, code was added to an upstream of the purescript
npm installer which sabotaged the completion of the installation process in a particularly interesting way. The subterfuge was particularly difficult to uncover & debug, taking 5 days for some of the most talented and skilled npmjs devs to unravel. It would be fair to say that this event “stumped” (their words, not mine) some of the most talented and well versed engineers in this space.
It’s my favourite dep malware, not because it resulted in some epic hack, but because it was so well executed by somebody with an innate understanding of the downstream landscape.
It’s a great thought exercise into the potential power of insider threats and also upstreams.
The malware takes advantage of how downstream projects load in the affected code, it uses conditional exploitation logic to hamper debug efforts, and uses just the right level of obfuscation to not immediately look suspect (no super sus looking B64’d blobs here). The malicious software also cleans up after itself.
In this post I hope to step through just how all this took place, so maybe we can all be impressed and together. This particular event got very little coverage, so hopefully you find reading about it notable even though it was a little while ago.
My colleagues really like it when I annotate malware for them, particularly side-by-side, so here’s an attempt at posting in that format.
Timeline
July 5 1pm UTC:
PureScript-0.13.2
released. Compiler maintainers are able to successfully install the project using the npm
installer.
July 5 9pm UTC:
A new version of load-from-cwd-or-npm
is published (3.0.2) containing exploit version 1 (listed below).
Bug reports beging stating that users cannot install purescript
. Maintainers have trouble reproducing the bug, as it won’t reliably reproduce and won’t occur during local checkouts.
July 9 1a UTC:
A user identifies that the bug is being caused by load-from-cwd-or-npm@3.0.2
and opens an issue asking them to fix. The issue is not determined to be malicious at this time. The issue is deleted by the maintainer of load-from-cwd-or-npm@3.0.2
.
July 9 5a UTC:
A new version of load-from-cwd-or-npm
is published that doesn’t contain an exploit.
July 9 8a UTC:
rate-map@1.0.3
is published which is basically a more advanced version of the load-from-cwd-or-npm
exploit.
July 9 11a UTC:
Maintainers still do not suspect any bad faith from the malicious upstreams.
July 9 1130a UTC:
Maintainer hdgarrood spots the malicious code and that this is a deliberately malicious act.
July 9 2p UTC:
A new version of PureScript
is released that drops all dependency on the rate-map
& load-from-cwd-or-npm
.
What you’re looking at / code intent
The samples below both sabotage the purescript
npm installer process to prevent it running successfully under certain conditions.
The Details: exploit version 1 load-from-cwd-or-npm@3.0.2
You can grab a copy of the dependency off of my repo here: load-from-cwd-or-npm@3.0.2
Load up a editor and open load-from-cwd-or-npm@3.0.2
index.js file.
Check out lines 50 - 83: - I am going to #annotate the below snippet for you, watch out for the comments (#) in the code snippet. The below code is designed to determine the environment it is running in, and decide what action to take.
The Details: exploit version 2 rate-map@1.0.3
You can grab a copy of the dependency off of my repo here: rate-map@1.0.3
Things get a little spicier from this point on. This version of the exploit contains the same do-while
loop decision gate on exploitation, but it includes some significant upgrades.
Final Notes
I intentionally skipped over some background information surrounding the disagreements between authors of purescript
and load-from-cwd-or-npm
etc. I just think the techniques used particularly in rate-map@1.0.3
are great uses of light (but effective) obfuscation and conditional exploitation.