- Nhà nghiên cứu chia sẻ trải nghiệm phát hiện lỗ hổng trong Nixpkgs có thể bị lợi dụng để chèn mã độc vào toàn bộ hệ sinh thái Nix
- Bài viết giải thích rủi ro mang tính cấu trúc của GitHub Actions khi dùng trigger
pull_request_target, nơi các PR từ người đóng góp bên ngoài cũng có thể làm lộ quyền nhạy cảm và secret
- Trên thực tế, tác giả đã chứng minh có thể leo thang đặc quyền trong công việc kiểm tra editorconfig và xác thực code owners bằng cách chèn lệnh và lợi dụng symbolic link
- Ngay sau khi được báo cáo, đội ngũ duy trì Nixpkgs đã nhanh chóng vá lỗ hổng, vô hiệu hóa workflow dễ bị tấn công và rà soát lại cách vận hành quyền hạn
- Bài viết nhấn mạnh tầm quan trọng của việc tách biệt dữ liệu không đáng tin với vận hành nhạy cảm, cấp quyền tối thiểu và siết chặt chính sách trong hạ tầng CI/CD của tổ chức
Khai thác toàn bộ hệ sinh thái Nix
Giới thiệu và bối cảnh
- Tại NixCon năm ngoái, nhà nghiên cứu và đồng nghiệp Lexi đã trình bày về lỗ hổng của Nixpkgs
- Lỗ hổng được phát hiện đã mở ra khả năng chèn mã độc vào toàn bộ hệ sinh thái Nix thông qua tấn công chuỗi cung ứng
- Đây là một trường hợp phản ứng cực nhanh, từ lúc phát hiện lỗ hổng đến báo cáo và khắc phục chỉ diễn ra trong vòng một ngày
- Do năm nay nhà nghiên cứu không thể tham dự NixCon, họ đã viết bài này để ghi lại chi tiết toàn bộ quá trình
GitHub Actions: mục tiêu dễ tổn thương
- GitHub Actions là hệ thống hỗ trợ nhiều tác vụ tự động hóa (CI/CD) trong kho mã nguồn
- Chỉ cần có quyền truy cập vào file workflow là việc chèn mã đã trở nên dễ dàng, vì vậy đây là mục tiêu chính của tấn công chuỗi cung ứng
- File workflow được viết bằng YAML, vốn không phải định dạng được thiết kế để thực thi, nên có thể tồn tại những lỗ hổng bảo mật khó lường
- Một ví dụ đơn giản là workflow thực thi lệnh khi có mã được push lên
Trigger pull_request_target nguy hiểm
- GitHub Actions có nhiều trigger khác nhau, trong đó pull_request_target có khác biệt lớn so với pull_request thông thường
pull_request_target mặc định có quyền read/write và quyền truy cập secret ngay cả với PR đến từ fork
- Nếu dùng trigger này sai cách, dữ liệu bên ngoài không đáng tin sẽ bị kết hợp với các quyền nhạy cảm
- Tài liệu chính thức của GitHub cũng cảnh báo rất rõ về rủi ro này
- Nhà nghiên cứu đã kiểm tra 14 workflow dùng pull_request_target trong kho Nixpkgs
Lỗ hổng editorconfig-checker
- Workflow dễ tổn thương đầu tiên được phát hiện dùng để kiểm tra quy tắc editorconfig
- Sau khi tính danh sách file thay đổi, workflow truyền chúng cho editorconfig-checker bằng
xargs
- Nếu bỏ qua cảnh báo bảo mật của lệnh xargs, sẽ xuất hiện lỗ hổng cho phép chèn các tên file được thiết kế ác ý (ví dụ:
--help)
- Khi đó có thể điều khiển editorconfig-checker theo ý muốn hoặc mở đường cho việc thực thi thêm lệnh (phân tích chi tiết hơn vẫn cần được xem xét thêm)
Lỗ hổng codeowners-validator: include file cục bộ
- Lỗ hổng thứ hai, nghiêm trọng hơn, được phát hiện trong workflow xác thực file CODEOWNERS
- Quy trình này checkout mã từ PR rồi dùng codeowners-validator để kiểm tra file
- Người gửi PR có thể thay file OWNERS bằng symbolic link để tham chiếu đến file tùy ý trong runner (ví dụ: credentials của action)
- Kết quả là nội dung của file đó sẽ được in ra log trong lúc xác thực, dẫn đến lộ GitHub token có quyền read/write
- Khi chiếm được token này, kẻ tấn công có thể push trực tiếp vào kho Nixpkgs
Biện pháp xử lý và bài học
- Sau khi nhận báo cáo lỗ hổng, maintainer của Nixpkgs là infinisil đã phản ứng ngay lập tức
- Tạm thời vô hiệu hóa các workflow dễ tổn thương
- Sửa và tách phần liên kết giữa dữ liệu không đáng tin với quyền hạn
- Sau khi vá bảo mật, đổi tên workflow để giảm vấn đề branch targeting
- Những bài học chính
- Tuyệt đối không kết hợp dữ liệu không đáng tin với secret và các tác vụ nhạy cảm, hoặc ít nhất phải cực kỳ thận trọng
- Tuân thủ nguyên tắc quyền tối thiểu
- Bắt buộc phải nắm rõ hướng dẫn chính thức về quyền trong GitHub Actions
- Nếu phát sinh lỗ hổng tương tự, quản trị viên tổ chức có thể vô hiệu hóa Actions hàng loạt bằng chính sách (bài viết có hướng dẫn cách thiết lập)
Kết luận
- Chỉ trong một ngày, nhà nghiên cứu đã phát hiện, báo cáo và tham gia vá một lỗ hổng có thể đẩy toàn bộ hệ sinh thái Nix vào nguy hiểm
- Qua đó, nhu cầu phải đặc biệt thận trọng khi dùng GitHub Actions, nhất là pull_request_target, đã được thể hiện rất rõ
- Tác giả gửi lời cảm ơn tới Intrigus của KITCTF, người đã hỗ trợ cho nghiên cứu, và tới infinisil vì đã phản ứng nhanh chóng
- Độc giả quan tâm được khuyến khích tham khảo video bài trình bày và các tài liệu bổ sung
- Nhìn chung, bài viết nhấn mạnh tầm quan trọng và các bài học thực tiễn trong quản lý bảo mật GitHub Actions
1 bình luận
Ý kiến trên Hacker News
Tôi nghĩ
pull_request_targetvề bản chất là không an toàn về mặt bảo mật, và GitHub nên loại bỏ hẳn tính năng này. Thông thường người ta nói rằng để dùngpull_request_targetan toàn thì chỉ cần không chạy mã do nhánh đó kiểm soát trong lúc xử lý, nhưng trên thực tế bề mặt tấn công rộng hơn nhiều do các vấn đề như argument injection hay local file inclusion. Hiện tại các trường hợp sử dụng hợp lệ thực sự chỉ quanh việc tự động gắn nhãn hoặc tự động để lại bình luận cho PR từ bên thứ ba. Tôi không nghĩ những việc như vậy cần mặc định cấp quyền ghi vào kho. GitHub nên có khả năng phát hành token chỉ giới hạn cho đúng tác vụ đó. Vì vậy trong zizmor tôi gắn cờ tất cả các trường hợp dùng trigger nguy hiểm nhưpull_request_target, xem zizmor dangerous triggerspull_requestkhông được kích hoạt. Khi đópull_request_targetgần như là lựa chọn duy nhất. Sẽ tốt hơn nếu github tạo một tùy chọn cho phép chạy workflow trên các PR không merge sạch, mặc định tắt và chỉ dùng cho các tác vụ như linter. Trước khi có điều đó, thật đáng tiếc là vì giới hạn này nên thực tế chỉ còn cách dùngpull_request_target. Nhân tiện, khi dùng các công cụ bên ngoài kiểu này thì nếu merge thủ công trên github sẽ phá hỏng toàn bộ flow, tuyệt đối không được merge thủ côngpull_request_targetvì hai lý do. Thứ nhất, workflow luôn chạy dựa trên main nên có thể chặn mã test chưa được kiểm chứng. Thứ hai, trong sub claim của JWT bên trong workflow,job_workflow_refđược cung cấp một cách quyết định, cho phép kiểm soát truy cập chi tiết trong các hệ thống dựa trên OIDCpull_request_targetchạy trong ngữ cảnh base của PR, nên được cho là ngăn mã độc đánh cắp repo hoặc secret. Nhưng thực tế thì nếu biết việc làm rò rỉ secret dễ đến mức nào, tình huống này khá buồn cườiNếu đội Nix đã áp dụng đúng như RFC tôi đề xuất, tức là đưa vào reproducible build được ký độc lập với signed commit/review, thì những kiểu tấn công chuỗi cung ứng ở chặng cuối như thế này đã là bất khả thi. Cuối cùng thì NixPkgs muốn bất kỳ ai cũng có thể chỉnh sửa dễ dàng, và sợ rằng những nỗ lực chú trọng bảo mật sẽ làm tình nguyện viên bỏ đi, vì mục tiêu là tập trung vào một bản phân phối cho sở thích. Điều đó không sao cả, nhưng nếu muốn nói rõ đặc tính này với mọi người và bảo vệ những thứ thực sự có giá trị, thì nên ngừng dùng hoặc khuyến nghị Nix trong các môi trường bảo mật trọng yếu. Một OS dùng để bảo vệ thứ gì đó phải yêu cầu ký phần cứng hai bên nghiêm ngặt cho mọi thay đổi, và không được đặt niềm tin vào một máy đơn lẻ hay một cá nhân duy nhất. Đó là lý do tôi tạo ra Stagex liên kết Stagex liên kết Codeberg
Dù chúng ta đã thiết kế hệ thống máy tính theo cách truyền thống từ rất lâu, tôi vẫn thấy cực kỳ khó hiểu khi các workflow ngày nay vẫn cấp bearer token, kể cả token sống ngắn, cho những chương trình được tin cậy. Nếu framework của GitHub Actions chỉ cung cấp quyền truy cập vào Unix socket có đặc quyền hoặc vào
ssh-agent, thì những lỗ hổng kiểu này đã khó bị khai thác hơn nhiềuCác action CI/CD cho pull/merge request đúng là cơn ác mộng. Khi viết bước test hay bước xác minh, lập trình viên thường nghĩ theo kiểu "mã của tôi đang chạy trong ngữ cảnh tài khoản github/gitlab của tôi". Điều đó đúng với commit của bản thân hoặc của đồng đội, nhưng với PR thì pipeline CI/CD đang chạy mã không đáng tin. Rất khó lúc nào cũng nhận thức chính xác được sự khác biệt này, và lúc chỉ chạy test đơn giản hoặc linter thì còn ổn, nhưng hễ cần tích hợp với hạ tầng và cần nhiều quyền hơn thì rủi ro tăng lên rất nhanh
pull_requestvàpull_request_target). Một cái (ví dụpull_request) gần như an toàn nếu không cố tình dùng sai, còn cái kia (ví dụpull_request_target) thì gần như không thể dùng an toàn. Vấn đề lớn hơn là GitHub lại buộc những việc bình thường như gắn nhãn cho PR hay để lại bình luận tự động chỉ có thể làm bằng trigger nguy hiểm (pull_request_target), khiến mọi người buộc phải chọn phương án bất ổn. Cách GitHub Actions cung cấp chức năng có cấu trúc rất dễ gây ra sai sótTheo thời gian tôi ngày càng lo về các cuộc tấn công chuỗi cung ứng. Không chỉ ở mức "liệu chuyện này có làm mình mất việc", hay "NixOS, CI/CD, Node lại có thêm vector tấn công mới", mà là một nỗi lo mang tính triết học hơn. Càng phụ thuộc nhiều thì số vấn đề phải xử lý càng tăng là điều không tránh khỏi. Ngay cả những thứ đáng lẽ phải dùng cho thoải mái cũng đã trở nên quá rối rắm — VSCode, Emacs, Nix, Vim, Firefox, JS, Node, tất cả plugin và các gói phụ thuộc của chúng chằng chịt vào nhau. Vì thế, thật xấu hổ nhưng tôi ngày càng đi tới một kết luận kỳ quặc là chỉ khi dùng giấy và những công nghệ tối thiểu, thật sự đơn giản, tôi mới có được chút cảm giác kiểm soát hay an toàn. Tôi biết điều đó là không hợp lý, nhưng tôi ngày càng chán ngấy sự phức tạp này. Giờ tôi thậm chí còn cảm nhận được cả giới hạn chịu đựng đối với độ phức tạp
Nếu đọc phần cảnh báo trong trang hướng dẫn của xargs, có câu “xargs cannot be used securely”. Nhưng vấn đề bảo mật ở đây khác với trường hợp được áp dụng trong câu đó. Trong trường hợp lần này chỉ cần thêm
--ở cuối là đủ để tránhCâu “xargs cannot be used securely” thường bị hiểu sai. Ví dụ, nếu chạy
cat "$HOME/changed_files" | xargs -r editorconfig-checker --thì có thể xử lý được đúng vấn đề cụ thể đang nói ở đây<div>{escapeHtml(value)}</div>cho từng giá trị HTML để chặn XSS vậy. Nếu ở đâu cũng phải tự tay áp dụng cách dùng an toàn thì ngay từ đầu cách tiếp cận đó đã sai--, và phần lớn cách dùng xargs đều dễ bị argument injection. Nói cách khác, cũng giống như việc mọi lệnh thực thi vốn dĩ đều nguy hiểm. Đây không phải lỗi của riêng xargs, mà là do thực tế công cụ bị lặp đi lặp lại trong nhiều ngữ cảnh quyền hạn khác nhauBài viết đó còn có một lỗ hổng nghiêm trọng ảnh hưởng rộng hơn nhiều: trong mã PR có thể đổi file OWNERS thành symlink để lộ ra các file tùy ý như file credential của github actions. Vì git hỗ trợ commit soft link nên gần như workflow nào cũng có nguy cơ kiểu này
pull_request_target, credential là của repo đích, tức repo sẽ được merge vào. Còn nếu chạy bằngpull_requestthì đó là credential của repo nguồn do kẻ tấn công kiểm soátTin “tốt” duy nhất có lẽ là OpenBSD và NetBSD vẫn dùng CVS để quản lý gói nên không bị ảnh hưởng bởi lỗ hổng này. Tôi không rõ FreeBSD thế nào. Theo một nghĩa nào đó, bảo mật là nhờ hiệu ứng che khuất. Dù vậy có vẻ các dự án đó cũng đang cân nhắc chuyển sang git, và OpenBSD có lẽ sẽ đi theo got(1)