How StepSecurity Detected and Helped Remediate the Axios npm Compromise

Summary
StepSecurity says it detected malicious axios versions 1.14.1 and 0.30.4 through package analysis and C2 traffic. The maintainer later confirmed social engineering and npm-token theft; Google and Microsoft attributed the operation to North Korean actors.
Key points
- StepSecurity’s AI Package Analyst flagged axios versions 1.14.1 and 0.30.4, citing indicators including an undocumented plain-crypto-js dependency and dropped provenance attestation.
- Harden-Runner detected an outbound connection to sfrclak[.]com from CI/CD runners that had installed the compromised package.
- The maintainer said an attacker used a fake collaboration setup and a RAT to access their machine and steal a long-lived npm token, allowing malicious versions to be published outside the project’s OIDC-based controls.
- The attacker also accessed the maintainer’s GitHub account and repeatedly deleted warning issues; GitHub suspended the account and npm removed the malicious packages.
- Google’s Threat Intelligence Group attributed the attack to UNC1069; Microsoft Threat Intelligence independently attributed it to Sapphire Sleet, which it identified as the same group.
- StepSecurity used public Harden-Runner insights to identify open-source projects that had installed the compromised package and notify their maintainers.
Article Details
- Victim Organization
- axios project
- Incident Type
- npm supply-chain compromise involving maintainer account takeover, theft of a long-lived npm access token, and publication of malicious package versions containing a remote access trojan
- Data Types Exposed
- Long-lived npm access token
- Operational Impact
- Malicious axios versions 1.14.1 and 0.30.4 were published and could install a remote access trojan on affected machines. The attacker also used the compromised maintainer account to delete security-warning issues. npm removed the malicious packages, and GitHub suspended the compromised account.
- Claim Status
- confirmed
Indicators of compromise
| Type | Indicator | Context |
|---|---|---|
| DOMAIN | sfrclak[.]com | C2 domain contacted by CI/CD runners that installed the compromised axios package. |
MITRE ATT&CK
People
Threat Actors
Sapphire SleetMicrosoft Threat Intelligence attributed the axios supply-chain attack to this North Korean state-sponsored group and identified it as the same group named UNC1069 by Google's Threat Intelligence Group.UNC1069Google's Threat Intelligence Group attributed the axios supply-chain attack to this North Korean threat actor.
Vendors
GitHubincident and have been continuously updating it. But the human story, the frantic evening, the deleted GitHub issues, the community rallying together at midnight, that part hasn't been told yet. This is that story.npmthousand packages as dependents. By download count, this is the largest compromise of a single package in npm history. A state-sponsored threat actor hijacked it, and when we created a GitHub issue to warn theStepSecurityAs soon as the compromised axios package was published to npm, StepSecurity's AI Package Analyst analyzed it in real time and returned a verdict of critical / rejected. The conclusion was unambiguous: this was a
Products
AI Package AnalystAs soon as the compromised axios package was published to npm, StepSecurity's AI Package Analyst analyzed it in real time and returned a verdict of critical / rejected. The conclusion was unambiguous: this was aAxiosIt has been a week since the axios compromise, and we wanted to take some time to reflect. We published a detailed technical analysis during the incident and have been continuously updating it. But the human story, theHarden-RunnerStepSecurity Harden-Runner, our purpose-built endpoint detection and response (EDR) agent for GitHub Actions, independently confirmed the analysis.plain-crypto-jsanalysis flagged six distinct suspicious indicators, such as an undocumented and unused dependency (plain-crypto-js) that was never imported anywhere in the source code, a version mismatch between package.json and
