GNU IFUNC: Thủ phạm thực sự đứng sau lỗ hổng bảo mật CVE-2024-3094

Phần mềm08 tháng 5, 2026·6 phút đọc

Lỗ hổng backdoor xz-utils (CVE-2024-3094) suýt nữa gây ra thảm họa an ninh mạng toàn cầu, nhưng nguyên nhân sâu xa không chỉ nằm ở mã độc. Bài viết này phân tích cách mà cơ chế GNU IFUNC và sự phụ thuộc vào SystemD đã tạo điều kiện cho cuộc tấn công này diễn ra, đồng thời đặt câu hỏi về tính an toàn của thiết kế phần mềm hiện tại.

GNU IFUNC: Thủ phạm thực sự đứng sau lỗ hổng bảo mật CVE-2024-3094

CVE-2024-3094, thường được biết đến với tên gọi "lỗ hổng backdoor xz-utils", là một sự cố an ninh mạng "suýt soát" mang tính toàn cầu. Nếu cuộc tấn công này không được phát hiện kịp thời bởi Andres Freund, phần lớn các máy chủ SSH trên hành tinh có thể đã bị chiếm quyền kiểm soát root bởi nhóm đứng sau vụ việc.

Tuy nhiên, thay vì chỉ tập trung vào việc mã độc xâm nhập vào kho lưu trữ xz-utils như thế nào, chúng ta cần nhìn sâu hơn vào hai quyết định thiết kế lâu đời trong phần mềm mã nguồn mở đã tạo điều kiện cho cuộc tấn công này: việc liên kết OpenSSH với SystemD và sự tồn tại của GNU IFUNC.

Mối liên hệ phức tạp trong liên kết độngMối liên hệ phức tạp trong liên kết động

Chuỗi cung ứng và sự phụ thuộc ngầm

Tại sao các bản phân phối Linux lại sửa đổi OpenSSH? Câu trả lời ngắn gọn là họ buộc phải làm vậy. OpenSSH được phát triển bởi cộng đồng OpenBSD và dành riêng cho OpenBSD. Dự án Portable OpenSSH chỉ là tập hợp các bản vá nỗ lực tối đa để thay thế các thành phần đặc thù của OpenBSD bằng các thành phần POSIX chung.

Trong trường hợp của CVE-2024-3094, Fedora và Debian đã duy trì các bản vá SystemD riêng cho các nhánh của OpenSSH để khắc phục một lỗi điều kiện tranh chấp (race condition) khi khởi động lại sshd. Điều này tạo ra một chuỗi phụ thuộc phức tạp:

  1. Một số bản phân phối Linux sửa đổi OpenSSH để phụ thuộc vào SystemD.
  2. SystemD phụ thuộc vào xz-utils.
  3. xz-utils sử dụng GNU IFUNC.
  4. Do đó, xz-utils kết thúc trong không gian bộ nhớ của OpenSSH.

Sự thay đổi trong tư duy về IFUNCSự thay đổi trong tư duy về IFUNC

Vấn đề cốt lõi nằm ở sự thiếu giao tiếp giữa các nhóm phát triển. Những người vá OpenSSH cho SystemD có lẽ không biết (hoặc không quan tâm) rằng libsystemd phụ thuộc vào xz-utils, và những người phát triển SystemD có thể không biết xz-utils đã bắt đầu sử dụng ifunc. Đây là một ví dụ điển hình về việc các lỗ hổng nằm trong những "khe hở" của sơ đồ tổ chức phát triển phần mềm.

GNU IFUNC là gì và nó hoạt động ra sao?

GNU IFUNC (Indirect Functions) là một tính năng cho phép xác định tại thời điểm chạy (runtime) phiên bản nào của một hàm sẽ được sử dụng. Nó thực hiện điều này bằng cách chạy một đoạn mã tùy ý để ảnh hưởng đến cách trình liên kết (linker) giải quyết các biểu tượng.

Mục đích ban đầu của IFUNC là cho phép các chương trình kiểm tra các tính năng của CPU (ví dụ: AVX2) lần đầu tiên khi một hàm được gọi, sau đó sử dụng bản triển khai tối ưu nhất cho CPU đó.

