Bất kỳ ai sử dụng RegEx đều tự biết bắn vào chân mình một cách dễ dàng như thế nào; nhưng, dữ liệu có thể được ghi RegEx là quá xấu để cách để RCE có thể không? Với VB Script, câu trả lời là có!
Trong bài viết này, tôi sẽ viết về những gì tôi viết là CVE-2019-0666. Bản vá bảo mật tháng 3 năm 2019 nhiều lỗi trong cùng một mã, do đó, CVE không chắc chắn.
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 3 năm 2019.
Vì vậy, nhị phân nhị phân
Chạy BinDiff giữa VBScript.dll bản vá trước và sau, chúng ta chỉ có thể thấy một số thay đổi được trả về.

Hai thay đổi trong RegExp lớp đã thu hút sự chú ý của tôi. Thật dễ dàng để thấy lỗi có thể xuất hiện như thế nào trong một tổ hợp thứ tự như cú pháp phân tích cú pháp RegEx. Please start from here.
RegExp :: AddRef

This change is rất đơn giản, nhưng yêu cầu tìm hiểu về theo dõi tham chiếu:
- Tham chiếu đến một đối tượng được tạo: tham chiếu bộ đếm được tăng thêm 1.
- Tham chiếu đến một đối tượng bị xóa: giảm chiếu tham chiếu bộ đếm 1.
- Tham chiếu bộ đếm 0: có thể xóa đối tượng.
Tracker referenced been designed to prevent chặn việc phân bổ of the object using (use the after miễn phí). Một đối tượng không bao giờ được xóa cho đến khi tất cả các tham chiếu của nó bị phá hủy.
Hàm đã cập nhật làm thoát ra thông tin nếu số lượng tham chiếu tăng lên trên 0x7FFFFFFF (cao nhất giá trị của một số nguyên có dấu). Lý do cho bản cập nhật là để ngăn chặn ẩn ẩn số nguyên.
Về mặt lý thuyết, bằng cách tạo đủ tham chiếu, mọi người có thể lặp lại tham chiếu bộ đếm về 0. Khi bộ đếm bằng 0, đối tượng có thể được giải phóng khi các tham chiếu vẫn tồn tại.
Trong thực tế, để tạo ra số nguyên, chúng ta phải tạo ra 4,294,967,296 tham chiếu. Lạc quan giả sử dụng một tham chiếu duy nhất chỉ có 4 byte, yêu cầu sử dụng khoảng 17 GB RAM. Mặc dù vậy, chúng tôi đã đạt đến giới hạn bộ nhớ thông tin từ rất lâu trước đó.
Một mình, this bug không phải là một mối đe dọa; tuy nhiên, kết hợp với một lỗi khác, nó có thể hướng dẫn sử dụng sau khi miễn phí (UAF). Ví dụ: một sự sửa chữa tài liệu tham khảo.
RegExpExec :: ReplaceUsingCallable
Lưu ý: chức năng này quá lớn để nhúng đầy đủ, vì vậy tôi chỉ lấy những thay đổi có liên quan.

Code now create a copy of memory point to a6 (object 6). Để hiểu lý do cho điều này, hãy xem tiếp phần sửa đổi.

