1. Giới thiệu
Tại VinCSS, chúng tôi liên tục chủ động theo dõi tình hình an ninh mạng, săn tìm các mẫu mã độc và đánh giá mức độ nguy hiểm của chúng, đặc biệt là các mẫu mã độc nhắm tới Việt Nam. Gần đây, trong quá trình thực hiện hunting trên nền tảng của VirusTotal, thực hiện tìm kiếm các mẫu byte đặc trưng liên quan tới nhóm Mustang Panda (PlugX), chúng tôi đã phát hiện một loạt mẫu mã độc mà chúng tôi nghi ngờ là của nhóm này được tải lên từ Việt Nam.
Tất cả các mẫu này đều có chung tên là “log.dll” và có tỉ lệ phát hiện khá thấp.
Dựa vào thông tin trên, chúng tôi cho rằng có khả năng mã độc đã được cài cắm vào một vài đơn vị ở Việt Nam, do đó chúng tôi quyết định phân tích các mẫu mã độc này. Trong quá trình phân tích, dựa vào các dấu hiệu tìm được, chúng tôi tiếp tục hunting các dữ kiện còn thiếu để bổ sung bức tranh đầy đủ hơn cho quá trình phân tích.
Để tiện hình dung, chúng tôi vẽ lại luồng thực thi của mã độc như sau:
Bài phân tích này của chúng tôi bao gồm:
- Phân tích kỹ thuật của file log.dll.
- Phân tích kỹ thuật của shellcode được giải mã từ log.dat.
- Phân tích PlugX Dll cũng như giải mã thông tin cấu hình của PlugX.
2. Phân tích log.dll
Trong danh sách các mẫu hunt được ở trên, chúng tôi lựa chọn mẫu có hash: 3171285c4a846368937968bf53bc48ae5c980fe32b0de10cf0226b9122576f4e
Sample này được submit lên VirusTotal từ Việt Nam vào thời gian 2022-04-25 14:04:36 UTC
Thông tin từ Rich Header cho thấy khả năng nó được compile bằng Visual Studio 2012/2013:
Kiểm tra các sections của file thì có thể thấy nó bị packed hoặc code bị obfuscated:
Sample có tên gốc là ljAt.dll, và nó export hai hàm là LogFree và LogInit:
Load sample vào IDA, tiến hành phân tích code của hai hàm trên:
- Hàm LogFree:
Quan sát tại hàm này có thể thấy code của nó đã bị obfuscated hoàn toàn bằng Obfuscator-LLVM, sử dụng kĩ thuật Control Flow Flattening.
Sau khi phân tích thêm, nhận thấy hàm này không thực hiện nhiệm vụ gì cả.
- Hàm LogInit:
Hàm này sẽ gọi tới hàm LogInit_0:
Cũng tương tự như trên, code tại hàm LogInit_0 cũng đã bị obfuscated hoàn toàn, phải mất rất lâu IDA mới decompile được code của hàm này:
Nhiệm vụ chính của hàm LogInit_0 là gọi tới hàm f_read_content_of_log_dat_file_to_buf để đọc nội dung của file log.dat và thực thi shellcode đã giải mã:
Code tại hàm f_read_content_of_log_dat_file_to_buf cũng bị obfuscated hoàn toàn:
Nhiệm vụ chính của hàm này như sau:
- Gọi hàm GetModuleHandleW để lấy handle của kernel32.dll.
- Gọi hàm GetProcAddress để lấy địa chỉ của các hàm APIs: VirtualAlloc, GetModuleFileNameA, CreateFileA, ReadFile.
- Sử dụng các hàm APIs trên để lấy đường dẫn tới file log.dat và đọc nội dung của file này vào vùng nhớ đã được cấp phát.
- Thực hiện giải mã nội dung của log.dat thành shellcode để sau đó shellcode này được thực thi bởi lời gọi từ hàm LogInit_0.
3. Phân tích shellcode
Căn cứ vào thông tin đã phân tích ở trên, chúng tôi biết được file log.dll sẽ đọc nội dung từ file log.dat và giải mã ra shellcode để thực thi tiếp. Dựa vào thông tin này, chúng tôi tiếp tục hunting file log.dat trên VirusTotal với phạm vi giới hạn nguồn submit là từ Việt Nam.
Kết quả có được như sau:
Với kết quả trên, tại thời điểm phân tích, chúng tôi lựa chọn file log.dat (2de77804e2bd9b843a826f194389c2605cfc17fd2fafde1b8eb2f819fc6c0c84) được submit lên VirusTotal vào thời gian 2022-04-20 12:33:19 UTC (tức là trước 5 ngày so với file log.dll ở trên).
Tiến hành debug và dump ra shellcode đã được giải mã:
Sử dụng hai công cụ là FLOSS và scdbg để có cái nhìn tổng quan về shellcode này. Kết quả có được như sau:
Với các kết quả có được ở trên, có thể thấy shellcode này sẽ thực hiện cấp phát vùng nhớ và sau đó gọi hàm RtlDecompressBuffer để giải nén dữ liệu với tham số truyền cho hàm là COMPRESSION_FORMAT_LZNT1.
Tiếp tục sử dụng IDA để phân tích shellcode này, nhiệm vụ chính của nó là giải nén ra một Dll và gọi tới hàm được export của Dll này để thực thi. Hàm thực hiện nhiệm vụ này được tôi đặt tên là f_load_dll_from_memory:
Code tại hàm này đầu tiên sẽ lấy ra địa chỉ base của kernel32.dll dựa vào giá trị hash được tính toán trước là 0x6A4ABC5B. Giá trị hash này cũng đã từng được chúng tôi đề cập trong bài phân tích này.
Tiếp theo sẽ lấy địa chỉ hàm API GetProcAddress:
Sau đó bằng kĩ thuật stackstring, shellcode cấu thành tên các hàm APIs và lấy địa chỉ của các hàm sau:
Tiếp theo, shellcode thực hiện cấp phát vùng nhớ (compressed_buf) có kích thước 0x2E552, sau đó đọc dữ liệu từ offset 0x1592 (on disk) và thực hiện vòng lặp xor với key là 0x72 để điền dữ liệu vào compressed_buf. Trên thực tế, kích thước của compressed_buf là 0x2E542, tuy nhiên 16 bytes đầu của nó được dùng để lưu thông tin về signature, uncompressed_size, compressed_size nên được cộng thêm 0x10.
Shellcode tiếp tục cấp phát vùng nhớ (uncompressed_buf) có kích thước là 0x4C000 và gọi hàm RtlDecompressBuffer để giải nén dữ liệu tại compressed_buf vào uncompressed_buf với định dạng nén là COMPRESSION_FORMAT_LZNT1.
Dựa vào kết quả phân tích ở trên, dễ dàng có được file Dll đã giải nén (tuy nhiên file này đã bị hủy thông tin header):
Sửa lại thông tin của header và kiểm tra bằng PE-bear, Dll này có tên gốc là RFPmzNfQQFPXX và chỉ export một hàm duy nhất là Main:
Quay trở lại với shellcode, sau khi giải nén ra được Dll trên memory, nó sẽ tiến hành nhiệm vụ của một loader thực hiện mapping Dll này vào một vùng nhớ mới. Sau đó, gọi tới hàm mà Dll này export (ở đây là hàm Main) để thực thi nhiệm vụ chính:
Lưu ý: Tại thời điểm phân tích shellcode này, chúng tôi vẫn chưa khẳng định nó là một biến thể của mã độc PlugX, mà chỉ đặt ra câu hỏi nghi ngờ. Chỉ tới khi phân tích Dll được giải nén ở trên, chúng tôi mới khẳng định chắc chắn đây là biến thể của PlugX và đặt lại tên các trường trong struct cho tường minh lại như trên.
4. Phân tích Dll đã giải nén
Chúng tôi sẽ không đi sâu vào phân tích chi tiết Dll này mà chỉ cung cấp những thông tin cần thiết để chứng minh đây là biến thế của PlugX cũng như quá trình giải mã thông tin cấu hình mà mã độc sẽ sử dụng.
4.1. Cách PlugX gọi hàm API
Ở biến thể này, thông tin về các hàm API được lưu tại các xmmword, sau đó được nạp vào thanh ghi xmm0 (128-bit), phần còn thiếu của tên hàm sẽ được nạp thông qua stack. Mã độc thực hiện lấy handle của Dll tương ứng tới các hàm API này, sau đó sử dụng GetProcAddress để lấy ra địa chỉ hàm API cần dùng:
4.2. Tạo thread chính để thực thi
Mã độc thực hiện điều chỉnh các token SeDebugPrivilege và SeTcbPrivilege cho chính tiến trình của nó nhằm có được quyền truy cập đầy đủ tới các tiến trình hệ thống. Sau đó, nó tạo một thread chính được đặt tên là “bootProc”:
4.3. Giao tiếp với C2
Mã độc có thể giao tiếp với C2 thông qua các giao thức TCP, HTTP hoặc UDP:
4.4. Các lệnh mã độc thực thi
Mã độc sẽ nhận lệnh từ kẻ tấn công để thực thi các chức năng tương ứng liên quan tới Disk, Network, Process, Registry, v..v...
Dưới đây là bảng tổng kết danh sách toàn bộ cách lệnh mà kẻ tấn công thực hiện thông qua mẫu mã độc này:
Nhóm lệnh |
Lệnh con |
Mô tả |
Disk |
0x3000 |
Lấy thông tin các ổ đĩa |
0x3001 |
Tìm file |
|
0x3002 |
Tìm file đệ quy |
|
0x3004 |
Đọc nội dung file |
|
0x3007 |
Ghi nội dung vào file |
|
0x300A |
Tạo thư mục |
|
0x300C |
Tạo tiến trình trình trên màn hình
ẩn |
|
0x300D |
Thực hiện sao chép, di chuyển, đổi
tên hoặc xóa file |
|
0x300E |
Thay thế tham số biến môi trường bằng
giá trị chỉ định |
|
Nethood |
0xA000 |
Liệt kê các tài nguyên mạng |
Netstat |
0xD000 |
Thu thập thông tin kết nối TCP |
0xD001 |
Thu thập thông tin kết nối UDP |
|
0xD002 |
Thiết đặt trạng thái của kết nối
TCP |
|
Option |
0x2000 |
Khóa màn hình của máy |
0x2001 |
Tắt máy ngay lập tức |
|
0x2002 |
Khởi động lại máy |
|
0x2003 |
Tắt máy an toàn |
|
0x2005 |
Hiển thị thông báo |
|
PortMap |
0xB000 |
Thực hiện port mapping |
Process |
0x5000 |
Liệt kê danh sách các tiến trình |
0x5001 |
Liệt kê danh sách các modules |
|
0x5002 |
Tắt tiến trình được chỉ định |
|
RegEdit |
0x9000 |
Thu thập thông tin registry |
0x9001 |
Tạo registry |
|
0x9002 |
Xóa registry |
|
0x9003 |
Sao chép registry |
|
0x9004 |
Liệt kê thông tin các giá trị của
một key cụ thể |
|
0x9005 |
Thiết lâp giá trị cho một key cụ
thể |
|
0x9006 |
Xóa giá trị của một key cụ thể |
|
0x9007 |
Lấy giá trị của một key |
|
Service |
0x6000 |
Thu thập cấu hình của service |
0x6001 |
Thay đổi các thông số cấu hình service |
|
0x6002 |
Khởi chạy service |
|
0x6003 |
Gửi mã điều khiển tới service |
|
0x6004 |
Xóa service |
|
Shell |
0x7002 |
Tạo pipe và thực thi command line |
SQL |
0xC000 |
Lấy thông tin nguồn dữ liệu |
0xC001 |
Thu thập thông tin trình điều khiển |
|
0xC002 |
Thực thi câu lệnh SQL |
|
Telnet |
0x7100 |
Thiết lập telnet server |
Screen |
0x4000 |
Mô phỏng hoạt động trên giao thức
RDP |
0x4100 |
Chụp ảnh màn hình |
|
KeyLog |
0xE000 |
Thực hiện chức năng của key logger, lưu vào file "%allusersprofile%\MSDN\6.0\USER.DAT" |
4.5. Giải mã thông tin cấu hình
của PlugX
Như đã phân tích ở trên, mã độc sẽ kết nối tới địa chỉ C2 thông qua các giao thức HTTP, TCP hoặc UDP tùy vào cấu hình được chỉ định. Vậy cấu hình này được lưu ở đâu? Với những mẫu cũ mà chúng tôi đã từng phân tích (1, 2, 3, 4) thì cấu hình của PlugX thường được lưu tại section .data với độ lớn là 0x724 (1828) bytes.
Quay trở lại với mẫu đang phân tích, chúng tôi thấy rằng trước bước thực hiện kiểm tra các tham số truyền vào khi mã độc thực thi, nó sẽ gọi tới hàm thực hiện nhiệm vụ giải mã cấu hình:
Đi sâu vào hàm này, kết hợp với việc debug thêm từ shellcode, đặt lại tên các trường trong struct đã tạo, chúng tôi có được thông tin:
- Cấu hình của PlugX được nhúng trong shellcode và bắt đầu từ offset 0x69.
- Độ lớn của cấu hình là 0x0150C (5388) bytes.
- Key giải mã là 0xB4.
Với toàn bộ thông tin đầy đủ ở trên, hoàn toàn có thể giải mã được thông tin cấu hình một cách dễ dàng:
IP |
Port |
86.78.23.152 |
53 |
86.78.23.152 |
22 |
86.78.23.152 |
8080 |
86.78.23.152 |
23 |
Ngoài danh sách các địa chỉ C2 như trên, còn có thêm thông tin liên quan đến thư mục được tạo trên máy nạn nhân để chứa các file của mã độc cũng như tên của service có thể được tạo:
Để dễ dàng hơn, tôi đã viết một python script để tự động trích xuất thông tin cấu hình cho biến thể này. Kết quả sau khi chạy script như sau:
5. Kết luận
Các nhà nghiên cứu của CrowdStrike lần đầu tiên công bố thông tin về Mustang Panda vào tháng 6 năm 2018, sau khoảng một năm quan sát các hoạt động của nhóm này. Tuy nhiên, theo nghiên cứu và tổng hợp từ nhiều công ty an ninh mạng khác nhau thì nhóm APT này đã tồn tại trong hơn một thập kỷ với các biến thể khác nhau được tìm thấy trên khắp thế giới. Mustang Panda, được cho là nhóm tin tặc có trụ sở hoạt động tại Trung Quốc, được đánh giá là một trong những nhóm APT có động cơ cao, áp dụng kĩ thuật tinh vi để lây nhiễm và cài cắm mã độc, mục tiêu có được quyền truy cập vào máy của nạn nhân để từ đó thực hiện các hoạt động gián điệp và đánh cắp thông tin.
Trong bài viết này, chúng tôi đã phân tích các kĩ thuật mà dòng PlugX RAT áp dụng để thực thi và tránh bị phát hiện. Qua đó, có thể thấy nhóm APT này vẫn đang hoạt động và liên tục tìm cách cải tiến kĩ thuật. VinCSS sẽ tiếp tục tìm kiếm các mẫu và biến thể khác có thể được liên kết với biển thể mà chúng tôi phân tích trong bài viết này.
6. Tài liệu tham khảo
- [RE012-1]Phân tích mã độc lợi dụng dịch Covid-19 để phát tán giả mạo “Chỉ thị của thủ tướngNguyễn Xuân Phúc” - Phần 1
- [RE012-2]Phân tích mã độc lợi dụng dịch Covid-19 để phát tán giả mạo “Chỉ thị của thủ tướngNguyễn Xuân Phúc” - Phần 2
- PlugX:A Talisman to Behold
- THOR: Previously Unseen PlugX Variant Deployed During Microsoft Exchange Server Attacks byPKPLUG Group
- Mustang Panda deploys a new wave of malware targeting Europe
- BRONZE PRESIDENT Targets Russian Speakers with Updated PlugX
- China-Based APT Mustang Panda Targets Minority Groups, Public and Private SectorOrganizations
7. Tổng hợp IOCs
log.dll -
db0c90da56ad338fa48c720d001f8ed240d545b032b2c2135b87eb9a56b07721 |
log.dll -
84893f36dac3bba6bf09ea04da5d7b9608b892f76a7c25143deebe50ecbbdc5d |
log.dll -
3171285c4a846368937968bf53bc48ae5c980fe32b0de10cf0226b9122576f4e |
log.dll - da28eb4f4a66c2561ce1b9e827cb7c0e4b10afe0ee3efd82e3cc2110178c9b7a |
log.dat - 2de77804e2bd9b843a826f194389c2605cfc17fd2fafde1b8eb2f819fc6c0c84 Decrypted config: [+]
Folder name: %ProgramFiles%\BitDefender Update [+]
Service name: BitDefender Crash Handler [+]
Proto info: HTTP:// [+]
C2 servers: 86.78.23.152:53 86.78.23.152:22 86.78.23.152:8080 86.78.23.152:23 [+]
Campaign ID: 1234 |
log.dat - 0e9e270244371a51fbb0991ee246ef34775787132822d85da0c99f10b17539c0 Decrypted config: [+]
Folder name: %ProgramFiles%\BitDefender Update [+]
Service name: BitDefender Crash Handler [+]
Proto info: HTTP:// [+]
C2 servers: 86.79.75.55:80 86.79.75.55:53 86.79.75.46:80 86.79.75.46:53 [+]
Campaign ID: 1234 |
log.dat - 3268dc1cd5c629209df16b120e22f601a7642a85628b82c4715fe2b9fbc19eb0 Decrypted config: [+]
Folder name: %ProgramFiles%\Common Files\ARO 2012 [+]
Service name: BitDefender Crash Handler [+]
Proto info: HTTP:// [+]
C2 servers: 86.78.23.152:23 86.78.23.152:22 86.78.23.152:8080 86.78.23.152:53 [+]
Campaign ID: 1234 |
log.dat - 02a9b3beaa34a75a4e2788e0f7038aaf2b9c633a6bdbfe771882b4b7330fa0c5
(THOR PlugX) Decrypted config: [+]
Folder name: %ProgramFiles%\BitDefender Handler [+]
Service name: BitDefender Update Handler [+]
Proto info: HTTP:// [+]
C2 servers: www.locvnpt.com:443 www.locvnpt.com:8080 www.locvnpt.com:80 www.locvnpt.com:53 [+]
Campaign ID: 1234 |
Dang Dinh Phuong – Threat Hunter
Tran Trung Kien (aka m4n0w4r) - Malware Analysis Expert
R&D Center - VinCSS (a member of Vingroup)
No comments:
Post a Comment