25/07/2011

EBFE vs CC

Introduction

Tout droit revenu d'un entretien pour un stage, et oui je ne suis encore qu'un étudiant, je m'empresse de revenir à mes activités de reverse de vx.
Je fais un billet sur ce que j'ai reverse car j'ai utilisé une technique (EBFE tricks )tout droit sorti du grenier.
Technique utilisé pour vaincre safedisc v1 et 2, si je dis pas de bêtises. Je l'avais utilisé pour Sim City 3000 ;).

La cible

> md5sum porno-rolik12.avi.exe
726537d1ea6b871a2db9b0a3e2098ce7  porno-rolik12.avi.exe

> sha1sum porno-rolik12.avi.exe
79458bdc8bc60faf5198b9b60f2c2657b4f5c3a8  porno-rolik12.avi.exe

> sha256sum porno-rolik12.avi.exe
6f91c648ee422571225a1839395118c2ac706ac83c7d0bc64e491e75e8c08bf1  porno-rolik12.avi.exe

Un winlocker plus ou moins original où j'ai dû déjouer certaines choses :

rolik_winlock.png

Il m'informe que si je veux supprimer leur widget je dois envoyer au compte beeline 9099416525 un montant de 500 USD.

First stage, First failed

On ouvre la bête avec Olly et là c'est le drame, on tombe sur un crypter en VB, sérieusement le vb à reverse c'est juste chiant ( voir même de la merde ).
La personne qui a inventé ce langage j'ai tout simplement envie de la tuer, car sur le marché un paquet de crypter sont écrits en vb et c'est à la porté de tout le monde de le faire.

Les mecs qui ont codés ce WinLocker doivent être juste idiot :

HKU\S-1-5-21-1004336348-1580436667-1708537768-1003\Software\Microsoft\Windows\CurrentVersion\Run\711527436: "C:\Documents and Settings\analys\711527436.exe"

C:\Documents and Settings\analys\711527436.exe

Après avoir copié le réel Winlocker, et changer la clef de registre pour se start au démarrage, ils font un appel à ShellExecuteW pour lancer "shutdown /r /f /t 3" :

rolik_shutdown.png

Nous avons exactement 3 secondes pour ouvrir une invite de commande et taper "shutdown -a" pour annuler la mise hors tension.
Nous sommes donc maitenant en mesure de travailler avec le réel WinLocker.

rolik_exec.png

Dans un précédent billet, je parlais d'injection dans des processus, ici c'est la même, sauf qu'au lieu de passer par kernel32, il passe direct pas ntdll, du coup il use ZwWriteVirtualMemory, ZwSetContextThread ... :

rolik_proc.png

Une fois le thread du processus resume, ces saguouins font appel à SystemParametersInfo, afin de bloquer la souris au sein de la fenetre du ransomware, faire semblant qu'on est dans un screensaver, du coup pas de Alt-Tab, ni Ctrl-Alt-Suppr possible.

p0wn stage 2

Il nous faut trouver un moyen pour essayer de prendre la main sur le processus dans lequel le processus actuel injecte du code, mais qui dit code injecté dit code qu'on peut modifier.
On va donc modifier le buffer et écrire notre propre code en breakant sur ZwWriteProcessMemory.
C'est là qu'arrive 2 instructions qui vont nous être bien utiles :

> rasm2 -d EBFE
jmp 0x8048000
> rasm2 -d CC
int3

Il faut trouver l'endroit (EIP), où le thread va reprendre son éxécution, un bp sur ZwSetContextThread, et regarder ce qu'il y a dans la structure CONTEXT :

rolik_context.png

Ok, notre thread reprendra en 0x0040f160, revenons en arrière afin maitenant de breaker sur ZwWriteVirtualMemory, et modifier le buffer d'écriture.
Nous devons calculer où se situe le future eip du thread dans le buffer :

> hex(0x40F160 - 0x40A000) # new eip - adresse ou il start a ecrire
'0x5160'
> hex(0x182408 + 0x5160)   # buffer + offset new eip
'0x187568'

rolik_write.png

Si vous regardez bien la stack :

On continue l'éxécution ( après avoir bien configuré Olly : Just-in-time debugging ) :

rolik_kdo.png

On nous invite gentiment à debugger le programme (int3 ftw), à partir de là c'est gagné, on tombe sur du upx, la routine habituel, dump processus, reconstruction de l'IAT. Et on peut travailler tranquilement avec le locker et le p0wn.
Car malheuresement ils usent l'API StrCmpW, pour checker le code, un bp dessus est :

rolik_serial.png

Le code est : "SEXXX".

23/07/2011

MBRLocker

Introduction

En ce moment je m'amuse à reverse quelques vx. Mais aujourd'hui c'est jour de fête, je suis tombé sur mon premier MBRLocker :

mbr_code

Reverse de la bête

Le problème que j'avais c'était comment dump mon mbr alors que j'étais infécté, revenir au snapshot précédent, attendre que le malware écrive le nouveau mbr et en 1 ligne de python dump le nouveau mbr :

file("vuln_mbr", "wb").write(file(r"\\.\PhysicalDrive0","rb").read(0x200))

Mais j'ai préféré opté pour une autre solution, convertir le disque de ma vm et allé récupérer le mbr à la main :

> vboxmanage clonehd --variant static analyse.vdi temp.vdi
> head -c 348 temp.vdi | hexdump -C
00000000  3c 3c 3c 20 4f 72 61 63  6c 65 20 56 4d 20 56 69  |<<< Oracle VM Vi|
00000010  72 74 75 61 6c 42 6f 78  20 44 69 73 6b 20 49 6d  |rtualBox Disk Im|
00000020  61 67 65 20 3e 3e 3e 0a  00 00 00 00 00 00 00 00  |age >>>.........|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  7f 10 da be 01 00 01 00  90 01 00 00 02 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000150  00 00 00 00 00 10 00 00  00 b0 00 00              |............|
0000015c

