4 điểm bởi GN⁺ 2025-12-15 | 3 bình luận | Chia sẻ qua WhatsApp
  • Gói npm độc hại Shai-Hulud 2.0 đã lây nhiễm máy của lập trình viên và chiếm quyền truy cập tổ chức GitHub của Trigger.dev
  • Việc lây nhiễm bắt đầu khi lập trình viên chạy pnpm install, khiến script preinstall của gói độc hại được thực thi; công cụ TruffleHog được dùng để đánh cắp thông tin xác thực
  • Kẻ tấn công đã clone 669 repository trong 17 giờ, sau đó trong 10 phút cố gắng force-push vào 199 nhánh và đóng 42 PR
  • Các gói và hệ thống production không bị xâm hại, cuộc tấn công được phát hiện trong 4 phút và quyền truy cập tài khoản đã bị chặn
  • Sau sự cố, hệ thống bảo mật được tăng cường bằng cách vô hiệu hóa script npm, nâng cấp lên pnpm 10, triển khai npm bằng OIDC, và áp dụng bảo vệ nhánh trên toàn bộ repository

Tổng quan cuộc tấn công

  • Ngày 25 tháng 11 năm 2025, trong lúc debug nội bộ trên Slack, xuất hiện dấu hiệu bất thường khi nhiều repository có commit “init” dưới danh nghĩa Linus Torvalds
  • Kết quả điều tra xác nhận sâu chuỗi cung ứng Shai-Hulud 2.0 đã lây nhiễm máy của lập trình viên và đánh cắp thông tin xác thực GitHub
  • Báo cáo cho biết con sâu này đã lây nhiễm hơn 500 gói npm và ảnh hưởng tới hơn 25.000 repository
  • Các gói npm chính thức của Trigger.dev (@trigger.dev/*, CLI) không bị lây nhiễm

Dòng thời gian tấn công

  • 04:11 UTC ngày 24 tháng 11: bắt đầu phát tán gói độc hại
  • 20:27 UTC: máy của một lập trình viên tại Đức bị lây nhiễm
  • 22:36 UTC: kẻ tấn công truy cập lần đầu và bắt đầu clone hàng loạt repository
  • 15:27~15:37 UTC (ngày 25 tháng 11): thực hiện cuộc tấn công phá hoại trong 10 phút
  • 15:32 UTC: phát hiện bất thường và chặn truy cập trong vòng 4 phút
  • 22:35 UTC: hoàn tất khôi phục toàn bộ các nhánh

Quá trình lây nhiễm

  • Khi lập trình viên chạy pnpm install, script preinstall của gói độc hại được thực thi để tải xuống và chạy TruffleHog
  • TruffleHog quét token GitHub, thông tin xác thực AWS, token npm, biến môi trường rồi gửi ra ngoài
  • Trên máy bị lây nhiễm đã tìm thấy thư mục .trufflehog-cache và các tệp liên quan
  • Gói gây ra lây nhiễm đã bị xóa nên không thể lần vết

Hoạt động của kẻ tấn công

  • Sau khi lây nhiễm, chúng tiếp tục trinh sát trong 17 giờ
    • Dùng hạ tầng tại Mỹ và Ấn Độ để clone 669 repository
    • Theo dõi hoạt động của lập trình viên và duy trì truy cập bằng token GitHub
    • Tạo một repository tên “Sha1-Hulud: The Second Coming”, được cho là dùng để lưu thông tin xác thực
  • Sau đó chúng thực hiện hành vi phá hoại trong 10 phút
    • Cố gắng force-push 199 nhánh trong 16 repository
    • Đóng 42 PR, một phần bị chặn bởi thiết lập bảo vệ nhánh
    • Tất cả commit đều hiển thị dưới dạng “Linus Torvalds <email> / init”

Phát hiện và ứng phó

  • Dấu hiệu bất thường được phát hiện theo thời gian thực qua cảnh báo Slack
  • Trong vòng 4 phút, quyền truy cập GitHub của tài khoản bị lây nhiễm đã bị chặn; sau đó mọi quyền truy cập vào AWS, Vercel, Cloudflare và các dịch vụ khác cũng bị thu hồi
  • Phân tích log AWS CloudTrail cho thấy chỉ có các lệnh gọi API chỉ đọc, không có truy cập dữ liệu production
  • AWS cũng phát hiện riêng hành vi đáng ngờ liên quan đến Shai-Hulud và gửi cảnh báo

Thiệt hại và khôi phục

  • 669 repository bị clone, 199 nhánh bị cố gắng force-push, 42 PR bị đóng
  • Do GitHub không có reflog phía máy chủ nên việc khôi phục gặp khó khăn, nhưng toàn bộ đã được phục hồi trong 7 giờ nhờ Event API và reflog cục bộ
  • Các gói npm và hạ tầng production không bị xâm hại

Lộ khóa GitHub App

  • Trong quá trình điều tra, một khóa riêng GitHub App đã được tìm thấy trong thùng rác trên laptop của lập trình viên
  • Khóa này có quyền read/write với repository của khách hàng và đã được xoay vòng ngay lập tức
  • Cơ sở dữ liệu (nơi lưu installation ID) không bị xâm hại nên không có bằng chứng truy cập repository khách hàng, nhưng cũng không thể loại trừ hoàn toàn
  • Nhóm đã yêu cầu GitHub hỗ trợ cung cấp thêm log và gửi email thông báo cho khách hàng

Phân tích kỹ thuật Shai-Hulud

  • Khi setup_bun.js chạy, nó cài runtime Bun và thực thi bun_environment.js ở chế độ nền
  • TruffleHog được dùng để thu thập thông tin xác thực trong thư mục $HOME
  • Dữ liệu thu thập được (contents.json, cloud.json, truffleSecrets.json, v.v.) được tải lên các repository GitHub ngẫu nhiên dưới dạng mã hóa base64 ba lớp
  • Nếu có token npm, nó sẽ chỉnh sửa và phát hành lại các gói của tài khoản bị lây nhiễm để phát tán con sâu
  • Nếu không có thông tin xác thực, nó sẽ cố gắng xóa thư mục home
  • Các tệp chỉ dấu lây nhiễm: setup_bun.js, bun_environment.js, .trufflehog-cache/ v.v.

Biện pháp tăng cường bảo mật

  • Vô hiệu hóa hoàn toàn script npm (ignore-scripts=true)
  • Nâng cấp lên pnpm 10: script bị vô hiệu hóa mặc định, thiết lập minimumReleaseAge (3 ngày) để trì hoãn cài đặt gói mới
  • Áp dụng npm Trusted Publishers dựa trên OIDC để loại bỏ token dài hạn
  • Áp dụng bảo vệ nhánh cho mọi repository
  • Triển khai Granted cho AWS SSO, mã hóa token phiên
  • Trên GitHub Actions, thay đổi để bắt buộc phê duyệt khi chạy workflow của contributor bên ngoài

Bài học cho các đội ngũ khác

  • Cơ chế thực thi mã tùy ý khi cài npm tự nó đã là một bề mặt tấn công
  • Cần đặt ignore-scripts=true và chỉ quản lý whitelist các gói thực sự cần thiết
  • Dùng pnpm minimumReleaseAge để trì hoãn cài đặt gói mới
  • Bảo vệ nhánhtriển khai dựa trên OIDC là các biện pháp bảo mật bắt buộc
  • Không lưu thông tin xác thực dài hạn trên máy cục bộ, chỉ cho phép triển khai qua CI
  • Tiếng ồn từ cảnh báo Slack lại là chìa khóa phát hiện

Khía cạnh con người

  • Lập trình viên bị lây nhiễm không có lỗi; thiệt hại xảy ra chỉ vì chạy npm install
  • Trong lúc tấn công, tài khoản đó được phát hiện đã tự động “star” hàng trăm repository ngẫu nhiên
  • Sự cố này không phải lỗi cá nhân mà phơi bày lỗ hổng cấu trúc của cả hệ sinh thái

Chỉ số tóm tắt

  • Từ lúc lây nhiễm đầu tiên đến đợt tấn công đầu tiên: khoảng 2 giờ
  • Thời gian kẻ tấn công duy trì truy cập: 17 giờ
  • Thời gian phá hoại kéo dài: 10 phút
  • Mất 5 phút để phát hiện, 4 phút để chặn
  • Hoàn tất khôi phục toàn bộ trong 7 giờ
  • Repository bị clone: 669 / Nhánh bị ảnh hưởng: 199 / PR bị đóng: 42

Tài nguyên tham khảo

  • Socket.dev: Shai-Hulud Strikes Again V2
  • Báo cáo phân tích từ PostHog, Wiz, Endor Labs, HelixGuard
  • Tài liệu về npm Trusted Publishers, pnpm onlyBuiltDependencies, minimumReleaseAge, Granted

3 bình luận

 
click 2025-12-15

Có vẻ như pnpm vốn có cấu trúc mà theo mặc định phải cho phép từng post-install riêng lẻ, nhưng rồi cuối cùng nhà phát triển cũng vô thức cho phép luôn.

 
lamanus 2025-12-16

Tôi hiểu là vì npm được đặt để chạy mặc định, nên họ đã chuyển sang pnpm và tắt mặc định đó để tăng cường bảo mật.

 
GN⁺ 2025-12-15
Ý kiến Hacker News
  • Chạy npm install không phải là cẩu thả
    Vấn đề là hệ sinh thái cho phép thực thi mã tùy ý trong quá trình cài đặt package
    Nhưng thất bại bảo mật ở mức nền tảng là việc dùng một package manager cho phép bên thứ ba đẩy mã vào sản phẩm của tôi mà không có bất kỳ bước kiểm chứng nào
    Rốt cuộc chúng ta đang phụ thuộc vô hạn vào thiện chí và năng lực của các package manager và những người vận hành chúng
    Ngoài ra có vẻ OP đang ám chỉ rằng thông tin xác thực đã được lưu ở dạng văn bản thuần trên filesystem

    • Tôi nghĩ cả hai đều là vấn đề
      Ở cấp độ ngôn ngữ, có thể xây dựng một cấu trúc giới hạn mã chỉ được đọc input, tiêu tốn tài nguyên và chỉ tạo ra output đúng kiểu
      Không giải quyết trọn vẹn bài toán chuỗi cung ứng, nhưng sẽ giảm phạm vi phơi nhiễm đi rất nhiều
    • Lối lập luận này quá vòng tròn
      Kiểu như “một người dùng công cụ này thì không sai, nhưng nếu ai cũng dùng thì hệ sinh thái có vấn đề”
      Việc nhiều công cụ cho lập trình viên vốn đã kém an toàn là điều đã được chứng minh nhiều lần
      Nếu thực sự quan tâm thì phải thể hiện bằng hành động
    • IDE plugin cũng vậy
      Khi dùng VS Code, thật phiền khi chỉ để thêm một chức năng nhỏ mà phải cài một plugin không rõ do ai làm ra
    • Tôi tò mò không biết có thể thiết kế package manager ngăn việc thực thi mã của bên thứ ba như thế nào
      Cuối cùng chẳng phải vẫn sẽ là một cấu trúc buộc phải chạy mã mà ta phải tin tưởng sao
    • Một số công cụ chỉ hỗ trợ file netrc cho xác thực http
      Nếu dùng git qua http thì kiểu đường lộ thông tin xác thực dạng plain text này gần như luôn tồn tại
  • Một năm trước, maintainer của pnpm đã đề xuất “chặn script post-install theo mặc định
    Từ góc nhìn người dùng thì sẽ bất tiện, nhưng về lâu dài tôi tin đây là thay đổi mà mọi người đều sẽ thấy đáng giá
    PR liên quan: pnpm/pnpm#8897

    • Vậy mà cùng một vấn đề vẫn đang lặp lại
      Rốt cuộc đây lại là thêm một ví dụ nữa cho thấy tính tiện lợi đã thắng bảo mật
  • Họ nói “cơ sở dữ liệu không bị xâm phạm”, nhưng nếu kẻ tấn công đã truy cập được AWS và secret thì theo tôi như vậy đã là bị xâm phạm rồi
    Nếu đã có khả năng truy cập thì nên được coi là xâm phạm

    • Nếu có log truy cập tài nguyên AWS, và không có dấu vết truy cập nào trước khi thu hồi quyền, thì có thể xem dữ liệu là an toàn
  • Sau khi mã độc được thực thi thì gần như không thể truy vết nguồn gốc
    pnpm install cũng hoàn tất bình thường nên rất khó phát hiện
    Nếu có EDR như Sentinel One hay CrowdStrike thì có lẽ đã có thêm nhiều đầu mối để điều tra

    • Nếu có EDR thì rất có thể chuỗi tấn công Trufflehog đã bị phát hiện hoặc chặn lại
  • Đoạn “đã clone tổng cộng 669 repo” khá gây chú ý
    Tôi thắc mắc một công ty có chưa đến 100 nhân viên mà có hơn 600 repo thì có bình thường không

    • Hoàn toàn bình thường. Repo là gia súc chứ không phải thú cưng
    • Tổ chức của chúng tôi có 7 người mà có 365 repo trên GitHub
      So với quy mô đội ngũ thì thâm niên và vòng đời dự án ảnh hưởng đến số lượng repo nhiều hơn
    • Nếu có một kiến trúc sư mê microservice thì sẽ ra kết quả như vậy
  • pnpm đã ngừng tự động chạy lifecycle script như preinstall rồi, nên có vẻ họ đã dùng bản cũ
    Xem PR liên quan

    • Cuối bài có nhắc là họ đã cập nhật lên major version mới nhất
    • Tôi cứ nghĩ đó là lý do chính để dùng pnpm nên thấy hơi khó hiểu
    • Nếu rốt cuộc họ đã cập nhật dependency bằng một package manager phiên bản cũ, thì đây là trách nhiệm của họ
    • Cũng có thể chính project có script postinstall
      pnpm chặn script của dependency, nhưng vẫn chạy script ở cấp project
  • Cảm ơn vì đã chia sẻ bản phân tích sự cố sau sự việc (post-mortem) một cách minh bạch
    Những trường hợp như thế này rất quan trọng với toàn ngành
    Tôi tò mò liệu traffic tấn công có thể phân biệt được với traffic phát triển thông thường hay không
    Bên tôi cũng đang muốn siết chặt egress filtering trong môi trường dev, nhưng npm install hay bị hỏng nên vẫn đang đau đầu

    • Có vẻ dùng tính năng danh sách IP được phép của GitHub Organization sẽ giúp phòng thủ trước kiểu tấn công này
  • Tôi đang suy nghĩ về bảo mật git trên laptop cá nhân
    Hiện tại tôi để SSH key ở máy local và push
    Tôi cũng có quyền admin nên khá rủi ro. Muốn biết có cách nào an toàn hơn không

    • Tôi khuyên dùng 1Password để quản lý SSH key và chữ ký Git, rồi dùng GitHub OAuth hoặc đăng nhập qua CLI cho push/pull
      Làm vậy có thể tách riêng khóa ký và khóa truy cập, còn tài khoản quản trị thì có thể quản lý riêng
      Tài liệu liên quan: 1Password SSH Agent, Git Commit Signing, GitHub OAuth, GitHub CLI Login
    • Tôi lưu khóa riêng SSH trong TPM và dùng nó từ SSH agent qua PKCS11
      Ưu điểm là khóa không thể bị đưa ra ngoài, nhưng nếu mã độc chạy trên máy của tôi thì vẫn còn rủi ro
      Ví dụ Linux, ví dụ macOS
    • Nếu laptop đã bị nhiễm mã độc thì không còn cách phòng thủ nào
      Có thể dùng TPM hay Yubikey để làm cho việc tấn công khó hơn đôi chút, nhưng không thể phòng thủ hoàn toàn
      Tác vụ quản trị nên được thực hiện trên một máy chuyên dụng riêng
    • Cũng có cách đưa GPG key vào Yubikey rồi dùng gpg-agent cho xác thực SSH
      Khi push hay commit thì nhập PIN để mở khóa
    • Nếu chặn push trực tiếp lên nhánh main và bắt buộc MFA, thì kẻ tấn công sẽ khó tiếp cận ngay được nhánh triển khai hơn
  • Commit mang tên Torvalds là một dấu hiệu nhận diện (signature) thường thấy sau khi bị lây nhiễm
    Bài phân tích chính thức của Microsoft cũng có nhắc tới
    Con worm này rất ồn ào, và một số kẻ tấn công đã lợi dụng thông tin xác thực bị lộ để biến repo riêng tư thành công khai hoặc chỉnh sửa readme nhằm mục đích quảng bá

  • Tôi tự hỏi nếu kẻ tấn công không phá hoại gì mà chỉ âm thầm lấy cắp thông tin thì liệu kiểu phát hiện này có còn khả thi không