Deobfuscate malware

Giới thiệu

Các file độc hại

Tôi tình cờ nhận được mẫu này từ một người quen, trong khi đó, người quen của tôi lại nhận được mẫu này từ một người quen khác, cứ thế,… Trong giới làm an ninh mạng ở Việt Nam hay có kiểu chia sẻ đơn giản như thế thôi. Ai cũng biết việc chia sẻ cho những người khác là cần thiết vì nó giúp cho chúng ta tự bảo vệ nhau, tự phát hiện các mối nguy hại từ bên ngoài. Nhưng ai chia sẻ xong cũng nói một câu: “Đừng bảo với ai là em gửi anh nhé!”, “Đừng share ai nữa nhé!”,…. Tôi cũng không rõ lắm họ muốn bí mật nguồn mẫu làm gì. Có lẽ không ai muốn người khác biết mình bị tấn công, sẽ ảnh hưởng đến công việc kinh doanh. Tôi tôn trọng các quyết định của người đã chia sẻ cho tôi. Tôi sẽ viết phương pháp tôi phân tích mẫu này để các bạn nếu có gặp một mẫu tương tự sẽ biết cách xử lý. Tôi không rõ mẫu mã độc này do nhóm nào viết, tuy nhiên phải khẳng định rằng phân tích không phải dễ.

Trong bài viết này, tôi sẽ trình bày phương pháp tôi vượt qua các lớp bảo vệ file thực thi của mã độc, phần còn lại là các hành vi của mã độc có lẽ cũng không cần thiết. Trong trường hợp các bạn muốn biết các hành vi của mẫu này, có thể đợi bài viết tiếp theo của tôi

1 phút cho quảng cáo: Nếu các bạn gặp những mẫu malware tương tự, các bạn hoàn toàn có thể liên lạc với tôi qua địa chỉ contact[at]develbranch.com hoặc facebook fanpage fb.com/develbranch và tôi sẽ giúp các bạn phân tích các mẫu mã độc. Tôi sẽ tính phí trong trường hợp cần thiết. Nếu thấy bài viết của tôi có giá trị, các bạn có thể chuyển khoản cho tôi một chút tiền để tôi có động lực viết các bài viết tiếp theo (hãy liên hệ với fanpage của tôi).

Môi trường thực hiện

  • Máy ảo windows 7 64 bit. Các bạn có thể sử dụng windows 10.
  • Một chương trình debugger trên windows: Tôi thích dùng x64dbg.
  • Một chương trình C++ compiler trên windows: Tôi thích dùng Microsoft Visual Studio 2015. https://visualstudio.microsoft.com/
  • CFF Explorer: Explorer Suite

Phân tích theo hành vi

Trong tất cả các bước phân tích mã độc chuẩn, chúng ta đều phải thực hiện phân tích theo hành vi để phần nào đoán được hành động mà mã độc sẽ thực hiện và chúng ta sẽ đưa ra phương án phân tích phù hợp. Để thực hiện việc này chúng ta cần:

  • File thực thi của mã độc, hoặc là một lệnh để chạy mã độc.
  • Dựng được chính xác môi trường mà mã độc sẽ thực thi. Ví dụ: Nếu mã độc chỉ chạy trên windows server thì các bạn sẽ không bao giờ phân tích theo hành vi nếu môi trường của các bạn là windows home.

Người bạn chỉ đưa tôi 4 file như trên hình, không thông tin, không mô tả gì thêm. Trong quá trình phân tích tôi cũng không có thêm một chút manh mối nào ngoại trừ đường dẫn của file. Với bấy nhiêu thông tin, chúng ta không thể thực thi được mã độc trên môi trường của mình. Do vậy sẽ không có phân tích theo hành vi. Chúng ta cần đoán theo cách của mình! Guess it your way!

Phân tích chi tiết

Trong số 4 files nhận được, tôi nhận thấy file cachuri.dll có chữ kí của Microsoft. Tôi hoàn toàn bỏ qua file này

Chữ ký của cachuri

File này là file xử lý cache trên server windows. Đây không phải module cài đặt mặc định của IIS server:

Mô tả cachuri

File cachuri.dll sử dụng các hàm trong iisutil2.dll, không có chữ ký hợp lệ Các file độc hại

