[Flare-On7] Chal9-crackinstaller write-up

m4n0w4r
tradahacking
Published in
10 min readOct 24, 2020

Firstly: Credit to my brother (cre4milk) for his technical review and keep me up when writing this write-up!!!!

1. Analysis

1.1. Phân tích crackstaller.exe

1.1.1. Dump credHelper.dll

Load file vào IDA, sau khi phân tích xong IDA sẽ đi tới hàm main (0x0140002124) của chương trình. Ngay tại đầu hàm, chương trình thực hiện giải mã ra một PE file ( credHelper.dll ):

Hình 1. Chương trình giải mã ra module credHelper.dll

Hàm giải mã đơn giản thực hiện cấp phát vùng nhớ có kích thước 0x1A600 bytes để lưu dữ liệu sau giải mã. Sử dụng vòng lặp xor để giải mã dữ liệu tại offset 0x17480 với key giải mã là [0x8, 0x67, 0x53, 0x9]:

Hình 2. Pseudo-code để giải mã module credHelper.dll

Sau đó, chương trình cấu thành đường dẫn để lưu file tại C:\Users\<account>\AppData\Local\Microsoft\Credentials\credHelper.dll. Sử dụng các hàm CreateFileW, WriteFile để tạo và ghi nội dung đã giải mã vào file:

Hình 3. Module credHelper được tạo ra tại thư mục C:\Users\<account>\AppData\Local\Microsoft\Credentials

Cuối cùng, sử dụng LoadLibraryW để load file credHelper.dll , sau đó gọi tới hàm DllRegisterServer() trong module credHelper.dll .

Hình 4. Chương trình load module credHelper.dll và gọi tới hàm export DllRegisterServer()

Kết thúc phần phân tích trên, tạm thời thu được file credHelper.dll . credHelper.dll chính là một COM DLL.

1.1.2. Phân tích hàm init_proc

Nếu chỉ dừng lại ở trên thì có vẻ không hợp lý lắm vì thấy còn nhiều khác trong chương trình. Sử dụng xrefs để tìm hàm gọi tới main cũng như kết hợp code coverage thông qua Lighthouse plugin, biết được chương trình thực hiện trick để hijack hàm runtime _initterm().

Hình 5. Hàm runtime _initterm() bị hijack và gọi tới hàm _init_proc()

Đi tới _init_proc() tại 0x0140002530 , đầu hàm này chương trình gọi hàm f_retrieve_service_apis_funcs (0x0140001CD8) để lấy địa chỉ các APIs thuộc hai thư viện advapi32.dll ; kernel32.dll . Tên hàm cần lấy được giải mã thông qua hàm decrypt_string đã mô tả ở trên. Danh sách các hàm cần lấy địa chỉ bao gồm:

Tiếp theo, tiến hành giải mã vào bộ nhớ hai driver là Capcom.sysDriver.sys.

Hình 6. Các hàm để giải mã các driver Capcom.sys và Driver.sys

Hàm giải mã thực hiện:

  • Tính SHA256 của chuỗi “ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=” được giá trị hash “e173d43de98094098259467ff632b4fc61496af96f3a354a006360d246e8166f
  • Thông qua hàm f_Salsa20_key_setup (0x0140001010) để transform giá trị hash ở trên thành key giải mã.
  • Gọi hàm f_Salsa20_decrypt (0x01400014EC) để giải mã PE file vào vùng nhớ đã cấp phát.
Hình 7. Pseudo-code để giải mã ra các driver Capcom.sys và Driver.sys

Tới đây, dump được 02 driver để phục vụ phân tích sau. Sau khi giải mã thành công hai driver trên, chương trình thực hiện tiếp:

  • Tạo file cfs.dll tại thư mục C:\Windows\System32.
  • Mapping nội dung đã giải mã của driver Capcom.sys ở trên vào file cfs.dll.
  • Gọi hàm f_start_service (0x0140001FB4), sử dụng các APIs liên quan đến service đã lấy được địa chỉ ở trên để khởi chạy một service dưới tên là cfs và lấy handle của device có đường dẫn là \\.\Htsysm72FB.
Hình 8. Chương trình khởi tạo một service có tên là cfs và tạo handle của device có dường dẫn \.\Htsysm72FB
  • Nếu khởi chạy được service, chương trình gọi tới hàm f_interact_with_driver (0x0140002C44) để thực hiện tương tác với Driver.sys. Nếu thành công sẽ dừng và xóa service cfs.