Old code đã được xác minh rằng a6 trỏ đến Buf1. Hiện tại tồn tại một bổ sung kiểm tra để so sánh Buf1 với copyof * a6 được thực hiện trước đó (Buf2).
Sự tồn tại của xác thực mã hóa rằng a6 trỏ đến Buf1 cho chúng ta biết một điều quan trọng: a6, nên trỏ tới Buf1, nhưng không có khả năng. Hơn nữa, kiểm tra mới có ý nghĩa rằng có thể thay đổi Buf1 giữa lần đầu tiên và lần thứ hai thành Exec.
Hiện tại, tôi hoàn toàn chắc chắn rằng chúng tôi đang tìm kiếm một thứ sử dụng miễn phí. Bản cập nhật hiện đang xác minh rằng a6 vẫn còn trỏ đến Buf1 và nội dung của Buf1 không thay đổi.
Có một lý do hợp lý mà hai kiểm tra này sẽ tồn tại: Buf1 là một số bộ nhớ được cấp phát, có thể được giải phóng khi gọi đến ReplaceUsingCallable (). Giả sử Buf1 đã được giải phóng và điều gì đó khác đã được cấp phát vào vị trí của nó: * a6 sẽ vẫn trỏ tới Buf1, nhưng Buf1 hiện tại sẽ chứa khác dữ liệu. Bây giờ mã hóa đã được vá để xác thực Buf1 cũng không thay đổi.
Để hiểu thêm về bản vá lỗi, chúng ta cần hiểu về RegExp.
RegExp Replace
RegExp có một chức năng thay thế để thay thế một hoặc nhiều kết quả phù hợp của một mẫu nhất định bằng một giá trị nhất định. Lấy this code làm ví dụ:
Set regex = New RegExpregex.Pattern = "a" regex.Global = SaiMsgBox regex.replace ("aaa", "b")
Đoạn mã trên sẽ thay thế phần đầu phiên bản của “a” trong chuỗi “aaa” bằng “b”; do đó, MsgBox sẽ xuất ra "kêu gọi". Bước qua cuộc gọi, tôi thấy rằng bên trong phương thức Replace () gọi ReplaceUsingString (). Chúng tôi cần truy cập ReplaceUsingCallable () và tôi khá chắc rằng mình biết cách.
ReplaceUsingCallable
Như tên của nó, chúng ta có thể gọi Replace () bằng cách sử dụng một đối tượng có thể được gọi là tham số. Tôi đã tìm hiểu thêm để tìm ra cách thức [ 1 ].
Đoạn mã sau khi thực hiện tương tự như phiên bản trước, nhưng thay vào đó gọi là ReplaceUsingCallable ().
Set regex = New RegExpregex.Pattern = "a" regex.Global = SaiMsgBox regex.replace ("aaa", GetRef ("lolRegex")) Hàm lolRegex (singleMatch, position, fullString) lolRegex = "b" Kết thúc chức năng
Về cơ bản, bất cứ khi nào mẫu được khớp, chức năng “lolregex” sẽ được gọi (điều này được gọi là gọi lại). Lệnh callback must be return about what they want to sample is being Replace with (in the case of them we are “b”).
Bây giờ tôi thậm chí còn chắc chắn rằng đây là một số loại được sử dụng sau khi miễn phí. Tôi đoán là trong quá trình gọi lại, chúng tôi có thể giải phóng Buf1 và bổ sung thứ gì đó khác tại cùng một địa chỉ. But before, we must know Buf1 is what.
Bí ẩn của Buf1
Buf1 được truyền dưới hàm đối số dạng, vì vậy tôi đặt một điểm ngắt ở đầu ReplaceUsingCallable (). Tôi ngắt điểm của tôi đã được kích hoạt, sau đó tôi điều hướng đến bộ nhớ của Buf1.


Buf1 được bổ sung trên heap; cũng có hai chữ “a” ở cuối bộ nhớ. Tìm kiếm 'a's có thể liên quan đến RegEx mẫu của tôi, tôi đã thay đổi mẫu thành "lolregex".

Thành công! Bây giờ tôi biết rằng Buf1 có liên quan đến RegEx mẫu của tôi.
Lệnh gọi lại của tôi được gọi ở giữa ReplaceUsingCallable (), vì vậy tôi quyết định mẫu sửa đổi ở đó.
Set regex = New RegExpregex.Pattern = "a" regex.Global = SaiMsgBox regex.replace ("aaa", GetRef ("lolRegex")) Hàm lolRegex (singleMatch, position, fullString) 'Thay đổi RegEx mẫu trong quá trình gọi regex.Pattern = "Tôi có thể không được phép làm điều này" lolRegex = "b" Kết thúc chức năng

Trả về tập lệnh về lỗi mã 0x80004005. Một positional Unique returned 0x80004005;
That is verify the pointer from before that.