Hai file còn lại hoàn toàn không phải file thực thi. Do đó chúng ta có thể đoán: cachuri.dll sẽ được một module nào đó của IIS server(inetsrv) load lên. Sau khi load cachuri.dll, iisutil2.dll độc hại cũng được load vào bộ nhớ của tiến trình. iisutil2.dll có thể sẽ sử dụng tiếp 2 file còn lại cho mục đích nào đó. Đến bước này, chúng ta vẫn chưa chạy mẫu mà hoàn toàn chỉ là đoán. Guess it your way!

Thực hiện phân tích iisutil2.dll

mở iisutil2.dll trong IDA pro

File này bị obfuscate khá kĩ và gây khó khăn nhiều trong quá trình phân tích. Tuy nhiên, sau khi phân tích code, tôi nhận ra luồng thực thi chính của chương trình như sau:

Lấy địa chỉ các hàm: CreateFileW, ReadFile, RtlDecompressBuffer Lấy địa chỉ các hàm

Mở file iisexpressshim.sdb và Đọc toàn bộ nội dung của file. Đọc nội dung file

Giải mã bằng thuật toán xor đơn giản Giải mã dữ liệu

Giải mã dữ liệu

Trong đoạn dữ liệu sau khi giải mã, sẽ có cấu trúc như sau:

  • 4 byte đầu: Kích thước dữ liệu trước khi nén
  • Toàn bộ khối dữ liệu đã nén

Giải mã dữ liệu

Sau đó, chương trình sẽ giải nén toàn bộ nội dung của file bằng hàm RtlDecompressBuffer. Đến đây, ta đã unpack được lớp thứ nhất.

