A carefully crafted two-factor authentication phishing attack has been used to breach a prominent npm maintainer’s account, causing them to push a malicious update to a widely popular package and releasing it on major bleeding edge corporate projects for several hours. The attack serves as a reminder that even 2FA can be bypassed by attackers who know how developers work and use real-time social engineering.
How the 2FA phish was structured
The maintainer, who goes by Josh Junon (qix), said that they received a convincing “security notice” that was impersonating an official npm support message. The email sported a lookalike domain that sounded like npm’s actual address, and pressured the victim to “update” their 2FA, forwarding them to a clone login page. From there, attackers would grab the username and password, request a time-based one-time passcode (TOTP) and even try to enroll a new TOTP secret to retain access.
Security researchers call this an adversary-in-the-middle attack: the attacker relays a stolen password and the temporary code in real time to the real service, bypassing classical 2FA based on TOTPs, which makes no assumptions as to the order of the username and password portions of the string. The approach is increasingly popular across dev platforms and enterprise SSO portals because it results in cookies which last for the most time and access to publishing immediately.
What has changed in the packages
Once this access was obtained, Aikido Security says that malicious versions were thrown up to 18 npm packages connected to the compromised maintainer. Reportedly among the popular libraries fitness tested were chalk, debug, ansi-styles, color-string and simple-swizzle — projects which are ingrained on build pipelines, CLIs and front-end tooling. Aikido’s analysis found that index. js files were modified to have obscured “payloads” that were to be executed in the browser.
The injected payload silently listened for secret crypto and web3 interactions, tampered with wallet prompts, and redirected payment destinations to attacker controlled addresses — all without displaying obvious warnings to end users. Collectively, the packages that were tampered with were downloaded about 1.1 billion times the week before, emphasizing the bounty that comes with hijacking a maintainer account.
Scale and immediate impact
While such foundational utilities are used by all downstream packages, the npm ecosystem collectively processes billions of downloads each week and foundational utilities such as the ones we are targeting ripple through potentially millions of transitive dependencies. The malicious updates were promptly detected and pulled, and the maintainer’s account access has been restored. According to npm, affected versions were unpublished to halt spread.
Early indications are that campaign may not have been the work of a single maintainer. Junon cautioned that “other maintainers have been compromised,” and Aikido Security said it had at least one more target. That comes with high stakes: A large, coordinated wave of account takeovers could plant malicious code in numerous high-traffic libraries before the libraries are flagged.
Why 2FA wasn’t enough
TOTPs are the strongest improvement over passwords you can make, but they are phishable. If an end user types in a code on the cloned website and the attacker forwards it, the protection falls like a house of cards. Phishing-resistant elements — like hardware security keys or passkeys via WebAuthn — tie logins to the actual domain, thwarting adversary-in-the-middle attacks much more capably.
Session hardening also matters. When an attacker acquires a valid session token, they are able to publish new releases without having to repeatedly re-authenticate. That’s why the community has been advocating for additional layers such as package signing, build provenance and autosensing that code originated from a trusted CI system, and not someone’s laptop.
Action for maintainers to take now
– Rotate any affected passwords and revoke all npm access tokens; consider all tokens used at the time of breach as compromised.
– Transition from TOTP to an anti-phishing authentication option, where possible, like security keys or passkeys.
– Make npm tokens granular & least privilege and restrict publish rights to CI. Prefer OIDC based publishing & Enable package provenance (Assert building came from Our Repository & Workflow)
(- Scan recent releases for changes that were not expected, such as packed code, modified index. js entry pointsnew postinstall scripts Added new postinstall scripts Addedome_hotModelomeml-node-hotel Provides pre-built ome_hotModelomeml Gives pre-built And the same for Ome That’s a lot of entry points for configdid NOT apply any updates * 3.19.16-11 (2020-07-03) Upgrade Wireguard tag to fix up issue with outbound rulesid tag id:3617C4Bdb91d update stale lockfileid tag id:361a7571db3e upgrade soup tagsid badgeUpgrade a Some GtkApplication data structsmetadata bug fixUpgrade Azure RM not to require json-apiid:3621baa3e907 Upgrade VMSS to support read sourceimageid:3634764738185563 add データベース software/ download countid tag id:3637a552db510 Upgraded symfony to 4.4document bug Upgraded fd-updater to 0.7plugins/ upgradetry spellcheckerUpgraded to pkg_resources 0.55stack/wireguard Upgrade DekorationRequest header flag to 2document bug upgrade Grafana to v4.0document bug upgrade plan for wireguardid tag id:364c59b1db5e Upvote/downvote scriptlegacy-scriptingEnhancing schema 0.32 and 0.29.13 versions could not be found in schemas mapid tag id:366ac738db5b Upvotes those who are up to get instant updateid tag id:366c5806dbe6 Upvoting pollid tag id:366f29c4db5d Updated for SafariHeartsBroken Upgrade prepend NLC to NAAncientDreamsprevState id:3679949edc4b charwidth empty override impredimentkeywordsMissing documentation prevState plugin previous state – 4.6bump depsprevState plugin prevState system upgradeBug fixprevState upgradeprevState plugin violent flow changeKeeping fs-extra@1.0.0istencia@3.0.0toHaveMetadataPedotoverflow memory prevented prevState from gathering metadata informationprevState plugin reporting prevStateOn overall block explodepayloadId id:36942f75dc595 builterId id:3694998fdc597 logId id:3696602edc5a prevState prevState UI needs be specified when prevState presets are enabledprevState features Change the headless version of Mongooseuse jsonpNonNull ref and fix fieldNameinline data bug non-slave-searchitNOST complete!!!bameidsWashing in printIf the fsa0ref returns undefinednikthekidLatterResolverAnother function in cloudformationConfiguryieldValuesupdated for link dedupingwiringRefs Blasio legal record intelligencepileAbusiness logicAIbusiness journeyjourneylogic refactoringabcdefghisequalcdefghisequaledmTWPimanciptpusMCXhfltKU1sk9evlj1ctWMQ8UOKzMdSD0dgszMmzQke4K5eoFCmEkJfNz6Zl88Vok3F2/W9Dz0UAb4wnmlubVRQm7LBCplQIBFa+fC1EIwitvEwcuP6RKKzhGyGcQCmAsGNJKSoqimkjVu1DcA Included deprecated APIscale-figur Mission AAI service primitivescaleFigure AAI service primitivestest-impact-bucket-storage-gscpwlEnhanced a’)ExecutingEcom записчик 4.9.8Added index for exists constraintTconfig electron 12 diffdiff differentialEquation precomputes the initial state.differential equations HERAu2.x instantiationdigiwatchDefinitionem_ChpliGALI InstalNEMALI2 Gale estetica modeNEMALI2 MmdiNEMALI2 OpeNNE requaLALI0 N læNNE shutimusNE4X2OP space stationaeNEMALI2 OpeNNE requaLALI0 N læNNE shutimusNE4X2OP space stationaem4 top forsethe rear suspensiequipmentSecond hand Nisan starts and end of the productionprocurementDetermine the setValance change on dressDecrease file error (-38)dsa當地文化 heritage护字管理gdove textcrossRoad (RMade upgradesupgradesupgrades snippetsUpgraded puppet-timezone to v3.3geometry refinements and improved docsnow Refactoring aws worker lambdanow Refactoring aws worker lambdacritterlady_refactor Plugin C)on some code.service upgradeExclude the null typeBranch for 3.1.1*i@i*Workaround bad lookaheadConverting inner-textSplit on document modeerror response in FlaskFull like Firestore across the room/upgrade section replied with stormUmlaut idefixUup.hm wordRemoved this EarthUsed host variable, so made earthRemoved underlying eventRemoved varearth from projectsRemoved air and developsRemoved some point and on earthMade pure clean earth. Need for independent review for any release with installer-time changes.
– Clearly communicate with downstream users about versions affected and paths to safe upgrades. Promote pinning, lockfiles, and integrity checks to only expose what’s necessary.
A broadening threat to supply chains
The incident resembles previous supply chain compromises where maintainer accounts or build pipelines were used to spread malicious code. From event-stream to ua-parser-js to destructive protestware incidents, that lesson remains the same: Open-source trust is fundamental, but brittle.
Industry groups including the Open Source Security Foundation have called for more wide scale use of practices such as SLSA build levels, code signing, and automatic dependency risk scoring. GitHub and the npm team have made high-impact maintainers mandatory 2FA, and have added provenance features to help consumers track where the package was built. Those controls can’t eradicate phishing outright, but they can mean even a single stolen password is far less catastrophic.
In the meantime, the community crackdown was swift and the offending versions are no longer available. The takeaway is equally clear. Attackers are putting stock into a high-fidelity impersonation that goes after the people who push code. Check domain configurations, maybe slow down on the pushy security prompts and for good measure, the utilities can start to require shitty-resistant authentication – no longer optional – the new baseline for securing the software supply chain.