[HN Gopher] Embedded malware in RC (NPM package)
       ___________________________________________________________________
        
       Embedded malware in RC (NPM package)
        
       Author : hjek
       Score  : 95 points
       Date   : 2021-11-05 17:13 UTC (5 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | tolmasky wrote:
       | If you're interested in preventing this sort of thing, I'd
       | appreciate comments on this
       | [RFC](https://github.com/npm/rfcs/pull/488) I just submitted to
       | npm to make install scripts _opt-in_ instead of _default
       | behavior_. While of course not perfect, this simple change would
       | certainly go a long way in increasing the difficulty in creating
       | these sorts of attacks, as right now as long as a computer even
       | installs the packages in question, not even running any code in
       | the package, the malicious program has a chance at running.
       | 
       | RFC: https://github.com/npm/rfcs/pull/488
       | 
       | Related HN post: https://news.ycombinator.com/item?id=29122473
        
         | ricardobayes wrote:
         | Non-tech solution is to pin versions and read (hacker)news
         | before updating.
        
         | forty wrote:
         | Isn't that only useful for the very rare minority of people
         | that install packages then don't run the code right after? :)
        
           | schemescape wrote:
           | I initially had the same thought, but I came to a different
           | conclusion because the presence of an install script is not a
           | given for most packages.
           | 
           | For example, if I installed a command line arguments parser
           | and it claimed to require running a setup script, I would
           | immediately be very suspicious.
           | 
           | For a huge package like TypeScript, I'd probably just
           | immediately let the script run and trust Microsoft to not
           | publish malware (and NPM to not change the package contents).
        
           | tolmasky wrote:
           | The fact that they don't need to be run is what makes them so
           | hard to spot and easy to sneak in. This allows for some very
           | hard to mitigate strategies. It's easier to sneak in packages
           | into metadata files like package.jsons than create "excuses"
           | to import files that aren't really needed. It can be abused
           | in more complicated ways as well, like for example:
           | 
           | 1. You add a totally "safe" dependency that you control,
           | let's call it "shell-dependency", as an innocuous part of a
           | PR to a "popular-package". Again, even if you inspect this
           | package, it's totally fine. The current version of shell-
           | dependency is 1.0.0, but it of course goes into the
           | package.json as "1.x.x"
           | 
           | 2. You now add malicious dependency to shell-dependency, and
           | bump shell-dependency to 1.0.1, meaning every consumer of
           | "popular-package" now gets your "malicious-package".
           | 
           | Notice that this was accomplished with zero traceable GitHub
           | history. Unless every package up the line uses a package-
           | lock.json (which is explicitly recommended against unless you
           | are an end-user application), "malicious-package" is able to
           | enter the dependency chain undetected. _If_ it required some
           | sort of code import, then it would have more opportunities to
           | be spotted. There are of course ways to do this with attacks
           | that require running code as well, but this makes it super
           | easy, especially considering that people often install
           | packages as root, even when they run their apps not as root.
        
           | Me1000 wrote:
           | Many packages on npm are not run in a local node environment,
           | they run in a browser which is sandboxed.
           | 
           | Regardless, just because there are additional vectors to
           | exploit the user doesn't mean closing off one vector isn't
           | worth doing.
        
         | chakkepolja wrote:
         | Problem is, as someone mentioned in the GitHub thread as well,
         | people just press OK, especially if it's a transitive
         | dependency.
        
           | tolmasky wrote:
           | I've answered this on GitHub, but will provide an answer here
           | too. Packages aren't like end-user software, for two
           | important reasons:
           | 
           | 1. The people involved are developers using development
           | components, who are much better equipped to understand an
           | installation failure and take proper action than an end user
           | who is just trying to click through to play a video game or
           | something. But more importantly:
           | 
           | 2. The vast majority of the installs happen on automated
           | machines (like CI), where you definitely want to fail when
           | something drastically different happens like a new package is
           | all of a sudden running a script. The tests would fail, you
           | would look at the reason, it would be because some weird
           | script is about to run on the machine, and you'd adjust your
           | PR accordingly. This allows multiple levels of consideration:
           | 1) the original author deciding to do something about the
           | failed install (even if it's just appending the flag and not
           | thinking about it), and 2) the PR reviewer having code-as-
           | data evidence that this code change would mean new foreign
           | code _not represented in the commits_ will be running on
           | their machines. This is huge.
           | 
           | The other important point about this is that packages
           | precisely have different risk models depending on whether you
           | are installing things locally or on production machines,
           | where a malicious package could be catastrophic. That's why
           | the RFC allows you to have individual user configuration
           | where you explicitly allow certain packages "from now on"
           | (which is I think the way most people want to think about
           | this: the first time you install something and it warns you
           | and you look into it, but from then on you say "this one is
           | good"). On the other hand, the actual scripts and repository
           | have no such configuration and thus require the installation
           | to include the specific flags, again, clearly documenting the
           | expected results of a seemingly innocent process that can
           | actually currently have bad consequences.
        
           | Me1000 wrote:
           | I don't think anyone is claiming making install script opt-in
           | will fix all the problems. But adding any friction
           | discourages the behavior. And if the norm changes, it's
           | reasonable to believe package maintainers will start opting
           | for dependencies that don't require install scripts. Thus
           | further encouraging people to not include them with their
           | packages.
        
         | Me1000 wrote:
         | Your comments on the RFC cover this, but I want to highlight a
         | two things:
         | 
         | 1) The vast majority of packages on npm don't require install
         | scripts to work.
         | 
         | 2) Many of them that currently include install scripts are just
         | ads asking people to contribute code or money to their project.
        
       | BonoboIO wrote:
       | Using npm is like russian roulette. Someday it makes your head
       | hurt really bad!
        
       | rndhouse wrote:
       | I've created Vouch in an attempt to address this problem:
       | 
       | https://github.com/vouch-dev/vouch
       | 
       | Vouch lets users create and share reviews for NPM packages.
       | Project dependencies can then be checked against those reviews.
       | 
       | Vouch uses extensions to interface with package ecosystems. It's
       | simple to create a new extension. Extensions currently exist for
       | NPM, PyPi, and Ansible Galaxy.
       | 
       | I'm currently working on a website to index known reviews and
       | publish official reviews.
       | 
       | I hope you guys find it useful! Drop by the Matrix channel if you
       | have any feedback to share: #vouch:matrix.org
        
         | schemescape wrote:
         | > The check command generates an evaluation report of local
         | project dependencies based on available reviews:
         | 
         | Is there an example of a generated report?
        
         | hjek wrote:
         | Sounds really interesting. I'm kinda scared to use npm right
         | now to be honest. What dangers may be hidden deep in the
         | dependency trees? But is a _review_ of every _update_ then
         | done, because rc used to be a legit package?
        
           | rndhouse wrote:
           | A review corresponds to a particular version number. But the
           | review process does not need to start from scratch with each
           | version number increment. Reviews from previous versions can
           | be leveraged to lessen the workload.
        
         | yjftsjthsd-h wrote:
         | I'd like this to work, but it seems like it relies on packages
         | being statically "good" or "bad" - what happens if a package is
         | legitimately well trusted but then the main dev gets backdoored
         | and a bit of extra code is injected?
        
           | rndhouse wrote:
           | That extra code will stand out as not having been reviewed.
        
             | yjftsjthsd-h wrote:
             | Oh, you mean to have reviewers look at every line of every
             | version (at least cumulatively). Yes, that would work, and
             | while the effort is significant I appreciate that it's the
             | effort you'd want regardless so this helps share the load.
        
               | rndhouse wrote:
               | That's right! The purpose of Vouch is to continually
               | decrease the cost of the review process with each new
               | review.
        
         | hn_throwaway_99 wrote:
         | That's not really the solution for this problem, though, which
         | is very specifically when a project maintainer's account gets
         | compromised, so then the bad guys publish a new malicious
         | version of that library that gets picked up by anyone using
         | non-pinned NPM versions (i.e. most everyone).
         | 
         | There are a couple more straightforward ways to do this:
         | 
         | 1. Require 2FA, ideally hardware key 2FA, for anyone publishing
         | a package with any sizable following.
         | 
         | 2. Make running of preinstall/install scripts opt-in.
         | 
         | 3. Make the semantic versioning syntax optionally more
         | restrictive. If I specify I want version ^2.2.1, I'd like to be
         | able to specify that I DON'T want to pull 2.2.2 the moment it
         | becomes available, but perhaps want some amount of latency
         | before pulling that.
        
           | ricardobayes wrote:
           | 2. Make running of preinstall/install scripts opt-in. They
           | already are, npm install --ignore-scripts
        
             | hn_throwaway_99 wrote:
             | What you've written there is literally the definition of
             | opt _out_.
        
           | rndhouse wrote:
           | Reviews in Vouch refer to a particular version of a software
           | package. If a new release is issued by a malicious actor, the
           | new release would require a new review.
           | 
           | But the review process does not need to re-start from
           | scratch. Reviews from other versions can be used to lessen
           | the workload.
           | 
           | On the subject of automatically updating packages: the Vouch
           | dependency analysis can be included in CI. Un-reviewed or
           | review failing dependency updates can be flagged for
           | attention.
        
             | throwaway984393 wrote:
             | Who's reviewing the software package's dependencies?
        
       | binarynate wrote:
       | This is why JS runtimes should add the ability to set permissions
       | on a per-module basis. Deno is a step in the right direction by
       | requiring permissions for a script to be specified (e.g. deno run
       | --allow-read --allow-net myscript.ts), but the permissions are
       | global for the entire script and can't (yet?) be configured
       | differently for each module / dependency.
        
       | bluefox wrote:
       | Is the advisory genuine?
       | 
       | It links to the github repo, where the latest commit is from 2018
       | for version 1.2.8.
       | 
       | It links to npmjs page, that shows 48 versions, where the latest
       | version is 1.2.8 from "3 years ago".
       | 
       | Yet it has 1.2.9/1.3.9/2.3.9 for "Affected versions".
       | 
       | Did npmjs "revert" these versions and any clue of their
       | existence? The npmjs page links to dominictarr's repository. The
       | npmjs site doesn't seem to have a "who owns this package name"
       | besides the repository/homepage links. Very confusing.
       | 
       | I remember some years ago there was some story involving the
       | original author's handing maintainership rights to some shady
       | dude. Is it about that time, or is it about something more
       | current?
        
         | speeder wrote:
         | I saw in one of the repos the maintainer confused about what
         | happened, seemly someone somehow impersonated him and released
         | new versions to npm without actually touching the repo itself!
        
       | hn_throwaway_99 wrote:
       | One of the thing I wish was really much easier to do with NPM is,
       | when running `npm update`, to only pick up the most recent
       | compatible versions _from X days ago_.
       | 
       | That is, for sensitive apps, I _don 't_ want to use versions that
       | are less than, say, a month or so old unless I specifically
       | override it. I want to stay up-to-date but not _too_ bleeding
       | edge, specifically to avoid situations like this.
        
       | ricardobayes wrote:
       | Seems like a good choice to work at a cybersecurity company these
       | days. Job security is guaranteed.
        
       | hjek wrote:
       | More info: https://therecord.media/malware-found-in-coa-and-rc-
       | two-npm-...
        
         | bluefox wrote:
         | Thanks.
         | 
         | > Since then, the npm security team has removed all the
         | compromised coa and rc versions to prevent developers from
         | accidentally infecting themselves.
         | 
         | Removing all trace of evidence is not something "security
         | teams" should do. Instead of sweeping security incidents under
         | the rug (where twitterverse resides), they should at least
         | mention the existence of these versions and that they contain
         | malware on the package page.
        
       | schleck8 wrote:
       | And yet again, twice in a row this time.
       | 
       | Note how the referenced Virustotal result has 40+ detections [1].
       | I'm still wondering why info like this isn't used by Pypi and
       | NPM. Chocolatey has Virustotal integration for all releases.
       | 
       | And it's not like Virustotal is the only option, there is Cape
       | [2] for dynamic execution, Metadefender, and Intezer Analyze just
       | to name a few.
       | 
       | Really confusing for such a vital supply chain component to be
       | this easily abused.
       | 
       | One of the highlights is when someone recently used NPM to spread
       | ransomware via a fake Roblox API package.[3]
       | 
       | [1]
       | https://www.virustotal.com/gui/file/26451f7f6fe297adf6738295...
       | 
       | [2] https://github.com/kevoreilly/CAPEv2
       | 
       | [3]
       | https://www.reddit.com/r/programming/comments/qgz0em/fake_np...
        
         | megumax wrote:
         | I think that volunteers (some of them maybe paid) should check
         | the validity of code, at least for projects over 10-100k
         | downloads. In case of crates.io (Rust), there is cargo-crev[1].
         | Also, npm should popularize 2FA.
         | 
         | [1]https://web.crev.dev/rust-reviews/
        
           | m4rtink wrote:
           | Say, like package maintainers do for major Linux
           | distributions ?
        
         | iancarroll wrote:
         | It's not clear that this would be useful; at least for the coa
         | package, the DLL was downloaded dynamically via a script, so
         | NPM would not have been able to detect it unless the script
         | itself was flagged. Not sure what Chocolatey does, but it's
         | also hard to threshold on VirusTotal when there are a lot of
         | FPs by random vendors.
        
           | schleck8 wrote:
           | I think Chocolatey has manual screens when there are more
           | than 5 detections, but not entirely sure
        
           | schemescape wrote:
           | Given that these attacks are becoming increasingly common,
           | package registries could at least install each package (prior
           | to publishing) in some isolated container or VM and then run
           | some similar malware detection on the resulting file system.
           | 
           | Honestly, I'm strongly considering moving away from the NPM
           | ecosystem because it's clearly become a target for malware.
        
             | thrashh wrote:
             | But attackers are not dumb. They would circumvent whether
             | loose checks the package manager may have. Just considering
             | your suggestion, the obvious immediate exploit is to not
             | deploy the attack payload right away. Nothing you will
             | think of will evade defeat.
        
               | schemescape wrote:
               | I agree that it is an unending arms race, but if NPM
               | doesn't even plug obvious holes (like running install
               | scripts by default), then they've lost my trust.
               | 
               | Edit: if anyone knows of a way to disable NPM from
               | running install scripts automatically (without having to
               | remember to specify --ignore-scripts on each invocation),
               | while still allowing me to use "npm run" to manually run
               | scripts (e.g. test scripts for my own packages), I'd love
               | to hear about it.
        
               | Ginden wrote:
               | npm ci --ignore-scripts
        
               | schemescape wrote:
               | Ah, sorry, I meant a way to _automatically_ do that (both
               | so I don 't have to type as much and so that I can't
               | accidentally forget to add that argument). Edited my
               | comment to reflect this.
        
               | jffry wrote:
               | You can run this                 npm config set ignore-
               | scripts true
               | 
               | which will update ~/.npmrc (you can also create project-
               | specific .npmrc files if you prefer)
               | 
               | You can see how NPM has been configured by running
               | npm config list
        
               | yardstick wrote:
               | "Don't let perfect be the enemy of good"
        
         | woodruffw wrote:
         | > Note how the referenced Virustotal result has 40+ detections.
         | I'm still wondering why info like this isn't used by Pypi and
         | NPM.
         | 
         | I was contracted to help build a malware analysis pipeline for
         | PyPI[1][2]. We don't currently have a VirusTotal
         | detector/analyzer (IIRC, we couldn't get a high-enough volume
         | API token on short order), but I think any work towards that
         | would be _greatly_ appreciated by both the PyPA members and the
         | Python packaging community!
         | 
         | [1]: https://pyfound.blogspot.com/2018/12/upcoming-pypi-
         | improveme...
         | 
         | [2]:
         | https://github.com/pypa/warehouse/tree/main/warehouse/malwar...
        
       | qwerty2021 wrote:
       | I checked the readme of both those packages and I can't for the
       | life of me understand why would anyone use either of them.
       | 
       | Why the fuck do all these leftpad is-even hello-world tic-tac-toe
       | packages have millions of downloads?
        
         | afavour wrote:
         | Command line argument parsing and config loading both seem like
         | very sensible library abstractions to me. This isn't leftpad.
        
           | havkd wrote:
           | Command line argument parsing and config loading both seem
           | like something that the standard library should provide.
        
             | megumax wrote:
             | Ok, now, what languages beside Python and Go provide
             | Command line argument parsing? And Go doesn't do that in a
             | `professional` way. You either write your own, which can
             | easily turn into a clusterfuck or use a third party
             | library. Even in Go, people use cobra[1]. Also embedding a
             | lot of functionality in a standard library isn't great as
             | well, because if some vulnerability is found, it's really
             | hard to patch it, because you need to push versions and
             | (for example on Linux) some distro maintainers won't push
             | it for `stability` etc. A standard library should provide
             | basic functionality (in most general areas), but not very
             | advanced one.
             | 
             | [1] https://github.com/spf13/cobra
        
               | kitsunesoba wrote:
               | It's not part of the standard library, but Swift has the
               | first-party ArgumentParser[0]. Other languages could use
               | a similar model (though what "first party" means for
               | JavaScript is unclear).
               | 
               | [0]: https://github.com/apple/swift-argument-parser
        
               | morelisp wrote:
               | > what languages beside Python and Go provide Command
               | line argument parsing?
               | 
               | Even POSIX gives you getopt(1) and getopt(3). What other
               | language doesn't? I can only think of Java.
        
               | afavour wrote:
               | Sure, and Node gives you the process.argv array. The
               | point is having higher level APIs than that.
        
               | voldacar wrote:
               | it feels like the more higher level APIs we add, the
               | shittier and more annoying software becomes
        
               | morelisp wrote:
               | getopt is a higher-level API. process.argv is equivalent
               | to, uh, argv.
        
               | megumax wrote:
               | POSIX is not ISO C or C++ standard. On Windows, what are
               | you gonna do?
               | 
               | Also, other languages are Rust, Kotlin, Swift (to name a
               | few `modern` ones). Yes, Kotlin and Swift have `first
               | class` CLI parsing libraries, but they are not part of
               | standard library.
        
               | morelisp wrote:
               | "First-party" is distinct from "first class". The
               | difference between a first-party library and the standard
               | library ranges from "slightly weaker compatibility
               | guarantees" to "it's supported in all environments where
               | it makes sense, but the language can run unhosted so
               | that's not everywhere" to "no difference at all, we just
               | didn't want to package it with the compiler".
               | 
               | You're also missing the forest for the "well actually"
               | trees: Lots of languages have argument parsing in their
               | stdlib.
        
               | Groxx wrote:
               | as if there even was one command-line or config standard.
               | especially across different operating systems.
               | 
               | it _absolutely_ does not belong in stdlibs, where it can
               | never be changed. that 's how you end up with too many
               | terrible CLI tools using Go's `flags` package.
        
               | MrStonedOne wrote:
               | php
        
           | qwerty2021 wrote:
           | >Command line argument parsing
           | 
           | 20-30 LoC maybe. `process.argv.slice(2).forEach(str => ...
           | )`.
           | 
           | there is no access to the raw command-line invocation, sadly,
           | so you really can't really do anything fancier than that.
           | 
           | >config loading
           | 
           | that thing "RC" package does - looking up the config file in
           | random locations - is really strange to me. aren't you the
           | one in control of where it is stored?
        
             | afavour wrote:
             | Just looking at the readme for Coa it's very obviously more
             | than the code you outlined. You're arguing against a
             | strawman here.
        
               | qwerty2021 wrote:
               | honestly, yeah. I just saw "command line parser" and
               | dismissed the rest as useless bloat
        
         | tyingq wrote:
         | Chained dependencies? If you can fool one popular package to
         | depend on you, you ride their coattails.
         | 
         | And perhaps some faked download numbers to lend an air of
         | authenticity.
        
           | havkd wrote:
           | Maybe we need to hold the popular packages accountable for
           | stuff like this.
        
             | jart wrote:
             | Before we go mobbing innocent open source devs on Twitter,
             | it'd be great to know how far NPM has progressed on 2FA. Up
             | until 2018 NPM didn't have 2FA at all. They just introduced
             | it. It'd be nice if they could give some kind of progress
             | report on how widely adopted it's become. Ideally it should
             | be required for publishing packages. Or at the very least,
             | it'd be great to have some transparency about which package
             | authors are actually using it, who aren't, and who's
             | delegated their authorization to some other vendor like
             | Travis -- so we as users can make our own informed choices
             | about risk. It'd also be useful to have charts that log
             | dependency gravity over time since an important question in
             | situations like this is: did RC and Coa go from 70k to 17m
             | users yesterday? Or have they been established for a long
             | time?
        
       | madjam002 wrote:
       | This is like the third one this week right?
       | 
       | I know people keep saying about post-install should be opt out
       | but then malware will just wait for first run instead.
       | 
       | How about an option to refuse to install any packages that have
       | been published in the past week/2 weeks? That way hopefully
       | malware like this would have been spotted before you end up
       | running it locally.
        
         | m4rtink wrote:
         | They could also attack metadata parsers next - I don't think
         | those are very hardened right now.
        
         | ant6n wrote:
         | If everybody waits two weeks, then nobody will notice it on the
         | first two weeks.
        
       | greenyoda wrote:
       | See also the ongoing discussion about malware in "Coa", another
       | NPM package: https://news.ycombinator.com/item?id=29116878
        
         | dang wrote:
         | Also recent:
         | 
         |  _NPM package 'ua-parser-JS' with more than 7M weekly download
         | is compromised_ - https://news.ycombinator.com/item?id=28962168
         | - Oct 2021 (141 comments)
        
       ___________________________________________________________________
       (page generated 2021-11-05 23:00 UTC)