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