Tofsee, znany również pod nazwą Gheg, to kolejny analizowany przez nas botnet. Jego głównym celem jest rozsyłanie spamu, jednak może on wykonywać także inne zadania. Jest to możliwe dzięki modularnej budowie malware’u – składa się on z głównego pliku wykonywalnego (tego, którym infekuje się użytkownik), który później pobiera z serwera C2 kilkanaście dodatkowych bibliotek DLL rozszerzających działanie kodu poprzez nadpisywanie niektórych wywoływanych funkcji swoimi własnymi. Przykładem takiej DLL-ki może być moduł do rozprzestrzeniania się poprzez postowanie wiadomości na Facebooku i VKontakte (rosyjskim portalu społecznościowym).
Komunikacja bota z botmasterem odbywa się przy użyciu niestandardowego protokołu opartego na TCP. Pierwszą wiadomość zawsze wysyła serwer tuż po nawiązaniu połączenia – zawiera ona przede wszystkim jednorazowy 128-bajtowy klucz użyty do szyfrowania dalszych komunikatów. Nie da się zatem odkodować komunikacji, jeśli się jej nie słucha od początku.
W każdym momencie bot trzyma w pamięci listę zasobów. Początkowo jest ona niemal pusta i zawiera jedynie podstawowe informacje, jak identyfikator bota, ale szybko jest uzupełniana przez dane otrzymane od serwera w kolejnych wiadomościach. Zasoby przyjmują różne formy – może to być np. lista tematów do wykorzystania w mailu, ale i także biblioteki DLL rozbudowujące funkcje bota. Dodatkowo, jeden z zasobów – work_srv – zawiera listę adresów IP serwerów C2. Jest to jedna z pierwszych wiadomości wysyłanych przez serwer i, co ciekawe, może nie zawierać samego siebie (wówczas połączenie jest kończone i wybierany jest losowy serwer z listy). Tak też się zazwyczaj dzieje podczas połączenia z C2 zapisanym na stałe w próbce – pełni on zatem rolę „wskaźnika” na rzeczywiste serwery.
Wysyłane emaile są tworzone w sposób losowy – Tofsee wykorzystuje do tego celu specjalny język skryptowy – przykładowy plik znajduje się niżej, w analizie technicznej. Zawiera on wydzielone pola, które zostaną losowo zastąpione pewnymi ciągami znaków – np. %RND_SMILE zostanie zmienione na jedną z kilkunastu emotikon. Dzięki temu prostsze filtry spamowe mogą je przepuścić.
Analiza techniczna
Lista adresów IP serwerów C2 jest zapisana w samym pliku w postaci zaszyfrowanej. Algorytm szyfrowania jest bardzo prosty – opiera się na XOR-owaniu ze stałym kluczem.
Odszyfrowane dane to trzy stałe IP wraz z portem, który w analizowanej próbce był równy 443 dla wszystkich odkodowanych adresów. Prawdopodobnie więc bot próbuje uniknąć wykrycia poprzez używanie portu przeznaczonego dla ruchu SSL/TLS.
Protokół komunikacji
Po nawiązaniu połączenia TCP, pierwsza wiadomość wychodzi od serwera. Ma ona zawsze 200 bajtów długości. Wygląda jednak na to, że końcowe bajty są niewykorzystane i zarezerwowane – być może w celach późniejszej rozbudowy protokołu. Także w tym miejscu wykorzystana jest prosta obfuskacja zawartości poprzez operacje bitowe:
Odszyfrowane dane składają się na nastepującą strukturę (nie poznaliśmy znaczenia wszystkich pól):
Od tego momentu, cała komunikacja (zarówno wiadomości przychodzące, jak i wychodzące) jest szyfrowana 128-bajtowym kluczem z pierwszej wiadomości. Klucz ten ulega modyfikacji przy każdym wysłanym/odebranym bajcie, zatem nie da się odszyfrować komunikacji, jeśli się jej nie słucha od początku. XOR-owanie jest użyte w taki sposób, że jedna funkcja służy zarówno do szyfrowania, jak i deszyfrowania:
Parametry:
- data – surowe dane
- key – krótki, 7-bajtowy klucz, inicjalizowany przed pierwszą wiadomością bajtami „abcdefg”
- main_key – 128-bajtowy klucz z powitania
- it – liczba już odebranych/wysłanych bajtów
Wszystkie wiadomości (poza powitalną) składają się z nagłówka oraz właściwych danych. Nagłówek zawiera następujące pola:
Protokół wspiera kompresję danych, ale jest ona używana tylko dla większych wiadomości. Pola op, subop1 i subop2 to pewne stałe, definiujące typ wiadomości. W analizowanej próbce widać kod obsługujący wiele różnych typów, ale w praktyce jedynie nieliczne z nich są wykorzystywane.
Payload jest wysyłany zaraz po nagłówku. Jego dokładna struktura zależy od typu wiadomości – kilka z nich przedstawię poniżej.
Pierwsza wiadomość wysyłana przez bota ma typy {1,0,0} (kolejno op, subop1, subop2) i składa się na dość pokaźną strukturę:
Niektóre z nazw pól (np. lid_file_upd) dostaliśmy „za darmo”, ponieważ bot zapisywał je pod takimi właśnie indeksami do wewnętrznej struktury danych mapującej nazwy zmiennych na ich zawartość. Znaczenia innych musieliśmy się domyślić sami.
Odpowiedź serwera może mieć różną formę. Najprostsza z nich następuje gdy op=0 – oznacza to pustą odpowiedź (lub koniec transmisji składającej się z wielu wiadomości). Jeśli op=2, to serwer wysyła nam nowy zasób – payload wiadomości jest wówczas taką strukturą:
Zazwyczaj tuż po połączeniu się z C2 wpisanym na sztywno do próbki pierwszą wiadomością po powitalnej, jaką bot otrzymuje, jest pojedynczy zasób o nazwie work_srv. Znajduje się w nim lista kilku adresów IP oraz portów (już różnych niż 443), na których nasłuchują właściwe serwery C2. Wówczas następuje rozłączenie z dotychczasowym serwerem i po chwili bot rozpoczyna komunikację od nowa z losowym z świeżo otrzymanych serwerów C2.
Jeśli op=1, wiadomość ma różne znaczenie w zależności od subop2 oraz, dodatkowo, pierwszych czterech bajtów payloadu (które najwyraźniej w tym wypadku pełnią funkcję flag). Przykładowo, jeśli spełnione są następujące warunki: op=1, subop2&1=0, flags=4, to jest to żądanie C2, aby bot wysłał mu wszystkie posiadane zasoby. Odpowiedzią bota jest wówczas skonkatenowana lista zasobów o postaci podobnej do wyżej pokazanej, po czym serwer wysyła dziesiątki wiadomości typu 2 (zawierające zasób) – zasoby, których bot jeszcze nie ma.
Zasoby
Każdy zasób jest identyfikowany typem – niewielką liczbą (nie większą niż 40, chociaż większość jest nawet mniejsza niż 10) oraz krótką nazwą, np. „priority”. Z analizowanych przez nas typów najciekawsze to:
Typ 5
Zawiera pluginy w postaci bibliotek DLL. Ponieważ pojedyncze symbole będące pozostałościami po kompilacji zostały w nich zostawione, mogliśmy się łatwo domyślić, jakie zadania mają poszczególne pluginy. W czasie analizy Tofsee pobierał następujące pluginy:
Nazwa zasobu – numer | Nazwa biblioteki | Hash MD5 DLL-ki |
---|---|---|
1 | ddosR.dll | fbc7eebe4a56114e55989e50d8d19b5b |
2 | antibot.dll | a3ba755086b75e1b654532d1d097c549 |
3 | snrpR.dll | 385b09563350897f8c941b47fb199dcb |
4 | proxyR.dll | 4a174e770958be3eb5cc2c4a164038af |
5 | webmR.dll | 78ee41b097d402849474291214391d34 |
6 | protect.dll | 624c5469ba44c7eda33a293638260544 |
7 | locsR.dll | 2d28c116ca0783046732edf4d4079c77 |
10 | hostR.dll | c90224a3f8b0ab83fafbac6708b9f834 |
11 | text.dll | 48ace17c96ae8b30509efcb83a1218b4 |
12 | smtp.dll | 761e654fb2f47a39b69340c1de181ce0 |
13 | blist.dll | e77c0f921ef3ff1c4ef83ea6383b51b9 |
14 | miner.dll | 47405b40ef8603f24b0e4e2b59b74a8c |
15 | img.dll | e0b0448dc095738ab8eaa89539b66e47 |
16 | spread1.dll | 227ec327fe7544f04ce07023ebe816d5 |
17 | spread2.dll | 90a7f97c02d5f15801f7449cdf35cd2d |
18 | sys.dll | 70dbbaba56a58775658d74cdddc56d05 |
19 | webb.dll | 8a3d2ae32b894624b090ff7a36da2db4 |
20 | p2pR.dll | e0061dce024cca457457d217c9905358 |
Sądząc po nazwach, Tofsee poza spamowaniem ma także inne funkcje, jak koordynowany DDoS, czy kopanie kryptowalut (jak się okazuje, jednym z pobieranych zasobów jest właśnie koparka Litecoinów).
Typ 11
Zawiera okresowo uaktualniane skrypty w nietypowym języku, których zadaniem jest rozsyłanie spamu. Przykładowy skrypt:
Język ma składnię delikatnie podobną do assemblera – na przykład „J” na początku linii oznacza „jump”, a „L” – zdefiniowanie etykiety (ang. label). Wewnątrz skryptu zawarte są także makra, które w trakcie wykonywania zamieniane są na inny tekst – przykładem może być %ATTNAME1.
Typ 7
Zawiera makra ogólnego przeznaczenia. Nazwa tych zasobów jest taka sama, jak makro, które opisują, np. %DATE_RAN_SUB (przypuszczalnie skrót od angielskiego „DATE RANDOM SUBJECT”). Zawartość zasobu to lista różnych podstawień oddzielonych znakami nowej linii. Przykładowa zawartość:
Ponieważ niektóre zmienne potrzebują skorzystać dosłownie ze znaku nowej linii, wprowadzone są także specjalne zmienne, jak %SYS_N służące właśnie do tego celu.
Typ 8
Zawiera makra lokalne. Ponieważ różne skrypty mogą chcieć korzystać ze zmiennych o tych samych nazwach, ale innej zawartości, niektóre makra są lokalne. Nazwy zasobów mają postać NUM%VAR, np. 1819%TO_NAME, gdzie 1819 to numer porządkowy skryptu będącego zakresem stosowania makra %TO_NAME.
Podstawienia zmiennych są rekurencyjne, co zresztą widać na wyżej wymienionym przykładzie %DATE_RAN_SUB – makra potrafią zawierać w swoim rozwinięciu inne makra. Ponadto język wspiera bardziej skomplikowane konstrukcje w rodzaju %RND_DIGIT[3], oznaczające trzy losowe cyfry (co jest często używane podczas generowania losowego koloru w postaci szesnastkowej), a także %{%RND_DEXL}{ %RND_SMILE}{}, oznaczający wybór jednego z %RND_DEXL, %RND_SMILE i pustego tekstu. Widać więc, że język jest dość elastyczny.
Reszta typów zawiera zaledwie pojedyncze zasoby i dlatego pominę ich opis w tym artykule.
Na zakończenie, załączam hash analizowanej próbki oraz reguły YARA pasujące do tej rodziny malware’u.
ae0d32e51f36ce6e6e8c5ccdc3d253a0 - analizowana próbka (przed rozpakowaniem)
Reguły YARY: