Nếu pipeline CI/CD của bạn giữ khoá AWS, token npm hay API key của dịch vụ AI, thì chỉ cần một tài khoản GitHub bị chiếm là toàn bộ những thứ đó có thể rời khỏi tổ chức trong vài phút. Đó chính xác là điều đã xảy ra trong đợt tấn công vừa được công bố, và nó ảnh hưởng tới cả doanh nghiệp tự phát triển lẫn doanh nghiệp thuê đơn vị khác làm phần mềm.

Chuyện gì đã xảy ra với các kho mã

Theo The Hacker News (đăng ngày 12/11/2026), dẫn phân tích của Aikido Security, kẻ tấn công bắt đầu từ thứ Ba tuần này. Chúng dùng thông tin đăng nhập bị đánh cắp của 4.837 tài khoản GitHub để cài một công cụ lấy bí mật vào các kho mà những tài khoản đó có quyền ghi. Công cụ này đã chạy trong ít nhất 1.806 kho.

Mỗi vụ bắt đầu bằng một pull request thử nghiệm tên docs: dependency audit notes, mở từ nhánh wfx-docs, nhằm kiểm tra tài khoản có ghi được vào kho hay không. Loại pull request này xuất hiện ở 61.764 kho. Nhóm SafeDep thì đếm được 8.093 kho nhận commit độc hại, và nhấn mạnh con số thật còn cao hơn vì họ chỉ quét nhánh mặc định của kho công khai.

Điểm đáng lo: sau khi đẩy mã, kẻ tấn công thường hoàn tác thay đổi trong vài phút và xoá log của lần chạy. Nhìn vào lịch sử commit, bạn có thể không thấy gì bất thường.

Mã độc lấy những gì từ runner

Công cụ này gom mọi thứ mà workflow có thể chạm tới: secrets và variables của GitHub Actions, GITHUB_TOKEN, token OIDC, khoá dịch vụ AI, và cả bí mật nằm trong bộ nhớ tiến trình của runner. Nó đọc các tệp thông tin xác thực của AWS, Azure, GCP, Kubernetes, npm, PyPI, SSH ở vị trí quen thuộc.

Nguy hiểm hơn, nó dùng chính những thứ lấy được để đi sâu vào hạ tầng đám mây. Nó đổi token OIDC của GitHub lấy phiên làm việc AWS, GCP, Azure; trên AWS có thể tạo khoá truy cập dài hạn cho người dùng IAM. Một số bước được thiết kế để tồn tại sau khi bạn xoay khoá: sửa trust policy của vai trò AWS cho phép tài khoản AWS bất kỳ đảm nhận, hoặc thêm federated credential tiền tố wfx-ci- vào ứng dụng Azure AD để workflow đăng nhập mà không cần secret.

Trong 7.309 kho, mã độc được chèn vào workflow có sẵn; 771 kho khác dùng hook postinstall trong package.json, tức là chạy cả trên máy lập trình viên khi npm install.

Kiểm tra kho của bạn từ thứ Ba trở đi

Với mọi kho mà tổ chức hoặc nhà thầu của bạn có quyền ghi, hãy tìm các dấu hiệu sau (theo danh sách trong bài của The Hacker News):

  • Nhánh wfx-docs, ci-check-NNNN, hoặc nhánh ngắn hạn dạng chore/ kèm 8 ký tự ngẫu nhiên.
  • Pull request tên docs: dependency audit notes.
  • Commit có thông điệp ci: refresh build cache (thêm khoảng 1.500 dòng vào workflow), chore: add ci helper (thêm script .ci-xxxxxx.sh và hook postinstall), chore: add dependency audit workflow, ci: restore hoặc ci: cleanup.
  • Tệp workflow lạ dạng .github/workflows/build-audit-xxxxxx.yml.

Một gợi ý hữu ích: commit của kẻ tấn công thiếu số lần chạy (run number) là dấu hiệu đáng tin hơn việc tìm các artifact tên wfx-, vì kho vẫn có thể mất dữ liệu dù không còn artifact nào.

