Khi nhận được mẫu mã độc, mình phân tích sơ bộ và tìm kiếm những bài phân tích về nó nhưng sau một buổi tìm kiếm thì mình thấy rất ít bài viết liên quan đến mẫu mã độc này, các bài viết phân tích cũng chỉ dừng lại khi mã độc gửi dữ liệu đến C&C vì vậy mình quyết định mổ xẻ mẫu này để hiểu hơn về nó.

Thông tin mã độc

  • Tên file: To-Do.doc
  • SHA256: 1e3f68e6bee74e8477f9f270068a4d20c89f84b1bc42e517e71c69f1417a5ce5

Stage 1: Phân tích ActiveMime, VBA

Mã độc là file ActiveMime với đuôi mở rộng .doc, khi mở file và click Enable Content, mã độc sẽ được kích hoạt

Nội dung khi mở mã độc

Sử dụng notepad++ xem qua nội dung file, mình thấy một số nội dung MSO được mã hóa dưới dạng base64.

mso data

Mình nghi ngờ những nội dung mso này độc hại và tìm kiếm thông tin liên quan về chúng, sau đó tiến hành giải mã và mình nhận ra luồng phân tích của mình vẫn đang đúng hướng :)). Khi giải mã base64 sẽ thu được activestream bắt đầu từ offset 0x32 chứa ole object được nén dưới dạng zlib, mình tiến hành viết script để lấy ole stream, script:

import zlib
import base64
import re
import sys

def get_mimestream(filepath):
    mso = ""
    data = open(filepath,"rb").read()  
    found = re.search("^Content-Location:\x20file:///[^\n]{0,999}?editdata\.mso.*?\r\n\r\n^((?:[A-Za-z0-9+/\r\n]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)\r\n",data,re.DOTALL|re.MULTILINE|re.IGNORECASE)
    if found:
        mso = found.group(1)
    return mso

def main():
    b64activemime = get_mimestream(sys.argv[1])    
    activemimestream = base64.b64decode(b64activemime)
    zobj = zlib.decompressobj()
    olestream = zobj.decompress(activemimestream[0x32:])
    open(sys.argv[1]+".ole","wb").write(olestream)

if __name__ == '__main__':
    main()

Sau khi có được ole object mình sử dụng oledump.py để trích xuất VBA(Macro) được giấu trong mã độc, macro sẽ được thi mỗi khi người dùng mở file .doc và click vào enable macro, một số hàm, thư viện được khai báo đáng chú ý được mình hightlight màu vàng:

Trích xuất vba code

Khi mình phân tích và debug Macro đã trích xuất, hành vi của nó như sau:

  • Ghi file “C: \ ProgramData \ Microsoft \ User Account Pictures \ guest.bmp“
  • Sao chép file vừa ghi vào “C: \ ProgramData \ Microsoft Outlook Sync \ guest.bmp“
  • Tạo và hiển thị một file có tên “Document.doc”
  • Đổi tên file từ “guest.bmp” thành “background.dll”

File dll được drop

  • Thực thi DLL bằng cách gọi hàm export OpenProfile

Stage 2: Phân tích background.dll

File background.dll có 3 hàm export DllGetClassObject, OpenProfile, DllEntryPoint. Mình sẽ phân tích DllEntryPoint đầu tiên vì nó là main entry.

Ngay từ đầu hàm DllEntryPoint(), mã độc đã được làm rối khiến cho IDA không nhận dạng đúng luồng thực thi, label jump. Nếu để như vậy phân tích mã độc này thực sự rất khó khăn, mình đã tiến hành tìm hiểu cách làm rối và loại bỏ nó.

Một phần hàm DllEntryPoint

Sau khi tìm hiểu, mình tóm tắt kỹ thuật như sau:

  • (1) Phần code thực thi bình thường.
  • (2)Lệnh dùng cho so sánh thiết lập các cờ cho lệnh nhảy phía dưới

(gồm test/cmp)

  • (3) Data dùng cho việc so sánh: là các giá trị qword, dword, word và byte.
  • (4) Các giá trị constant tương ứng để so sánh
  • (5) Lệnh nhảy: short jump hoặc far jump
  • (6) Giá trị địa chỉ nhảy đến
  • (7) Mã rác
  • (8) Địa chỉ nhảy đến

Cấu trúc code kỹ thuật làm rối

Với jump short, điều kiện jump luôn đúng nên các lệnh jump sẽ luôn được thực hiện. Còn jump far, điều kiện luôn sai, vì thế các lệnh jump sẽ không được thực hiện.

Jump far

Để deobfuscate (loại bỏ làm rối), mình viết python script tìm kiếm các partern của lệnh test/cmp + jump và thay thế chúng bằng lệnh NOP (0x90), script đầy đủ:

import pefile
import sys
import re

patterns = {"jshort": [b'\x66\xF7\x05......[\x70-\x79]',
					   b'\xF6\x05.....[\x70-\x79]',					   
					   b'\x48\xF7\x05........[\x70-\x79]',
					   b'\xF7\x05........[\x70-\x79]',
					   b'\x80\x3D.....[\x70-\x79]',
					   b'\x66\x81\x3D......[\x70-\x79]',
					   b'\x48\x81\x3D........[\x70-\x79]',
					   b'\x81\x3D........[\x70-\x79]'],
			 "jfar" : [b'\x66\xF7\x05......\x0F[\x80-\x89]',
					   b'\xF6\x05.....\x0F[\x80-\x89]',				   
					   b'\x48\xF7\x05........\x0F[\x80-\x89]',		
					   b'\xF7\x05........\x0F[\x80-\x89]',							   		
					   b'\x80\x3D.....\x0F[\x80-\x89]', 		
					   b'\x66\x81\x3D......\x0F[\x80-\x89]',
					   b'\x48\x81\x3D........\x0F[\x80-\x89]',			
					   b'\x81\x3D........\x0F[\x80-\x89]']}