Hình 9. Chương trình bắt đầu tương tác với driver Capcom.sys

Đi sâu vào f_interact_with_driver, hàm này thực hiện nhiệm vụ:

  • Gọi hàm f_decrypt_string để giải mã ra chuỗi “DriverBootstrap”.
  • Parse Driver.sys trên bộ nhớ để lấy địa chỉ RVA của hàm DriverBootstrap. Từ đó lấy được địa chỉ offset của hàm DriverBootstrap tại 0x3170.
  • Gọi hàm VirtualAlloc để cấp phát vùng nhớ có kích thước 45 bytes (0x2D) để chứa nội dung của một shellcode.
  • Tạo một context với nội dung gồm: địa chỉ base của Driver.sys; kích thước của Driver.sys (0x5800); địa chỉ offset của hàm DriverBootstrap (0x3170) thuộc driver này; địa chỉ của sub_140002A10 thuộc crackstaller.exe.
  • Cuối cùng gửi một IO Control Code có giá trị 0xAA013044 tới Capcom.sys (lúc này đã được mapping và chạy dưới service name là cfs) thông qua hàm DeviceIoControl để inject shellcode đã được cài đặt ở trên tới Capcom.sys (Refs: Capcom Rootkit Proof-Of-Concept; Capcom Driver Analysis)
Hình 10. Chương trình gửi IO control code với data có chứa shellcode tới capcom.sys.

1.1.3. Phân tích sub_140002A10

Như phân tích ở trên, nội dung của shellcode có tham chiếu tới sub_140002A10 . Đoạn mã tại địa chỉ này thực hiện nhiệm vụ cơ bản như sau:

  • Gọi hàm f_find_ntoskrnl_module (0x0140002768) để tìm module ntoskrnl.exe thông qua giá trị hash được tính toán trước là 0xC036346A.
  • Gọi hàm f_resolve_ntoskrnl_apis (0x0140002964) với tham số truyền vào là các giá trị hash cho trước để lấy địa chỉ của các hàm APIs thuộc ntoskrnl.exe. Thuật toán maching hash cơ bản như sau:
Hình 11. Pseudo-code để resolve các apis của ntoskrnl.exe
  • Cấp phát vùng nhớ (pool memory) với độ lớn bằng kích thước của Driver.sys (0x5800) và gắn pool tag cho nó là “FLAR”.
  • Sao chép nội dung của Driver.sys vào vùng nhớ đã cấp phát.
  • Tìm kiếm giá trị “0xDC16F3C3B57323” và thay thế bằng giá trị “0x41424143414242”.
  • Xây dựng context và gọi hàm PsCreateSystemThread với entry point của thread được tạo chính là địa chỉ của hàm DriverBootstrap thuộc Driver.sys.
Hình 12. Pseudo-code shellcode gọi tới hàm DriverBootstrap trong driver.sys

1.2. Phân tích driver.sys

1.2.1. Hàm DriverBootstrap

Theo phân tích tại sub_140002A10, sub này sẽ gọi PsCreateSystemThread để tạo một system thread nhằm thưc thi hàm DriverBootstrap trong driver.sys. Load driver.sys vào IDA, code tại hàm DriverBootstrap thực hiện nhiệm vụ sau:

  • Parse header của driver.sys để lấy địa chỉ của DriverEntry.
  • Gọi hàm IoCreateDriver để thưc thi code tại DriverEntry.
Hình 13. Function DriverBootstrap() chạy như một loader để load lại driver.sys lên bộ nhớ và thực thi driver.sys bằng cách gọi IoCreateDriver()

1.2.2. Hàm main của driver.sys

Code tại DriverEntry thực hiện đăng kí một callback function (f_callback_function) tại địa chỉ 0x0140004570.

Hình 14. Driver.sys đăng ký một callback để bắt các sự kiện thay đổi trong registry

Bằng việc đăng kí hàm callback như trên thì Driver.sys chính là một filter driver. Hàm callback này là một RegistryCallback, sẽ giám sát, chặn hoặc thay đổi hành động tác động vào registry. Code tại hàm f_callback_function thực hiện nhiệm vụ sau:

  • Gọi hàm decrypt_string để giải mã ra chuỗi {CEEACC6E-CCB2-4C4F-BCF6-D2176037A9A7}\Config. Như vậy, hàm callback sẽ chặn bắt những sự kiện thay đổi xảy ra trên registry key này.
  • Tính SHA256 của data là 0xDC16F3C3B57323, tuy nhiên giá trị này đã bị thay thế bằng “BBACABA” như đã phân tích tại sub_140002A10.