On récupère l'offset de notre disque dans le fichier : 0x0000b000.
On go chercher le mbr :

> echo $((0x0000b000))
45056
dd if=./temp.vdi of=./vuln_mbr bs=512 count=1 skip=$((45056/512))

On regarde que c'est bien ca :

> file vuln_mbr
mbr_vuln.bin: x86 boot sector; partition 1: ID=0x7, active, starthead 1, startsector 63, 20948697 sectors, code offset 0x80

Bon bah reste plus qu'à reverse le truc, moins de 512 octets de code ca devrait allé vite :

> objdump -D -b binary -mi386 -Maddr16,data16 mbr_vuln

Je vais pas tout vous balancer tout le disas mais juste des lignes inéressantes :

88:	b4 00                  mov    ah,0x0
8a:	57                     push   di
8b:	cd 16                  int    0x16
8d:	5f                     pop    di
8e:	eb 37                  jmp    0xc7					     

...

c7:	3c 0d                  cmp    al,0xd
c9:	75 55                  jne    0x120
cb: be 8c 7d               mov    si,0x7d8c
ce: bf 00 6c               mov    di,0x6c00
d1: b5 00                  mov    ch,0x0
d3:	ea 72 7d 00 00         jmp    0x0:0x7d72

Ici on fait appel à l'interrupt 0x16 avec ah = 0 afin de lire un caractère, si ce caractère est égale à 0xd (Carriage Return) alors on part en 0x172 ( 0x7d72 - 0x7C00 ), le bootloader est chargé en 0x7C00 par le BIOS.
Surement la routine de vérifiction de notre code :

172: 8a 0e 8b 7d          	  mov    cl,BYTE PTR ds:0x7d8b
176: ac                   	  lods   al,BYTE PTR ds:[si]
177: 8a 15                	  mov    dl,BYTE PTR [di]
179: 80 f2 ed             	  xor    dl,0xed
17c: 47                   	  inc    di
17d: 3a c2                	  cmp    al,dl
17f: 0f 85 58 ff          	  jne    0xdb
183: 49                   	  dec    cx
184: 75 f0                	  jne    0x176
186: ea 36 7d 00 00       	  jmp    0x0:0x7d36

On peut voir clairement que dans %cl, il y aura la taille du code attendu, %si lui pointe vers le serial auquel on va être comparé, %di pointe sur ce qu'on a rentré, donc tous nos carac vont être xored avec 0xED.
Il suffit d'aller voir ce qui traine en 0x18B ( 0x7d8B - 0x7C00 ) :

> hexdump -s 0x18B mbr_vuln
0000018c  07 a0 ad dc d9 d8 bf b9  00 00 00 00 00 00 00 00

>>> chr(0xa0 ^ 0xED)
'M'
>>> chr(0xad ^ 0xED)
'@'
>>> chr(0xdc ^ 0xED)
'1'
>>> chr(0xd9 ^ 0xED)
'4'
>>> chr(0xd8 ^ 0xED)
'5'
>>> chr(0xbf ^ 0xED)
'R'
>>> chr(0xb9 ^ 0xED)
'T'

Le code serait donc : "M@145RT", on teste et ca boot :]

Bonus

Je trouvais leur message, un peu triste, alors j'ai voulu pour le fun coder un petit bootloader qui déssine une bière, mais qui ne fait rien au final, car 512 octets pour pouvoir caller un dession c'est hard, du coup j'ai 2 stage, le stage 1 qui init la gdt, passer en mode protected, on load le printer de la biere en memoire (0x1000), et on finira par l'exec, voici le résultat :

mbr_beer

Le code :

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>

16/07/2011

Dungeon Siege for the lulz

Introduction

Il y a bien longtemps avec un ami nous jouions à Dungeon Siege, un jeu de role/action développé par Gas Powered Games sorti le 7 mai 2002. Dans cette article je me suis penché sur le fait de me faire un no-cd et etre en mesure de parser leurs fichiers de sauvegarde multijoueur.

No-Cd

Première chose à faire, c'est de regarder si le binaire est packé :

dungeon_peid

Ici nous avons affaire avec un packer dont je ne connaissais pas l'existance, et en googelant, on tombe sur aucun tutorial pour pouvoir l'unpacker mais on va voir quand fait il n'y a rien de compliqué.
Quand on ouvre le binaire sous OllyDbg, on tombe sur la chose suivante :

007C0000 > /EB 10           JMP SHORT DSLOA.007C0012
007C0002   |0300            ADD EAX,DWORD PTR DS:[EAX]
007C0004   |0000            ADD BYTE PTR DS:[EAX],AL
007C0006   |0200            ADD AL,BYTE PTR DS:[EAX]
007C0008   |0000            ADD BYTE PTR DS:[EAX],AL
007C000A   |0000            ADD BYTE PTR DS:[EAX],AL
007C000C   |0000            ADD BYTE PTR DS:[EAX],AL
007C000E   |0000            ADD BYTE PTR DS:[EAX],AL
007C0010   |0000            ADD BYTE PTR DS:[EAX],AL
007C0012   \55              PUSH EBP
007C0013    E8 00000000     CALL DSLOA.007C0018
007C0018    5D              POP EBP
007C0019    81ED 18000000   SUB EBP,18

