axios maintainer explains North Korean UNC1069's social engineering playbook: fake Slack invite, then a Teams call that delivered a RAT
Contents
In the previous article, I covered the technical guts of the RAT dropper hidden in the poisoned axios release. But what the community really wanted to know was: how was the maintainer’s account actually compromised?
Update on April 5, 2026: later public reports make it clear this was not limited to axios. The same campaign also targeted maintainers tied to Fastify, Lodash, dotenv, Mocha, and Node.js core. Read the follow-up here.
On April 2, axios lead maintainer Jason Saayman published a postmortem. In the comments, corneliusroemer asked for the exact social-engineering playbook, and bcomnes argued that sharing it would help others defend themselves. Jason then laid out the full attack chain that started the incident.
The Full Social-Engineering Chain
Here is the attack path Jason described in the comment thread. The attackers adapted a playbook that Google GTIG documents as UNC1069, then tailored it to Jason personally.
graph TD
A["Initial contact from someone<br/>posing as the founder of a real company"] --> B["Invite to a fake Slack workspace<br/>with a fully cloned brand"]
B --> C["Shared LinkedIn posts<br/>and fake profiles for team members<br/>and other OSS maintainers"]
C --> D["Set up a Microsoft Teams meeting<br/>that looked like a group call"]
D --> E["During the meeting,<br/>they said something was out of date"]
E --> F["Installed software disguised as<br/>a Teams-related update"]
F --> G["RAT: full control of the machine"]
G --> H["Stole npm credentials<br/>2FA could be bypassed through the RAT"]
H --> I["Published axios 1.14.1 / 0.30.4<br/>with a poisoned dependency chain"]
A Complete Clone of a Real Company
The attackers first contacted Jason while impersonating the founder of a real company. And “impersonating” is too weak a word here. They cloned the company, the brand, the corporate identity, and even the founder’s appearance.
The Fake Slack Workspace
Jason was invited to what looked like a real Slack workspace.
they then invited me to a real slack workspace. this workspace was branded to the companies ci and named in a plausible manner. the slack was thought out very well, they had channels where they were sharing linked-in posts
The workspace followed the company’s brand guidelines in its name and design. Channels shared LinkedIn posts, with links that were probably real company accounts. They even prepared fake profiles for team members and for other OSS maintainers.
The goal was to make it feel like Jason was not the only person being invited.
The Microsoft Teams Call
After the Slack exchange, they scheduled a Teams meeting. It looked like multiple people were on the call.
the meeting said something on my system was out of date. i installed the missing item as i presumed it was something to do with teams, and this was the RAT.
During the call, they told him something on his system was outdated. Jason assumed it was a Teams-related update and installed the software they asked for. That was the RAT (Remote Access Trojan).
Jason summed it up like this:
everything was extremely well co-ordinated looked legit and was done in a professional manner.
The attack ran for roughly two weeks from initial contact to RAT installation. The postmortem timeline says, “About 2 weeks before March 31: social engineering campaign initiated against the lead maintainer.”
Why 2FA Did Not Help
DanielRuf asked in the comments whether npm 2FA had been enabled, and Jason answered:
i have been told that once the RAT is on your machine they have full unilateral control of everything on your machine. and yes i did have 2fa enabled on my account.
2FA was enabled. But once a RAT controls the machine, TOTP codes are generated on the same compromised device, so the attacker can read them too. Browser sessions, saved credentials, and npm tokens are all inside the attacker’s reach.
feross, the Socket CEO, said it bluntly:
Enable npm’s mandatory 2FA for publish, and use a hardware key if possible. TOTP on a compromised machine is not 2FA.
TOTP on a compromised machine is not 2FA. Once the endpoint trust assumption is gone, every defense layered on top of it falls with it.
The Same Trick Hit Other OSS Maintainers
Right after Jason’s account, Node.js ecosystem maintainer voxpelli described almost the exact same attack pattern.
I had a similar incident a few weeks back where someone invited me to a podcast a week later and then invited me into a group with others who would be interviewed in the same podcast episode
In voxpelli’s case, the cover story was a podcast. They made it look like other guests were involved, sent promotional images and pre-interview questions, and then, right before the recording, claimed there was a connection issue and tried to get him to install an unsigned macOS app.
Thankfully I was paranoid and didn’t run it
Voxpelli refused to run it. The attacker then asked him to execute a curl command, and when that was also refused, the whole conversation was deleted. Later analysis showed it was an infostealer. Voxpelli maintains packages like Mocha, so he is a valuable target.
The comparison is simple:
| Item | axios maintainer (Jason) | Mocha maintainer (voxpelli) |
|---|---|---|
| Cover story | Corporate collaboration | Podcast appearance |
| Trust-building | Fake Slack workspace | Fake guest group + pre-interview materials |
| Prep time | About 2 weeks | About 1 week |
| Malware lure | ”Update” during a Teams call | ”Connection issue” right before recording |
| Outcome | RAT infection | Refused and escaped |
DanielRuf also noted that fake hiring interviews are part of the same family of attacks. Company visit, podcast, job interview: the story changes, but the structure is the same.
axios Had Already Adopted “Best Practices”
shaanmajid inspected the npm registry metadata and published the results in a Gist. The key point is that axios v1.x had been using OIDC Trusted Publisher since 2023, and the four most recent releases (1.13.4 through 1.14.0) were all published from GitHub Actions with provenance attached.
The poisoned 1.14.1 release had neither OIDC binding nor provenance. The registry metadata makes the anomaly obvious. But provenance verification is still effectively opt-in, and almost nobody checks it.
The most important line from shaanmajid:
The strictest package-level setting npm offers maintainers — “Require two-factor authentication and disallow tokens (recommended)” — does not block local
npm publish.
Even npm’s strictest setting does not block local CLI publishing. The browser may show a 2FA prompt, but a software TOTP on a compromised machine is easy to bypass. crates.io in Rust can be configured to allow only OIDC-based publishing, but npm cannot.
The defenses shaanmajid recommends are:
| Defense | Layer | Effect |
|---|---|---|
| FIDO2 hardware key | Maintainer | A RAT cannot remotely press a physical key. This was the only maintainer-side protection that actually mattered here. |
| Enforced OIDC-only publishing | Registry | Reject local CLI publishes at the registry level. crates.io already has this. |
| Dependency cooldown period | Consumer tooling | The poisoned release was live for only about 3 hours. A 3-day cooldown would have blocked automatic pickup. Dependabot, Renovate, and bun support minimumReleaseAge. |
| Provenance verification | Consumer tooling | Legit releases had OIDC provenance; the poisoned one did not. Verifying it would have exposed the anomaly immediately. |
The Attacker Also Deleted Issues
The postmortem timeline contains one more important detail:
March 31, around 01:00 UTC: community members file issues reporting the compromise. The attacker deletes them using the compromised account.
About 40 minutes after the poisoned release went out, community members noticed the anomaly and filed issues. The attacker used Jason’s compromised account to delete them, slowing detection and keeping the bad release alive longer.
axios collaborator DigitalBrainJS, despite having lower permissions than Jason, moved the takedown forward in PR #10591 and contacted npm directly to get the package removed. feross reported that Socket flagged the release within 6 minutes, but npm removal still took about 2 hours after that.
The Easy-to-Miss npx Infection Path
ahmadnassri pointed out another problem: npx.
npxinvocations produce a non-deterministic dependency resolution at run-time, no package-lock trace.
Because npx resolves dependencies at runtime, it leaves no trace in a lockfile. In a world where CI/CD and MCP setup docs routinely recommend npx <package>@latest, any environment that pulled axios indirectly through npx during the 3-hour poisoned window was also exposed. A normal lockfile-based impact analysis would miss that path completely.