Bạn có thể bắt đầu bằng lệnh sau trên bản sao cục bộ để xem lịch sử, kể cả nhánh đã bị xoá khỏi giao diện:

git fetch --all --prune
git log --all --since="2026-11-10" --oneline \
  --grep="ci: refresh build cache" \
  --grep="chore: add ci helper" \
  --grep="chore: add dependency audit workflow"
grep -R "postinstall" package.json

Lệnh trên chỉ tìm theo thông điệp commit đã công bố; nếu không có kết quả cũng chưa thể kết luận an toàn, hãy kiểm tra thêm nhật ký hoạt động (audit log) của tổ chức trên GitHub.

Nếu đã dính: xoay khoá theo thứ tự nào

Coi mọi secret và vai trò đám mây mà workflow đọc được là đã bị lộ. SafeDep khuyến nghị:

  1. Thu hồi token, quyền OAuth app, khoá SSH và phiên đăng nhập của tác giả commit; bật xác thực hai lớp.
  2. Xoay toàn bộ secret và variable mà kho đọc được.
  3. Máy nào đã chạy npm install trên commit bị ảnh hưởng thì coi như bị xâm nhập và xoay khoá trên máy đó.

Vì mã độc có thể để lại cửa hậu chịu được việc xoay khoá, hãy rà thêm phía đám mây:

  • Entra ID: federated credential tên wfx-ci-* mà bạn không tạo.
  • AWS CloudTrail: phiên AssumeRole hoặc AssumeRoleWithWebIdentity tên wfx-*, theo sau là CreateAccessKey hoặc UpdateAssumeRolePolicy.
  • GCP: các lệnh gọi GenerateAccessToken và SignJwt từ workload identity pool của GitHub.
  • Vault: đăng nhập JWT từ issuer OIDC của GitHub.

Giảm thiệt hại cho lần sau

Đợt này cho thấy bảo vệ tài khoản chưa đủ; cần giả định rằng một ngày nào đó workflow sẽ chạy mã không phải của mình. Vài việc nên làm trong tuần:

  • Ưu tiên OIDC với vai trò đám mây có quyền tối thiểu, thời hạn ngắn, giới hạn theo kho và nhánh, thay vì khoá dài hạn trong secret.
  • Đặt quyền mặc định của GITHUB_TOKEN ở mức chỉ đọc, chỉ nâng quyền ở từng job cần thiết.
  • Bật branch protection và yêu cầu review cho thay đổi trong .github/workflows/.
  • Tách secret theo môi trường (staging, production) và dùng environment có người phê duyệt cho bước triển khai.
  • Rà soát nhà thầu, cộng tác viên: ai còn quyền ghi vào kho, đã bật 2FA chưa.

Chúng tôi từng bàn về cách GitHub xử lý khoá bị lộ trong bài secret scanning của GitHub, và về hậu quả của bí mật nằm cứng trong ứng dụng ở vụ Wyndham lộ dữ liệu vì mật khẩu trong app. Cả hai cùng một bài học: bí mật nằm ở đâu thì kẻ tấn công sẽ tìm tới đó. Nếu bạn đang giao dự án cho đơn vị bên ngoài, xem thêm cách chúng tôi tổ chức công việc tại trang dự án của Besoft.

Việc nên làm ngay sáng nay: chọn năm kho quan trọng nhất, chạy lệnh kiểm tra ở trên, đối chiếu audit log và ghi lại danh sách secret mà từng workflow đang đọc. Nếu danh sách đó dài hơn bạn nghĩ, đó là lúc cắt bớt.

Nguồn tham khảo

  • The Hacker News, "Attacker Used 4,837 Hijacked GitHub Accounts to Run a CI Secret Stealer in 1,806+ Repositories", 12/11/2026: https://thehackernews.com/2026/11/4837-hijacked-github-accounts-ran-ci.html
  • Phân tích kỹ thuật của SafeDep: https://safedep.io/blog/github-actions-wfx-credential-stealer
  • Phân tích kỹ thuật của Aikido Security: https://www.aikido.dev/blog/compromised-github-accounts-push-ci-secret-stealer-to-tens-of-thousands-of-repositories