Phân tích CVE-2026-31431: Cơ chế "Copy Fail" và vai trò phòng thủ của Rootless Containers

Cloud & DevOps05 tháng 5, 2026·5 phút đọc

Bài viết đi sâu phân tích lỗ hổng CVE-2026-31431 trong nhân Linux, cho phép kẻ tấn công thao túng page cache để leo thang đặc quyền. Thử nghiệm thực tế chứng minh rằng kiến trúc rootless của Podman thành công trong việc ngăn chặn thoát container nhờ cơ chế User Namespace mapping.

CVE-2026-31431, còn được biết đến với tên gọi "Copy Fail", là một lỗ hổng bảo mật nghiêm trọng trong nhân Linux thu hút sự chú ý lớn của cộng đồng an ninh mạng gần đây. Lỗ hổng này cho phép kẻ tấn công thao túng bộ nhớ đệm trang (page cache) để ghi đè các tệp nhị phân hệ thống, từ đó leo thang đặc quyền.

Trong bài viết này, chúng ta sẽ cùng mổ xẻ cơ chế hoạt động của shellcode trong exploit này, thiết lập một phòng thí nghiệm để chạy thử nghiệm, và quan trọng nhất là kiểm chứng khả năng bảo vệ của kiến trúc container không root (rootless) trước cuộc tấn công này.

Phân tích Shellcode

Một điều đáng chú ý là payload được nhúng trong bản khai thác công khai thực chất không phải là shellcode thô, mà là một tệp thực thi ELF hoàn chỉnh được nén lại.

Kịch bản khai thác sẽ ghi đè phần đầu của tệp /usr/bin/su bằng tệp ELF nhỏ gọn này. Khi hệ thống thực thi lệnh su, nó sẽ tải các trang nhớ bị hỏng từ page cache và chạy mã độc thay vì tiện ích su hợp pháp.

Khi giải mã và phân tích tệp nhị phân này, chúng ta thấy các lệnh gọi hệ thống (syscall) quan trọng sau:

  • setuid(0): Đặt ID người dùng hiệu quả thành 0 (root).
  • execve("/bin/sh"): Thực thi một shell bash với quyền root vừa thu được.
  • exit(0): Thoát sạch nếu lệnh thực thi thất bại.

Kỹ thuật "ELF golfing" (tối ưu hóa kích thước ELF) được sử dụng để loại bỏ các phần tiêu đề (Section Headers), giúp nén payload xuống chỉ vài chục byte để qua mặt các bộ lọc.

Cơ chế khai thác lỗ hổng

Exploit này tận dụng API mật mã của kernel (AF_ALG socket) có sẵn cho người dùng không có đặc quyền. Quy trình hoạt động diễn ra như sau:

  1. Tạo một socket AF_ALG và liên kết với thuật toán authencesn(hmac(sha256),cbc(aes)).
  2. Sử dụng sendmsg để gửi dữ liệu độc hại (các phần của shellcode).
  3. Sử dụng splice() để chuyển các trang nhớ của /usr/bin/su vào bộ đệm của socket mật mã mà không cần sao chép vào không gian người dùng.
  4. Lỗi ghi trong bộ nhớ tạm thời (scratch write) của authencesn sẽ trực tiếp ghi 4 byte dữ liệu từ sendmsg vào page cache, hoàn toàn bỏ qua quyền truy cập tệp.

Quá trình này lặp đi lặp lại cho đến khi toàn bộ payload độc hại được chèn vào page cache. Khi su được thực thi, nó sẽ tải payload này và cấp quyền root cho kẻ tấn công.

Thiết lập Rootless Podman

Để kiểm chứng khả năng phòng thủ, tôi đã thiết lập một máy ảo Fedora 43 với nhân kernel 6.17.1 (phiên bản bị lỗi) và cài đặt rootless Podman với cấu hình sau:

  • Tạo người dùng hệ thống chuyên dụng (podman).
  • Cấp phát dải UID/GID phụ (Sub-UID/Sub-GID) lớn.
  • Sử dụng pasta cho mạng (thay thế cho slirp4netns).

Tại sao Rootless Containers lại ngăn chặn được leo thang?

Khi chạy exploit bên trong container rootless, cuộc tấn công thành công trong việc ghi đè /usr/bin/su và tạo ra một shell root bên trong container (lệnh setuid(0) trả về kết quả thành công). Tuy nhiên, quyền root này bị giới hạn bởi ánh xạ User Namespace.

Rootless Podman dựa vào tính năng User Namespace của Linux. Khi khởi động container, Podman tạo ra một không gian tên người dùng nơi UID 0 bên trong container được ánh xạ tới một UID không có đặc quyền trên máy chủ.

Trong thử nghiệm này, root bên trong container (UID 0) được ánh xạ trực tiếp tới UID 1000 trên máy chủ (tài khoản podman). Do đó, shell "root" mà exploit tạo ra không có nhiều đặc quyền hơn tài khoản người dùng podman thông thường. Nó không thể sửa đổi tệp hệ thống máy chủ, không thể truy cập /etc/shadow hay tương tác với các tiến trình bên ngoài container.

Kiểm chứng với eBPF

Để quan sát kernel xử lý các syscall mà không bị giới hạn bởi ptrace, tôi sử dụng công cụ bpftrace trên máy chủ. Kết quả cho thấy:

  • Khi không có strace: setuid(0) trả về 0 (thành công). Bên trong container, ta có root shell.
  • Khi có strace: Kernel trả về -1 (EPERM) để ngăn chặn debugger chiếm quyền kiểm soát tiến trình đặc quyền.

Điều này xác nhận rằng exploit hoạt động như dự kiến bên trong container, nhưng kết quả của nó được cô lập bởi biên giới của User Namespace.

Bằng chứng từ uid_map

Bằng chứng cuối cùng nằm trong bảng ánh xạ UID của kernel. Bên trong container rootless:

         0       1000          1
         1     100000      65536
     65537     524288      65536

Dòng đầu tiên là quan trọng nhất: 0 1000 1 nghĩa là UID 0 (root) bên trong container tương ứng với UID 1000 trên máy chủ.

Kiểm tra từ phía máy chủ, tiến trình sleep chạy trong container thực chất thuộc sở hữu của user podman, không phải root. Mặc dù có lời nhắc lệnh root@... bên trong container, mọi hành động đều bị giới hạn bởi những gì UID 1000 có thể làm trên hệ thống chủ.

Kết luận

Rootless containers đã xử lý kịch bản thoát container này đúng như mong đợi. Exploit đã đạt được leo thang đặc quyền đầy đủ trong không gian tên của container, nhưng ranh giới của User Namespace đã ngăn chặn nó gây hại cho máy chủ vật lý.

Đây chính là kịch bản mà các kiến trúc rootless được thiết kế để giải quyết. Đối với những ai đang sử dụng OpenShift, tôi khuyến nghị mạnh mẽ nên bật tính năng hỗ trợ User Namespace cho các pod. Tính năng này cung cấp sự cô lập tương tự như chúng ta đã chứng minh với rootless Podman, đảm bảo rằng các lỗi leo thang đặc quyền kernel như "Copy Fail" không thể thoát ra khỏi biên giới của pod ngay cả khi exploit thành công.

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