Tôi đặt điểm ngắt trên kiểm tra con trỏ và kiểm tra cả * a6 và Buf1. Buf1 vẫn là địa chỉ của cũ RegEx mẫu của tôi (hiện đã được bổ sung). Thật không may, * a6 hiện được đặt thành null. This error Khai thác sẽ yêu cầu cài đặt * a6 trở lại địa chỉ của Buf1.
Con trỏ xác thực vượt qua
Sau một số lượng lớn kỹ thuật đảo ngược, tôi phát hiện ra vấn đề: mặc dù công việc đặt regex.Pattern thành một giá trị mới giải phóng Buf1, nhưng nó không bổ sung một bộ đệm mới.

Tìm hiểu sâu hơn, tôi phát hiện ra chức năng RegExpComp :: Compile () chịu trách nhiệm tạo Buf1. Thật không thể, chúng tôi không thể gọi regex.compile () một cách xác định ràng buộc trong VBScript (mặc dù có thể có ở các ngôn ngữ khác).
Do thực tế biên dịch () là một VTable chức năng, tôi không thể chỉ nhìn vào XRefs để xem nó được gọi ở đâu. Thay vào đó, tôi đặt một điểm ngắt ở đầu Biên dịch (); Sau đó tôi kiểm tra callstack.

Không có gì đáng tự nhiên khi biên dịch () được gọi trong một cuộc gọi tới regex.replace (). Bằng cách thực hiện một cuộc gọi dư thừa để thay thế () bên trong lệnh gọi lại, có thể giới hạn mẫu mới biên dịch của tôi.
Set regex = New RegExpregex.Pattern = "a" regex.Global = SaiMsgBox regex.replace ("aaa", GetRef ("lolRegex")) Hàm lolRegex (singleMatch, position, fullString) regex.Pattern = "Tôi có lẽ không is allow this thing "call regex.replace (" "," ") 'biên dịch mô hình lực lượng nhưng không làm được gì cả lolRegex =" b "Kết thúc chức năng
Bây giờ khi tôi chạy lệnh của mình, kiểm tra lại không thành công, nhưng vì một lý do khác.

Vấn đề ở đây rất đơn giản: mới biên dịch mẫu phải được cấp phát ở cùng địa chỉ với cũ địa chỉ. Cannot kham nghiem tuc the heap the memory tuy nhiên, có một cách giải quyết.
Sự cố gắng khai thác ngăn xếp chồng
Tôi sẽ không đi quá sâu vào cách hoạt động của heap ở đây. Nếu bạn muốn hiểu sâu hơn, tôi khuyên bạn nên đọc bài báo gốc về “Phong thủy đống” [ 2 ].
Khi cấp phát các khối bộ nhớ nhỏ, trình phân bổ heap sử dụng một thuật toán để chọn một số không gian trống.
Heap Coaleasing
Khi giải phóng một khối, trình phân bổ heap sẽ kiểm tra xem một trong hai khối liền kề có còn rảnh hay không. Nếu hai khối lân cận còn trống, các khối này sẽ được hợp nhất thành một khối lớn hơn (liên kết lại).