def main():
	datas = bytearray(open(sys.argv[1], 'rb').read())
	pe = pefile.PE(data = datas)
	text_section = pe.sections[0]
	data = text_section.get_data()
	basetext = text_section.PointerToRawData
	
	patterns_jshorts = patterns.get("jshort")
	for pat in patterns_jshorts:
		matchs = re.finditer(pat, data, re.DOTALL)
		for m in matchs:
			numofbyte = m.end() - m.start() + 1 + data[m.end()]
			data[m.start() : m.start() + numofbyte] = b'\x90'*numofbyte

	patterns_jfars = patterns.get("jfar")
	for pat in patterns_jfars:
		matchs = re.finditer(pat, data, re.DOTALL)
		for m in matchs:
			numofbyte = m.end() - m.start() + 4
			data[m.start() : m.start() + numofbyte] = b'\x90'*numofbyte

	datas[basetext : basetext + len(data)] = data
	open("deobf_"+sys.argv[1], 'wb').write(datas)

if __name__ == '__main__':
	main()

Sau khi deobfuscate, mình thấy cuộc này lại trở nên tươi đẹp xD và điều quan trọng bây giờ là mình có thế dùng F5 thần thánh

Hàm DllEntryPoint sau khi deobfuscate

Hàm DllEntryPoint có nhiệm vụ chính là khởi tạo giá trị cho các biến toàn cục

Mã độc khởi tạo giá trị cho biến toàn cục

Bây giờ mình quay lại hàm OpenProfile – được gọi trực tiếp từ VBA code, hàm này tạo 1 thread thực thi và tạo task scheduler để đạt được persistence.

Hàm OpenProfile

 

Mã độc sử dụng COM object để tạo task scheduler:

Mã độc khởi tạo tài nguyên để truy xuất COM object

Task scheduler có tên Winrar Update thực thi mã độc mỗi 10 phút một lần, khi thực hiện sẽ gọi đến hàm export DllGetClassObject.

 

Task scheduler sau khi mã độc tạo

Mỗi khi task scheduler thực hiện, Hàm export DllGetClassObject sẽ được thực thi, hàm này tạo process với command line rundll32 kernel32.dll, Sleep. Sau đó tiến hành inject 1 file dll khác vào memory process vừa tạo và thực thi

Mã độc tạo mới process để inject

Hàm Inject()

Sau khi mã độc inject, mình dump memmory của process rundll32 thu được 1 file dll – dumped.dll

Stage 3: Dump memory, phân tích dumped.dll

Tương tự như background.dll, dumped.dll cũng được làm rối. Mình chạy script ở stage2 để loại bỏ code làm rối.

Khi inject mã độc gọi hàm PzbexWXxRuXzsAG() được export trong dumped.dll, hàm này có nhiệm vụ reflective load chính nó và sẽ thực thi DllMain của nó:

Hàm DllMain đầu tiên mã độc thu thập thông tin trên máy nạn nhân gồm địa chỉ vật lý, username, computername, tiến trình đang chạy; liệt kê file, thư mục trong thư mục C:\programdata:

Mã độc thu thập thông tin máy nạn nhân

 

Mã độc thu thập tiến trình đang thực thi trên máy nạn nhân

Mã độc truy xuất file, folder trong programdata folder

Mã độc mã hóa dữ liệu thu thập bằng thuật toán mã hóa DES với key 0xE4, 0x7D, 0xBD, 0x2A, 0x5D, 0xA8, 0xCE, 7

Một phần config thuật toán DES được mã độc sử dụng

Dữ liệu sau khi giải mã được gửi đến C&C được đặt trên Glitch https://confusion-cerulean-samba.glitch[.]me/e1db93941c

Mã độc gửi dữ liệu đến C&C

Sau đó, server phản hồi dữ liệu là dạng 7z có kích thước trên 128byte thì sẽ tiến hành ghi dữ liệu vào C:\programdata\Microsotf edge download\properties.bin

Mã độc kiểm tra và ghi dữ liệu phản hồi từ C&C

Cuối cùng, mã độc tạo task tương tự như stage2 dưới tên chrome update và extract dữ liệu được tải xuống từ C&C để thực thi payload cũng như persistence

 

Tasksch config được tạo bởi mã độc

Đối với mẫu mã độc này, mình chỉ có thể phân tích đến đây vì không thể tải xuống payload từ C&C của kẻ tấn công.

Nhìn chung, đây là mã độc nhằm mục đích thu thập thông tin máy nạn nhân, tải xuống những file mã độc khác và thực thi chúng.

Dựa vào kết quả phân tích cũng như nguồn threat intelligent, mình cho rằng đây là 1 mẫu mã độc liên quan đến nhóm APT32 và Việt Nam cũng là nạn nhân của nhóm tấn công này.

Cảm ơn mọi người đã quan tâm theo dõi!

By,

RE Team

1.099 lượt xem