Post Mortem: axios NPM supply chain compromise(github.com) |
Post Mortem: axios NPM supply chain compromise(github.com) |
Which is basically phishing:
> The meeting link itself directed to a spoofed Zoom meeting that was hosted on the threat actor's infrastructure, zoom[.]uswe05[.]us.
> Once in the "meeting," the fake video call facilitated a ruse that gave the impression to the end user that they were experiencing audio issues.
> The recovered web page provided two sets of commands to be run for "troubleshooting": one for macOS systems, and one for Windows systems. Embedded within the string of commands was a single command that initiated the infection chain.
...
it seems the correct muscle memory response to train into people is that "if some meeting link someone sent you doesn't work, then you should create one and send them the link"
(and of course never download and execute anything, don't copy scripts into terminals, but it seems even veteran maintainers do this, etc...)
see Infection Chain here https://cloud.google.com/blog/topics/threat-intelligence/unc...
textarea at the bottom of this comment: https://github.com/axios/axios/issues/10636#issuecomment-418...
This and every other recent supply chain attack was completely preventable.
So much so I am very comfortable victim blaming at this point.
This is absolutely on the Axios team.
Go setup some smartcards for signing git push/commit and publish those keys widely, and mandate signed merge commits so nothing lands on main without two maintainer sigs, and no more single points of failure.
It seems the Axios team was largely practicing what you're preaching. To the extent they aren't: it still wouldn't have prevented this compromise.
If I understand it correctly, your suggestions wouldn’t have prevented it, which is evidence that this is not as trivially fixable as you believe it is.
Yet most developers I work with just use it reflexively. This seems like one of the biggest issues with the npm ecosystem - the complete lack of motivation to write even trivial things yourself.
The next incarnation of this, I worry, is that the malware hibernates somehow (e.g., if (Date.now() < 1776188434046) { exit(); }) to maximize the damage.
I mean the compromised machine registers itself on the command server and occasionally checks for workloads.
The hacker then decides his next actions - depending on the machine they compromised they'll either try to spread (like this time) and make a broad attack or they may go more in-depth and try to exfiltrate data/spread internally if eg a build node has been compromised
I feel like npm specifically needs to up their game on SA of malicious code embedded in public projects.
Interesting it got caught when it did.
Given the "extreme vigilance" of the primitive "don't install unknown something on your machine" level is unattainable, can there really be an effective project-level solutions?
Mandatory involvement of more people to hope not everyone installs random stuff, at least not at same time? (though you might not even have more people...)
Seems to me that one drastic tactic NPM could employ to prevent attacks like this is to use hardware security. NPM could procure and configure laptops with identity rooted in the laptop TPM instead of 2FA. Configure the NPM servers so that for certain repos only updates signed with the private key in the laptop TPM can be pushed to NPM. Each high profile repo would have certain laptops that can upload for that repo. Set up the laptop with a minimal version of Linux with just the command line tools to upload to NPM, not even a browser or desktop environment. Give those laptops to maintainers of high profile repos for free to use for updates.
Then at update time, the maintainer just transfers the code from their dev machine to the secure laptop via USB drive or CD and pushes to NPM from the special laptop.
Adding postinstall should require approval from NPM. NPM clients should not install freshly published packages. NPM packages should be scanned after publishing. High profile packages should verify upstream git hash signature. NPM install should run in sandbox and detect any attempt to install outside project directory.
But npm being part of multi trillion company cannot be bothered to fix any of these. Instead they push for tighter integration with GitHub with UX that suck.
That would be a beautiful example of Cobra effect: what about updates that fix vulnerabilities? You're gonna force users to wait couple days or a week before they can get malware removed?
The problem would be new versions that fix security issues though, and because this is all open source as soon as you publish the fix everyone knows the vulnerability. You wouldn’t want everyone to stay on the insecure version with a basically public vulnerability for a week.
Point 4 from https://npmdigest.com/guides/npm-trusted-publishing#ux-probl...
(I wrote that guide page for myself because I always get annoyed when dealing with npm OIDC)
NPM rejected PRs to support optional signing multiple times more than a decade ago now, and this choice has not aged well.
Anyone that cannot take 5 minutes to set up commit signing with a $40 usb smartcard to prevent impersonation has absolutely no business writing widely depended upon FOSS software.
Normalized negligence is still negligence.
Then you would have created just an axios clone. AKA re-inventing the wheel. The issue isn't the library itself, but rather the fact that it's popular and provided a large enough attack surface.
You can actually just clone the axios package and use it as is from your private repo and you would not have been affected.
The wheel is the native fetch API, nobody needs to reinvent it.
All you'd do in that scenario is make your own hubcap to put on top.
I use "xhr" via fetch extensively, it can do everything in day to day business for years with minimal boilerplate.
(The only exception known to me being upload progress/status indication)
The multiple supply chain attacks against NPM packages would, of course, be solved if we simply stop using third-party libraries.
const x = await fetch(...); await x.json();
"intercept" code that runs before every request?
const withAuth = (res, options) => fetch(res, { ... do stuff here });
Before each action you need to enter your 2fa code.
I got so frustrated with npm end of last year that I wrote a whole guide covering that issue: https://npmdigest.com/guides/npm-trusted-publishing
Still needs to be published first, but looks like it automates all the annoying UI things you mentioned.
the implicit trust we have in maintainers is easily faked as we see
Just sign commits and reviews. It is so easy to stop these attacks that not doing so is like a doctor that refuses to wash their hands between patients.
If you are not going to wash your hands do not be a doctor.
If you are not going to sign your code do not be a FOSS maintainer.
If maintainers really cannot afford that, they should flag it as a major big bold print supply chain risk on the readme: "We cannot afford 4 yubikeys for our maintainers and thus all code is signed with software keys in virtual machines as a best effort defense. Donate to our fund [here] to raise $500 for dedicated release hardware"
Friends and I have gotten 100s of yubikeys and nitrokeys donated to FOSS maintainers, but FOSS maintainers have to be willing to say they would use them and signal that they need them.
Honestly though, anyone that cannot afford $40 I expect is at high risk of being bribed or having to give up contributing to take on more work, so we should significantly fund any project signaling that much desperation.
No. As a user of your package, I want assurance that the package you publish does what it says it does and does not contain malware. This is different from the package having been published by you. I want protection against you going rogue, not only from you being impersonated. 2FA on your side does not protect me against you going rogue. A comaintainer does.
So the correct quote would be: Anyone that cannot find a comaintainer to review all the code and to prevent deliberate sabotage has absolutely no business writing widely depended upon FOSS software.
Operate under the assumption all accounts will be taken over because centralized corporate auth systems are fundamentally vulnerable.
This is how you actually fix it:
1. Every commit must be signed by a maintainer key listed in the MAINTAINERS file or similar
2. Every review/merge must be signed by a -second- maintainer key
3. Every artifact must be build deterministically and be signed by multiple maintainers.
4. Have only one online npm publish key maintained in a deterministic and remotely attestable enclave that validates multiple valid maintainer signatures
5. Automatically sound the alarm if an NPM release is pushed any other way, and automatically revoke it.
Whats even more stupid is they actually started mandating 2FA for high risk packages, and FIDO2 supports being used to actually sign artifacts, but they instead simply use it for auth, and let releases stay unsigned. Even the developers they insisted hold cryptographic signing keys, they insist on only throw-away signatures for auth, but not using them for artifact signing to prevent impersonation. It is golf clap level stupid.
Consider them a CDN that wants to analyze your code for AI training for their employer and nothing more. Any security controls that might restrict the flow of publishing even a little bit will be rejected.
Arrgh. You're looking at the closest thing to a root cause and you're just waving over it. The culture of "just paste this script" is the problem here. People trained not to do this (or, like me, old enough to be horrified about it and refuse on principle) aren't vulnerable. But you just... give up on that and instead view this as a problem with "muscle memory" about chat etiquette?
Good grief, folks. At best that's security theater.
FWIW, there's also a root-er cause about where this culture came from. And that's 100% down to Apple Computer's congenital hatred of open source and refusal to provide or even bless a secure package management system for their OS. People do this because there's no feasible alternative on a mac, and people love macs more than they love security it seems.
I don't understand. I used Linux for a long time before I switched to Mac, and the "copy this command and paste it in your terminal" trope was just as prevalent there.
Edit: wait, did the attacker intercept the totp code as it was entered? Trying to make sense of the thread
One must sign commits -universally- and -also- sign reviews/merges (multi-party) and then -also- do multi party signing on releases. Doing only one step of basic supply chain security unfortunately buys you about as much defense as locking only a single door.
I do however certainly assign significant blame to the NPM team though for repeatedly refusing optional package signing support so packages with signing enabled can be refused at the server and client if unsigned by a quorum of pinned keys, but even aside from that if packages were signed manually then canary tools could have detected this immediately.
I think NPM is fully to blame here. Packages that exceed a certain level of popularity should require signing/strong 2FA. They should implement more schemes that publishers can optionally enable, like requiring mandatory sign-off from more than 1 maintainer before the package is available to download.
Then on the package page it should say: "[Warning] Weak publishing protection" or "[Checkmark] This package requires sign-off from accountA and accountB to publish".
they had 2FA, but likely software TOTP (so it was either autofilled via 1password (or similar), or they were able to steal the seed)
at this point I think publishing an npm app and asking people to scan a QR with it is the easiest way (so people don't end up with 1 actual factor)
Until NPM can enforce those basic checks though, you have to roll your own CI to do it yourself, but large well funded widely used projects have an obligation to do the basics to protect their users, and their own reputations, from impersonation.
Even if they did sign the code, What's stopping them slipping some crypto link in. And do they also need to check all the transitive depdencies in their code?
Sitting back and expecting Microsoft to keep the community safe is going to continue to end badly. The community has an obligation to each other.
Like, no one is making someone go bring a bunch of food to feed the homeless, but if you do, you have some basic social obligation to make sure it is sanitary and not poison.
People who give things away for free widely absolutely have obligations, and if they do not like those, they should hand off the project to a quorum of responsible maintainers and demote themselves to just a contributor.
>if they do not like those, they should hand off the project to a quoarum of >responsible maintainers and demote themselves to just a contributor.
The most responsible thing to do is to release it under an OSS license and let whoever, yes - including you, fork and maintain their own copy if it's that important.
Is a food pantry giving away free food obligated to check expiration dates and make sure the food is properly sealed?
Volunteer work absolutely has obligations, and I do not know why software volunteers are exempt from any responsibility unless they are being paid.
If you do not want to do the volunteer work in a safe way, please hand off the job to a volunteer willing to do so.
Are you really saying there is just something fundamental about javascript developers that makes them unable to run the same basic shell commands as Linux distribution maintainers?
You are of course right that a signed package ecosystem would be great, it's just that you're asking people to do this labour for you for free. If you pay some third party to verify and sign packages for you? That's totally fine. Asking maintainers already under tremendous pressure to do yet another labour-intensive security task so you can benefit for free? That's out of balance.
Are they incapable of doing it? Probably not. Does it take real labour and effort to do it? Absolutely.
(As we’ve seen from every GPG topology outside of the kinds of small trusted rings used by Linux distros and similar, there’s no obvious, trustworthy, scalable way to do decentralized key distribution.)
Identity continuity at a minimum, is of immense defensive value even though we will not know if the author is human or trusted by any humans.
That said any keys that become attached to projects that are highly depended on would earn a lot of trust that they are human by getting a couple of the 5k+ of people worldwide with active well trusted PGP keys to sign theirs via conferences or otherwise, as it has always been.
Two immediate problems: (1) package distribution has nothing to do with git (you don’t need to use any source control to publish a package on most indices, and that probably isn’t going to change), and (2) this doesn’t easily account for expiry, revocation, or the more basal reality that most people just aren’t good at key management. I think a workable design can’t make these assumptions.
> That said any keys that become attached to projects that are highly depended on would earn a lot of trust that they are human by getting a couple of the 5k+ of people worldwide with active well trusted PGP keys to sign theirs via conferences or otherwise, as it has always been.
This doesn’t scale to graphs of hundreds of thousands of maintainers, like PyPI has. I’m also not convinced it’s ever really worked on smaller scales either, except it in the less useful “nerd cred” sense.
You said that you "also" blame NPM, but they're the only party who should get any blame until they get their shit together.
[1] https://github.com/axios/axios/issues/10636#issuecomment-418...
I would argue that the problem is network accessibility, not programmability.
It would not be an advantage for your front door lock to be infinitely reprogrammable. It’s just a liability.
You still, however, know that the author is who they say they are, and that other people (the distro maintainers) believe that author to be the correct entity, and believe them to have been uncompromised. And any such compromise would, by definition, affect all users of the repo and presumably be detected by them and not by you in the overwhelmingly common case.
"Just run this script" short circuits all of that. YOU, PERSONALLY, ALONE have to do all the auditing and validation. Is the link legit? Did it come from the right place? Is it doing something weird? Was the sender compromised? There's no help. It's all on you. Godspeed.
This doesn't mean anything since "who they say they are" is an anonymous username with no real life correlation. Might as well be completely anonymous.
> that other people (the distro maintainers) believe that author to be the correct entity
No? Anyone can make an account and upload to AUR and it has exactly 0% to do with the distro maintainers. Packages can be removed if they're malicious, but websites can also be removed via browser-controlled blacklists (which I don't like btw but it's how it works nowadays).
> And any such compromise would, by definition, affect all users of the repo and presumably be detected by them and not by you in the overwhelmingly common case.
This is true of a popular website that advertises install instructions using curl | bash as well.
I've been using Linux for the past 2 decades and my general experience is that it is in no way more secure than Windows or Mac, just way less popular and with a more tech savvy userbase.
They won't do this, I have talked to them plenty of times about it. But, if they did, the supply chain attacks would almost entirely stop.
It does in stagex, and could in any project. The same maintainer keys that sign commits and reviews are the same keys that must sign releases.
> (2) this doesn’t easily account for expiry, revocation, or the more basal reality that most people just aren’t good at key management. I think a workable design can’t make these assumptions.
I do not accept this excuse. People keep up with passports and birth certificates and you are generally not allowed to have backups of those. I for one am not going to assume that most programmers are incapable of writing down 24 english words on paper on as many backups as the need and being able to recover at least one of those in the future if needed to recover a key.
If a developer really cannot keep track of something so trivial, I absolutely do not trust them not to get their identity stolen by someone seeking to push a supply chain attack
> This doesn’t scale to graphs of hundreds of thousands of maintainers, like PyPI has. I’m also not convinced it’s ever really worked on smaller scales either, except it in the less useful “nerd cred” sense.
Say that to the 5444 PGP keys in the current web of trust that signs and maintains most packages for every major linux distribution running the bulk of the services on the internet. It works just fine.
Simply make it a hard requirement for popular dependencies and developers that cannot figure out how to type 2 commands to generate a key and put it on a smartcard, and write down a 24 word backup, should not be maintainers,
That may sound harsh, but being a maintainer of popular FOSS means an obligation to do the bare minimum to not get your identity stolen, like signing code and releases.
Last century doctors all balked at the idea of washing hands or tools between patients even though it provably resulted in better health outcomes on average.
"But look, everyone is negligent, and they are not likely to change" is not an excuse to not adopt obvious massive harm reduction with little effort.
My team and I practice everything I am preaching here and any responsible project can do the same to protect their projects even if the majority ignorantly do not.
For better or worse, you do trust people like this (assuming you're running a nonzero amount of Python, Ruby, Rust, or whatever else software).
> Say that to the 5444 PGP keys in the current web of trust that signs and maintains most packages for every major linux distribution running the bulk of the services on the internet. It works just fine.
That's tiny, and is exactly my point: these kinds of small rings of trust don't remotely resemble the trust topology in a free-for-all packaging ecosystem.
> "But look, everyone is negligent, and they are not likely to change" is not an excuse to not adopt obvious massive harm reduction with little effort.
This is not the argument being advanced. The argument is that we need to do better (in terms of misuse-resistance, etc.) than long-lived keys and the kinds of nerd-cred "get good" assumptions made in PGP-style webs of trust.
Nobody thinks that signing is bad; the problem is when you push the median developer to adopt it without any clear contingency plans for when, not if they fail to uphold the invariants you assume.
Waiting for corpos to fix it has not worked in one entire forever, so I would rather lower the barrier of entry to decentralized systems that are still an IETF standard securing the backbone of the internet.
At the end of the day there are only tens of thousands of authors of globally deployed FOSS libraries and we absolutely can and must scale cryptographic identity to them to avoid supply chain attacks that hit _everything_.
Secondly, we should double down and not put all the pressure on authors. We need to make it easy for anyone with a reputable key to review and sign any FOSS code that exists. A decentralized and standardized audit system. Working on an implementation of that right now in fact.
No, that's affirmatively incorrect. AUR and PPA both require authenticated accounts. The "real life correlation" may be anonymous to you, but it is trackable in a practical sense. And more importantly, it's stable: if someone pushes an attack to AUR (or NPM, whatever) the system shuts it down quickly.
And the proof is THAT IS EXACTLY WHAT HAPPENED HERE. NPM noticed the Axios compromise before you did, right? QED. NPM (and AUR et. al.) are providing herd protection that the script-paste hole does not.
Those scripts you insist on running simply don't provide that protection. The only reason you haven't been compromised is because you aren't important enough for anyone to care. The second you get maintainership over a valuable piece of software, you will be hacked. Because you've trained yourself to be vulnerable and (especially!) becuase you've demonstrated your softness to the internet by engaging in this silly argument.
> It would not be an advantage for your front door lock to be infinitely reprogrammable. It’s just a liability.
Er, most door locks are infinitely reprogrammable, because being able to rekey them without having to replace the whole unit is a huge advantage and the liability/disadvantage is minimal (falling under "It rather involved being on the other side of this airtight hatchway" in an unusually almost-literal sense where you have to be inside the house in order to rekey the lock, at which point you could also do anything else).