En voyant la chose suivante, on peut émettre l'hypothèse suivante :
Le packer fait un call à une routine qui vient s'occuper de decompresser l'éxecutable, puis jump ou ret sur le real entry point. Il nous suffit donc de step jusqu'au CALL, placer un hardware breakpoint sur l'acces à la valeur de ESP au moment de cette appel, et nous devrions arriver sur l'entry point.

dungeon_esp

On run le programme, et après s'etre mangé quelques exceptions dans la tronche que l'on repasse au programme, on arrive ici :

0040A39B      55            DB 55                                    ;  CHAR 'U'
0040A39C      8B            DB 8B
0040A39D      EC            DB EC
0040A39E      6A            DB 6A                                    ;  CHAR 'j'
0040A39F      FF            DB FF
0040A3A0      68            DB 68                                    ;  CHAR 'h'
0040A3A1      E8            DB E8
0040A3A2      96            DB 96
0040A3A3      72            DB 72                                    ;  CHAR 'r'
0040A3A4      00            DB 00
0040A3A5      68            DB 68                                    ;  CHAR 'h'

On analyse le code, et on voit la mise en place d'une SEH, un appel à kernel32!GetVersion, GetCommandLineA, ca a l'air de ressembler à notre real OEP. On dump donc notre processus, et on reconstruit l'IAT avec ImportRec :

dungeon_dump
dungeon_imp

En fait à partir de là le no-cd est deja fait, car ces boulets cré un processus qui va etre chargé de vérifier si le cd est bien dans le lecteur, si oui on continue la décompression et on lance le jeu, sinon on ExitProcess.
En fait on bypass cette vérification en unpackant à la main en ayant bien mis le cd original dans le lecteur cd et le tour est joué étant donné que la vérification n'aura plus lieu.

dungeon_process

I want to cheat

J'étais un peu sur ma faim après cette protection lame.
Je me suis donc mis en tete d'écrire un lecteur / unpacker pour les sauvegardes de types multijoueur (fichier .dsparty). Un logiciel fait deja bien le travail "TankViewer", mais malheuresement, source closed c'est moche.
Je décide donc de regarder d'un peu plus près avec un éditeur hexa :
dungeon_winhex1

dungeon_winhex2

La première image ressemble typiquement à un header de sauvegarde pour Dungeon Siege, j'ai meme entouré des offset plutot intéressants qu'on retrouve sur la deuxieme image, ou on peut voir le nom de 3 fichiers :

C'est en googelant des betises que j'ai réussi à tomber sur ce site, qui grace auquel j'ai tout dessuite compris comment était formé les fichiers de sauvegarde.
A partir de là j'étais en mesure de pouvoir ecrire un décodeur fonctionnel.
Il faut savoir que chacun des 3 fichiers est compressé avec zlib. D'ailleurs ca m'a fait pas mal chié de faire marcher cette daube avec MinGW. Je vais pas éterniser la chose et realease les sources : Reader.c, Reader.h.
Une fois les 3 fichiers récupérés, ils sont tres facile à comprendre ansi qu'à parser, voici un exemple.
On peut trés bien imaginer la suite, créer un éditeur complet de sauvegarde ( cheateur ? ), mais lister tous les objets ca relève de beaucoup de travail et je n'y vois pas vraiment d'amusement.
Voici quand meme un apercu de l'output de mon outil :

reader_preview

14/06/2011

Un OEP Finder pour escargot en C

Introduction

Je voulais m'amuser avec l'API de debug sous windows ( voir autre chose que ptrace() ).
J'ai donc décidé d'écrire un OEP finder pour le packer escargot.
Voici le cheminement de mon programme :

unpack_esc

Source

Voici les sources : main.c, main.h

Conclusion

Ce programme peut vous apprendre les subtilités des debug register, comment utiliser l'API de debug de Windows, et jouer avec le format PE.
Have fun.

14/06/2011

PECompact2 unpacking

Introduction

Dans la même lignée, il y avait du PECompact pendant la defcon.
Alors j'enchaine les vidéos :

Vidéo

Binaire

Voici le binaire en question pour pouvoir vous entrainer : calender.exe.

14/06/2011

tElock v0.98 unpacking

Introduction

C'est aprè la defcon 19 que je suis retombé, sur le packer tElock.
Mais cette fois j'ai décidé d'unpack la bête ( qui est toute bidon ).
Donc voici une vidéo que j'ai faite exprès pour vous présenter la chose :

Vidéo

Binaire

Voici le binaire en question pour pouvoir vous entrainer : calender.exe.

11/06/2011

Unpacking or not unpacking that is the question

Introduction

C'est en regardant l'analyse faite par Nicolas Brulez du Kaspersky Lab de gpcode, que je vois une technique de détéction d'unpacking d'UPX. Je décide de regarder de plus près et recréer le meme comportement ainsi qu'ajouter une autre technique en utilisant les TLS callback.

UPX

Avant de sauter sur le réel OEP, upx fait la chose suivante :

00418DBD   .  61            POPAD
00418DBE   .  8D4424 80     LEA EAX,DWORD PTR SS:[ESP-80]
00418DC2   >  6A 00         PUSH 0
00418DC4   .  39C4          CMP ESP,EAX
00418DC6   .^ 75 FA         JNZ SHORT test.00418DC2
00418DC8   .  83EC 80       SUB ESP,-80
00418DCB   .- E9 3082FEFF   JMP test.00401000

Du fait que eax soit égale à une valeur proche de la stack, on peut écrire le bout de code suivant pour détecter l'unpacking UPX :

.code
start:
call DetectUnpack
...
DetectUnpack:
push ebp
mov ebp, esp
mov ecx, esp
sub ecx, 078h    ; 80h - saved ebp (4 octets) - saved eip (4 octets)
jnz quit
leave
ret

