TL;DR

Ngày 31/3/2026, tài khoản npm của maintainer jasonsaayman bị chiếm quyền. Kẻ tấn công publish hai phiên bản độc hại: axios@1.14.1 (tag latest) và axios@0.30.4 (tag legacy), cả hai inject dependency plain-crypto-js@4.2.1. Khi chạy npm install, postinstall hook tải về và chạy RAT (Remote Access Trojan) xuyên nền tảng cho macOS, Windows, Linux — beacon về C2 sfrclak[.]com:8000. Package độc sống ~3 tiếng trước khi bị gỡ. Microsoft quy trách nhiệm cho Sapphire Sleet (Bắc Triều Tiên); Google/Mandiant tracking dưới tên UNC1069 với backdoor WAVESHAPER.V2. Axios có ~100 triệu lượt tải mỗi tuần — blast radius khổng lồ.

Bị ảnh hưởng? Kiểm lockfile ngay, rotate toàn bộ secret, downgrade về axios@1.14.0 hoặc 0.30.3, và đừng tin máy đã bị — rebuild từ image sạch.

Chuyện gì đã xảy ra

Đây không phải typosquat, cũng không phải bug của axios. Kẻ tấn công có quyền publish trực tiếp trên tài khoản chính thức sau một chiến dịch social engineering tinh vi kéo dài ~2 tuần: mạo danh founder của một công ty, tạo một Slack workspace giả được branding kỹ, hẹn meeting qua MS Teams, rồi lừa maintainer cài một "update bị thiếu" — thực chất là RAT. Từ đó, token npm long-lived bị lấy cắp.

Trong vòng 39 phút (00:21 → 01:00 UTC ngày 31/3), kẻ tấn công publish hai nhánh latestlegacy song song để tối đa hóa độ phủ. Không một dòng code axios nào bị sửa. Diff nhị phân giữa axios@1.14.01.14.1 trên 86 file cho thấy chỉ duy nhất package.json thay đổi — thêm dependency mới plain-crypto-js, đồng thời script "prepare": "husky" bị bỏ đi (dấu hiệu publish thủ công, bypass release pipeline chuẩn).

Vì sao nó đáng sợ

Axios là HTTP client phổ biến nhất của JavaScript, ~100 triệu lượt tải/tuần trên nhánh 1.x và ~83 triệu trên 0.30.x. Bất kỳ project nào chạy npm install (hoặc bun install) trong cửa sổ ~3 tiếng đó đều có thể bị — mà không cần chạm vào một dòng code axios nào.

Đây là một trong những cuộc tấn công chuỗi cung ứng được chuẩn bị kỹ lưỡng nhất từng được ghi nhận nhắm vào một package top-10 npm. Kẻ tấn công pre-stage decoy plain-crypto-js@4.2.0 đúng 18 giờ trước đó để tạo lịch sử registry giả. 56 file crypto trong package ấy bit-for-bit giống hệt thư viện chính chủ crypto-js, khiến phân tích diff vô hiệu. Và khi npm install chạy, C2 callback đầu tiên xuất hiện trong 1.1 giây — trước khi npm resolve xong các dependency khác.

Chi tiết kỹ thuật

Dropper setup.js nặng 4.209 bytes, dùng hai lớp mã hóa để né static analysis:

  • Layer 1: reversed Base64 (đảo chuỗi, thay _ bằng =, rồi decode).
  • Layer 2: XOR cipher với key OrDeR_7077, hằng số 333, index theo vị trí (7*i²%10).

Sau khi deobfuscate, dropper detect OS qua os.platform() rồi gọi về C2 sfrclak[.]com:8000 (IP 142.11.206.73, hosted trên Hostwinds). Payload tầng hai được chọn theo nền tảng, nhưng ba bản (PowerShell/Windows, C++/macOS, Python/Linux) đều implement cùng một RAT spec: beacon 60 giây, HTTP POST, body JSON Base64, User-Agent cứng mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0) — UA IE8 trên macOS/Linux chính là signal phát hiện tốt nhất.

Google/Mandiant định danh RAT này là WAVESHAPER.V2: reconnaissance (hostname, uptime, process list), in-memory PE injection, thực thi PowerShell/shell tùy ý, enumerate file system, persistence qua %PROGRAMDATA%\system.bat + registry Run\MicrosoftUpdate trên Windows.

MụcGiá trị
Versions độcaxios@1.14.1, axios@0.30.4
Dependency tiêmplain-crypto-js@4.2.1 (decoy 4.2.0 publish trước 18h)
Versions an toànaxios@1.14.0, axios@0.30.3
C2sfrclak[.]com:8000 / 142.11.206.73
Thời gian sống trên npm~2h 53m (1.14.1) & ~2h 15m (0.30.4)
Lần đầu C2 callback1.1 giây sau npm install
Snyk IDSNYK-JS-AXIOS-15850650
Phát hiện bởiSocket scanner (~6 phút), StepSecurity, Elastic, cộng đồng

