The fetch api has been widely available in browsers for a decade now. And in node since 18. A competent developer could whip up a more axios-like library with fetch in a day easily. You can do all the cool things like interceptors with fetch too.
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.
> A competent developer could whip up a more axios-like library with fetch in a day easily.
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.
Ok, well have AI write some table stakes for you in 10 minutes with 100% test coverage and only provide exactly what "table stakes" you are missing without any bells and whistles.
Not much we didn't know (you're basically SOL since an owner was compromised), however we now have a small peek into the actual meat of the social engineering, which is the only interesting news imho: https://github.com/axios/axios/issues/10636#issuecomment-418...
jasonsaayman and voxpelli had useful write ups from the "head on a swivel" perspective of what to watch out for. Jason mentioned "the meeting said something on my system was out of date." they were using Microsoft meeting and that's how they got RCE. Would love more color on that.
> 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.
they are cloning Zoom and MS Teams, and try to get people to either copy a script (which is in a textarea that's conveniently too small to show the whole script, and scrollbars are hidden by CSS, and there's a copy button, and when you paste it into the terminal you'll see last few lines, also look innocent, but there's a curl | zsh or `mshta` somewhere in there), download and run a binary/.dmg (and it might be even signed by GoogIe LLC. - the name chosen to look good in the usual typeface used on macOS).
...
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...)
> it seems the correct muscle memory response [is something other than] never download and execute anything
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.
> 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.
Most of the copy-paste Linux command used to be 'sudo aptitude install -y blahblah'.
It is worth noting though that Ubuntu's PPAs became at some point widespread enough to have pasting a new repo source as a standard practice as well (which would open the way to this kind of attack for sure)
It's really not, and to the extent it is it's an echo of the nonsense filtering from elsewhere. Linux distros went decades without this kind of thing by packaging the popular stuff securely. People who wanted the source knew how to get it. The "just copy this command" nonsense absolutely came from OS X first.
Arch has pacman and that worked so well that it had to have AUR which is just glorified curl | bash. Linux distros managed it for decades when the vast majority of binaries you would run are made by nerds for nerds. If the original maintainer isn't willing to securely package it then you're often SOL.
Other comment already said, but it seems it was likely a clone of the web interface rather than the actual teams client. You can see a lot more details in this comment on the github thread (not by the axios maintainer, but goes over the same threat group and campaign) https://github.com/axios/axios/issues/10636#issuecomment-418...
An owner being compromised is absolutely survivable on a responsibly run FOSS project with proper commit/review/push signing.
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.
Did you investigate the maintainer compromise and publication path? The malicious version was never committed or pushed via git. The maintainer signs his commits, and v1 releases were using OIDC and provenance attestations. The malicious package versions were published locally using the npm cli after the maintainer's machine was compromised via a RAT; there's no way for package maintainers to disable/forbid local publication on npmjs.
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.
I can not find a single signed recent commit on the axios repo. It is totally yolo mode. Those "signed by github" signatures are meaningless. I stand by my comment in full.
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.
What you sign or don't sign in your Git repo doesn't matter because NPM doesn't publish from a Git repo. Signing commits is still useful for your contributors and downstream forks but it won't have any effect on the users who use your package via NPM.
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".
What they need to mandate is hardware anchored passkeys/fido2/webauthn for both auth and package signing, with the -option- to sign with PGP for those that have well trusted PGP keys.
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.
Don't need to require hardware 2fa tokens. Just a mobile app would be sufficient. Publish to a staging area then require confirmation on mobile to make it go live. Maybe include a diff of changes files for good measure.
So you think the answer is replacing a requirement for a 6-digit 2FA code that can be typed into the npm publishing CLI with a requirement for a device that has a camera that can scan a QR code and then... what? What does the QR code do on the device? How does the npm CLI present the QR code?
Simply supporting passkeys gives people domain locked login via qr/phone, or any fido2 usb device. No more keyboard entry required for login other than username, which means phishing is off the table. Standards are great if we can get anyone to use them.
Like I said. One must sign commits -universally- and -also- sign reviews/merges (multi-party) and then -also- do multi party signing on releases. The code in the release must match the code from git, or no publish.
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.
I agree, I just think it's pointless to discuss Axios' commit-signing practices or lack thereof when NPM doesn't support any of it. It seems like axios was already using Trusted Publishing [1] and it still didn't get caught.
You said that you "also" blame NPM, but they're the only party who should get any blame until they get their shit together.
It wasn’t done through git. It was a direct npm publish from the compromised machine. If you read further down in the comments (https://github.com/axios/axios/issues/10636#issuecomment-418...), it seems difficult to pick the right npm settings to prevent this attack.
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.
To prevent supply chain attacks you need multi party cryptographic attestation at every layer, which is pretty straight forward, but you are correct, NPM and GitHub controls absolutely will not save you. Microsoft insists their centralized approach can work, but we have plenty of evidence it does not.
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.
And for 5 there should be help on the NPM end to make it so that the alarms can fire before the new update is actually revealed to the public. There could be a short staging time where it could be revoked before any harm has been done. During this staging time NPM should also scan the package through a malware scanner before allowing it to go public.
I agree that would be nice, but NPM absolutely will not do any basic supply chain integrity work. They are actively opposed to it citing concerns that it might turn off lower skill developers that would be too annoyed by tapping a yubikey to sign releases or code. I have talked to them enough times over the years to have completely given up here.
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.
This only works up to a point. Some human needs some way of changing the publication setup in case something goes wrong or changes. What you're asking is blowing a proverbial e-fuse once the setup is known to be working. This is software, shit will go wrong at some point and you need a way to make changes.
Of course, which is why all the (decent) tooling for this is provider agnostic, and provides documentation for multi-party-sharded backups so a quorum of maintainers can always re-assemble the key by hand for any reason if needed.
The part that keeps bugging me about incidents like this is how the clearest signal was already there and nobody acted on it. Every legitimate v1 release had OIDC provenance attestations, and the malicious one just... didn't. That's a binary check you could automate in any CI pipeline in about ten minutes, but almost nobody does.
I've started adding a provenance verification step to our deploy pipelines after the event-stream incident years ago, and it's caught weird stuff twice now, both times just maintainers accidentally publishing from their local machine instead of CI, not actual attacks, but the point is the same mechanism would catch this. The real problem isn't that we lack signing infrastructure or better NPM policies. It's that the ecosystem has trained everyone to treat dependency updates as a solved problem the moment you have a lockfile. Lockfiles protect you from silent republishing of existing versions, but they do nothing when the attack comes as a "legitimate" new patch release. Diffing your lockfile for unexpected new transitive deps on every deploy is table stakes, and most teams I've worked with still don't do it.
Another point: do NOT use the "~" or "^" versions for automatic updates. Just lock everything tight in your package files. Then have an alert on the lockfile changes.
The interesting detail from this thread is that every legitimate v1 release had OIDC provenance attestations and the malicious one didn't, but nobody checks. Even simpler, if you're diffing your lockfile between deploys, a brand new dependency appearing in a patch release is a pretty obvious red flag.
npm could solve half of this by letting packages opt into OIDC-only publishing at the registry level. v1 already had provenance attestations but the registry happily accepted the malicious publish without them.
The provenance gap is real, but most developers just rely on their scanning tools to block bad packages - which normally relies on a CVE - and malicious packages don't get CVE numbers.
GHSA and OSV have started tracking them as advisories - but the infrastructure to actually find these in a given system is still lacking. Most tools are still just checking NVD/CVE.
So the attestation signal was there, the malware flag comes later - normally after the malicious version is already pulled - and nothing in the average developer's toolchain is telling them that they got hit.
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.
> NPM clients should not install freshly published packages.
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?
In cases like this that isn’t an issue, NPM takes the malicious package down and you roll back to the previous version.
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.
This could be controlled by npm. Client ask for available versions anyway. If package is security fix then it can be made available instantly. But this delay gives time for security scanners and time to notify maintainers that package was published.
This is why I've moved to native fetch for most projects. The fewer dependencies in the chain, the smaller the attack surface. For API clients especially,
fetch + a thin wrapper is usually enough.
Looks like a very sophisticated operation, and I feel for the maintainer who had his machine compromised.
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
That's the reality of modern war. Many countries are likely planting malware on a wide scale. You can't even really prove where an attack originated from, so uninvolved countries would also be smart to take advantage of the current conflict. Like if you primarily wrote German, you would translate your malware to Chinese, Farsi, English, or Hebrew, and take other steps to make it appear to come from one of those warring countries. Any country who was making a long term plan involving malware would likely do it around this time.
npm process to setup OIDC is way too frustrating. There is just so much friction. You need the package to first exists in the registry, meaning you have to first create an API token and push something. And only then can you enable OIDC for that specific package. After adding the repo + workflow names, you have to save. Then finally toggle the “only allow OIDC publishing”.
Before each action you need to enter your 2fa code.
NPM is designed to let you run untrusted code on your machine. It will never work. There is no game to step up. It's like asking an ostrich to start flying.
It’s far from a complete solution, but to mitigate this specific avenue of supply chain compromise, couldn’t Github/npm issue single-purpose physical hardware tokens and allow projects (or even mandate, for the most popular ones) maintainers use these hardware tokens as a form of 2FA?
The attacker installed a RAT on the contributor’s machine, so if they had configured TOTP or saved the recovery codes anywhere on that machine, the attacker could defeat 2FA.
Oh, yes, I missed that the TOTP machine was compromised:\ Would that then imply that it would have been okay if codes came from a separate device, eg. a TOTP app on a Palm OS device with zero network connectivity? (Or maybe these days the easiest airgapped option is an old android phone that stays in airplane mode...)
I mean, I guess attestation might have some value, but it feels like moving the goalposts. Under the threat model of a remote attacker who can compromise a normal networked computer, I can't think of an attack that would succeed with a programmable TOTP code generator that would fail if that code generator was not reprogrammable. Can you?
> 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).
Sorry, attestation is the goalpost. The community wants certainty that the package was published by a human with authority, and not just by someone who had access to an authority’s private keys. That is what distinguishes attestation from authentication or authorization.
Yes, unfortunately authenticator apps just generate TOTP codes based on a binary key sitting in plain sight without any encryption. Not that it would help if the encrypting/decrypting machine is pwned.
All maintainers need to do is code signing. This is a solved problem but the NPM team has been actively rejecting optional signing support for over a decade now. Even so maintainers could sign their commits anyway, but most are too lazy to spend a few minutes to prevent themselves from being impersonated.
With what keys, and how do you propose establishing trust in those keys?
(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.)
If the keys that signed the early commits of a trusted FOSS project suddenly change without being signed by the previous keys, that should merit a higher level of consensus at release time, or waiting periods, etc.
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.
If the solution is 'maintainers just need to do xyz', then it's not a solution, sorry. It's not scalable and which projects become 'successful' and which maintainers accidentally become critical parts of worldwide codebases, is almost pure chance. You will never be able to get all the maintainers you need to 'just' do xyz. Just like you will never be able to get humans to 'just' stop making mistakes. So you had better start looking for a solution that doesn't rely on humans not making mistakes.
It scales just fine for thousands of maintainers of thousands of packages for every major linux distribution that powers the internet. You just have to automate enforcement so people do not have a choice.
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?
No, it really doesn't scale that well. 'Thousands' of packages is laughable compared to the scale of npm. And even at the 'thousands' scale distros are often laughably out of date because they're so slow to update their packages.
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.
My 7 teammates and I on stagex actually maintain all this zero-trust signing and release process I am suggesting for several hundred packages and counting. Not asking anyone to do hundreds like my team and I are, but if authors could just at least do the bare minimum for the code they directly author that would eliminate the last gaping hole in the supply chain.
Any good payload analysis been published yet? Really curious if this was just a one and done info stealer or if it potentially could have clawed its way deeper into affected systems.
This article[0] investigated the payload. It's a RAT, so it's capable of executing whatever shell commands it receives, instead of just stealing credentials.
Seems to me the root of the problem was that the guy was using the same device for all sorts of stuff.
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.
they can simply make an app that requires tapping a button, so people don't end up with TOTP seeds stored in their password manager on the same notebook where they run 'publish' from
> something on my system was out of date. i installed the missing item
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...)
The interesting detail from the GitHub thread is shaanmajid's observation that every legitimate v1 release had OIDC provenance attestations and the malicious one didn't, but nobody checks. Even simpler, if you're diffing your lockfile between deploys, a brand new dependency appearing in a patch release is a pretty obvious red flag without needing any attestation infrastructure.
this is why i pin every dependency hash in my python projects. pip install --require-hashes with a locked requirements file catches exactly this, if the package hash changes unexpectedly the install fails. surprised this isn't the default in the npm ecosystem
Npm and the other JavaScript package managers do generate and check lockfiles with hashes by default. This was a new release, not a republishing of an old version (which isn’t possible on the npm registry anyway).
i wasn't aware npm lockfiles check hashes by default now. my concern is more about the initial install before a lockfile exists, like in CI from a fresh clone without a committed lockfile. but you're right, once the lockfile is there the hash mismatch would be caught.
Nope, the most restrictive option available is to disallow tokens and require 2FA. I think that using exclusively hardware 2FA and not having the backup codes on the compromised machine probably would have prevented this attack though.
Someone in the linked Github thread describes an attack where the attackers waited for the victim to use their Yubikey for an AWS login, giving the attackers access to AWS as well. I don't think hardware 2FA is safe against a RAT.
No. axios (v1 at least; not v0) were setup to publish via OIDC, but there's no option on npmjs for package maintainers to restrict their package to *only* using OIDC. The maintainer says his machine was infected via RAT, so if he was using software-based 2FA, nothing could have prevented this.
Actually there is an option to restrict to only OIDC publishing. It is a bit hidden and relies on a different form for reasons I really cannot understand. Npm UX is just so bad.
I never understood why all the CAS tutorials pushed axios. This was before vite and build-scripts was how you did react. After the compromise I reviewed some projects and converted them to pure JS fetch and vite.
I ask this on every supply chain security fail: Can we please mandate signing packages? Or at least commits?
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.
Perhaps, but if it's gotten to the point where millions of people download the unsigned code, signing should probably become required. Even reproducible builds.
Required by who though? If your business etc depends upon some code, it's up to you to ensure its quality, surely? You copy some code onto your machine then it's your codebase, right?
While I think anyone unwilling to sign their code is negligent, I also feel anyone unwilling to ensure credible review of code has been done before pushing it to production is equally negligent.
Anyone that maintains code for others to consume has a basic obligation to do the bare minimum to make sure their reputations are not hijacked by bad actors.
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.
No they don't! They have literally no obligations to you - and you've got the MIT/APL/GPL license to prove it. You're getting the benefit of their labour for free!
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?
They have basic obligations as highly trusted FOSS software maintainers, a role they allowed themselves to be elected into, to make sure their hard earned goodwill and trust is not stolen by a bad actor. They also have a basic obligation to make sure they have accountability and review of all code before it gets to their users.
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.
They literally owe you nothing. They can walk away tomorrow, sell their github account, introduce breaking changes, add bugs, die, add crypto links, whatever.
>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.
"Anyone that cannot spend $40+ to give every FOSS maintainer a smartcard and maybe even separate machines for releases and make the more secure workflow truly 5 minutes has absolutely no business widely depending upon FOSS"
A $50 used laptop from goodwill and a $40 yubikey will do the job.
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.