Hình 15. Nếu sự thay đổi xảy ra trong registry key {CEEACC6E-CCB2–4C4F-BCF6-D2176037A9A7}\Config thì driver.sys sẽ tiếp tục tính toán để giải mã ra nội dung của một password
  • Dựa vào giá trị hash tính được ở trên, sử dụng thuật toán salsa20 để thiết lập key giải mã. Sau đó thực hiện giải mã dữ liệu ra RC4_key. Đây chính là key cần tìm để giải mã ra flag. Key này có độ dài là 15.
Hình 16. Nội dung mã hóa của password trong driver.sys

1.3. Phân tích CredHelper.dll

1.3.1. Hàm DllRegisterServer

Quay trở lại với file credHelper.dll đã dump được ở trên, tại hàm main của crackstaller.exe sẽ gọi tới hàm DllRegisterServer() của dll này. Load vào IDA, nhiệm vụ của hàm DllRegisterServer() đơn giản như sau:

  • Gọi GetModuleFileNameW để lấy đường dẫn của credHelper.dll. Dll này nằm tại C:\Users\<account>\AppData\Local\Microsoft\Credentials\credHelper.dll.
  • Gọi StringFromGUID2 để tạo GUID string {CEEACC6E-CCB2-4C4F-BCF6-D2176037A9A7} đồng thời tạo đường dẫn là CLSID\{CEEACC6E-CCB2-4C4F-BCF6-D2176037A9A7}. Điều này này sẽ signal đoạn code giải mã password trong driver.sys mà chúng ta đã đề cập ở trên.
  • Cuối cùng thiết lập giá trị registry tương ứng tại HKEY_CLASSES_ROOT.
Hình 17. Thực hiện đăng kí DLL Server
  • Kết quả có được tương tư như hình dưới đây:
Hình 18. Kết quả sau khi đăng kí thành công credHelper.dll trong Registry

Sau khi đăng ký như một COM DLL Server, credHelper.dll sẽ expose ra các methods như sau:

Hình 19. Các methods được expose bởi credHelper.dll

1.3.2. Hàm GetPassword

Tại hàm GetPassword() có địa chỉ 0x018000153 , chương trình thực hiện nhiệm vụ đọc ra giá trị của từ Registry và transform nó thành :

Hình 20. Hàm GetPassword chịu trách nhiệm thiết lập key giải mã

1.3.3. Hàm GetFlag

Tại hàm GetFlag() có địa chỉ 0x01800016D8, code tại đây thực hiện giải mã ra chuỗi flag cần tìm với key giải mã có được ở trên và gán chuỗi này vào Registry tương ứng:

Hình 21. Hàm GetFlag chịu trách nhiệm giải mã encrypted Flag

Như vậy, sau khi phân tích code của credHelper.dll có được nội dung của flag đã bị encrypt như trên hình. Nhiệm vụ bây giờ là cần tìm được password để giải mã.

2. Get RC4_key through remote kernel debugging

Kết thúc toàn bộ quá trình phân tích ở trên, có thể hình dung được flow thực hiện của crackstaller.exe như sau:

  • crackstaller.exe bị hijack _initterm(), nên sẽ thực thi _init_proc() trước hàm main. Tại đây, giải mã ra 02 driver là Capcom.sysDriver.sys (filter driver). Capcom.sys được mapping vào cfs.dll và được khởi chạy dưới dạng SERVICE_KERNEL_DRIVER.
  • Tạo một shellcode và gọi hàm DeviceIoControl để inject shellcode này vào Capcom.sys.
  • Shellcode khi thưc thi sẽ nhảy tới sub_0140002A10 của crackstaller.exe. Tại đây, thực hiện thay thế data trong Driver.sys và gọi hàm PsCreateSystemThread để tạo thread thực thi lệnh bắt đầu từ hàm DriverBootstrap thuộc Driver.sys.
  • Code tại DriverBootstrap lấy địa chỉ hàm DriverEntry và gọi hàm IoCreateDriver để thực thi hàm này.
  • Code tại DriverEntry gọi hàm CmRegisterCallbackEx để đăng kí một callback_function. Callback_function sẽ chờ khi credHelper.dll được load lên để thực hiện giải mã ra một RC4_key.
  • Hàm main của crackstaller.exe giải mã ra credHelper.dll. Lấy địa chỉ của hàm DllRegisterServer() và gọi hàm này để thực thi.
  • Hàm DllRegisterServer() của credHelper.dll khi được load lên có nhiệm vụ tạo ra một COM Object trong Registry là {CEEACC6E-CCB2-4C4F-BCF6-D2176037A9A7}. Đồng thời, nó sẽ lấy Password trong Registry làm RC4_key để giải mã ra Flag.

