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