A partir de ce snippet de code, la détection de ce check d'anti unpack est trè facilement bypassable, en changeant simplement le saut conditionnel.
Avec un peu d'obfuscation, on peut arriver à quelque chose de plus funky. Mais ce n'est pas le but de cette article.
Ce que font majoritairement le genre de personnes qui unpack du UPX, ils dumpent et reconstruisent l'IAT avec ImportRec ( ou à la main ).
C'est en jouant avec les TLS Callback que je me suis dit y'a moyen de faire retarder ce processus en venant écraser le PeHeader, ProcDump ( un plugin d'ollydbg ) se sert des sections tables pour fonctionner correctement. En lui enlevant ces informations il perd la boule.

unpack_bad_dos

TLS

Quand on lit la doc de Microsft, le Thread Local Storage (TLS) est un mécanisme qui permet à Windows de défininir des objets qui ne sont pas des objets sur la stack, des variables propres à chaque thread qui éxécute le code. Chaque thread pourra tenir à jour une variable qui lui sera propre. Ces informations sont dans le PEHeader.
Un développeur, si il le souhaite peut définir des TLS Callback fonctions, qui seront désignés à initialiser / nettoyer les objets.
En gros ces fonctions seront appelés avant l'entry point du programme.
Voici la structure :

;0x00    Raw Data Start VA       Starting address of the TLS template
;0x04    Raw Data End VA         Address of last byte of TLS template
;0x08    Address of Index        Location to receive the TLS index
;0x0C    Address of Callbacks    Pointer to array of TLS callbacks
;0x10    Size of Zero Fill       Size of unused data in TLS template
;0x14    Characteristics         (reserved but not checked)

A partir de là on peut commencer à coder un exemple de code masm :

TLS_CALLBACK	      PROTO :PVOID,:DWORD, :PVOID

TLS_DIRECTORY STRUCT
 lpTLS_Template_RDS		LPVOID ?
 lpTLS_Template_RDE		LPVOID ?
 lpTLS_Index		LPVOID ?	
 lpTLS_Callbacks_Array	LPVOID ?
 dwTLS_Filler_Size		DWORD ?
 dwCharacteristics		DWORD ?
TLS_DIRECTORY ENDS

.data
TLS_Directory TLS_DIRECTORY <NULL,NULL,OFFSET TLSIndex,OFFSET CallBacksArray, NULL,NULL>

CallBacksArray DD TLS_CALLBACK, NULL  
TLSIndex DWORD    NULL
MsgBoxCaption  db "...", 0
GoodText       db "MAIN", 0
TLSText     db "CALLBACK", 0

.code
start:
  invoke MessageBox, NULL, addr GoodText, addr MsgBoxCaption, MB_OK
  invoke ExitProcess, NULL
start:
;call DetectUnpack
invoke MessageBox, NULL, addr GoodText, addr MsgBoxCaption, MB_OK
invoke ExitProcess, NULL

TLS_CALLBACK PROC  DllHandle :PVOID, Reason :DWORD, dwReserved :PVOID
    invoke MessageBox, NULL, addr TLSText, addr MsgBoxCaption, MB_OK
    RET
TLS_CALLBACK ENDP	

end start

Et le make.bat qui va avec :

@echo off

if exist "first.obj" del "test.obj"
if exist "first.exe" del "test.exe"

\masm32\bin\ml /c /coff "test.asm"
if errorlevel 1 goto end

\masm32\bin\Link /SUBSYSTEM:WINDOWS "test.obj"
if errorlevel 1 goto end

:end
pause

Le problème que j'ai rencontré c'est que je ne savais pas quoi comment option passer au linker pour mettre en place dans le peheader la TLSTable.
De ce fait j'y suis allé à la main avec un éditeur Hexadécimal.
Ce qui est facile c'est qu'avec ce code la TLS Table se trouve juste à l'entrée de la section data.

unpack_winhex

On teste et ca marche, ca nous affiche bien 2 messages box d'affilé.
On va faire évoluer notre code pour eraser le pe header au moment de la TLS :

TLS_CALLBACK PROC  DllHandle :PVOID, Reason :DWORD, dwReserved :PVOID
   push 0 
   call GetModuleHandleA 
   push eax 
   push esp 
   push 4
   push 1 
   push eax 
   xchg edi, eax 
   call VirtualProtect 
   xor  ecx, ecx 
   mov  ch, 10h
   rep  stosb 
   leave
   ret
TLS_CALLBACK ENDP

On reéedite à la main notre binaire avec notre éditeur Héxadécimal. Et en breakant sur l'entry point, on va regarder que notre PeHeader s'est bien fait écraser :

unpack_peheader

Ca a l'air de plutot bien marcher. Bon il faut le savoir que si on conf bien ollydbg, on peut arriver à breaker dessus :

unpack_conf

Ou dans IDA, une fois le binaire chargé, pressez CTRL + E, en voyant l'adresse de l'entry point, les tls callback devrait etre juste avant :

unpack_ida

Bon maitenant la tache qui reste c'est essayer de refaire passer UPX par dessus le tout, quand on lit les changelog :

Changes in 3.06 (04 Sep 2010):
  * win32/pe: TLS callback support contributed by Stefan Widmann. Thanks!

On décide donc de tester, mais :( WTF, on se mange une vieille erreur :

unpack_upx

A partir de là j'étaits bloqué, je me demandais si j'essayais de patcher upx ou trouver un autre moyen. C'est en continuant de regarder mon binaire avec WinHex que me vient une idée, à cause du FileAlignement ( champ dans le PeHeader ), il y a un peu de place entre les sections et la premiere section du binaire ( dans notre exemple section .text ). Malheuresement il faudrait pour cela faire du call à LoadLibrary, GetProcAdress. Pour ca faudrait résousdre à la main toutes les entrées mais je ne l'ai pas encore fait pour l'instant par manque de temps : saison des partiels.
Toute facon prochainement, je vais me lancer dans l'écriture d'un packer je reprendrais surement de là pour expliquer ma démarche.
Sur ceux have fun.

10/06/2011

Vous reprendrez bien un peu de RE ?

Introduction

Après la defcon, j'avais de grosse envie de continuer à reverse des choses (en l'occurence des binaires win32), j'ai donc voulu alller sur le site crackmes.de, mais malheuresement le site est indisponible actuellement.
Je décide donc d'aller plus loin et me pencher sur un malware, chose que j'ai déjà faite mais sur un blog non-officiel.
Je vais donc comme à mes habitudes sur le site malwaredomainlist.com et visite un domaine foireux pour récupérer un sample.

Informations

Name : pusk.exe
MD5 (pusk.exe) = 76e6e91590b47eba7f13ed164c867999
SHA1 (pusk.exe) = 833312a8b90851e68b75dbb6f1532b64df1e15fa
SHA256 (pusk.exe) = b05df025e7833f99691b019f8ee4656858cc721daa052ba26085be0d0b49d7ef

Break it

Première chose à faire c'est regarder si l'éxécutable est packé, pour ca on sort Peid :

pusk_peid

Peid ne détecte rien, de plus la table d'IAT (Import Adress Table) bronche, tout du moins pas d'entrées vers GetProcAdress, LoadLibrary ...
Mais en regardant les strings présentes dans le binaire, on voit des "kernel32.dll", "ntdll.dll".
Je décide donc d'étudier le code de plu près avec le disas.
Je me rends compte qu'il decrypt un nouveau PE dans un zone alloué au préalable :

pusk_valid_pe

On sort lordPe et on va dump cette zone, et essayer de reconstruire le pe :

pusk_lordpe

Si on avait voulu travailler avec ce dump, il aurait fallu éditer pas mal de choses(PeHEader, Sections...), mais j'ai juste dump pour le faire manger à Peid et voir si il me détectait quelque chose : Non.

En regardant le code de plus près, on peut voir ceci : ( image avec commentaire )

pusk_virtual

On regarde le memory map :

pusk_before_after

Il a effectivement changee l'accés de pas mal de zone mémoire qui lui sont propres.

On peut émettre l'hypothèse qu'il va re-unpack quelque chose dans cette zone étant donné qu'il a memset tout à 0x00.
On regarde le code plus loin et on voit un jump :

00A4137C    83C4 28         ADD ESP,28
00A4137F    83C4 08         ADD ESP,8
00A41382  - FF25 1C309700   JMP DWORD PTR DS:[40301C]                ; pusk.006631D0
00A41388    33D2            XOR EDX,EDX
00A4138A  ^ 0F85 B7FEFFFF   JNZ 00A41247

Qui nous fait tomber sur :

pusk_push_ad

Hmm ca sauve tous nos registres (UPX Like ?), bah on descend dans le code et chercher un POPAD et un jmp :

pusk_pop_ad

Donc le real oep serait en "2634", de plus on voit des strings ASCII plutot intéressante :

"adw: start"
"adw: license self delete"
"adw: lang self delete"
"adw: install"

On va deja dump le process, fix l'IAT avec ImportRec et travailler sur ca ( je vais pas expliquer la tache, un paquet de tuto traite de ce sujet ).

Au tout dÉ le binaire va créer un thread pour la routine en 00602612 :

00602612    68 84966000     PUSH dump_.00609684              ; ASCII "adw: start def block thread"
00602617    E8 CA2B0000     CALL dump_.006051E6
0060261C    59              POP ECX
0060261D    68 6C966000     PUSH dump_.0060966C              ; UNICODE "MSASCui.exe"
00602622    E8 B1200000     CALL dump_.006046D8
00602627    68 D0070000     PUSH 7D0
0060262C    FF15 E0606000   CALL DWORD PTR DS:[<&kernel32.Sleep>]    ; kernel32.Sleep
00602632  ^ EB E9           JMP SHORT dump_.0060261D

pusk_thread

Ce pauvre thread va juste checker si le programme "MSACui.exe" fait partie de la liste des process (routine 6046d8), et sleep pendant 2000 millisecondes, puis reboucle, ansi de suite.

Pour l'instant je vais partir sur la routine adw: install, ou l'on peut remarquer des choses fun :

00601FDC  |.  50            PUSH EAX
00601FDD  |.  68 80886000   PUSH dump_.00608880                      ;  UNICODE "ElCo]N}BMG"
00601FE2  |.  E8 621E0000   CALL dump_.00603E49
00601FE7  |.  8D85 54FFFFFF LEA EAX,DWORD PTR SS:[EBP-AC]
00601FED  |.  68 18956000   PUSH dump_.00609518                      ; /S2 = ".exe"
00601FF2  |.  50            PUSH EAX                                 ; |S1
00601FF3  |.  FF15 A4616000 CALL DWORD PTR DS:[<&shlwapi.StrCatW>]   ; \StrCatW

La routine 00603e49 est une simple routine pour decrypt les chaines (XOR 4), jai donc récupéré toutes les chaines du binaires et écrit un script python :

dump_8080 = "ElCo]N}BMG"

unknow = ["WkbpsevaXImgvkwkbpXSmj`kswXGqvvajpRavwmkjXTkhmgmawXW}wpai", "*a|a",
          "lppt>++!w+!w+!w)`mvagp",
          "E`kfa[Bhewl[The}av*a|a","T5oEhImC6Of3B~T1pI5UFM2@WW=6g75Etcno=hRO",
          "ghmgokh`*kvc?waevglfvae`*kvc", "Wkbpseva",
          "<7e1b<7f)1ee3)0be3)ffb1)27<6=e``6=2a",
          ".A@W*p|p", "Vjcqrdw`YHlfwjvjcqYLkq`wk`q%@}uijw`wYAjrkijda",
          "Vjcqrdw`YHlfwjvjcqYRlkajrvYFpww`kqS`wvljkYUjilfl`vYDqqdfmh`kqv",
          "Vjcqrdw`YHlfwjvjcqYRlkajrvYFpww`kqS`wvljkYUjilfl`vYDvvjfldqljkv",
          "*~mt?*vev?*jbk?*p|p?*a|a?*fep?*gki?*gi`?*vac?*iwm?*lpi?*lpih?*cmb?*fit?*ntc?*erm?*itc?*itac?*ikr?*it",
          "Fm`fn@}`Vlbkdqpw`v", "Vds`_jk`Lkcjwhdqljk", "IjrWlvnCli`Q|u`v",
          "lppt>++!w+040*tlt;p}ta9wpepw", "ebbm`9!w""wqfm`9!w""esko",
          "waevglehmoa*kvc?ghmgonegowkjrmhha*kvc?waevglepikwtlava*kvc?waevglfacej*kvc?waevglejp*kvc",
          "E`kfa[Bhewl[The}av*a|a",
          "<2a<e0=1)713g)073g)f2a=)57a313fbefef",
          "31be7<f3)<f=0)0==1)e`76)16a=7<<23=10",
          "`g52af7g)a4b2)002b)<`63)=56615b<a2`e"]


def decrypt(ch):
    name = ""
    for c in ch:
        name += chr(ord(c) ^ 4)
    print "Name = " + name
        


decrypt(dump_8080)
for val in unknow:
    decrypt(val)

Voici l'output :

Name = AhGkYJyFIC
Name = Software\Microsoft\Windows\CurrentVersion\Policies\System
Name = .exe
Name = http://%s/%s/%s-direct
Name = Adobe_Flash_Player.exe
Name = P1kAlMiG2Kb7FzP5tM1QBI6DSS92c31Apgjk9lVK
Name = clickold.org;searchbread.org
Name = Software
Name = 83a5f83b-5aa7-4fa7-bbf5-63829add296e
Name = *EDS.txt
Name = Rnguv`sd]Lhbsnrngu]Houdsodu!Dyqmnsds]Envomn`e
Name = Rnguv`sd]Lhbsnrngu]Vhoenvr]BtssdouWdsrhno]Qnmhbhdr]@uu`bildour
Name = Rnguv`sd]Lhbsnrngu]Vhoenvr]BtssdouWdsrhno]Qnmhbhdr]@rrnbh`uhnor
Name = .zip;.rar;.nfo;.txt;.exe;.bat;.com;.cmd;.reg;.msi;.htm;.html;.gif;.bmp;.jpg;.avi;.mpg;.mpeg;.mov;.mp
Name = BidbjDydRhfo`utsdr
Name = R`wd[nodHognsl`uhno
Name = MnvShrjGhmdUxqdr
Name = http://%s/404.php?type=stats
Name = affid=%ssubid=%sawok
Name = searchalike.org;clickjacksonville.org;searchatmosphere.org;searchbegan.org;searchant.org
Name = Adobe_Flash_Player.exe
Name = 86e8a495-357c-437c-b6e9-13e757bfabab
Name = 75fa38b7-8b94-4995-ad32-52e938867954
Name = dc16eb3c-e0f6-446f-8d27-912251f8e6da

On peut y voir en premier le nom de l'éxecutable qui va étre crée, des noms de domaines, une clef de registre, des urls ... etc.
Next :
La routine en 0060485C va simplement disable le task_manager, stack call de advapi32.RegSetValueExW

pusk_disable_task.png

On regarde aussi la routine de crétion ( plutot recopie ) car c'est exactement le meme binaire qui est copiée : ( disas commenté)

00602157  |.  FF15 84616000 CALL DWORD PTR DS:[<&shell32.SHGetSpecialFol>;  shell32.SHGetSpecialFolderPathW
0060215D  |.  8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328]
00602163  |.  50            PUSH EAX                                     ; /\Path = "C:\Documents and Settings\All Users\Application Data"
00602164  |.  FF15 A8616000 CALL DWORD PTR DS:[<&shlwapi.PathAddBackslas>; \PathAddBackslashW
0060216A  |.  8D85 54FFFFFF LEA EAX,DWORD PTR SS:[EBP-AC]
00602170  |.  50            PUSH EAX                                     ; /S2 = "AhGkYJyFIC.exe"
00602171  |.  8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328]               ; |
00602177  |.  50            PUSH EAX                                     ; |S1 = "C:\Documents and Settings\All Users\Application Data\"
00602178  |.  FF15 A4616000 CALL DWORD PTR DS:[<&shlwapi.StrCatW>]       ; \StrCatW
0060217E  |.  8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328]
00602184  |.  56            PUSH ESI                                     ; /FailIfExists = FALSE
00602185  |.  50            PUSH EAX                                     ; |NewFileName = "C:\Documents and Settings\All Users\Application Data\AhGkYJyFIC.exe"
00602186  |.  8D85 D0FAFFFF LEA EAX,DWORD PTR SS:[EBP-530]               ; |
0060218C  |.  50            PUSH EAX                                     ; |ExistingFileName = "C:\Documents and Settings\analys\My Documents\T",E9,"l",E9,"chargements\pusk\dump_.exe"
0060218D  |.  FF15 E8606000 CALL DWORD PTR DS:[<&kernel32.CopyFileW>]    ; \CopyFileW
00602193  |.  6A 05         PUSH 5                                       ; /IsShown = 5
00602195  |.  56            PUSH ESI                                     ; |DefDir = NULL
00602196  |.  8D85 D8FCFFFF LEA EAX,DWORD PTR SS:[EBP-328]               ; |
0060219C  |.  56            PUSH ESI                                     ; |Parameters = NULL
0060219D  |.  50            PUSH EAX                                     ; |FileName = "C:\Documents and Settings\All Users\Application Data\AhGkYJyFIC.exe"
0060219E  |.  68 78946000   PUSH dump_.00609478                          ; |Operation = "open"
006021A3  |.  56            PUSH ESI                                     ; |hWnd = NULL
006021A4      90            NOP                                          ; \ShellExecuteW

J'ai NOP le call à ShellExecute afin de pouvoir continuer à analyser le binaire.
Car plus loin il va ajouter une valeur dans la RegKey : Software\Microsoft\Windows\CurrentVersion\Run :

0012C6E4   80000001  |hKey = HKEY_CURRENT_USER
0012C6E8   0012C718  |Subkey = "Software\Microsoft\Windows\CurrentVersion\Run"
0012C6EC   0012C6FC  \pHandle = 0012C6FC


0012C6D8   00000060  |hKey = 60
0012C6DC   0012CF74  |ValueName = "AhGkYJyFIC"
0012C6E0   00000000  |Reserved = 0
0012C6E4   00000001  |ValueType = REG_SZ
0012C6E8   0012E22C  |Buffer = 0012E22C
0012C6EC   00000086  \BufSize = 86 (134.)

Ce qui m'interessait de voir surtout après c'était la routine "adw download rootkit", mais malheuresement elle failed :(.
L'URL :

http://searchbread.org/pica1/531-direct

Les domaines ne répondent pas c'est dommage. Pareil pour la routine "adw download exe"...
On continue et on aboutit donc sur la routine "self delete" :

00602707  |> \68 08986000   PUSH dump_.00609808                                         ;  ASCII "adw: self delete"
0060270C  |.  E8 D52A0000   CALL dump_.006051E6
00602711  |.  59            POP ECX
00602712  |.  E8 7F2B0000   CALL dump_.00605296
00602717  |.  E9 D7010000   JMP dump_.006028F3

La routine 605296 delete le fichier et ensuite ExitProcess tout le code qui suit est obsolète.

Bon maitenant on peut bosser sur l'autre binaire.

AhGkYJyFIC.exe

MD5 (AhGkYJyFIC.exe) = 865ea9c1a3fbd1f089749951004b39aa
SHA1 (AhGkYJyFIC.exe) = 9162235f005d7b256d9034201799cdeb9731c14f
SHA256 (AhGkYJyFIC.exe) = 2c7cf2452d1a3f37a9985e7dfdbe2b349e2d5ee4755f5f14460279855c6d882e

On peut remarquer que nous n'avons pas la meme empreinte qu'au départ avec "pusk.exe".
L'unpacking se passe exactement de la meme maniere que pour le précédent binaire.
J'ai omis une chose lors de ma première analyse, une routine qui teste si son nom de process est "AhGkYJyFIC.exe" si c'est le cas on part sur la routine "adw: work"

006026AF  |> \E8 07FBFFFF   CALL dump_.006021BB
006026B4  |.  85C0          TEST EAX,EAX
006026B6  |.  75 64         JNZ SHORT dump_.0060271C
.....
0060271C  |> \68 FC976000   PUSH dump_.006097FC                      ;  ASCII "adw: work"

Dans le disas on voit plusieurs call à CreateThread, 2 routines sont importantes à voir :

006022AF

Crétion d'un fichier temporaire extrait des ressources :

pusk_14081828

On étudiera ce fichier aprés, c'est le fakeAV :]

00603785

Utilisation de l'éxecutable attrib.exe (man page) sur chacun des fichiers du disque dur, afin de les rendre tous "caché".
Et le pourquoi c'est que le programme en lui meme va modifier des clefs de registre qui changeront la visibilité de chacun des fichiers :
"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\Hidden\SHOWALL" -> "CheckedValue" -> "0".

14081828.exe

Voici le fakeAV que nous attendions tant :

pusk_fakeav

En fait à partir de là, la partie est gagné en faisant un simple strings sur le binaire on trouvait la clef pour se register.

pusk_register

Une fois le logiciel register, ca va restaurer comme il faut les clefs de registre, le wallpaper, et rapeller attribute.exe sur tous les fichiers du disque.
Et ouvrir notepad avec le message suivant :

Thank you for your purchase, Windows XP Recovery!
Your activation code: 8475082234984902023718742058948
EDS URL: http://www.edsawake.org/customers/dl/Recovery.exe
Contact us through Help&Support section in the Windows XP Recovery menu or by phone +1.877.2820139

Voilà c'est la fin de cette article sur une occupation lorsque crackmes.de n'est pas disponible.

07/06/2011

Defcon 19 b400 writeup

Hint

Retrive the key

Résolution

Pour commencer on va ouvrir le fichier avec PEiD, qui nous détecte :

tElock 0.98b1 -> tE!

tElock

Pour ce type de packer, il va falloir configurer correctement notre Ollydbg, aucune exception ne doit etre passé au programme :

b400_olly_config

Aprè avoir laissé olly chargé le processus, nous sommes actuellemt à l'entry point :

00438BD6 >^E9 25E4FFFF      JMP b400_e32.00437000

Lancez le programme avec F9. Puis enchainez les excpetions avec SHIT + F9.
On va compter combien de fois olly break ( ici 20 fois ), apres qu'une exception est était levé, voir le listing ci - dessous :

Afin de trouver l'OEP, on va breaker sur le dernier JNB, 19 ième combinaison de SHITF + F9.
A partir de là, on appuie sur ALT + M ( on ouvre le memory map ) et on va set un breakpoint d'accés mémoire sur la section de code :

b400_memory_map

Une fois le breakpoint set, SHIT + F9 et on tombe ici :

0042A000     B8             DB B8
0042A001     30             DB 30                                    ;  CHAR '0'
0042A002     5C             DB 5C                                    ;  CHAR '\'
0042A003     43             DB 43                                    ;  CHAR 'C'
0042A004     00             DB 00
0042A005     50             DB 50                                    ;  CHAR 'P'
0042A006     64             DB 64                                    ;  CHAR 'd'
0042A007     FF             DB FF
0042A008     35             DB 35                                    ;  CHAR '5'
0042A009     00             DB 00
0042A00A     00             DB 00
0042A00B     00             DB 00
0042A00C     00             DB 00
0042A00D     64             DB 64                                    ;  CHAR 'd'
0042A00E     89             DB 89
0042A00F     25             DB 25                                    ;  CHAR '%'
0042A010     00             DB 00
0042A011     00             DB 00
0042A012     00             DB 00
0042A013     00             DB 00
0042A014     33             DB 33                                    ;  CHAR '3'
0042A015     C0             DB C0
0042A016     89             DB 89
0042A017     08             DB 08
0042A018     50             DB 50                                    ;  CHAR 'P'
0042A019     45             DB 45                                    ;  CHAR 'E'
0042A01A     43             DB 43                                    ;  CHAR 'C'
0042A01B     6F             DB 6F                                    ;  CHAR 'o'
0042A01C     6D             DB 6D                                    ;  CHAR 'm'
0042A01D     70             DB 70                                    ;  CHAR 'p'
0042A01E     61             DB 61                                    ;  CHAR 'a'
0042A01F     63             DB 63                                    ;  CHAR 'c'
0042A020     74             DB 74                                    ;  CHAR 't'
0042A021     32             DB 32                                    ;  CHAR '2'

On tombe ici sur le réel OEP du packer tElock : "0042A000".
Je n'ai pour l'instant pas analysé le code car on voit quelque chose d'intéressant, un nouveau packer apparament "PECOMPACT2".
Donc rien ne sert de se précipiter à dump le process et reconsruire l'IAT.
Continuons sur notre lancé et jouons avec PeCompact.

PeCompact

J'ai dumpé le process à partir de ce moment et vérifié avec PEiD :

PECompact V2.X-> Bitsum Technologies   * Sign.By.fly *

Si on analyse (CTRL + A) le code vu au dessus, on voit une anti-debug tricks :

0042A000   B8 305C4300      MOV EAX,b400_e32.00435C30    ; Mise en place 
0042A005   50               PUSH EAX                     ; SEH Handler
0042A006   64:FF35 00000000 PUSH DWORD PTR FS:[0]
0042A00D   64:8925 00000000 MOV DWORD PTR FS:[0],ESP
0042A014   33C0             XOR EAX,EAX                  ; Creation d'une
0042A016   8908             MOV DWORD PTR DS:[EAX],ECX   ; PageFault

Un apercu de la fenetre de l'état de la stack, pour confirmer :

b400_seh_handler

Pour bypass cette tricks, il suffit d'aller poser un breakpoint en 00435C30.
Run le programme jusqu'à l'access violation.
Pressez SHIFT + F9 pour passer l'exception au programme.

En "00426016" la meme tricks est présente, on regarde le seh handler : "00430C30", et on va poser notre breakpoint, et SHIFT + F9.

A partir de là, on step over...step over...step over... jusqu'à :

00430CF2   FFE0             JMP EAX

Qui va nous faire arriver sur un PUSHAD :

0042E1E0   . 60             PUSHAD

Ici apparament c'est le réel entry point du packer PeCompact.
Mais comme précedement, continuons l'analyse.
A partir de ce moment, je me dis tiens peut etre de l'UPX derriere, je descends dans le disas :

0042E35D   . 61             POPAD
0042E35E   . 8D4424 80      LEA EAX,DWORD PTR SS:[ESP-80]
0042E362   > 6A 00          PUSH 0
0042E364   . 39C4           CMP ESP,EAX
0042E366   .^75 FA          JNZ SHORT b400_e32.0042E362
0042E368   . 83EC 80        SUB ESP,-80
0042E36B   .-E9 F031FDFF    JMP b400_e32.00401560

Je pose un breakpoint sur le JMP.
Apres tout ca j'etais perdu, je savais plus trop ou j'allais, le programme m'affichait "hello world".
Et il avait l'air de decrypt encore pas mal de merde dans la section code et data.
Je décide donc de regarder de plus près tout ce qu'il a pu decrypt, je voyais des chaines PE passer, ca m'avait l'air d'etre un semblant de pe header mais malheuresement non.
Pas du tout ou alors je m'y prenais mal car je recuperais le ms dos header du bin orignal afin de reconstruire un nouveau pe.
Bref a force de tourner en rond, j'ai grep sur tout le dump et là la clef est sortie :

> strings dump3.exe | grep PE
HowCanThisPossiblyBeAValidPEFile?
PEC2^O
gEPEI

Key

HowCanThisPossiblyBeAValidPEFile?

Pages : 1 2