Kỹ thuật chống điều tra

Sau khi launch payload tầng hai, dropper tự xóa dấu vết: xóa setup.js, xóa package.json chứa hook, rồi rename package.md (đã chuẩn bị sẵn, version 4.2.0 không có postinstall) thành package.json. Nếu bạn inspect node_modules/plain-crypto-js/ sau đó sẽ thấy... không có gì bất thường. Tệ hơn: chạy npm list plain-crypto-js sẽ báo version 4.2.0 — version "an toàn" — khiến IR team dễ kết luận nhầm là máy không bị.

Bằng chứng duy nhất còn lại là sự tồn tại của thư mục node_modules/plain-crypto-js/ — vì package này không phải dependency của bất kỳ phiên bản axios chính thức nào. Tìm thấy nó = đã bị.

So với các vụ tương tự

So với chiến dịch Shai-Hulud (compromise 600+ package), Axios có blast-radius-per-package lớn hơn nhiều và mức độ tinh vi vận hành cao hơn hẳn: pre-stage, double-obfuscation, ba RAT implementation song song, anti-forensics, self-destruct. Microsoft và Google đồng thuận đây là tác phẩm của Bắc Triều Tiên — Sapphire Sleet (theo MSTIC) hoặc UNC1069 (theo GTIG), actor đã hoạt động từ 2018–2020, chủ yếu tài chính (ăn cắp ví crypto, target VC & blockchain).

UNC6780/TeamPCP — cũng NK-nexus — gần đây poison LiteLLM, Trivy, Checkmarx với SANDCLOCK credstealer. Nói cách khác: chuỗi cung ứng open-source đang là chiến trường địa chính trị.

Ai cần lo

  • CI/CD pipeline không pin version chạy npm install định kỳ hoặc theo commit — đặc biệt các job chạy đêm giờ UTC trùng cửa sổ 00:21–03:29.
  • Developer đã chạy npm install / npm update trong cửa sổ này.
  • Ai dùng npx <pkg>@latest — non-deterministic, không có lockfile trace.
  • Project depend vào @qqbrowser/openclaw-qbot hoặc @shadanai/openclaw — exposure không phụ thuộc cửa sổ thời gian.

Theo Unit 42, victim thực tế trải dài high-tech, higher education, insurance, media, medical, retail — khắp US, châu Âu, Trung Đông, Nam Á, Úc. Dù actor chủ đích nhắm finance/crypto, RAT đã beacon từ mọi nơi.

Khắc phục & hardening

Nếu nghi ngờ bị:

  1. Grep lockfile (package-lock.json, yarn.lock, bun.lock) cho axios@1.14.1, axios@0.30.4, hoặc plain-crypto-js (bất kỳ version).
  2. Kiểm tra thư mục node_modules/plain-crypto-js/ — tồn tại = đã dính.
  3. Đối xử như full system compromise. Đừng clean in-place — rebuild từ image sạch.
  4. Rotate mọi credential (từ máy sạch khác): npm token, AWS/GCP/Azure keys, SSH keys, GitHub PAT, Docker config, .env, DB connection strings, session cookies.
  5. Check network log outbound tới sfrclak.com hoặc 142.11.206.73:8000.

Hardening dài hạn:

  • Pin exact version — bỏ ^~: "axios": "1.14.0".
  • Force transitive: "overrides": { "axios": "1.14.0" }.
  • Dùng npm ci --ignore-scripts trong CI — chặn postinstall hook từ gốc.
  • Bật cooldown / min-release-age: npm v11.10+ minimumReleaseAge, pnpm v10.16+ tương tự, Yarn v4.10+ npmMinimalAgeGate, Dependabot cooldown.
  • Block DNS/proxy: sfrclak.com, 142.11.206.73.
  • Sandbox dev environment, vault secret (aws-vault / OS keychain), deploy EDR.
  • Với maintainer high-download package: publish qua OIDC từ CI, bật 2FA bằng hardware key, không giữ long-lived token trên máy cá nhân.

Axios sẽ làm gì tiếp theo

Đội axios công bố lộ trình: wipe toàn bộ thiết bị + reset credential; chuyển hẳn sang OIDC Trusted Publisher flow (publish gắn chặt với GitHub Actions workflow qua chữ ký, không còn token long-lived); thiết lập immutable release; review tất cả GitHub Actions theo best practice; hợp tác với OpenJS Security Working Group. Publish trực tiếp từ máy cá nhân — mà vụ này đã khai thác — sẽ chấm dứt.

Bài học lớn hơn: các package top npm giờ là active target cho social engineering cấp quốc gia. Một maintainer trung thực click nhầm một update giả là đủ để lây 100 triệu máy. OIDC + publish-from-CI + hardware 2FA không còn là nice-to-have — đó là baseline mới.

Nguồn: axios post-mortem #10636, Microsoft Security, Google Threat Intelligence, Elastic Security Labs, Snyk, Unit 42, Socket, StepSecurity. Advisory chính thức: CISA Alert.