Big Fish: Khai thác lỗ hổng kernel 37 năm tuổi trong OpenBSD để leo thang đặc quyền root

Công nghệ16 tháng 10, 2026·8 phút đọc

Công ty bảo mật Calif đã công bố nghiên cứu khai thác thành công lỗ hổng kernel tồn tại gần 37 năm trong OpenBSD, bắt nguồn từ bộ đếm gắn kết bộ nhớ chia sẻ được định nghĩa là số nguyên 16-bit có dấu. Bằng cách kết hợp tràn số nguyên, race condition và ghi ngoài biên, nhóm nghiên cứu đã đạt được quyền root cục bộ ngay cả khi KARL, SMAP và SMEP đều được bật.

Big Fish: Khai thác lỗ hổng kernel 37 năm tuổi trong OpenBSD để leo thang đặc quyền root

Big Fish: Khai thác lỗ hổng kernel 37 năm tuổi trong OpenBSD để leo thang đặc quyền root

Công ty nghiên cứu bảo mật Calif vừa công bố chi tiết về một lỗ hổng kernel tồn tại gần 37 năm trong OpenBSD, bắt nguồn từ mã nguồn BSD gốc. Bằng cách kết hợp tràn số nguyên, race condition và ghi ngoài biên, nhóm nghiên cứu đã giành được quyền root cục bộ ngay cả khi hệ thống đã bật KARL, SMAP và SMEP. Đây là minh chứng rõ ràng cho thấy những lỗi tưởng chừng vô hại trong thiết kế dữ liệu có thể tồn tại hàng thập kỷ và trở thành mục tiêu khai thác nghiêm trọng.

Minh họa nghiên cứu bảo mật kernel OpenBSDMinh họa nghiên cứu bảo mật kernel OpenBSD

Nguồn gốc lỗ hổng: Một bộ đếm short 16-bit

Câu chuyện bắt đầu từ một chi tiết rất nhỏ trong mã nguồn BSD gốc: bộ đếm gắn kết bộ nhớ chia sẻ shm_nattch được định nghĩa là kiểu signed 16-bit short thay vì kiểu số nguyên lớn hơn.

  • Khi một tiến trình gắn (attach) vào một phân đoạn bộ nhớ chia sẻ System V, bộ đếm này sẽ tăng lên.
  • Giá trị tối đa của short có dấu là 32.767.
  • Nếu bạn liên tục fork tiến trình và gắn vào bộ nhớ chia sẻ, bộ đếm sẽ vượt qua ngưỡng này và tràn sang giá trị âm.

Sự tràn số nguyên này không chỉ gây lỗi hiển thị — nó mở đường cho use-after-free thông qua một race condition, và khi kết hợp với một lỗi ghi ngoài biên khác trong kernel, kẻ tấn công có thể leo thang đặc quyền lên root.

Các lỗi này đã được vá trong errata 026 và errata 018 của OpenBSD.

Chuỗi khai thác: Từ bộ đếm âm đến root shell

Điều đáng chú ý là nhóm Calif đã khai thác thành công mà không cần lỗ hổng rò rỉ thông tin (info leak) — một điều kiện thường được coi là bắt buộc để vượt qua các cơ chế bảo vệ hiện đại. Dưới đây là tóm tắt các bước chính:

  1. Ánh xạ 127 phân đoạn bộ nhớ chia sẻ SysV, giải phóng chúng bằng munmap(), rồi fork 259 tiến trình để bộ đếm gắn kết tràn xuống giá trị -32.643.
  2. Dọn dẹp và sắp xếp heap, sử dụng các chỉ số từ sysctl(KERN_MALLOCSTATS) như kb_total và kb_totalfree để theo dõi cấp phát và xác định thời điểm cấp trang mới.
  3. Thắng race condition bằng cách dùng IPC_RMID ở một tiến trình trong khi tiến trình hỗ trợ gắn vào một phân đoạn bộ nhớ chia sẻ bị nhầm kích thước.
  4. Căn chỉnh cấp phát socket IP và đặt IP_MULTICAST_TTL về giá trị khác mặc định là 2, từ đó cấp phát cấu trúc struct ip_moptions 16 byte cố định. Từ cuối cùng của cấu trúc này đóng gói hai bộ đếm 16-bit: imo_num_memberships | imo_max_memberships << 16.
  5. Kích hoạt ghi ngoài biên -1 có điều kiện để ghi đè cả hai trường đếm thành viên.
  6. Đóng socket để duyệt qua các thành viên nhóm, kích hoạt giảm nguyên tử làm thay đổi ánh xạ PTE tự tham chiếu hai lần — chuyển nó từ ánh xạ người dùng sang ánh xạ giám sát, từ đó thực thi mã kernel bằng cách ghi đè một con trỏ kernel có thể dự đoán.
  7. Dọn dẹp kế toán bộ nhớ chia sẻ và khôi phục con trỏ kernel đã bị ghi đè để giành được root shell.

Điểm mấu chốt về mặt kỹ thuật: lỗ hổng này đạt được thực thi mã thông qua một primitive "write-what-where", trong đó:

  • Write được gây ra bởi một TAILQ_REMOVE.
  • What là ánh xạ bộ nhớ không gian người dùng được chuyển sang chế độ giám sát để vượt qua SMAP/SMEP.
  • Where là một vị trí con trỏ kernel có thể dự đoán được.

Vì sao KARL, SMAP và SMEP không đủ?

