Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book

Deobfuskacja JavaScript

24 lutego 2015, 14:40 | Teksty | komentarze 2
JavaScript to skryptowy język programowania używany głównie na stronach WWW. Aby przekonać się o tym, jak szeroko jest on stosowany, wystarczy zainstalować plugin przeglądarkowy NoScript i odwiedzić dowolną stronę internetową – brak włączonej obsługi skryptów pozbawi zapewne stronę sporej części funkcjonalności. Skrypty JS odpowiadają przede wszystkim za interakcję z użytkownikiem: dynamicznie ładowane treści stron, reagowanie na wciśnięcia przycisków czy ruchy myszki.

Umiejętnie zastosowany JavaScript może jednak służyć do wykonania złośliwego kodu na komputerze nieświadomego użytkownika. W jaki sposób analizować takie zagrożenia?

Aby przekierować nieświadomego użytkownika na stronę, która będzie próbować wykonać złośliwy kod w przeglądarce, wystarczy prosta linijka:

<script>
document.location="http://www.sekurak.pl";
</script>

listing 1.Już pierwszy rzut oka na powyższy kod mówi nam, za co jest on odpowiedzialny. Możemy utrudnić identyfikację kodu, na przykład rozbijając adres URL na kilka zmiennych:

<script>
a="sek";f="urak";c=".pl";d="http";e="://www.";
document.location=d+e+a+f+c;
</script>

listing 2.Powyższy kod generuje dokładnie tę samą akcję, co fragment z pierwszego przykładu, chociaż wymaga odrobiny skupienia, aby zrozumieć sens jego działania. Aby jeszcze trochę utrudnić, spróbujmy zakodować łańcuchy znaków, zmienić nazwy zmiennych, a całość rozrzucić po losowych tablicach:

<script>
var _0x834b=["\x73\x65\x6B","\x75\x72\x61\x6B","\x2E\x70\x6C","\x68\x74\x74\x70","\x3A\x2F\x2F\x77\x77\x77\x2E","\x6C\x6F\x63\x61\x74\x69\x6F\x6E"];a=_0x834b[0];b=_0x834b[1];c=_0x834b[2];d=_0x834b[3];e=_0x834b[4];document[_0x834b[5]]=d+e+a+b+c;
</script>

listing 3.Nadal zbyt jasno widać, że próbujemy przekierować użytkownika? Zastosujmy wobec tego jeszcze parę zabiegów i otrzymamy bardzo zawikłany kod, który realizuje dokładnie to samo zadanie, co kod z pierwszego listingu:

<script>
eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('g 6=["\\t\\u\\j","\\w\\n\\f\\j","\\i\\k\\h","\\q\\9\\9\\k","\\r\\l\\l\\8\\8\\8\\i","\\h\\m\\o\\f\\9\\s\\m\\v"];g 7=[6[0],6[1],6[2],6[3],6[4],6[5]];a=7[0];b=7[1];c=7[2];d=7[3];e=7[4];p[7[5]]=d+e+a+b+c;',33,33,'||||||_0x3725|_0x64df|x77|x74||||||x61|var|x6C|x2E|x6B|x70|x2F|x6F|x72|x63|document|x68|x3A|x69|x73|x65|x6E|x75'.split('|'),0,{}))
</script>

listing 4.

 Jeśli ktoś potrafi bez problemu rozkodować schemat działania powyższego skryptu, może zrezygnować z czytania reszty artykułu.

Pozostałych Czytelników zapraszam do zapoznania się z narzędziami służącymi do analizy obfuskowanego kodu JavaScript.

Obfuskacja kodu

Obfuskacja (ang. obfuscation) to proces celowej modyfikacji kodu źródłowego w taki sposób, aby był on mniej czytelny, a rezultat jego wykonania nie zmienił się. Obfuskacji nie należy mylić z kompresją (angielski termin to „minification”, polskie “minifikacja” jest dość nieszczęśliwym, chociaż stosowanym tłumaczeniem), która polega na maksymalnym skondensowaniu kodu w celach wydajnościowych, czego wynikiem może być również jego częściowa obfuskacja.

W jakim celu obfuskuje się kod?

