[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)