OpenBSD nổi tiếng với KARL — cơ chế liên kết lại kernel để ngẫu nhiên hóa cả địa chỉ hàm lẫn địa chỉ dữ liệu. Tuy nhiên, theo phân tích của Calif, các bộ cấp phát bộ nhớ của kernel vẫn mang tính xác định.

  • Cả bộ cấp phát lúc khởi động lẫn bộ cấp phát best-fit đều thiếu ngẫu nhiên hóa địa chỉ cơ sở.
  • Do đó, các cấp phát sớm trong quá trình boot luôn rơi vào cùng vị trí dự đoán được trên cùng một phần cứng.
  • Khi những cấu trúc có thể dự đoán này chứa con trỏ hàm, chúng trở thành mục tiêu lý tưởng.

Trong trường hợp cụ thể, exploit nhắm vào các ioctl handler của lo0 và enc0: ghi đè một trong số đó để chiếm luồng điều khiển, và dùng cái còn lại để khôi phục con trỏ đã bị ghi đè.

Cơ chế phòng thủ và khai thác kernelCơ chế phòng thủ và khai thác kernel

Các biện pháp giảm thiểu được đề xuất

Ngoài các bản vá errata đã phát hành, Calif đề xuất một loạt biện pháp để loại bỏ hoặc giảm khả năng tái sử dụng các hướng khai thác tương tự:

1. Safe-unlinking

Kỹ thuật này kiểm tra bất biến của danh sách liên kết đôi trước khi thực hiện ghi bộ nhớ unlink. Nếu bất biến bị vi phạm, hệ thống gọi abort() hoặc panic() tùy vào chế độ người dùng hay kernel, ngăn chặn hỏng bộ nhớ.

FreeBSD và NetBSD đã triển khai safe-unlinking trong sys/queue.h của họ.

2. Ngẫu nhiên hóa địa chỉ ánh xạ PTE tự tham chiếu

Thay vì cố định vị trí, có thể dùng một byte ngẫu nhiên từ .openbsd.randomdata để chọn slot PML4 (320 + (random_byte & 127)) tại thời điểm boot, rồi cài entry đệ quy tại đó. Khi ấy PTE_BASE trở thành biểu thức runtime, cho ra các địa chỉ cơ sở từ 0xffffa00000000000 đến 0xffffdf8000000000 với bước 512 GiB. Nếu không phù hợp với ngẫu nhiên hóa, có thể đánh dấu PTE là read-only và thay đổi CR0.WP khi ghi.

3. Phi xác định hóa bộ cấp phát kernel

Có hai hướng tiếp cận:

  • Ngẫu nhiên hóa địa chỉ cơ sở tại thời điểm boot — gần như miễn phí vì khoảng trống tạo ra vẫn dùng được về sau.
  • Ngẫu nhiên hóa thứ tự cấp phát ở runtime — có overhead nhất định nhưng có thể giảm bớt nhờ thay đổi allocator sâu hơn.

Chỉ cần một trong hai là đủ để phá vỡ vị trí con trỏ dự đoán được, không cần thêm lỗ hổng phần mềm hay phần cứng.

4. Tính năng compiler

  • Array bounds sanitizer (tập con của Undefined Behaviour sanitizer) có thể loại bỏ truy cập ngoài biên với mảng kích thước cố định. Calif đã đề xuất FreeBSD bật tính năng này trong build release, đồng thời báo cáo riêng 21 vấn đề mà nó có thể phát hiện, trong đó có một số tác động leo thang đặc quyền cục bộ.
  • Alloc Tokens — tính năng compiler sắp ra mắt — giúp cô lập các cấp phát chứa con trỏ khỏi các cấp phát không chứa con trỏ, khiến các lỗi tràn từ vùng không chứa con trỏ khó khai thác hơn, đồng thời khiến các lớp lỗ hổng như use-after-free không thể dùng vùng cấp phát loại đối lập làm bộ nhớ thay thế.

Tổng kết về phòng thủ chủ động trong kỷ nguyên AITổng kết về phòng thủ chủ động trong kỷ nguyên AI

Góc nhìn cho người dùng và quản trị viên Việt Nam

Với các tổ chức tại Việt Nam đang vận hành OpenBSD cho router, tường lửa, máy chủ DNS hoặc hạ tầng VPN — đặc biệt trong các hệ thống ngân hàng, viễn thông và cơ quan nhà nước — sự kiện này là lời nhắc quan trọng:

  • Cập nhật errata ngay lập tức. Các bản vá 026 và 018 đã khắc phục lỗ hổng này.
  • Không nên coi KARL, SMAP, SMEP là bảo hiểm tuyệt đối. Chúng nâng cao chi phí tấn công nhưng không loại bỏ hoàn toàn khả năng khai thác khi allocator còn mang tính xác định.
  • Ưu tiên phòng thủ chủ động, đúng với triết lý mà OpenBSD theo đuổi từ khi thành lập: kiểm tra mã nguồn, thay thế API không an toàn, băm mật khẩu tiên tiến, phân tách đặc quyền, sandboxing, ASLR và W^X.

Kết luận

Khi các công cụ phát hiện lỗ hổng hỗ trợ bởi AI ngày càng phổ biến, việc giảm thiểu toàn bộ lớp lỗ hổng thay vì vá từng lỗi riêng lẻ trở nên cấp thiết hơn bao giờ hết. Như Calif nhấn mạnh: đầu tư vào cải thiện phòng thủ trước khi lỗ hổng bị khai thác sẽ nâng cao rào cản cho kẻ tấn công, đảm bảo khả năng phục hồi lâu dài.

Trường hợp Big Fish là minh chứng sống động rằng một dòng khai báo short sai từ 37 năm trước vẫn có thể đe dọa an ninh hệ thống ngày nay — và rằng trong bảo mật, chi tiết nhỏ nhất cũng có thể là cá lớn.

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