Coalescing là một vấn đề khi cố gắng khai thác sử dụng sau khi miễn phí. Nếu khối chúng ta cần phân bổ lại được liên kết xuống dưới, thì bộ đệm mới sẽ được cấp phát ở địa chỉ thấp hơn.
Để kết hợp lại, chúng ta có thể sử dụng phương pháp ghép mảnh ghép (LFH).
Low parser nhóm
Fold plug-in vốn rất chậm, làm thực tế là plug-in phải tìm kiếm một khối miễn phí để phù hợp với kích thước được yêu cầu.
LFH cải thiện hiệu suất hoàn thiện hoàn thiện bằng cách nhóm các trình bổ sung có tương đồng kích thước. Giả sử sử dụng trình bổ sung 30 byte được yêu cầu: nếu có một chồng xếp chuyên nghiệp trong đó tất cả các bổ trợ là 30 byte, thì trình bổ sung có thể chỉ cần trả về đầu tiên khối miễn phí (không cần tra size). Bởi vì tất cả các khối trên LFH phải có cùng một kích thước và liên kết tính năng sẽ được vô hiệu hóa.
Windows XP trở xuống không hỗ trợ LFH. Mặc dù vậy, đây không phải là vấn đề vì các hệ thống như vậy rất dễ dàng được khai thác thông qua các kỹ thuật khai thác khác nhau trong ngăn xếp (thiếu các biện pháp giảm thiểu).
LFH tự động plug-in ngẫu nhiên
Windows 8 trở lên, một biện pháp giảm thiểu mới được giới thiệu để tạp chí bổ sung công việc khai thác UAF. LFH bây giờ không plug-in thêm các khối liên kết, thay vào đó, tự động hóa. Danh sách plugin miễn phí của các khối, chọn các ngẫu nhiên khối mỗi khi plug-in được yêu cầu.
May thay, LFH Random không phải là vấn đề đối với chúng tôi; Tôi sẽ giải thích tại sao.
Khi khai thác UAF không bổ sung đích địa chỉ, 1 trong 3 bình thường.
- bộ nhớ không được cấp phát, trong trường hợp đó, chương trình cố gắng cố gắng sử dụng bộ nhớ chưa được khởi tạo.
- bộ nhớ được cấp phát lại bởi một thứ khác, trong trường hợp đó, chương trình bị treo khi cố gắng sử dụng một số dữ liệu ngẫu nhiên.
- chương trình thực hiện kiểm tra tỉnh táo trên bộ nhớ, không an toàn nếu có điều gì đó mà bạn không muốn ở đó.
Vui lòng kiểm tra con trỏ mà chúng tôi đang cố gắng bỏ qua? Về cơ bản, nó xác nhận bộ nhớ đã được cấp phát bằng lệnh gọi tới RegExpComp :: Biên dịch (tức là chứa một mẫu RegEx hợp lệ). Nếu chúng tôi không bổ sung lại cùng một địa chỉ, chúng tôi sẽ không bỏ qua việc kiểm tra con trỏ, do đó chương trình sẽ không bị lỗi!
Tất cả những gì chúng ta cần làm là thiết lập một ngoại lệ xử lý, sau đó chỉ cần tiếp tục cố gắng bổ sung mẫu mới tại địa chỉ cũ. Ai nói kiểm tra bảo mật là không tốt cho các nhà phát triển khai thác?
'The next next that ... Khi has error. sample call regex.Replace ("", "") Next'Cố gắng kích hoạt sử dụng sau khi miễn phí lên 5000Đối với idx = 0 Đến 4999 regex.Pattern = "aaaaa" retval = regex.Replace ("aaaaabbbbb", GetRef ("lolRegex")) 'nếu hàm return to public, thì use-after-free của chúng tôi, tôi đã thành công. If kiểm tra lại, MsgBox "Số điện thoại cố gắng" & idx & "thành công!", 48, "Thành công lớn !!!" Thoát để Kết thúc nếuKế tiếpHàm lolRegex (singleMatch, position, fullString) '
Chạy lệnh mới, chúng ta sẽ nhận được một cái gì đó như thế giới này.

Bây giờ câu hỏi là, điều này hữu ích như thế nào? Để sử dụng LFH, cả cũ mẫu và mới mẫu phải có kích thước bằng nhau. Tại sao công việc thay thế một mẫu RegEx bằng một mẫu khác có cùng kích thước có thể khai thác được không? Chà, trong trường này, kích thước không quan trọng…
Một mô hình hành vi độc hại
Tôi đã tìm kiếm các bổ trợ được thực hiện dựa trên dữ liệu có trong đệm mẫu. Tiếp theo, tôi thu hồi phân bổ cho những người được thực hiện trước khi tôi thay thế đệm mẫu, nhưng được sử dụng sau đó. Tôi đã tìm thấy một.