Chúng ta có thể mô tả toàn bộ quá trình này thông qua đoạn code python sau. Rất may là có 1 phiên bản giải nén lznt1 được implement hoàn toàn bằng python(https://twitter.com/nullandnull/status/772989022079586304) của Google. Thật quá tốt: https://github.com/google/rekall/blob/e57446eb8ecbcf5019c1a978f469955a5078c829/rekall-core/rekall/plugins/filesystems/lznt1.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
'''Giải mã file iisexpressshim.sdb'''
import sys
import lznt1

def decrypt(buf):
	out = ''
	index = 0
	for c in buf:
		out += chr(ord(c) ^ (index % 0xff))
		index += 1
	return out

cdata = decrypt(open(sys.argv[1], 'rb').read())
open(sys.argv[1]+'.decrypted', 'wb').write(lznt1.decompress_data(cdata[4:]))

Kết quả giải mã Giải mã dữ liệu

File PE này không lỗi, mặc dù VirtualSize, RawSize và RawAddress đều bằng 0 . Chỉ đơn giản, file này cần được load theo một cách khác. Tôi đọc thuật toán trong loader của chương trình và nhận ra: Chúng ta có thể tính toán các giá trị VirtualSize, RawSize và RawAddress hoàn toàn chỉ cần dựa vào VirtualAddress.

  • VirtualSize = RawSize = (VirtualAddress của section sau - VirtualAddress của section trước)
  • RawAddress = VirtualAddress

Sau khi fix lại code, file PE hoàn toàn đủ tiêu chuẩn để load vào IDA và đọc. Hoạt động của file này như sau:

  • Tại DllMain, lấy image base của cachuri.dll
  • Thay bảng export của cachuri.dll thành bảng export của mã độc.

Kĩ thuật này có ưu điểm:

  • Có thể coi đây là một kĩ thuật nhằm thay thế file cachuri.dll bằng một file độc. Các hành vi độc hại nếu xuất hiện sẽ xuất hiện từ tiến trình của Inetsrv, chính xác hơn là bên trong cachuri.dll. Tuy nhiên, nếu chỉ quét trên đĩa bằng các antivirus thông thường thì hoàn toàn không thể phát hiện được. cachuri.dll vẫn có chữ kí bình thường của Microsoft.
  • Một số chương trình EDR khi phát hiện hành vi độc hại sẽ gửi các file của tiến trình liên quan (file thực thi, DLL liên quan) về máy chủ để phân tích. Bằng cách này, người viết malware hoàn toàn tránh được các EDR.

Tôi tiếp tục debug nhưng có vẻ không thành công, vì chúng ta không có actor để trigger mã độc.

Thực hiện phân tích iisexpressshim.sdb

Tôi thử phân tích hoàn toàn bằng IDA và không sử dụng trình debugger. Khi phân tích hàm DllGetClassObject, tôi nhận thấy malware sử dụng tên của thư mục chứa nó như một CLSID. Trong code của chương trình cần query class có clsid này để lấy về đối tượng xử lý tương ứng. malware CLSID

Do đó, để có thể tạo một đối tượng thực thi độc hại, chúng ta cần tạo một tên thư mục với clsid bất kì và sử dụng nó. Các bước cụ thể như sau:

  • Tạo một thư mục có tên là {CB8A1CEF-292D-421E-AC71-88451B5C7F2B}. Giá trị này tôi lấy ngẫu nhiên.
  • Sử dụng đoạn code dưới đây để tạo object.
     CLSID    clsid_malware;
     LPVOID   ppv = NULL;
     HMODULE h = LoadLibraryA("cachuri.dll");
     DLLGETCLASSOBJECT fnDllGetClassObject = (DLLGETCLASSOBJECT)GetProcAddress(h, "DllGetClassObject");
     HRESULT hr = CLSIDFromString(TEXT("{CB8A1CEF-292D-421E-AC71-88451B5C7F2B}"), &clsid_malware);
     hr = fnDllGetClassObject(clsid_malware, IID_IClassFactory, &ppv);
     printf("ppv = %p\n", ppv);
    

    Sau đoạn code này, chúng ta có đối tượng, tạm gọi là CMalware2 của mã độc. Tiếp tục phân tích các method của đối tượng này, tôi nhận thấy CMalware2 chỉ là object cơ sở, không implement một chức năng nào. Tôi thử phân tích hàm QueryInterface thì nhận ra nếu query với CLSID {839D7762-5121-4009-9234-4F0D19394F04} sẽ nhận được object CMalware1 của mã độc.

malware vtable

Tiếp tục phân tích, tôi phát hiện ra hàm làm việc chính của malware: hàm này có tác dụng load một file PE khác nằm trong logo.png và thực thi nó trên bộ nhớ. Để trigger tới chỗ này, tôi viết đoạn mã sau:

int main(int argc, char** argv) {
	CLSID clsid_malware;
	LPVOID   ppv = NULL;
	HMODULE h = LoadLibraryA("cachuri.dll");
	DLLGETCLASSOBJECT fnDllGetClassObject = (DLLGETCLASSOBJECT)GetProcAddress(h, "DllGetClassObject");
	HRESULT hr = CLSIDFromString(TEXT("{CB8A1CEF-292D-421E-AC71-88451B5C7F2B}"), &clsid_malware);
	hr = fnDllGetClassObject(clsid_malware, IID_IClassFactory, &ppv);
	printf("ppv = %p\n", ppv);
	IUnknown *pMalware = (IUnknown*)ppv;
	hr = CLSIDFromString(TEXT("{839D7762-5121-4009-9234-4F0D19394F04}"), &clsid_malware);

	IMalware *malware_obj;
	hr = pMalware->QueryInterface(clsid_malware, (LPVOID*) &malware_obj);
	printf("main object = %p\n", malware_obj);

	DWORD_PTR vptr = (DWORD_PTR)(*(DWORD_PTR*)malware_obj);
	printf("vtbl = %08x\n", vptr);
	DWORD_PTR ml_fn = *(DWORD_PTR*)(vptr + 3 * sizeof(DWORD_PTR));
	RUNMALWARE run = (RUNMALWARE)(ml_fn - 0x5620 + 0x10B0);
	run((void*)malware_obj); // extract malware
	return 0;
}

malware vtable

Để đơn giản quá trình, chúng ta sẽ để cho malware thực thi nốt phần còn lại của nó:

  • Cấp phát bộ nhớ cho malware (VirtualAlloc)
  • Giải mã toàn bộ malware lên memory
  • Fixup relocation
  • Resolve IAT. Chúng ta sẽ đặt breakpoint tại hàm GetProcAddress để dừng thực thi trong khi malware được resolve các hàm API
  • Dump vùng mem trên ra bộ nhớ, và vì file này đã được mapping trên memory và có thay đổi, nên sử dụng cộng cụ pe_unmapper của hasherezade (https://github.com/hasherezade/pe_recovery_tools/tree/master/pe_unmapper)

Dump file nhận được ra đĩa, chúng ta tạm gọi là logo.dll

Phân tích logo.dll

DLL này khá đơn giản với một hàm DllEntry. Chúng ta có thể coi đây là entrypoint của chương trình (Phân biệt với entrypoint của file).

Đây là mã giả của DllEntry:

 char __stdcall DllEntry(int a1, int a2, int a3, int a4)
{
  char result; // al

  SetErrorMode(0x8007u);
  result = sub_100569F0((int)sub_100031C0);
  if ( result )
  {
    result = sub_10055E20();
    if ( result )
    {
      while ( 1 )
        Sleep(0xFFFFFFFF);
    }
  }
  return result;
}

Sau khi phân tích, tôi nhận ra đây lại là một PE loader khác. Nhiệm vụ của đoạn code này như sau:

  • Cấp phát và Giải mã một file PE được nhúng trong chương trình vào một vùng nhớ
  • Resolve các địa chỉ API sẽ được sử dụng
  • Thực thi entrypoint của file PE

Quá trình cấp phát và giải mã file thực thi cũng giống như các loader bình thường, tuy nhiên khi resolve địa chỉ các API, loader này đã bỏ đi tất cả các trường quan trọng trong file PE. Chúng ta không có header của file PE để có thể dump file xuống đĩa và phân tích như mọi khi. Chúng ta sẽ làm gì khi không có header??

Như thường lệ, khi đến được entrypoint của chương trình, tôi dùng x64dbg để dump memory xuống bộ nhớ. Có một lưu ý rằng mẫu này cấp phát tới 0x899400 bytes cho file PE mới. Lưu ý, trong bài viết của tôi, vùng nhớ được cấp phát là 0x800000.

Giải mã dữ liệu

Khi phân tích kĩ một chút, chúng ta nhận thấy loader bỏ qua 0x4E0000 bytes đầu tiên của vùng nhớ này (Chả hiểu để làm gì?).

Giải mã dữ liệu

Giải mã dữ liệu

Như tôi đã nói, các trường quan trọng bị xóa hết thông tin, chúng ta không có đủ thông tin để khôi phục file này.

Giải mã dữ liệu

Giải mã dữ liệu

Rebuild lại file

Chúng ta có 1 thông tin duy nhất: Imagebase = 0x800000 + 0x4E0000 = 0xCE0000.

Nếu để ý 1 chút, chúng ta có thể coi toàn bộ file PE này là 1 section. Do đó chúng ta có thể tạo một section cho file PE này như sau.

NumberOfSections = 1

Giải mã dữ liệu

Bước tiếp theo là fix bảng IAT, tôi sử dụng Scylla : https://github.com/NtQuery/Scylla

Sau khi điền đúng OEP tôi tìm thấy, Scylla lấy lại gần như toàn bộ bảng IAT của mã độc. Có 1 số hàm mà Scylla không resolve được, chúng ta cần fix lại cẩn thận bằng tay bằng chức năng Disassemble của Scylla.

Fix IAT

Có một lưu ý nhỏ: Các malware sử dụng PE Loader sẽ có 1 số ảnh hưởng như sau:

  • Nếu malware sử dụng các hàm dạng GetModuleHandle với tham số NULL (cả 2 version Ansi và Unicode) đều bị lỗi do hàm này trả về module handle của file ban đầu, không phải file mới được load.
  • Handle của file mới được load sẽ không được hệ thống quản lý.

Do đó, malware sẽ phải tự viết hàm GetModuleHandle và điền vào bảng IAT. Chúng ta có thể nhìn thấy rõ đoạn kiểm tra: Nếu tham số đầu vào là NULL, hàm sẽ trả về địa chỉ 0xCE0000, chính là imagebase của chúng ta.

Fix IAT

Sau khi resolve được đầy đủ các API

Fix IAT

Cuối cùng, chúng ta fix lại vào file đã dump ra ban đầu. Lưu ý là Scylla không cho chúng ta sửa imagebase, do đó chúng ta sẽ phải sửa lại các địa chỉ RVA cho hợp lý.

Fix IAT

Do imagebase không chuẩn nên phải chỉnh lại:

Fix IAT

Đây là lớp cuối trong toàn bộ các lớp mã hóa nhằm bảo vệ file.

Bonus: C&C Server

1
2
3
4
5
6
7
8
cdn.arlialter.com:8888
cdn.arlialter.com:8531
var.alieras.com:8531
fbcn.enantor.com:8531
fbcn.enantor.com:8888
ww1.erabend.com:8888
var.alieras.com:8888
ww1.erabend.com:8531

Ủng hộ tác giả

Các bạn có thể giúp tôi có thêm động lực viết bài bằng cách chuyển cho tôi một chút tiền trà nước qua địa chỉ:

Tên tài khoản: NGUYEN HONG QUANG

Số tài khoản: 0011004035548

Ngân hàng: Vietcombank