- Commit
0226b56của rhboot/shim đã sửa CVE-2023-40547, phát sinh do tin cậy nguyên vẹn giá trị kích thước trong header HTTP trong quá trình nhận tệp - Nếu header bị chỉnh sửa chỉ định kích thước nhỏ hơn dữ liệu thực tế nhận được, shim có thể cấp phát vùng nhớ nhỏ hơn buffer cần thiết
- Mã cũ dùng giá trị header để cấp phát, nhưng khi sao chép lại dùng metadata của giao thức, có thể dẫn đến ghi vượt giới hạn (out-of-bounds write)
- Bản vá thêm kiểm tra
*buf_size < rx_message.BodyLengthtrongreceive_http_response()củahttpboot.c; nếu thất bại thì xử lý bằngEFI_BAD_BUFFER_SIZEvàInvalid Content-Length - Phạm vi thay đổi là thêm 7 dòng, xóa 1 dòng trong một tệp
httpboot.c, đồng thời sửa lỗi chính tảContent-LenghtthànhContent-Length
Luồng phát sinh lỗ hổng
- CVE-2023-40547 là vấn đề xảy ra khi shim lấy tệp qua HTTP hoặc các giao thức liên quan
- Trong quá trình cấp phát buffer để lưu dữ liệu nhận được, giá trị kích thước trong header HTTP được sử dụng
- Header HTTP có thể bị chỉnh sửa và có thể chỉ định kích thước nhỏ hơn dữ liệu thực tế nhận được
- Luồng xử lý cũ dùng giá trị header để cấp phát buffer, còn khi sao chép dữ liệu từ buffer rx thì lại dựa trên metadata của giao thức
- Sự khác biệt này có thể khiến dữ liệu lớn hơn buffer đã cấp phát bị sao chép vào, từ đó gây ra ghi vượt giới hạn (out-of-bounds write)
Nội dung bản vá
- Bản vá thêm kiểm tra phòng vệ vào
receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size)tronghttpboot.c - Khi
*buf_size == 0, bản vá sửa lỗi chính tả trong thông báo lỗi cũ rồi chuyển đếngoto errorFailed to get Content-Lenght→Failed to get Content-Length
- Kiểm tra mới xác nhận điều kiện
*buf_size < rx_message.BodyLength- Nếu điều kiện đúng, đặt
efi_status = EFI_BAD_BUFFER_SIZE - In lỗi
Invalid Content-Length - Sau đó chuyển đến
goto error
- Nếu điều kiện đúng, đặt
Phạm vi thay đổi
- Tệp được thay đổi là một tệp duy nhất:
httpboot.c - Khối lượng thay đổi là thêm 7 dòng, xóa 1 dòng
- Điểm cốt lõi là logic kiểm tra xem độ dài nội dung nhận được
rx_message.BodyLengthcó lớn hơn kích thước cấp phát*buf_sizehay không
Ghi nhận liên quan
- Commit này được đánh dấu là thay đổi nhằm khắc phục CVE-2023-40547
- Thông điệp commit nêu rõ vấn đề bắt nguồn từ việc tin cậy sai vào header HTTP
- Người báo cáo lỗ hổng được ghi nhận là Bill Demirkapi thuộc Microsoft Security Response Center
1 bình luận
Các ý kiến trên Hacker News
shim là một EFI bootloader thường được dùng trong các bản phân phối Linux muốn bật Secure Boot
Từ góc nhìn của bản phân phối, họ muốn người dùng có thể bật Secure Boot dễ dàng bằng khóa ký Microsoft được cài sẵn, thay vì phải tự đăng ký khóa
Tuy nhiên, vì Microsoft thường không ký cho các bootloader GPL như GRUB, nên shim, thứ có thể được ký bằng khóa Microsoft, đã được tạo ra; shim kiểm tra chữ ký của mục tiêu mà nó sẽ khởi động bằng một khóa riêng gọi là Machine Owner Key, tức MOK
Khi chỉ định EFI binary để shim khởi động, có thể đưa vào HTTP URL; nếu HTTP server lúc này độc hại, nó có thể gây ghi ngoài phạm vi
Tuy vậy, thông thường shim được dùng để khởi động bootloader giai đoạn 2 cục bộ như GRUB, nên trong phần lớn cài đặt, khả năng đây trở thành vấn đề có vẻ thấp
Secure Boot ngay từ đầu đã được thiết kế để có thể thu hồi cả các binary đã được ký thông qua danh sách DBX; khi đưa danh sách này vào UEFI, binary đó sẽ bị từ chối ngay cả khi có chữ ký hợp lệ
Nếu chữ ký của các binary shim cũ có lỗi này được thêm vào danh sách, mỗi người có thể cập nhật danh sách trên thiết bị của mình; nó cũng có thể được phân phối bằng cập nhật capsule như LVFS, và nếu bạn tự quản lý khóa Secure Boot cùng các biến, bạn cũng có thể tải danh sách từ https://uefi.org/revocationlistfile rồi đăng ký
Nếu đúng như vậy thì nó đã không được xếp mức Critical
Lỗi này có thể bị khai thác khi mã độc có đặc quyền cục bộ ghi đè phân vùng EFI, khi thực hiện tấn công trung gian trong mạng lân cận có bật PXE boot, hoặc qua tấn công trung gian từ xa khi dùng HTTP boot
Một kẻ tấn công từ xa không có đặc quyền, nếu ở vị trí trung gian và máy nạn nhân dùng HTTP boot, có thể khai thác mà không cần truy cập trực tiếp
Một kẻ tấn công từ xa đã có quyền và khả năng thực thi mã trên máy nạn nhân có thể vượt qua Secure Boot nếu firmware hỗ trợ HTTP, ngay cả khi nạn nhân không dùng HTTP boot
Ví dụ, kẻ tấn công có thể thay đổi biến thứ tự khởi động để trỏ đến server do mình kiểm soát, hoặc ghi đè bootloader trên phân vùng EFI bằng các image shim và GRUB2 hợp lệ, rồi trong
grub.cfgcho chainload một shim mới qua HTTPLý do là cú pháp thiết bị của GRUB2 có thể chỉ định các thiết bị được hỗ trợ, bao gồm HTTP
Ngoài ra, một kẻ tấn công lân cận không có đặc quyền nhưng ở vị trí trung gian, nếu máy nạn nhân dùng PXE boot, có thể khai thác bằng cách nối chuỗi theo kiểu shim qua PXE → GRUB2 qua PXE → shim qua HTTP
Nguồn: https://techcommunity.microsoft.com/t5/hardware-dev-center/u...
Nếu điều khoản chống Tivoization của GPLv3 có thể yêu cầu cung cấp khóa ký Secure Boot, thì chẳng phải khóa ký MOK cũng phải được cung cấp khi có yêu cầu sao
Nếu vậy, tức là bất kỳ ai cũng có thể nhận được khóa để ký mã tùy ý sẽ được khởi động gián tiếp thông qua Secure Boot; tôi không rõ điều đó khác biệt đáng kể thế nào so với việc Microsoft chỉ cấp khóa ký cho một dự án GPLv3 như GRUB
Trên các máy BIOS cũ, tôi từng cấu hình để WBM chainload GRUB, nhưng trên máy UEFI thì chưa thử nên không biết có điểm nào bị chặn hay không
Có thể có người thắc mắc “tại sao lại khởi động từ một server không đáng tin cậy hoặc đã bị xâm nhập?”, hoặc “nếu server đã bị xâm nhập thì chỉ cần gửi binary độc hại là xong, vậy chuyện này chẳng phải vô nghĩa sao?”; nói ngắn gọn thì binary cuối cùng mà shim khởi động phải được ký bằng MOK
Vì vậy, dù khởi động trong một mạng đã bị xâm nhập, khởi động qua HTTP, hay khởi động từ một server đã bị xâm nhập, thì bất kể có HTTPS hay không, cùng mức bảo đảm bảo mật vẫn phải được duy trì
Secure Boot không ngăn hạ cấp, nên việc một server đã bị xâm nhập có thể được dùng cho tấn công hạ cấp là vấn đề tách biệt với lỗ hổng này
Dù sao thì chống tấn công hạ cấp cũng phải được triển khai riêng bằng một cách chắc chắn hơn
Tuy nhiên, tôi không rõ vì sao shim cần trực tiếp hỗ trợ HTTP boot
Lẽ ra có thể xử lý trong một EFI binary cục bộ thứ hai được ký bằng MOK, nhưng có lẽ họ đã xem đây là một tính năng tương đối đơn giản để triển khai
Tôi không hiểu vì sao đoạn mã này lại xử lý độ dài phần thân theo hai tiêu chí
Theo RFC, trong HTTP/1.1, Content-Length là thông tin có thẩm quyền về độ dài phần thân của yêu cầu/phản hồi HTTP
Dữ liệu trên đường truyền vượt quá độ dài đó, theo định nghĩa, là một phần của thông điệp khác
Ngược lại, nếu Content-Length lớn hơn
rx_message.BodyLengththì có nghĩa là chưa nhận đủ toàn bộ thông điệp, nên phải chờ thêm hoặc báo lỗi hết thời gian chờDù thế nào, nếu không có bảo đảm rằng
rx_message.BodyLengthbằng Content-Length thì đó là giá trị saiNếu muốn xử lý khoan dung hơn thì không có lý do gì phải xem header Content-Length; chỉ cần lấy
rx_message.BodyLengthlàm kích thước bộ đệm và diễn giải toàn bộ dữ liệu trên đường truyền là thông điệp đã nhậnMã hiện tại phức tạp không cần thiết, và bug kiểu này lọt vào là vì vậy
Nhìn vào đoạn mã xung quanh https://github.com/rhboot/shim/blob/0226b56513b2b8bd5fd281bc... sẽ thấy trong vòng lặp, nó nhận từng mảnh dữ liệu và mỗi lần đều kiểm tra dữ liệu mới có vượt quá dung lượng bộ đệm được xác định bởi Content-Length hay không
Nhưng trước đây nó không thực hiện kiểm tra đó với lần đọc đầu tiên nằm ngoài vòng lặp, và đó chính là bug
Tuy nhiên tôi không thấy đoạn mã kiểm tra cuối cùng xem kích thước đã tải xuống có bằng
*buf_size, tức Content-Length, hay khôngNếu điều kiện này bị phá vỡ thì có thể là dấu hiệu kết nối đã bị đóng quá sớm
Đây rõ ràng là bug và thật tốt là đã được sửa, nhưng tôi tự hỏi ai lại khởi động thiết bị của mình từ một host không đáng tin cậy
Nếu kẻ tấn công đã chiếm được dịch vụ HTTP đến mức có thể gửi header độc hại, thì việc tránh lỗi tràn này là vấn đề nhỏ nhất; chứng chỉ cũng đã bị xâm phạm, và chúng cũng có thể gửi payload hợp lệ theo chuẩn nhưng chứa mã độc
Đúng là bug, nhưng tôi không chắc nó có phải Critical hay không
Họ thực sự tin vào một chiến lược bảo mật trong đó mọi thứ có khả năng bị xâm phạm đều không được ký bởi Secure Boot
Chỉ cần có một thứ đã ký nhưng có lỗ hổng, nó có thể được dùng để giải mã đĩa mã hóa Secure Boot+TPM của tất cả mọi người
Thật khó hiểu vì sao cách tiếp cận này từng được xem là một mô hình bảo mật hợp lệ, và các lỗ hổng kiểu này thì đã có đầy rẫy
Hơn nữa, họ hoàn toàn phớt lờ con voi trong phòng là Windows
Ví dụ về kiểu tư duy này: https://lkml.org/lkml/2018/4/3/767
Bất chấp lo ngại của Linus, trong nhiều bản phân phối, nếu khởi động bằng Secure Boot thì chế độ toàn vẹn thực sự được bật
Có khả năng là do chính sách của Microsoft và việc các bản phân phối bị buộc phải làm theo quy trình được mô tả trong thread đó để nhận chữ ký Microsoft UEFI
Kết quả là khi bật Secure Boot, các tính năng của bản phân phối thường bị hạn chế, chẳng hạn như không dùng được chế độ ngủ đông
Khi có việc quan trọng, nên dùng chữ S trong HTTP
Khởi động thiết bị cũng nằm trong số đó, và header HTTPS thì luôn được mã hóa
Dù vậy, đây vẫn là một bug được tìm ra rất tốt
Header sai có thể được gửi theo cả hai cách
Vì mã hóa cần thời gian và ngày tháng chính xác
RTC có thể hợp lệ, nhưng tôi cũng không biết nó có xử lý múi giờ tốt không, và dù sao thời gian cũng có thể bị lệch
Có vẻ bạn chưa hiểu đúng vấn đề
Content-lengthkhông phải là độ dài phần thân thực tế, mà là độ dài sau Content-encodingCác bản build shim này có bao gồm httpboot không?
Theo tôi biết, shim chỉ là để chạy các EFI binary khác trên đĩa, và tôi không nhớ từng thấy tính năng khởi động qua mạng của shim thực sự được dùng
Có thể tôi không hiểu rõ, nhưng tôi tưởng hầu hết HTTP client chỉ đọc đến đúng Content-Length đã chỉ định, và coi là lỗi nếu số byte đọc được ít hơn Content-Length
Theo tôi thấy, đặc tả UEFI dường như không quy định cụ thể hành vi khi header Content-Length và độ dài phần thân phản hồi không khớp nhau
Vì vậy hoàn toàn có khả năng một số triển khai chỉ tạo yêu cầu
connection:closevà không kiểm tra Content-LengthLỗ hổng này do MSRC báo cáo và mô tả CVE không có nội dung về khai thác thực tế
Có thể sau này sẽ được công bố, hoặc cũng có thể chỉ là vấn đề mang tính lý thuyết
Có thể giải thích vì sao đọc ít hơn độ dài phần thân thực tế lại nguy hiểm không?
Tôi đã nghĩ điều ngược lại mới nguy hiểm
Nhưng kích thước lại được lấy từ header HTTP có thể bị thao túng, và kẻ tấn công có thể chỉ định một kích thước nhỏ hơn dữ liệu nhận được
Trong trường hợp này, mã dùng giá trị header để cấp phát, còn khi sao chép từ bộ đệm nhận thì dùng kích thước trong metadata của giao thức, dẫn đến ghi ngoài phạm vi