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