Autorzy oprogramowania robią to najczęściej w celu ukrycia działania własnych programów, a hakerzy w celu ukrycia sposobu, w jaki exploit wykonał się na komputerze niczego nieświadomego użytkownika. W trakcie analizy incydentu możemy napotkać na fragmenty kodu JavaScript, który został wykonany w przeglądarce użytkownika. Złośliwy kod może zostać osadzony na stronie internetowej na przykład przez atak XSS lub też wyświetlony użytkownikowi za pośrednictwem odpowiednio przygotowanej reklamy (malvertising). Odszyfrowanie takiego kodu, w celu jego analizy jest możliwe, a dostępne narzędzia ułatwiają tę pracę, automatyzując niektóre jej elementy.

W sieci dostępnych jest sporo obfuskatorów JavaScript, a ich funkcjonalność ograniczona jest jedynie przez fantazję ich twórców. Przyjrzyjmy się ich działaniu dla tego samego kodu źródłowego:

document.location="http://www.sekurak.pl";

listing 5. I tak Javascript Obfuscator pozamienia kodowanie znaków, a funkcję document.location wywoła poprzez argumenty pewnej skomplikowanej tablicy, JS obfuscate wykorzysta funkcję eval(), operacje na łańcuchach znaków oraz funkcje warunkowe, a obfuscator o wdzięcznej nazwie JSFuck wykorzysta fakt, że każdy skrypt JS może zostać zapisany jako łańcuch znaków i zwróci bardzo długi i niezrozumiały kod, zawierający tylko sześć unikalnych symboli: [, ], (, ), + oraz !. Wśród obfuskatorów warto wspomnieć także o projekcie JScrambler, który może służyć nie tylko do obfuskacji, ale także optymalizacji kodu JS. Wykorzystanie JScramblera w celu obfuskacji JavaScriptu wymaga jednak wykupienia abonamentu, chociaż funkcjonalność narzędzia można sprawdzić w bezpłatnym okresie próbnym.

Deobfuskacja

Deobfuskacja kodu jest długim i mozolnym procesem składania części oryginalnego skryptu z zakodowanych różnymi metodami fragmentów. Często w deobfuskacji nie chodzi o uzyskanie oryginalnego kodu (co jest zazwyczaj niemożliwe w racjonalnym czasie), ale o poznanie zasady działania i logiki, ukrytej na przykład w złośliwych skryptach przekierowujących użytkowników na przykład na strony hostujące exploit kity.

1. JSBeautifier

Jednym z elementów obfuskacji kodu jest usuwanie wcięć, spacji oraz znaków nowej linii. JSbeautifier pomoże pozbyć się tego problemu i jednym kliknięciem “upiększy” nasz złośliwy kod, a przy zaznaczonej opcji “Detect packers and obfuscators” dokona wstępnej deobfuskacji kodu. JSbeautifier dostępny jest w postaci bezpłatnej aplikacji webowej.

Strona główna jsbeautifier.org

Strona główna jsbeautifier.org

Kod z listingu 4 został przerobiony przez JSbeautifier do następującej postaci:

var _0x3725 = ["\x73\x65\x6B", "\x75\x72\x61\x6B", "\x2E\x70\x6C", "\x68\x74\x74\x70", "\x3A\x2F\x2F\x77\x77\x77\x2E", "\x6C\x6F\x63\x61\x74\x69\x6F\x6E"];
var _0x64df = [_0x3725[0], _0x3725[1], _0x3725[2], _0x3725[3], _0x3725[4], _0x3725[5]];
a = _0x64df[0];
b = _0x64df[1];
c = _0x64df[2];
d = _0x64df[3];
e = _0x64df[4];
document[_0x64df[5]] = d + e + a + b + c;

listing 6.Jeśli wybierzemy dodatkowo opcje “Unescape printable chars encoded as \xNN or \uNNNN” nasz kod staje się na tyle czytelny, że możemy przewidzieć, jaki będzie rezultat jego wykonania:

var _0x3725 = ["sek", "urak", ".pl", "http", "://www.", "location"];
var _0x64df = [_0x3725[0], _0x3725[1], _0x3725[2], _0x3725[3], _0x3725[4], _0x3725[5]];
a = _0x64df[0];
b = _0x64df[1];
c = _0x64df[2];
d = _0x64df[3];
e = _0x64df[4];
document[_0x64df[5]] = d + e + a + b + c;

