[CVE49] Microsoft Windows LNK Remote Code Execution Vulnerability - CVE-2020-1299



Shell Link là một định dạng binary file, chứa thông tin cho phép truy cập đến các đối tượng dữ liệu khác. Định dạng này có phần mở rộng là “LNK”, chúng ta thường gọi là shortcut file. Về cấu trúc của định dạng này rất phức tạp, Microsoft có cung cấp document về định dạng file LNK để tham khảo[1].

Tôi đã theo dõi các bản vá của Microsoft trong nhiều năm. Năm 2018, tôi nhận thấy Microsoft Windows bị phát hiện có 2 bug LNK đã được khắc phục và đều là lỗ hổng RCE. Gần đây @Lays đã tìm ra được 1 loạt các lỗi về parsing file LNK, với định dạng file là dạng binary này, tôi nghĩ rất phù hợp để sử dụng fuzzing. Tuy nhiên cần reverse và tìm hiểu cách xử lý file LNK này trên Windows.

Giới thiệu

File Explorer trước đây gọi là Windows Explorer (explorer.exe), là một ứng dụng quản lý tệp và được tích hợp sẵn trong các hệ điều hành Microsoft Windows từ Windows 95 trở đi. Nó cung cấp một giao diện GUI để truy cập các hệ thống tập tin. Nó cũng bao gồm nhiều thành phần giúp người dùng tương tác như taskbar, desktop,…

Explorer có rất nhiều tính năng, trải qua mỗi phiên bản hệ điều hành nó lại được Microsoft nâng cấp và phát triển thêm. Ở đây tôi phát hiện ra rằng Explorer sẽ tự động parsing file LNK nếu file LNK đó xuất hiện trong ngữ cảnh mà Explorer đang truy xuất. Ví dụ nếu chúng ta đang ở màn hình desktop, Explorer sẽ parsing các file LNK xuất hiện ở desktop và có thể sẽ ở trong 1 số thư mục thứ cấp (về độ sâu của thư mục mà Explorer có thể truy xuất thì tôi không biết).

Tìm hiểu parsing LNK file trong Explorer

Làm thế nào ta có thể xây dựng một harness để fuzz định dạng file LNK này? Chúng ta cần phải dựa vào Explorer, hãy debug thử, dùng windbg attach vào process explorer.exe:


Tôi đặt break-point tại 2 hàm là CreateFileACreateFileW, tôi đoán rằng Explorer sẽ sử dụng nó để đọc file trước khi parsing.


Sau 1 vài lần break trên hàm CreateFileW thì đến đây tôi thấy Explorer đang gọi hàm CreateFileW với file “Process Hacker 2.lnk”, file LNK này nằm ở desktop. Tôi view call stack tại điểm breakpoint này:


Chúng ta có thể thấy rằng một loạt các API liên quan đến CShellLink được gọi trong DLL windows.storage.dll. Đến đây tôi quay trở lại MSDN tìm hiểu về các api liên quan đến CShellLink này và tôi tìm thấy thông tin này[2]:


Chúng ta hoàn toàn có thể tạo chương trình parsing LNK bằng IShellLink interface. Dựa vào những thứ MSDN cung cấp tôi sử dụng IPersistFile để load và parsing LNK file, đây là harness tôi sử dụng để fuzzing.


Tôi debug thử với harness mà tôi xây dựng trên file “test_debug.lnk”


So sánh call stack giữa harness của tôi và với Explorer có vẻ khá giống nhau. Tôi quyết định sử dụng harness này để fuzzing.

Fuzzing LNK

Corpus đối với file LNK này tôi thấy cũng có sẵn khá nhiều trên các repo github. Tôi tìm và download về, sử dụng chúng để fuzz. Vẫn với winafl[3]Dynamorio[4] quen thuộc, tôi sử dụng nó với coverage_module là windows.storage.dll. Chạy fuzzing với 1 master và 7 slave. Về fuzzing và coverage mọi người có thể tìm đọc ở các blog của tôi trước đây[5][6].

Sau một thời gian chạy fuzzing, tôi quay lại kiểm tra các crash của mình, tôi nhận thấy có 4 unique crash:
  • Out of bound read ở trong windows_storage!CRegFolder::_AttributesOf
  • Out of bound read ở trong windows_storage!CRegFolder::_CreateCachedRegFolder
  • Out of bound read ở trong shell32!CControlPanelCategoryFolder::_IsValidCategoryPidl
  • Double free ở windows_storage!DSA_DestroyCallback
Tôi kiểm tra 4 crash này trên Explorer thì chỉ có lỗi double free mới gây crash cho explorer.exe, còn 3 lỗi out of bound read thì không. Tôi thông báo cả 4 lỗi này cho Microsoft nhưng Microsoft chỉ chấp nhận một lỗi double free của tôi. Họ từ chối 3 lỗi còn lại vì crash không xảy ra trên cấu hình mặc định của hệ thống và không thể khai thác được (về phần cấu hình mặc định của hệ thống tôi nghĩ do harness tôi sử dụng không đầy đủ như Explorer khi parsing 1 file LNK).

CVE-2020-1299

Lỗi double free tôi tìm thấy ở trên đã được fix trong bản vá tháng 6 của Microsoft, dưới đây là 1 chút về nguyên nhân gây ra lỗi này:

Chúng ta có struct DSA như sau:

DSA object:

struct DSA
{
  INT nItemCount;
  LPVOID pData;
  INT nMaxCount;
  INT nItemSize;
  INT nGrow;
};

DSA object[7] được khởi tạo tại DSA_Create[8] và insert thêm các item với hàm DSA_InsertItem[9]. Trong khi insert thêm các item, nó sẽ cấp phát một vùng nhớ cho trường pData trong struct DSA.

Khi giải phóng DSA, chương trình đã gọi hàm DSA_DestroyCallback hai lần để giải phóng cùng một object DSA


Hàm s_DestroyCacheItemCB xảy ra lỗi:


Khi tiến hành giải phóng trường pData, nó đã không kiểm tra sự khả dụng của vùng nhớ này dẫn đến lỗi double free. Vùng nhớ pData đã được free ở trước đó, tôi không phân tích sâu hơn về nguyên nhân tạo sao một object DSA lại bị destroy 2 lần có thể do một điều kiện nào đó trong luồng xử lý trước.

Đối với lỗi này chúng ta có thể sử dụng use after free trên object DSA để trigger RCE.

Kết luận

Trên đây là toàn bộ quá trình tôi nghiên cứu tìm ra một attack surface đối với file LNK, áp dụng fuzzing để tìm lỗi của quá trình parsing LNK. Tại thời điểm tôi tìm lỗi này, tôi chỉ nhắm mục tiêu vào DLL windows.storage.dll mà không biết rằng LNK còn có một loại nữa là LNK search (sau khi ZDI viết blog phân tích một lỗi của @Lays tôi mới biết định dạng này)[10]. Ngoài windows.storage.dll sử dụng để parsing LNK file mà còn có windows.storage.search.dllStructuredQuery.dll. Trong bài blog tới, tôi sẽ nói về 1 số lỗi tôi tìm thấy trong StructuredQuery.dll nhưng Microsoft không sửa mặc dù nó có thể gây ra DOS tạm thời. Microsoft gợi ý tôi viết blog nói về các lỗi đó và họ tự tin rằng có thể trả lời mọi câu hỏi của khách hàng với những lỗi họ không fix.




Click here for English version


Researcher: linhlhq from Infiniti Team - VinCSS (a member of Vingroup).

No comments:

Post a Comment