Tuy nhiên, không có gì ngăn chặn việc chạy các đoạn mã phức tạp hơn trong bộ giải quyết (resolver) của IFUNC. Điều này có nghĩa là bạn có thể chạy mã tùy ý trước khi hàm main trong bất kỳ chương trình nào sử dụng IFUNC mà bạn đã khai báo.

Tại sao GNU IFUNC là một ý tưởng tồi?

Mặc dù được thiết kế như một công cụ tối ưu hóa hiệu suất, GNU IFUNC thực sự mang lại nhiều rủi ro hơn là lợi ích, đặc biệt là trong bối cảnh của CVE-2024-3094.

1. Quá phức tạp để sử dụng an toàn

IFUNC cực kỳ khó sử dụng đúng cách. Có quá nhiều trường hợp ngoại lệ và tài liệu chính thức thì rất khan hiếm. Điều này khiến người dùng lầm tưởng rằng việc áp dụng ifunc là đơn giản. Ngay cả các nhà phát triển GCC cũng từng gọi đây là một sai lầm và cân nhắc thêm các cảnh báo để bù đắp cho tính mong manh của nó.

2. Phá vỡ cơ chế bảo vệ RELRO

Bằng cách cho phép mã tùy ý chạy trong khi Bảng bù toàn cầu (GOT) vẫn có thể ghi được, các biện pháp bảo vệ được RELRO cung cấp trở nên vô nghĩa. Điều này vi phạm Nguyên tắc ít ngạc nhiên nhất (Principle of Least Astonishment): không một người hợp lý nào lại mong đợi việc tải một thư viện động lại làm suy yếu một tính năng bảo mật được thiết kế để bảo vệ chính nó.

IFUNC là một hướng đi khó khănIFUNC là một hướng đi khó khăn

3. Không mang lại hiệu suất vượt trội

Lập luận chính ủng hộ IFUNC thường là hiệu suất, nhưng các thử nghiệm cho thấy chi phí của chính IFUNC không hề nhỏ. Trong các bài kiểm tra so sánh, việc gọi một hàm IFUNC tốn nhiều thời gian hơn gần hai lần so với việc sử dụng con trỏ hàm (function pointer) thông thường. Đối với các hàm thực sự cần tối ưu hóa, chi phí gọi hàm thường không đáng kể so với thời gian thực thi logic bên trong, khiến lợi ích của IFUNC trở nên không rõ ràng.

Các giải pháp thay thế an toàn hơn

Có nhiều cách khác để xử lý việc lựa chọn mã dựa trên CPU mà không cần dùng đến IFUNC, tất cả đều đơn giản và an toàn hơn:

  • Con trỏ hàm toàn cục: Thay vì để trình liên kết xử lý, ta có thể giải quyết con trỏ hàm tại thời điểm chạy một cách rõ ràng. Mặc dù con trỏ có thể ghi được, ta có thể sử dụng mprotect(2) để đánh dấu chúng chỉ đọc (read-only) sau khi giải quyết.
  • Sử dụng LD_PRELOAD: Nếu bạn biết các tính năng CPU cần thiết, bạn có thể chỉ định thư viện phù hợp thông qua biến môi trường $LD_PRELOAD.
  • Tách biệt các tệp nhị phân: Đóng gói nhiều tệp nhị phân cho các kết hợp tính năng CPU khác nhau và sử dụng logic tại thời điểm cài đặt để chọn tệp phù hợp nhất cho máy chủ.

Kết luận

GNU IFUNC là một tính năng ngách của gcc/ld.so mà ít người biết đến trước khi nó được sử dụng trong CVE-2024-3094. Nó có những cạm bẫy không rõ ràng và tài liệu không đầy đủ. Bằng cách cho phép trình liên kết chạy mã tùy ý trước khi main, IFUNC làm suy yếu một trong những giả định cơ bản nhất của lập trình: việc tải một thư viện sẽ không làm thay đổi chương trình của bạn một cách vốn có.

Lợi ích hiệu suất của IFUNC là có thật, nhưng không tốt hơn đáng kể so với các giải pháp thay thế. Chúng ta nên cân nhắc vô hiệu hóa IFUNC theo mặc định trong gcc và chỉ sử dụng nó trong nội bộ glibc, nơi các nhà phát triển hiểu rõ những đánh đổi liên quan. Đối với phần mềm chung, sự an toàn của chuỗi cung ứng phải được đặt lên trên sự tiện lợi của việc tối ưu hóa vi kiến trúc.

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