Gần đây, chúng tôi có
tiếp cận một mẫu malware lạ. Kết quả trả về của sandbox cho thấy đây là một
biến thể của dòng malware Netwire được pack bằng một loại crypter viết bằng
VB6. Chúng tôi đã tiến hành phân tích mẫu này nhằm hiểu sâu về kỹ thuật của
malware, đồng thời xây dựng thành tài liệu tham khảo cho những mẫu malware sử
dụng kĩ thuật tương tự.
1. Công cụ sử dụng
Danh sách các công cụ
được sử dụng trong bài viết:
2. Thông tin cơ bản về sample
File Hash (SHA-256): d381c5a5eeb46759bb5ebce67eb50cc61f91a75c204d6ec1c7750937f7f4c3f1
Nguồn: Any.run
Phân tích sơ bộ bằng các chương trình PE Scanner
cho thấy mã độc được viết bằng Visual
Basic 6.0
3. Phân tích crypter
Một mẹo khi dịch ngược chương trình được biên dịch
từ VB6 là sẽ có một loạt các lệnh jump nhảy đến các function được viết trong
chương trình. Như trong trường hợp này, các lệnh jump nằm ở địa chỉ 0x40DF3C. Có thể tìm các lệnh jump này bằng mẫu “81 6C 24 04 ?? ?? ?? ?? E9”
Có thể thấy mã độc chỉ có hai function chính. Chọn
function tại địa chỉ 0x40E9DE để phân tích vì function này có kích thước lớn nhất. Tiếp tục trace dần
sẽ tới một hàm call như sau:
Với những ai đã quen với việc dịch ngược các
chương trình được viết bằng VB6 sẽ nhận thấy điểm bất thường khi trong code của
chương trình VB6 gọi thẳng đến một thanh ghi. Tiếp tục debug vào lệnh call sẽ tới
đoạn shellcode:
Toàn bộ đoạn shellcode này đã được obfuscated.
Tuy nhiên, kích thước của nó cũng khá nhỏ nên nếu trace dần từng bước và tổng hợp
thông tin, chúng ta sẽ thấy shellcode thực hiện những bước sau:
·
Gọi hàm VirtualAlloc để cấp phát bộ nhớ.
·
Giải mã
shellcode và copy vào vùng bộ nhớ vừa được cấp phát.
·
Tiến hành
gọi shellcode mới.
Tới đây, có thể dùng x64dbg để dump toàn bộ shellcode mới và tiến hành phân tích. Để dump shellcode, lựa chọn đoạn cần dump trong cửa sổ Hex, chuột phải và chọn Binary > Save To a File:
Với shellcode đã dump, có nhiều công cụ hỗ trợ để phân tích. Trong bài này, chúng tôi sử dụng jmp2it để load shellcode lên và debug bằng x64dbg. Chạy jmp2it với command như sau:
“Jmp2it.exe
shellcode.bin 0x0 pause”
Tiến hành attach x64dbg vào process jmp2it.exe để debug shellcode. Shellcode lúc này được load ở địa chỉ 0x30000.
·
Trace từng
lệnh để debug.
·
Viết
tool/script để deobfuscate đoạn shellcode.
Vì shellcode là khá lớn nên ở đây chúng tôi lựa chọn phương án viết script để thực hiện deobfuscate đoạn shellcode này.
3.1. Deobfuscate shellcode
Để viết được script thực hiện deobfuscate đoạn shellcode, chúng ta cần hiểu một số pattern của đoạn shellcode. Ví dụ như sau:
·
Pattern
Push Reg/Pop Reg
·
Pattern
Inc Reg/Dec Reg
·
Pattern
Add Reg, Const/ Sub Reg, Const
·
Pattern
Push Reg/Xor Reg, Const/ Pop Reg
·
Pattern
CLD, CLC
·
…
Các pattern trên tương ứng với lệnh NOP. Sau khi xác định được các pattern này, dùng python để viết script deobfuscate. Chi tiết script xem tại đây. Để sử dụng, lựa chọn đoạn mã cần deobfuscate và chạy script.
*Lưu ý: script trên tạm
thời bỏ qua tìm kiếm pattern 2 bytes vì pattern ngắn, không có hiệu quả nhiều
trong việc obfuscate và rất dễ nhầm lẫn khi deobfuscate bằng cách
search/replace byte pattern. Để xác định pattern 2 bytes một cách chính xác thì
nên dùng pattern bằng asm. Hiện tại x64dbg chưa support tốt các pattern bằng
asm.
3.2. Heaven’s gate
Shellcode áp dụng kỹ thuật heaven’s gate[1] để làm rối trong quá trình debug. Đây là kỹ thuật thực thi mã từ x86 sang x64 bằng lệnh far jmp. Bằng cách đơn giản check ở địa chỉ FS:[0xC0] để xem hệ thống có phải là x64 hay không? Nếu là x64, shellcode dùng kỹ thuật heaven’s call. Để debug tiếp được trơn tru trên x86, chúng tôi tiến hành patch lệnh nhảy sau lệnh so sánh để "lừa" shellcode thực thi trên x86.
3.3.
Resolve API
Có thể nói GetProcAddress là một API quan trọng để shellcode có thể tìm và gọi các hàm API khác. Để tìm địa chỉ của hàm GetProcAddress cần có địa chỉ của kernel32.dll. Shellcode ở trên sẽ tiến hành truy xuất tới PEB->Ldr->InMemoryOrderModuleList và lấy địa chỉ của module tương ứng với tên kernel32.dll.
Sau khi có địa chỉ của kernel32.dll, shellcode tiếp tục tìm địa chỉ của API bằng API hash. Ở đây hash được sử dụng là DJB hash và giá trị hash của hàm “GetProcAddress” là 0xCF31BB1F[2].
Dựa vào địa chỉ của kernel32.dll và API GetProcAddress, shellcode tiến hành resolve một loạt các API sau:
·
LoadLibraryA
·
TerminateProcess
·
EnumWindows
·
ZwProtectVirtualMemory
·
DbgBreakPoint
·
DbgUIRemoteBreakin
3.4.
Anti attach
3.5. Restore
hook/breakpoint tại các hàm Zw*/Nt*
Shellcode thực hiện scan pattern “B9 ?? ?? ?? ?? 8D 54 24 04” và “33 C9 8D 54 24 04” trong code của thư viện ntdll. Đây là pattern nằm trong các hàm Zw*/Nt* gọi đến system call. Sau khi tìm thấy pattern này, shellcode sẽ khôi phục lại 5 bytes đầu tiên của các hàm:
Một điểm hay cần học ở kỹ thuật này là các hàm Zw*/Nt* có các system call number tăng dần theo luồng từ trên xuống dưới. Nhờ đặc điểm này, shellcode có thể khôi phục lại lệnh “mov eax, system call number” một cách chính xác:
Bug: Nếu hàm API với system call number là 0 hoặc dòng lệnh “lea edx, dword ptr ss:[esp+4]” bị thay đổi (hook, breakpoint,…), shellcode sẽ hủy toàn bộ system call number của ntdll.
3.6.
Set hidden thread
Shellcode gọi hàm ZwSetInformationThread với tham số 0x11 để hide thread[3] trước debugger:
3.7.
Kiểm tra breakpoint
Shellcode sử dụng một hàm check trước khi gọi API. Nội dung hàm check như sau:
·
Gọi NtGetThreadContext để check hardware breakpoint[3].
·
Kiểm tra
opcode 0xCC
(int 3) để phát hiện software
breakpoint[3].
3.8. Create process và code injectionCuối cùng shellcode thực hiện code injection bằng cách:
·
Gọi CreateProcessInternalW với flag là CREATE_SUSPENDED.
·
Gọi ZwUnmapViewOfSection với base là 0x400000.
·
Sử dụng ZwCreateSection/NtMapViewOfSection để cấp
phát bộ nhớ.
· Nếu ZwCreateSection/NtMapViewOfSection bị lỗi,
chuyển sang dùng API ZwAllocVirtualMemory để cấp phát bộ nhớ.
·
Gọi NtWriteVirtualMemory để inject shellcode mới vào bộ nhớ.
· Cuối cùng
gọi hàm NtSetThreadContext/NtSetThreadContext/ NtResumeThread để chạy shellcode và TerminateProcess để exit.
3.9. Phân tích shellcode thứ haiĐoạn shellcode thứ hai thực chất là đoạn shellcode thứ nhất với config khác. Thay vì inject shellcode như đoạn shellcode thứ nhất, mục đích của đoạn shellcode này là tải về payload, decrypt và thực thi. Công việc chính nó thực hiện bao gồm:
· Sử dụng
hàm của thư viện wininet.dll để
download payload từ địa chỉ https://drive[.]google[.]com/uc?export=download&id=1zEuX2HZcVvTYp7wzGtD1IXOSVLTBWVUe
· Giải mã
payload và map vào memory để thực thi. Payload này có kích thước 0x1AA40, 40 bytes đầu tiên là phần header của payload.
3.10. BonusKhi load payload Netwire đã dump bằng x64dbg và đặt breakpoint tại 0x409E8A, sẽ thu được config đã decrypted:
4. Tài liệu tham khảo
Các nguồn tham khảo được sử dụng làm tư liệu cho bài viết:
---------------------------------- Để tiện theo dõi, chúng tôi cung cấp bài phân tích dưới dạng PDF: File Name: CSS-RD-ADV-200304-011_Unpack crypter của malware Netwire bằng x64dbg.pdf File Hash (SHA-256): 717efd6b8dd9a8a40ee34386311ab0f5689eb1f5f8fbd6df30b9cfdd8abe02c0 Dang Dinh Phuong R&D Center - VinCSS (a member of Vingroup) |
Hay quá anh ơi <3
ReplyDeleteA good blog always comes-up with new and exciting information and while reading I have feel that this blog is really have all those quality that qualify a blog to be a one. crypter
ReplyDelete