Như vây, dữ liệu mã hóa của Flag đã biết thông qua phân tích credHelper.dll. Để có được key giải mã mà không cần viết lại thuật toán thì cách nhanh nhất là debug vào Driver.sys. Để debug được, sử dụng VirtualKD-Redux (do VirtualKD tương thích không tốt từ VMware15 trở đi) nhằm hỗ trợ Windbg thực hiện remote debugging vào máy ảo được hiệu quả hơn.

Tại máy ảo, thực hiện debug crackstaller.exe. Đặt bp tại lời gọi tới hàm VirtualAlloc, sau đó thực thi code để chương trình thiết lập giá trị cho shellcode buffer, tới hàm DeviceIoControl thì dừng lại. Follow vùng nhớ chứa shellcode và sửa lệnh sti (0xFB) thành int3 (0xCC):

Hình 22. Thay thế byte đầu tiên của shellcode bằng opcode 0xCC (trap to debugger)

Trace qua lệnh DeviceIoControl, lúc này Windbg sẽ signal và dừng lại tại lệnh int3. Sử dụng lệnh eb để restore lại byte đã patch:

Hình 23. Khôi phục lại byte đã patch

Trace code tại Windbg sẽ nhảy tới hàm 0x0140002A10 . Tại đây sẽ thấy code tại hàm này thực hiện thay đổi dữ liệu như đã phân tích ở trên:

Hình 24. Code tại hàm 0x0140002A10 thực hiện thay thế SHA input

Tiếp tục trace code cho tới khi dừng lại tại lời gọi hàm PsCreateSystemThread. Lúc này, thanh ghi rax đang trỏ tới hàm địa chỉ hàm DriverBootstrap của Driver.sys. Gõ lệnh bp rax để đặt một bp tại hàm này:

Hình 25. Đặt bp tại hàm DriverBootstrap (đang trỏ bởi thanh ghi rax) của Driver.sys

Thực thi chương trình sẽ dừng lại tại đầu hàm DriverBootstrap. Tiếp tục trace code và đặt bp tại lời gọi tới hàm IoCreateDriver, lúc này thanh ghi rdx sẽ trỏ tới địa chỉ của hàm DriverEntry. Gõ lệnh bp rdx để đặt bp:

Hình 26. Đặt bp tại hàm DriverEntry (đang trỏ bởi thanh ghi rdx) của Driver.sys

Tiếp tục thực thi chương trình, trace code tới lời gọi hàm CmRegisterCallbackEx. Lúc này, thanh ghi rcx sẽ trỏ tới địa chỉ của hàm callback. Gõ lệnh bp rcx để đặt bp tại hàm callback này:

Hình 27. Đặt bp tại hàm callback (đang trỏ bởi thanh ghi rcx)

Cho thực thi chương trình sẽ dừng lại tại đầu hàm callback. Lúc này, nó sẽ chờ cho tới khi credHelper.dll được load lên và chạy. Do đó, xóa bp đã đặt ở đầu hàm, tìm và đặt bp tại chỗ gọi hàm giải mã ra RC4_key, sau đó cho thực thi. Để có thể trigger được bp đã đặt, tại máy ảo thực hiện lệnh sau để chạy credHelper.dll:

Hình 28. Thực thi lệnh đăng kí COM DLL nhằm trigger hàm callback

Quạy lại Windbg, bp đã hit. Trace qua hàm giải mã và thực hiện dump giá trị tại rsp+0D8h sẽ có được RC4_key là “H@n $h0t FiRst!”:

Hình 29. Dump password sau khi hàm callback giải mã thành công

3. Decrypt flag

Với RC4_key ở trên, sử dụng CyberChef để thực hiện giải mã ra flag để submit là S0_m@ny_cl@sse$_in_th3_Reg1stry@flare-on.com:

Hình 30. Sử dụng password tìm được để giải mã ra flag

End.

m4n0w4r

--

--