listing 7.Zbliżoną funkcjonalnością do JSbeautifier cechuje się na przykład web aplikacja JSPretty.com. Warto sprawdzić wynik deobfuskacji w kilku “upiększaczach” lub używać ich naprzemiennie, ponieważ każdy może inaczej radzić sobie z przesłanym kodem.

2. JSDetox

JSDetox to linuxowe narzędzie do analizy JavaScriptu napisane w języku ruby. Strona domowa projektu zawiera krótki poradnik instalacji JSDetox pod Linuxem, która sprowadza się do pobrania paru paczek związanych z frameworkiem ruby oraz sklonowaniem repozytorium z githuba. Narzędzie uruchamia mały serwer HTTP, domyślnie otwarty na porcie 3000, na którym czeka graficzny interfejs użytkownika. JSDetox umożliwia szerszą analizę złośliwych skryptów JavaScript, zmianę nazw używanych zmiennych, a nawet kontrolowane uruchomienie kodu w emulowanym środowisku. Użyjmy go do kodu z listingu 4, uruchamiając go w JSDetox. W zakładce Execution pojawi się ostrzeżenie, że analizowany JavaScript próbował uruchomić funkcję eval(), a po kliknięciu “Show Code” można zobaczyć, jaki argument przesłany został do tej właśnie funkcji.

JSDetox w akcji.

JSDetox w akcji.

Używając przycisku “Send to Analyze”, możemy otrzymany wynik przekazać do okienka analizy i ponownie użyć opcji formatowania i upiększania. Dużą zaletą JSDetox jest możliwość zmiany nazw zmiennych, dzięki czemu analityk może łatwiej prześledzić, gdzie w skrypcie wykorzystywane są obliczane wartości. Ta prosta refaktoryzacja wymaga jedynie kliknięcia w interesującą nas zmienną i podania jej nowej nazwy. Zmiany należy następnie zatwierdzić i przeanalizować kod ponownie. Używając opcji dostępnych w pakiecie JSDetox, możemy bardzo szybko uporządkować kod z listingu 4 do następującej postaci:

var url = ["sek", "urak", ".pl", "http", "://www.", "location"];
var zmienna = [url[0], url[1], url[2], url[3], url[4], url[5]];
a = zmienna[0];
b = zmienna[1];
c = zmienna[2];
d = zmienna[3];
e = zmienna[4];
document[zmienna[5]] = d + e + a + b + c;

listing 8. Jego dalsza, ręczna analiza nie powinna już przysporzyć większych problemów.

JSDetox posiada wirtualne funkcje, które wspomagają analizę podejrzanych skryptów. Analizowany kod może w dowolnym miejscu zostać przerwany przy użyciu wbudowanej funkcji jsdetox.break(), a każda zmienna może zostać zalogowana do konsoli przy użyciu funkcji jsdetox.puts(). JSDetox może także przetwarzać fragmenty kodu osadzone w elementach strony internetowej oraz pomóc przy analizie umieszczonego shellcode’u.

Mimo ciekawej funkcjonalności JSDetoxowi zdarza się utracić responsywność (w przypadku przesłania do analizy dużej porcji nieparsowalnego kodu) lub też działać inaczej, niż można by się tego spodziewać (przy zmianie nazw zmiennych). Z tego powodu zalecane jest kopiowanie wyników pośrednich do plików tekstowych, aby możliwe było ich przywrócenie w przypadku awarii GUI. Mimo tych małych wad i delikatnych problemów ze stabilnością i użytecznością JSDetox jest bardzo przydatnym narzędziem podczas analizy obfuskowanych plików .js.

3. JSUNPACK

JSUNPACK to webaplikacja do automatycznej analizy kodu JavaScript. Po przekazaniu do JSUNPACK kodu z listingu 4 bardzo szybko otrzymaliśmy informację, że JavaScript próbuje wykonać przekierowanie.

//jsunpack.url documentlocation = http://www.sekurak.pl