Sau khi xem công việc biên dịch mẫu, tôi hiểu rằng “Cgrp” là nhóm số trong RegEx mẫu. Fptshop.com.vn được thực hiện trước khi gọi ReplaceUsingCallable (); làm điều đó, điều đó cũng là trong thời gian quá tôi pa-re-pa-tít tái bổ sung mô hình.
RegExp :: ReplaceUsingCallable () thực thi nhiều cuộc gọi đến một hàm được gọi là RegExpExec :: Exec (). Exec () chịu trách nhiệm thực hiện đối với thi mẫu; bên trong là đoạn mã sau.

Bộ nhớ được thực hiện dựa trên Cgrp giá trị của mẫu của tôi; mà, tôi có thể thay đổi. Nếu tôi đã biên dịch lại mẫu với một đồng kích thước, nhưng với nhiều nhóm regex hơn, tôi sẽ làm tràn ngập “group_array”.
Ví dụ: mẫu “aaaaaa” chứa 1 nhóm, trong khi “(a) (a)” chứa 2 nhóm và có cùng độ dài. Do setup memset toàn bộ đệm, kích thước của mẫu trong mỗi nhóm là không liên quan.
Yêu cầu đối với RegEx (ploit)
To done too many Xếp hạng thành công khai, với một số yêu cầu.
First, RegExpExec :: Exec () phải được gọi sau khi tôi thay thế mẫu đệm. Điều này có thể được thực hiện bằng cách đặt tùy chọn RegExp.Global thành True.
Khi Global được đặt, RegExp :: ReplaceUsingCallable sẽ thay thế mọi phiên bản của phiên bản mẫu cho. Dòng như sau.
- Call RegExpExec :: Exec () để xác định xem có bất kỳ kết quả phù hợp nào của regex mẫu trong nguồn chuỗi hay không.
- If found a match pattern found, hãy gọi lệnh callback đã được cung cấp.
- Trận đấu thay thế bằng giá trị được trả về bởi lệnh gọi lại.
- Nếu toàn cục được đặt, hãy nhập một vòng lặp thực hiện 1 - 3, cho đến khi tất cả các kết quả phù hợp được thay thế.
Những gì tôi có thể làm là đặt Toàn cầu thành Đúng, sau đó tạo ra một chuỗi khớp với mẫu ban đầu ít nhất hai lần. Khi callback được gọi lại, tôi sẽ thay thế mẫu bằng một mẫu có cùng kích thước, nhưng chứa nhiều nhóm hơn. Trong hai lần gọi tới Exec (), sẽ xuất hiện một bóng mờ.
Sau một số tính toán, tôi đã tìm thấy một mẫu đơn và nhiều nhóm có cùng kích thước bên trong. Độc hại của tôi chứa 30 nhóm, trong khi gốc chỉ chứa 1.
malicious_pattern = "(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)(a)"
original_pattern = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"Hoàn thiện mã sẽ làm tràn không hợp lệ chồng lên nhau, dẫn đến cố định chương trình. Để chứng minh, tôi xâu chuỗi lỗi với CVE-2019-0768, cho phép thi VBScript trong IE11.
Bây giờ, hãy truy cập trang sử dụng IE11 trên hệ thống không có bản vá bảo mật tháng 3 năm 2019! Tôi đã sử dụng Windows 7 vì hầu hết các sự cố đều không hoạt động trên Windows 10.

Sự kết hợp
Fptshop.com.vn Với một thủy nguyên, có thể chuyển sang đọc / ghi tùy ý, do đó RCE. Vì ràng buộc quản lý, tôi sẽ không cung cấp bất kỳ thông tin nào về cách đạt được khai thác vũ khí hóa.
Điều thú vị có thể bỏ qua lời nhắc nhở “Bật ActiveX”. Fptshop.com.vn Hay hơn nữa, công việc khai thác có thể được kích hoạt trong bất kỳ ứng dụng nào của công cụ lưu trữ kết xuất IE. Mouse drive có thể kích hoạt thực thi mã hóa từ bên trong tài liệu Office mà không cần bật macro.
Dù sao, đó là tất cả! Cảm ơn bạn đã đến với buổi nói chuyện của tôi về cách viết RegEx an toàn và bảo mật!