Posted on 02/06/2021 by CyTeam
Phân tích lỗ hổng IIS của Microsoft (CVE-2021-31166) có thể ảnh hưởng tới hơn 20K máy chủ tại Việt Nam
Vũ Duy Thành – CyRadar
Trong bản cập nhật tháng 5/2021 mới đây, Microsoft đã vá nhiều lỗi phần mềm chạy trên hệ điều hành Windows 10, phần lớn trong số đó có mức độ từ vừa đến nghiêm trọng, nhưng đáng chú ý nhất là lỗ hổng bảo mật CVE-2021-31166.
1. Giới thiệu về IIS và lỗ hổng CVE-2021-31166
Internet Information Service (“IIS”) hiện đang là một trong những web server phổ biến trên thế giới nói chung và Việt Nam nói riêng. Đặc điểm của nó là linh hoạt, bảo mật, dễ mở rộng và dễ quản lý. IIS hỗ trợ tất cả các công nghệ web hiện nay như HTML, ASP.NET, JSP, PHP… do đó được nhiều cá nhân, tổ chức tin dùng.
CVE-2021-31166 là một lỗ hổng tồn tại trên driver HTTP.sys được cài đặt và sử dụng bởi IIS. Kẻ xấu có thể xây dựng một gói tin đặc biệt để gửi đến server làm server ngừng hoạt động từ đó khiến dịch vụ bị ngưng trệ (tấn công từ chối dịch vụ) hoặc tệ hơn, có thể điều khiển từ xa server.
2. Tổng quan
Hình dưới mô tả tổng quan lỗ hổng CVE-2021-31166:
3. Xây dựng môi trường phân tích
- Hệ điều hành windows 10 version 2004 build 19041
- IP: 10.0.0.128/24
- Máy chủ web server IIS được cấu hình:
- Truy cập dịch vụ máy chủ cung cấp từ máy khác trên cùng mạng:
4. Proof of Concept
POC của CVE-2021-31166 rất đơn giản. Đó chỉ là một GET request đến server IIS:
Dựa vào header “Accept-Encoding” có một dấu phẩy (,) bất thường, ta có thể suy đoán lỗi có liên quan đến việc phân tích cú pháp header “Accept-Encoding”.
Ngay khi nhận được gói tin POC, server lập tức crash:
5. Phân tích crash dump
Call stack tại vị trí gây dump:
Ta thấy hàm cuối cùng trước khi kernel nhảy đến những hàm xử lý lỗi đó là hàm UlFreeUnknownCodingList của module HTTP. Chúng ta sẽ phân tích từ vị trí đó:
Đó là một chỉ thị “int 0x29” với mã lỗi 0x3 – có nghĩa kernel chủ động crash do có một cấu trúc dữ liệu bị lỗi. Truy ngược lại đoạn mã trước đó:
Hàm “UlFreeUnknownCodingList” có chức năng unlink một danh sách liên kết vòng tròn với tham số truyền vào là node đầu của danh sách. Nó sẽ thực hiện với từng node thuộc danh sách, với mỗi node nó sẽ kiểm tra tính hợp lệ của danh sách tại node đó, và sẽ tự crash nếu nhận thấy ko hợp lệ. Đó là điều đã xảy ra trong trường hợp của CVE đang được phân tích. Vậy cấu trúc danh sách liên kết được truyền vào là gì? Và tại sao nó lại bị corrupt?
6. Miêu tả quá trình gây ra lỗi
6.1. Header “Accept-Encoding” là gì
Header “Accept-Encoding” là danh sách những thuật toán mã hóa client đề nghị server sử dụng. Trong request gửi đến server, header “Accept-Encoding” có dạng:
Trong một request, có thể tồn tại nhiều dòng “Accept-Encoding”.
Sau khi nhận được đề nghị, server sẽ lọc ra những tên thuật toán hợp lệ, sau đó chọn một trong số đó để mã hóa với client rồi thông báo cho client bằng header “Content-Coding”.
Thông tin thêm về header “Accept-Encoding” có thể được tìm thấy tại rfc7231 (ietf.org)
6.2. Mô tả cách diễn ra lỗi
Server sử dụng hàm HTTP!UlAcceptEncodingHeaderHandler để parse tất cả header “Accept-Encoding” trong request. Với mỗi dòng “Accept-Encoding”, hàm trên sẽ sử dụng hàm HTTP!UlpParseAcceptEncoding.
Ví dụ một header “Accept-Encoding”:
Accept-Encoding: gzip, aaaa, bbbb;
Hàm HTTP!UlpParseAcceptEncoding sẽ thực hiện một vòng lặp, kết quả là lấy ra chuỗi “gzip” còn hai chuỗi “aaaa”, “bbbb” sẽ được đưa vào một danh sách liên kết vòng chuyên để chứa những field-value “Accept-Encoding” không hợp lệ. Node đầu danh sách được lưu trên stack và có kiểu dữ liệu LIST_ENTRY64. Những node còn lại trong danh sách có kiểu:
Sau khi phân tích toàn bộ, danh sách những cái tên không hợp lệ sẽ được free bằng cách gọi đến hàm HTTP!UlFreeUnknownCodingList với tham số là địa chỉ node đầu của danh sách. Lúc này danh sách có hình dạng như sau:
Trong khi parse một trường, nếu giá trị trả về không phải ERROR_SUCCESS, hàm HTTP!UlpParseAcceptEncoding ngay lập tức thực hiện free toàn bộ danh sách những cái tên không hợp lệ. Đây là điểm gây ra lỗi. Với một dấu phẩy (,) thừa như trong POC, chương trình đã allocate 4 node, nhưng lại chỉ có 3 cái tên, và qua các thao tác gán liên kết, HTTP!UlpParseAcceptEncoding đã liên kết các node đó với một node thuộc internal structure gây xáo trộn cấu trúc. Hình dưới sẽ mô tả:
Khi phân tích field cuối cùng trong header “Accept-Encoding” của POC, kết quả trả về khác 0, do đó hàm HTTP!UlpParseAcceptEncoding kích hoạt hàm HTTP!UlFreeUnknownCodingList với tham số truyền vào vẫn là địa chỉ của root node. Hàm HTTP!UlFreeUnknownCodingList sẽ chạy vòng lặp đi đến từng node trong danh sách. Với mỗi node, hàm sẽ kiểm tra tính hợp logic của node đó nếu phù hợp sẽ thực hiện deallocate và unlink khỏi root node. Root node được lấy từ Blink của node được deallocate, mà trong trường hợp này không phải root node thực sự mà là node trong internal struct (màu tím), vì thế Flink của root node thực sự vẫn là node 2 (vừa được deallocate) do đó địa chỉ của node 2 có nguy cơ bị free lần thứ hai.
Đoạn code kiểm tra tính hợp lệ của vị trí node trong danh sách fail do đó ngay lập tức kernel bị crash:
Với trường hợp của chúng ta, đoạn code kiểm tra tính hợp lệ không được thỏa mãn do đó kernel tự crash chính mình.
7. Đánh giá, kết luận
Số lượng server bị ảnh hưởng tính trên toàn thế giới vào khoảng 5,899,463 (theo thống kê từ Shodan Search Engine):
Số lượng server bị ảnh hưởng tính trên toàn Việt Nam vào khoảng 26,623 (theo thống kê từ Shodan Search Engine):
Tính đến thời điểm bài phân tích, trên thế giới chưa có tài liệu công bố về việc khai thác lỗ hổng CVE-2021-31166, tuy vậy, Microsoft vẫn xếp lỗ hổng vào loại “Remote Code Execution”, vì vậy vẫn có khả năng một kẻ tấn công xây dựng nên gói tin thực hiện việc khai thác thành công do đó CyRadar khuyến cáo người dùng, quản trị hệ thống cài đặt bản cập nhật trong thời gian sớm nhất có thể.
8. Tài liệu tham khảo
Zero Day Initiative — CVE-2021-31166: A Wormable Code Execution Bug in HTTP.sys