listing 9. Na tym jednak analiza się nie zakończyła, JSUNPACK przeanalizował także docelową stronę i przekazał informacje o wszystkich jej mniej lub bardziej podejrzanych fragmentach. W odróżnieniu od JSDetox, JSUNPACK wymaga przesłania danych na zewnętrzny serwer, wobec czego nie powinien być używany do analizy treści zawierających poufne dane. Istnieje co prawda opcja “Private”, której zaznaczenie spowoduje, że analiza nie będzie publicznie dostępna, ale wciąż może być współdzielona z innymi analitykami bezpieczeństwa. Do użytku offline twórcy zalecają skorzystanie z jsunpack-n, który jest silnikiem strony webowej i może zostać uruchomiony lokalnie. Innymi narzędziem podobnym do JSUNPACK jest wepawet.

Analiza złośliwego JavaScriptu

UWAGA! Złośliwy kod powinien być analizowany w wirtualnym, izolowanym środowisku.

Przeanalizujmy zatem JS-redirect wykorzystywany przez Exploit Pack Fiesta. Plik pcap, w którym znajduje się JavaScript, można pobrać ze strony, a złośliwy skrypt znajdziemy w 61 pakiecie podsłuchanej transmisji.

Pierwsze spojrzenie na plik nie nasuwa zbyt wiele koncepcji o jego działaniu, chociaż można się spodziewać, na jaką stronę przekierowuje (autorzy nie zadali sobie za dużo trudu, aby to ukryć):

Wyeksportowany z pliku pcap, zobfuskowany kod JavaScript.

Wyeksportowany z pliku pcap, zobfuskowany kod JavaScript.

W pierwszej kolejności damy popisać się omawianej w artykule web aplikacji JSbeautifier. Wstępna deobfuskacja, a raczej uporządkowanie kodu, daje już pierwsze efekty. Pojawiają się słowa kluczowe, widać podział na funkcje i ogólny zarys struktury skryptu.

Ułożenie kodu w bloki przez JSBeautifier.

Ułożenie kodu w bloki przez JSBeautifier.

W paru miejscach ciągi znaków są intencjonalnie przerwane i połączone przy pomocy operatora dodawania. Możemy wrzucić plik w takiej postaci do JSDetoxa, który połączy je z powrotem, bądź też zrobić to ręcznie w edytorze plików, przy okazji zaznajamiając się z kodem.

Manualne składanie rozłączonych łańcuchów znaków.

Manualne składanie rozłączonych łańcuchów znaków.

Kod jest już podzielony na logiczne bloki, czas spojrzeć, w jaki sposób działa, i odnaleźć miejsce startu. Jedyny fragment, nie objęty funkcjami to początek kodu oraz jego ostatnia linia. Na początku skryptu znajdują się jednak tylko przypisania, wobec czego można spodziewać się, że właściwe działanie zaczyna się od wywołania funkcji lwnScmat0x() z argumentem naKtBhuNiPs.

Pierwsze wywołanie funkcji w kodzie.

Pierwsze wywołanie funkcji w kodzie.

Aby uzyskać bardziej przejrzysty obraz, zmieńmy ich nazwę przy pomocy funkcji Rename wbudowanej w GUI JSDetox. Nowa nazwa nie musi odzwierciedlać działania funkcji, ale powinna być w miarę unikalna. Zmiana nazw funkcji z losowych na łudząco podobne (np. temp1, temp2, temp3 itd.) na pewno nie ułatwi analizy kodu. Po zmianie nazw dużo łatwiej jednak znaleźć definicje obu funkcji oraz zidentyfikować sposób działania skryptu.

Doszliśmy do momentu, gdzie należy spełnić 3 warunki w instrukcji if, aby wykonać potencjalnie złośliwy kod.

Warunek, który należy spełnić, aby (najprawdopodobniej) zostać przekierowanym na stronę exploitu.

Warunek, który należy spełnić, aby (najprawdopodobniej) zostać przekierowanym na stronę exploitu.

Jeśli odszukamy te funkcje u góry kodu, zauważymy, że pierwsza z nich odpowiedzialna jest za pliki cookie, druga zawiera w jednym z przypisań ciąg “Trident”, a trzecia wydaje się sprawdzać architekturę systemu operacyjnego Windows… Nadajmy im zatem tymczasowe nazwy:

Warunek if po podstawieniu tymczasowych nazw funkcji.

Warunek if po podstawieniu tymczasowych nazw funkcji.

Pora na kolejne wnioski. Obie funkcje odwołują się do dwóch funkcji: DXUIZPlEg() zwracającej ciąg User Agent przeglądarki użytkownika oraz IwASNDXkbz() sprawdzającej zawartość jednego łańcucha znaków w drugim.

java_Kali9

Identyfikacja funkcji w środku skryptu.

Można wobec tego wywnioskować, że funkcja, którą do tej pory nazywaliśmy trident_XX() ma na celu identyfikację przeglądarki Internet Explorer, a funkcja Windows_platform_ver() pozwala stwierdzić, czy JavaScript ma do czynienia z wersją 64 bitową systemu operacyjnego Microsoftu. Pora wykonać kolejny Rename.

Zmiana nazwy tymczasowej na docelową w oparciu o działanie funkcji.

Zmiana nazwy tymczasowej na docelową w oparciu o działanie funkcji.

Po sprawdzeniu instrukcji warunkowej można dojść do wniosku, że exploit kierowany jest do użytkowników Internet Explorera na systemach innych niż 64-bitowy Windows.

Instrukcja if po kolejnej zmianie nazw funkcji.

Instrukcja if po kolejnej zmianie nazw funkcji.

Co dzieje się po spełnieniu warunków? Do tego przyda się znowu notatnik i manualne przeklejanie zawartości zmiennych, które są przypisywane tuż przed instrukcją if. Niejasna pozostaje jeszcze linia 84 i 85 z poniższego screena.

Instrukcja warunkowa po ręcznej modyfikacji zmiennych.

Instrukcja warunkowa po ręcznej modyfikacji zmiennych.

Wyjaśnienia można szukać u góry skryptu, w prostych przypisaniach, mających na celu zaciemnienie kodu. Po raz kolejny warto użyć funkcji Rename, aby uzyskać czysty obraz lub też przekleić wartości bezpośrednio do zmiennych w bloku instrukcji warunkowej.

Zmiana nazw zmiennych, przypisywanych na początku skryptu.

Zmiana nazw zmiennych, przypisywanych na początku skryptu.

Kiedy już wiemy, co chce zrobić skrypt i przy jakich warunkach, możemy wydrukować sobie wynik przy pomocy wbudowanej w JSDetox funkcji jsdetox.puts(). Warto pamiętać, że JSDetox działa w emulowanym środowisku i że niektóre zmienne środowiskowe są dla niego obce. Dlatego też wykomentowaliśmy instrukcję warunkową oraz elementy listenera w funkcji final_func(). Efekt końcowy otrzymujemy w konsolce JSDetox:

Element iframe, generowany przez skrypt.

Element iframe, generowany przez skrypt.

Dalsza analiza

Ze względu na różne techniki obfuskacji JavaScriptu, omawiane w artykule narzędzia warto używać naprzemiennie. Czasem może okazać się, że to, czego nie rozwiąże jeden deobfuskator, z powodzeniem można odszyfrować w innym. Często jednak obfuskacja będzie na tyle złożona, że dopiero manualna edycja całego kodu pozwoli na poznanie jego zawartości.

Po kolejne próbki obfuskowanego kodu zajrzeć można chociażby na wspomnianą już stronę

Linki

1. Obfuskatory

2. Upiększacze kodu

3. Deobfuskacja

4. Pliki pcap – tu można szukać obfuskowanych skryptów JavaScript

–Marcin Szymankiewicz

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

  1. nshadov

    Bardzo dobry, techniczny artykuł. Ciekawie przedstawiony i przydatny temat. Ciekawą opcją narzędzia jest jeszcze Malzilla. Wskazane przez Ciebie chętnie przeklikam.

    Gratulacje :)

    Odpowiedz
  2. s

    a wiecie że eval wystarczy zmienić na alert lub odpowiedniki eval na odpowiednik alert i to cała deobfuskacja !
    Bo wiecie że obfuskacja w językach skryptowych w założeniach ma uniemożliwiać detekcje automatycznym systemom detekcji (AV itp)

    Odpowiedz

Odpowiedz