Archive for février, 2007
Parfois, à force de regarder dans les entrailles de Win on tombe sur des trucs on sait pas ce que c’est, ni à quoi ca sert, mais c’est là, alors on regarde le peu de doc sur le sujet et on découvre au final que c’est pas con de l’avoir foutu :} Il s’agit en fait d’une arborescence appelée ObpRootDirectoryObject maintenue par l’object manager de Win et qui contient les objets utilisés par le noyau. Comme vous le savez les drivers sont des objets du noyau ainsi si on load un driver un objet le référençant sera ajouté et DONC un anti-totokit pourrait aller lire cette struture et découvrir notre driver, ouin
Commençons par essayer de lire cette table à partir du UserLand, pour cela on va utiliser les API native qui nous fournissent de plus grandes possibilitées, on à besoin de jouer avec NtOpenDirectoryObject() pour obtenir un handle sur l’ObjectDirectory puis de NtQueryDirectoryObject pour pouvoir « lire » sont contenu. Les prototypes des fonctions sont :
NtOpenDirectoryObject(
OUT PHANDLE DirectoryHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes
);
NtQueryDirectoryObject(
IN HANDLE DirectoryHandle,
OUT PVOID Buffer,
IN ULONG BufferLength,
IN BOOLEAN ReturnSingleEntry,
IN BOOLEAN RestartScan,
IN OUT PULONG Context,
OUT PULONG ReturnLength OPTIONAL
);
Ces API sont documentées dans le « WIN 2000 Native API reference » de Gary Nebbett (dispo dans le dossier ebooks/ de mon repo, mais shuuuut!). Ce qui nous intéresse est l’ObjectDirectory maintenu par le noyau qui porte le nom de ObpRootDirectoryObject, on la dénome tout simplement ‘\’ :} On lit l’arborescence de façon récursive en réappelant la fonction dès qu’on trouve un nouveau « dossier », un nouvel objet de type ObjetDirectory en fait et puis on admire le résultat défiler pendant 10 mins. A noter que si vous voulez pas vous peter les yeux le tool WinObj de sysinternals réalise très bien cela et possède surtout une GUI, mon code est juste là pour montrer comment utiliser ces API.
http://www.microsoft.com/technet/sysinternals/SystemInformation/WinObj.mspx
Alors à l’exécution on voit un dossier Driver qui contient tout nos objets sur les drivers :
Newpath : Driver
ObjectName : NDIS
ObjectType : Driver
ObjectName : KSecDD
ObjectType : Driver
ObjectName : Beep
ObjectType : Driver
ObjectName : Raspti
ObjectType : Driver
ObjectName : Mouclass
ObjectType : Driver
ObjectName : Kbdclass
ObjectType : Driver
[...]
Maintenant qu’on sait manipuler ces API, regardons à quoi ressemble une structure OBJECT_DIRECTORY, hop straff bunny on lance le kernel debugger et on examine les symbols :
lkd> dt nt!_OBJECT_DIRECTORY
+0x000 HashBuckets : [37] Ptr32 _OBJECT_DIRECTORY_ENTRY
+0x094 Lock : _EX_PUSH_LOCK
+0x098 DeviceMap : Ptr32 _DEVICE_MAP
+0x09c SessionId : Uint4B
+0x0a0 Reserved : Uint2B
+0x0a2 SymbolicLinkUsageCount : Uint2B
Le premier champ est un tableau de 37 pointeurs sur des _OBJECT_DIRECTORY_ENTRY :
lkd> dt nt!_OBJECT_DIRECTORY_ENTRY
+0x000 ChainLink : Ptr32 _OBJECT_DIRECTORY_ENTRY
+0x004 Object : Ptr32 Void
Une structure OBJECT_DIRECTORY_ENTRY contient un pointeur sur la structure OBJECT_DIRECTORY_ENTRY suivante (ChainLink) et un champ contenant un pointeur sur l’objet quelle référence (Object).
Maintenant qu’on connaît les structures on choppe l’adresse de l’ObpRootDirectoryObject et on la dump :
lkd> dd nt!ObpRootDirectoryObject l 1
8055fb58 e1001588 00000000 00000001 00000000
lkd> dt nt!_OBJECT_DIRECTORY e1001588
+0x000 HashBuckets : [37] 0xe10078a0 _OBJECT_DIRECTORY_ENTRY
+0x094 Lock : _EX_PUSH_LOCK
+0x098 DeviceMap : (null)
+0x09c SessionId : 0xffffffff
+0x0a0 Reserved : 0
+0x0a2 SymbolicLinkUsageCount : 0x82
Dumpons ce qui nous intéresse, le tableau de pointeurs sur les _OBJECT_DIRECTORY_ENTRY :
lkd> dd e1001588 l 25 //(0x25=37)
e1001588 e10078a0 e171f518 00000000 e1007150
e1001598 00000000 00000000 e15c3600 00000000
e10015a8 00000000 e14971b8 e10000d0 00000000
e10015b8 00000000 e133dd20 e1747970 00000000
e10015c8 e133c750 00000000 00000000 e1007890
e10015d8 e15be088 e15ad5d0 e15be0f8 e148a118
e10015e8 e133c658 e1b5ca28 e10004b8 e1007738
e10015f8 00000000 00000000 00000000 e1007880
e1001608 e1000060 e15cb688 00000000 e1680d80
e1001618 00000000
Après on peut lire la liste chainée, la fin est déterminé par un ChainLink à nul. Ici j’ai prit la liste qui correspondait à l’ObjectDirectory référençant les drivers :
lkd> dt nt!_OBJECT_DIRECTORY_ENTRY e133c750 -l ChainLink
ChainLink at 0xe133c750
---------------------------------------------
+0x000 ChainLink : (null)
+0x004 Object : 0xe133c798
lkd> !object 0xe133c798
Object: e133c798 Type: (81ff04f0) Directory
ObjectHeader: e133c780
HandleCount: 0 PointerCount: 79
Directory Object: e1001588 Name: Driver
Hash Address Type Name
---- ------- ---- ----
00 81f29d30 Driver NDIS
81f2a658 Driver KSecDD
81dc8318 Driver Beep
01 81e04738 Driver Raspti
81ec3a80 Driver Mouclass
03 81e16bf8 Driver Kbdclass
[...]
Evidemment on peut directement utilisé la commande !object du kernel debugger pour dumper l’ObpRootDirectoryObject :
lkd> !object e1001588
Object: e1001588 Type: (81ff04f0) Directory
ObjectHeader: e1001570
HandleCount: 0 PointerCount: 35
Directory Object: 00000000 Name:
130 symbolic links snapped through this directory
Hash Address Type Name
---- ------- ---- ----
00 81f2a300 Device Ntfs
e1007688 Directory ArcName
01 e170d2c0 Port SeLsaCommandPort
03 e1001718 Key REGISTRY
06 e19c3330 Port XactSrvLpcPort
09 e1497200 Directory NLS
10 e1007fc0 SymbolicLink DosDevices
13 e168b2e0 Port SeRmCommandPort
14 e17207d0 Port LsaAuthenticationPort
81d99d48 Event LanmanServerAnnounceEvent
81f29750 Device Dfs
16 e133c798 Directory Driver
19 e10075a0 Directory Device
20 e163e208 Directory Windows
21 81df5be8 Event SAM_SERVICE_STARTED
e1340040 Directory Sessions
22 e168f0d0 Directory RPC Control
e168f5b8 Port SmApiPort
23 e1495900 Directory BaseNamedObjects
e10005f8 Directory KernelObjects
24 e133c6a0 Directory FileSystem
e1001128 Directory GLOBAL??
25 81cef718 WaitablePort NLAPublicPort
26 e1000500 Directory ObjectTypes
27 e1007780 Directory Security
e174f040 Port ErrorLogPort
31 e1007c38 SymbolicLink SystemRoot
81d8d490 Device Cdfs
32 81cc9768 WaitablePort NLAPrivatePort
e1007e78 Directory Callback
33 81e15470 Event UniqueSessionIdEvent
81f6ede0 Event SeLsaInitEvent
35 e1340550 Directory KnownDlls
Je reviens sur les valeur prise par le champ Object de la structure OBJECT_DIRECTORY_ENTRY, il s’agit d’un pointeur sur l’objet, lorsque qu’on utilise la commande !object du kernel debugger celle ci va aller lire à l’adresse demandé moins
0×18, voyez avec l’exemple :
lkd> !object e1001588
Object: e1001588 Type: (81ff04f0) Directory
ObjectHeader: e1001570=e1001588 – 0×18
Pour en fait retrouver une structure OBJECT_HEADER qui va nous dire à quoi correspond l’objet:
lkd> dt nt!_OBJECT_HEADER
+0x000 PointerCount : Int4B
+0x004 HandleCount : Int4B
+0x004 NextToFree : Ptr32 Void
+0x008 Type : Ptr32 _OBJECT_TYPE
+0x00c NameInfoOffset : UChar
+0x00d HandleInfoOffset : UChar
+0x00e QuotaInfoOffset : UChar
+0x00f Flags : UChar
+0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
+0x010 QuotaBlockCharged : Ptr32 Void
+0x014 SecurityDescriptor : Ptr32 Void
+0x018 Body : _QUAD
Dans le cas d’un driver on à :
lkd> !object 81f29d30
Object: 81f29d30 Type: (81fb5900) Driver
ObjectHeader: 81f29d18
HandleCount: 0 PointerCount: 20
Directory Object: e133c798 Name: NDIS
Voilà maintenant qu’on sait comment tout cela fonctionne, on comprend mieux comment fonctionne les anti-rootkits qui utilisent cette techniques pour retrouver les drivers.
Mais un rootkit à déjà contourner la chose, un prototype poster sur rooktit.com et portant le nom de unreal.a
http://rootkit.com/newsread.php?newsid=647
En fait je viens de m’apercevoir qu’ils ont release le code, j’expliquerais son fonctionnement plus tard afin de montrer comme le rootkit se cache dans l’ObpRootDirectoryObject.
Le code vu plus haut :
http://ivanlef0u.fr/repo/ObjectDir.rar
Enjoy.
février 28th, 2007
Il m’est arrivé un truc de maladouf récemment, le genre de choses qui vous coupe la respiration comme un coup de poing de Mc Tyson dans le ventre. J’étais pépère chez moi idlant entre mon code et xchat jusqu’au moment ou une connaissance du net vient me dire d’aller voir mes mails, gruut ? Je me logge tranquil sur ma boite mail, w00t un mail !, évennement qui pour moi mérite de sortir le champomy :]
« From : Christophe Devine », hum ca me dit quelque chose, hop 2 clicks sur google et j’arrive sur http://fr.wikipedia.org/wiki/Aircrack
Bon ok, Ivan reste calme, boit un peu de champomy ca va aller … ce gars à juste dev aircrack, fait abstration de ca et n’oublie pas ta devise : « i know kung-fu ».
« Subject : Stage »
OMG je me rappelais avoir envoyé mon CV à cette fameuse connaissance du net ! Je commence à fear, mes mains sont moites, une goutte de sueur coule sur ma tempe, mon coeur s’emballe, mes yeux s’écarquillent … j’ouvre le mail.
Le mail parle de travailler sur projet basé sur le boot rootkit d’EEYE, un rootkit qui se charge avant l’OS en utilisant le MBR (Master Boot Record) pour corrompre le système, utilisant ensuite divers trics pour manipuler les drivers sur système …. WHAOU comme demande de stage ca fait de la peur :}
Apres Mr Devine me propose de le rencontrer, non pas sur irc MAIS dans le monde réel, chose que je déteste à cause de la lumière dans le ciel qui fait mal aux yeux OO. Anyway ce genre de rencontre ca ne se refuse pas, hop un café une chaussure et c’est partit pour 500 km de train, arrivé sur place vers les midi, il me propose d’aller déjeuner et c’est lui qui invite wOOt :] Petit resto jap bien sympathique avec une discussion intéressante, je m’aperçois vite que Windows c’est pas trop son domaine, j’en profite pour lui placer hook IRP dans sa face suivit d’un DKOM à la Mc Tyson, pwoned
Après visite des locaux, là ou je devrais passer 2 mois prochainement, ambiance bonne enfant mais avec du bon l33t qui traîne, par contre niveau Windows c’est pas encore ca ….
Arf déjà l’heure de partir, j’allais leur mettre une rouste à Street Fighter 2 (ou pas), hop je me retape 3h de train, retour à la base, debriefing.
Pfiou, journée de ouf mais j’ai mon stage :] Comme me la dit Mr Devine pour eux trouver des stagiaires motivés et surtout ayant un peu de connaissance en sécu n’était pas du tout simple, ce que je comprend parfaitement, on apprend pas ce genre de choses à l’école. Moi en attendant je suis content, j’avais peur de trouver un pauvre truc de merde ou j’aurais du coder en PHP (ARGH), je vais pouvoir tirer profit de ce que je sais faire sous Win, une grand avancé pour un petit jeune comme moi, mais bon ca devait bien arriver à jour imho.
Ce qu’il faut retenir c’est que dans l’info le domaine de la sécu est vraiment à part, les personnes douées dans le domaine sont souvent des autodidactes penchant vers le coté sombre de l’informatique et comme vous le savez dans notre magnifique pays qu’est la France on ne juge pas une personne à ses compétences mais à ses diplomes … Même si vous êtes plus skillé qu’une taps qui à bac+32, sans diplôme vous serez toujours moins bien récompensé. Ensuite si vous voulez « prouver » votre valeur, pas la peine de haXorer les sites du gouv, montrez ce que vous savez faire, exposez des techniques un peu originales, pondez des papers. Mon blog m’a beacoup aidé de ce coté, c’est un peu ma vitrine, en gros ce qu’il y avait sur mon CV n’a pas été décisif, ouais je sais …l’école ca sioux :]
Merci encore à Mr Devine pour ce super stage :}
février 22nd, 2007
On continue notre exploration des heap overflows sous Win, cette fois ci on s’attaque à l’étude d’un autre méchanisme de protection. Je veux parler du cookie ajouté dans la structure HEAP_ENTRY qui permet (un peu comme celui qu’on ajoute dans la pile) de vérifier si le chunk à été corrompu, le système, lors de la manipulation des chunks regarde si celui-ci correspond bien à la valeur donné au départ. Avant d’expliquer tout cela je vais parler des structures plus « générales » utilisées par le heap manager en userland.
Accrochez vous parce que c’est violent, rien de ceci n’est documenté officiellement, MS fournit juste les définitions des structures nécessaire pour le débuggage.
Les valeurs des structures dumpées ici sont celles obtenu avec le code de la partie précédente.
Tout d’abord partons d’en haut, dans la structure PEB (Process Environment Bloc) on trouve en 0×90 un pointeur sur un tableau de pointeurs de structure HEAP, celles-ci sont crées lors d’un appel à HeapCreate (RtlCreateHeap).
kd> dt nt!_PEB 7ffdf000
[...]
+0x088 NumberOfHeaps : 5
+0x08c MaximumNumberOfHeaps : 0x10
+0x090 ProcessHeaps : 0x7c98de80 -> 0x00140000
[...]
Ici notre process possède 5 heaps.
kd> dd 7c98de80 ; 5 heaps
7c98de80 00140000 00240000 00250000 00320000
7c98de90 00340000 00000000 00000000 00000000
Celui qu’on à crée est le dernier, dumpons la structure HEAP à l’aide du kernel debugger.
kd> dt nt!_HEAP 00340000
+0x000 Entry : _HEAP_ENTRY
+0x008 Signature : 0xeeffeeff
+0x00c Flags : 0x1000
+0x010 ForceFlags : 0
+0x014 VirtualMemoryThreshold : 0xfe00
+0x018 SegmentReserve : 0x100000
+0x01c SegmentCommit : 0x2000
+0x020 DeCommitFreeBlockThreshold : 0x200
+0x024 DeCommitTotalFreeThreshold : 0x2000
+0x028 TotalFreeSize : 0x11f
+0x02c MaximumAllocationSize : 0x7ffdefff
+0x030 ProcessHeapsListIndex : 5 ;5 ème heap
+0x032 HeaderValidateLength : 0x608
+0x034 HeaderValidateCopy : (null)
+0x038 NextAvailableTagIndex : 0
+0x03a MaximumTagIndex : 0
+0x03c TagEntries : (null)
+0x040 UCRSegments : (null)
+0x044 UnusedUnCommittedRanges : 0x00340588 _HEAP_UNCOMMMTTED_RANGE
+0x048 AlignRound : 0xf
+0x04c AlignMask : 0xfffffff8
+0x050 VirtualAllocdBlocks : _LIST_ENTRY [ 0x340050 - 0x340050 ]
+0x058 Segments : [64] 0x00340640 _HEAP_SEGMENT ;tableau de descripteurs de segments
+0x158 u : __unnamed
+0x168 u2 : __unnamed
+0x16a AllocatorBackTraceIndex : 0
+0x16c NonDedicatedListLength : 1
+0x170 LargeBlocksIndex : (null)
+0x174 PseudoTagEntries : (null)
+0x178 FreeLists : [128] _LIST_ENTRY [ 0x340710 - 0x340710 ]
+0x578 LockVariable : 0x00340608 _HEAP_LOCK
+0x57c CommitRoutine : (null)
+0x580 FrontEndHeap : (null)
+0x584 FrontHeapLockCount : 0
+0x586 FrontEndHeapType : 0 ''
+0x587 LastSegmentIndex : 0 ''
Grosse structure qui contient plein d’infos, notamment on trouve un tableau de 64 pointeurs sur des structure HEAP_SEGMENT, je cite encore l’article de Kortchinsky paru dans Misc basé sur les travaux de Matt Connover : « le tableau des segments du tas : d’une taille fixe (64 entrées), il regroupe les informations nécessaires à la gestion de chaque segment : adresse de base, nombre de pages, première entrée dans le tas, dernière entrée dans le tas, etc. »
kd> dd 00340000 + 58 ; 1 seul segment
00340058 00340640 00000000 00000000 00000000
Un seul segment pour nous.
kd> dt nt!_HEAP_SEGMENT 00340640
+0x000 Entry : _HEAP_ENTRY
+0x008 Signature : 0xffeeffee
+0x00c Flags : 0
+0x010 Heap : 0x00340000 _HEAP
+0x014 LargestUnCommittedRange : 0
+0x018 BaseAddress : 0x00340000
+0x01c NumberOfPages : 1
+0x020 FirstEntry : 0x00340680 _HEAP_ENTRY
+0x024 LastValidEntry : 0x00341000 _HEAP_ENTRY
+0x028 NumberOfUnCommittedPages : 0
+0x02c NumberOfUnCommittedRanges : 0
+0x030 UnCommittedRanges : (null)
+0x034 AllocatorBackTraceIndex : 0
+0x036 Reserved : 0
+0x038 LastEntryInSegment : 0x00340708 _HEAP_ENTRY
Il est tout de même possible d’avoir des infos plus claires à l’aide de la commande !heap du kernel debugger.
kd> !heap -a 00340000
No heaps to display.
Index Address Name Debugging options enabled
1: 00340000
Segment at 00340000 to 00341000 (00001000 bytes committed)
Flags: 00001000
ForceFlags: 00000000
Granularity: 8 bytes
Segment Reserve: 00100000
Segment Commit: 00002000
DeCommit Block Thres: 00000200
DeCommit Total Thres: 00002000
Total Free Size: 0000011f
Max. Allocation Size: 7ffdefff
Lock Variable at: 00340608
Next TagIndex: 0000
Maximum TagIndex: 0000
Tag Entries: 00000000
PsuedoTag Entries: 00000000
Virtual Alloc List: 00340050
UCR FreeList: 00340588
FreeList Usage: 00000000 00000000 00000000 00000000
FreeList[ 00 ] at 00340178: 00340710 . 00340710
00340708: 00088 . 008f8 [10] - free
Segment00 at 00340640:
Flags: 00000000
Base: 00340000
First Entry: 00340680
Last Entry: 00341000
Total Pages: 00000001
Total UnCommit: 00000000
Largest UnCommit:00000000
UnCommitted Ranges: (0)
Heap entries for Segment00 in Heap 00340000
00340000: 00000 . 00640 [01] - busy (640) -> 1600d structure HEAP (sizeof 1416d)
00340640: 00640 . 00040 [01] - busy (40) -> 64d struture HEAP_SEGMENT (sizeof 60d)
00340680: 00040 . 00088 [01] - busy (80) -> 128d notre bloc de 128 octets
00340708: 00088 . 008f8 [10]
Comme on peut le voir, chaque structure est aussi « allouée » dans la mémoire et possède donc comme premier membre un HEAP_ENTRY.
Lors d’un appel à HeapCreate (RtlCreateHeap) on récupère un handle sur le heap qui est tout simplement l’adresse de la structure HEAP.
Maintenant qu’on à posé les bases intéressons nous à ce fameux cookie. Voiçi les valeurs que j’ai obtenu après plusieurs essais avec le code de la partie 2.
Allocating Memory
MemAlloc: 0x340688
pUsedEntry : 0x340680
Size: 17
PreviousSize: 8
SmallTagIndex: 123
Flags: 1
UnusedBytes: 8
SegmentIndex: 0
Allocating Memory
MemAlloc: 0x340688
pUsedEntry : 0x340680
Size: 17
PreviousSize: 8
SmallTagIndex: 70
Flags: 1
UnusedBytes: 8
SegmentIndex: 0
Allocating Memory
MemAlloc: 0x340688
pUsedEntry : 0x340680
Size: 17
PreviousSize: 8
SmallTagIndex: 134
Flags: 1
UnusedBytes: 8
SegmentIndex: 0
Même si l’adresse du bloc alloué ne change pas, le cookie (SmallTagIndex) lui varie à chaque exécution. Allay hop un petit breakpoint en écriture sur la zone oû est inscrit le cookie (1 byte) lors de l’appel à HeapAlloc (RtlAllocateHeap) et on tombe sur ce petit code.
ntdll RtlAllocateHeap
7C92B5CC 8BC6 MOV EAX,ESI ;eax=esi=addr de HEAP_ENTRY
7C92B5CE C1E8 03 SHR EAX,3 ;eax >> 3
7C92B5D1 0FB6C0 MOVZX EAX,AL ; eax=(UCHAR)eax
7C92B5D4 8985 2CFFFFFF MOV DWORD PTR SS:[EBP-D4],EAX ;osef
7C92B5DA 33C9 XOR ECX,ECX ;ecx=0
7C92B5DC 8A4F 04 MOV CL,BYTE PTR DS:[EDI+4] ;edi=addr du HEAP, +4 -> SmallTagIndex cookie du HEAP
7C92B5DF 33C8 XOR ECX,EAX
7C92B5E1 884E 04 MOV BYTE PTR DS:[ESI+4],CL ; écrit dans le chunk le SmallTagIndex
Ce qui correspond à : SmallTagIndex=(UCHAR)(pHeapEntry>>3) XOR (pHeap->HeapEntry->SmallTagIntex)
Il est donc possible en ayant le handle sur le heap renvoyé par HeapCreate et l’adresse du bloc renvoyé par HeapAlloc de recalculé ce cookie. Dans le code précédent cela revient à écrire :
UCHAR Cookie=(UCHAR)(((ULONG)MemAlloc-8)>>3) ^ (*((PUCHAR)hHeap+0×4));
Truc chelou que je n’est pas compris le cookie n’est vérifié que lors de l’appel à HeapFree oO.
ntdll RtlFreeHeap
7C9204D7 8BC6 MOV EAX,ESI ;eax=esi=HEAP_ENTRY
7C9204D9 C1E8 03 SHR EAX,3 ;eax >> 3
7C9204DC 3246 04 XOR AL,BYTE PTR DS:[ESI+4] ;al=(UCHAR)((eax >> 3) XOR pHeapEntry->SmallTagIndex)
; le XOR étant reversible (a=b xor c <=> c= a xor b <=> b = c xor a) on recalcul le SmallTagIndex de la struc HEAP
7C9204DF 3247 04 XOR AL,BYTE PTR DS:[EDI+4] ; si egaux alors le xor vaut 0, sinon ya eu fuckage du chunk
7C9204E2 0F85 31B80200 JNZ ntdll.7C94BD19
En cas d’altération du chunk la fonction HeapFree nous renvoie le numéro d’erreur 87 : invalid parameter.
La question à 1000 € est de savoir comment est calculer le cookie de la structure HEAP elle même :] Hop on pose un petit breakpoint en écriture et on tombe sur :
ntdll RtlCreateHeap
7C9357AF BA 0000FE7F MOV EDX,7FFE0000
7C9357B4 8B02 MOV EAX,DWORD PTR DS:[EDX]
7C9357B6 F762 04 MUL DWORD PTR DS:[EDX+4]
7C9357B9 0FACD0 18 SHRD EAX,EDX,18
7C9357BD 8B4D E4 MOV ECX,DWORD PTR SS:[EBP-1C] ;ecx=adresse de HEAP
7C9357C0 8841 04 MOV BYTE PTR DS:[ECX+4],AL ;HEAP+4 -> SmallTagIndex
Hum chose étrange on trouve une adresse hardcodé 0x7FFE000, alors là coup de chance je google dessus et arrive sur :
http://www.uninformed.org/index.cgi?v=2&a=2&p=15
Magnifique, en 0x7FFE0000 on tombe sur une struct commune à tous les process: KUSER_SHARED_DATA, ses 2 premiers champ sont des variables s’incrémentant en fonction du temps.
lkd> dt nt!_KUSER_SHARED_DATA 7FFE0000 -a TickCountLow
+0x000 TickCountLow : 0x9d294f
lkd> dt nt!_KUSER_SHARED_DATA 7FFE0000 -a TickCountLow
+0x000 TickCountLow : 0x9d29bc
lkd> dt nt!_KUSER_SHARED_DATA 7FFE0000 -a TickCountLow
+0x000 TickCountLow : 0x9d29f2
lkd> dt nt!_KUSER_SHARED_DATA 7FFE0000 -a TickCountLow
+0x000 TickCountLow : 0x9d2a26
lkd> dt nt!_KUSER_SHARED_DATA 7FFE0000 -a TickCountMultiplier
+0x004 TickCountMultiplier : 0xa03afb7
Celle la semble constant ....
Anayway cela nous donne pour le code précédent :
ntdll RtlCreateHeap
7C9357AF BA 0000FE7F MOV EDX,7FFE0000
7C9357B4 8B02 MOV EAX,DWORD PTR DS:[EDX] ;KUSER_SHARED_DATA->TickCountLow
7C9357B6 F762 04 MUL DWORD PTR DS:[EDX+4] ;KUSER_SHARED_DATA->TickCountMultiplier
7C9357B9 0FACD0 18 SHRD EAX,EDX,18
SHRD Double Precision Shift Right
0F AC SHRD r/m32,r32,imm8 3/7 r/m32 gets SHR of r/m32 concatenated with r32
SHRD shifts the first operand provided by the r/m field to the right as many
bits as specified by the count operand. The second operand (r16 or r32)
provides the bits to shift in from the left (starting with bit 31). The
result is stored back into the r/m operand. The register remains unaltered.
7C9357BD 8B4D E4 MOV ECX,DWORD PTR SS:[EBP-1C] ;ecx=adresse de HEAP
7C9357C0 8841 04 MOV BYTE PTR DS:[ECX+4],AL ;HEAP+4 -> SmallTagIndex
Bon ca c’était juste pour le fun hein
On sait enfin comment recalculer le cookie en connaissant bien l’environnement :] Concernant les techniques d’exploitations je ne sais pas encore si j’en parlerais, elles semblent assez difficiles, j’ai regardé les papers et codes de Matt Connover sur le sujet et ….. GROUNF ! je verrais si je continue en fonction de ma motivation. En attendant vous pourvez aller voir les docs que j’ai regroupé sur le sujet dans mon repo http://ivanlef0u.fr/repo/index.php?path=./windoz/heap
Ivanlef0u
février 18th, 2007
Suite de la série consacrée à l’études des Heap Overflows, la dernière fois j’avais conclu en montrant que modifier la LIST_ENTRY d’un chunk libre pouvait conduire à une manipulation de la mémoire du process lors de l’allocation de ce chunk. Maintenant on va passer à un exemple concret, je tiens juste à dire que j’ai chialé ma race car les fonction de gestion de heap ne se comportent pas de la même façon si elles sont débuggées ou pas. Tout ca à cause du putin de NtGlobalFlags, anyway c’est toujours un autre anti debugger trick, référencé bien sur par les gars d’OpenRCE 
http://www.openrce.org/reference_library/anti_reversing_view/16/NtGlobalFlag%20Debugger%20Detection/
Finit de pleurer et passons à l’action.
Voici le code qui va nous faire suer :
#include
#include
typedef struct _HEAP_ENTRY
{
union
{
struct
{
USHORT Size;
USHORT PreviousSize;
};
PULONG SubSegmentCode;
};
UCHAR SmallTagIndex;
UCHAR Flags;
UCHAR UnusedBytes;
UCHAR SegmentIndex;
} HEAP_ENTRY, *PHEAP_ENTRY;
typedef struct _HEAP_FREE_ENTRY
{
union
{
struct
{
USHORT Size;
USHORT PreviousSize;
};
PULONG SubSegmentCode;
};
UCHAR SmallTagIndex;
UCHAR Flags;
UCHAR UnusedBytes;
UCHAR SegmentIndex;
LIST_ENTRY FreeList;
} HEAP_FREE_ENTRY, *PHEAP_FREE_ENTRY;
void main()
{
HANDLE hHeap;
PUCHAR MemAlloc;
PHEAP_ENTRY pUsedEntry;
PHEAP_FREE_ENTRY pFreeEntry;
hHeap=HeapCreate(0, 0, 0x1000); //4096 bytes=1 PAGE
printf("Allocating Memory");
MemAlloc=(PUCHAR)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 128);
if(!MemAlloc)
{
printf("Error with HeapAlloc : %dn", GetLastError());
goto bye;
}
}
printf("nMemAlloc: 0x%xn", MemAlloc);
/***************** Allocated chunk *****************/
pUsedEntry=(PHEAP_ENTRY)((PUCHAR)MemAlloc-sizeof(HEAP_ENTRY));
printf("pUsedEntry : 0x%xn", pUsedEntry);
printf("Size: %dn", pUsedEntry->Size);
printf("PreviousSize: %dn", pUsedEntry->PreviousSize);
printf("SmallTagIndex: %dn", pUsedEntry->SmallTagIndex);
printf("Flags: %dn", pUsedEntry->Flags);
printf("UnusedBytes: %dn", pUsedEntry->UnusedBytes);
printf("SegmentIndex: %dn", pUsedEntry->SegmentIndex);
printf("nFreing memoryn");
if(!HeapFree(hHeap, 0, MemAlloc))
{
printf("Error with HeapFree : %dn", GetLastError());
goto bye;
}
/***************** Freed chunk *****************/
pFreeEntry=(PHEAP_FREE_ENTRY)((PUCHAR)MemAlloc-sizeof(HEAP_ENTRY));
printf("pFreeEntry: 0x%xn", pFreeEntry);
printf("Size: %dn", pFreeEntry->Size);
printf("PreviousSize: %dn", pFreeEntry->PreviousSize);
printf("SmallTagIndex: %dn", pFreeEntry->SmallTagIndex);
printf("Flags: %dn", pFreeEntry->Flags);
printf("UnusedBytes: %dn", pFreeEntry->UnusedBytes);
printf("SegmentIndex: %dn", pFreeEntry->SegmentIndex);
printf("Next Chunk 0x%xn", pFreeEntry->FreeList.Flink);
printf("Prev Chunk 0x%xn", pFreeEntry->FreeList.Blink);
bye:
HeapDestroy(hHeap);
}
On se crée un nouveau Heap de 4096 bytes avec HeapCreate et on alloue dedans 128 bytes avec HeapAlloc, ensuite on dump la structure HEAP_ENTRY qui précède le bloc alloué, jusqu’ici rien de violent. Après on libère notre bloc avec HeapFree et on dump notre HEAP_FREE_ENTRY, enfin on destroy notre heap avec HeapDestroy.
A l’exécution on obtient :
C:ProgHackcheap>heap_alloc
Allocating Memory
MemAlloc: 0x340688
pUsedEntry : 0x340680
Size: 17
PreviousSize: 8
SmallTagIndex: 95
Flags: 1 //HEAP_ENTRY_BUSY
UnusedBytes: 8
SegmentIndex: 0
Freing memory
pFreeEntry: 0x340680
Size: 304
PreviousSize: 8
SmallTagIndex: 95
Flags: 16 //HEAP_ENTRY_LAST_ENTRY|HEAP_ENTRY_FILL_PATTERN|HEAP_ENTRY_EXTRA_PRESENT
UnusedBytes: 8
SegmentIndex: 0
Next Chunk 0x340178
Prev Chunk 0x340178
Le bloc est alloué en 0×340688 la structure HEAP_ENTRY est donc 8 bytes avant en 0×340680, n’oubliez pas que le champ size contient la taille, 128(notre bloc) + 8(la struct HEAP_ENTRY)=136, divisé par 8 nous donne bien 17.
A la libération est ajouté la structure LIST_ENTRY pointant sur le chunk précédent (respectivement suivant) libre. Ici les pointeurs sont égaux, osef ca ne change rien.
Souvenez vous, lors de l’allocation, le système « unlink » notre chunk de la LIST_ENTRY de chunk libres avec le code suivant :
mov eax, Flink
mov ecx, Blink
mov [ecx], eax
mov [eax+4], ecx
En choisisant bien les valeurs de Flink et de Blink lors de l’altération du chunk par débordement il est alors possible d’écrire ou l’on veut en mémoire.
Windows XP depuis le sp2, pour nous faire chier à rajouté une petite vérification :
ntdll RtlAllocateHeap :
.text:7C92142E mov edi, [ecx] ; ecx=Blink, edi contiendra le Flink du prev chunk
.text:7C921430 cmp edi, [eax+4] ; eax=Flink, le cmp compare le Flink du prev chunk au
.text:7C921430 ; Blink du next chunk, si pas egaux alors ya eu corruption
.text:7C921433 jnz loc_7C944380
.text:7C921439 cmp edi, edx ; edx pointe sur la LIST_ENTRY de notre chunk
.text:7C92143B jnz loc_7C944380 ; le cmp compare le Flink du prev chunk à l'addr de notre chunk
; si pas egaux alors ya eu fuckage
.text:7C921441 mov [ecx], eax ; PrevChunk vers le NextChunk
.text:7C921443 mov [eax+4], ecx ; NextChunk vers le PrevChunk
On en revient à ce que disait Connover lors de la conf BlackHat 2004 : « B->Flink->Blink == B->Blink->Flink == Header to free (B) ». Cette simple protection suffit à faire échouer de nombreuses tentatives d’exploitation des heap overflows.
Mais cela n’est pas finit, un autre méchanisme existe, je veux bien sur parler du fameux « cookie » insérer dans les structures HEAP_ENTRY sous le nom de SmallTagIndex, le principe est simple si le cookie a été altéré alors il y a eu corruption du chunk. Mais ça on le verra cela dans la prochaine partie…
Ivanlef0u
février 16th, 2007
Alex Ionescu nous fait découvrir sur son blog un jolie petite API, RtlRemoteCall, je cite :
« Basically, it hijacks any thread of your choice into any process that you have access to, and sends the thread to a new “CallSite” by updating its EIP there. It also allocates a new ESP, and pushes up to 4 parameters at most »
Mr Totoscu nous promet le code dans pas longtemps, par curiositée je lance mon IDA et jette un coup d’oeil rapide sur RtlRemoteCall.
Le prototype de la fonction est le suivant :
NTSTATUS
NTAPI
RtlRemoteCall(
IN HANDLE Process,
IN HANDLE Thread,
IN PVOID CallSite,
IN ULONG ArgumentCount,
IN PULONG Arguments,
IN BOOLEAN PassContext,
IN BOOLEAN AlreadySuspended
);
Le fonctionnement est assez intuitif :
1. Appel sur NtSuspendThread pour stoper le thread passé en param.
2. NtGetContextThread récupère une structure CONTEXT sur le thread, celle ci contient l’état de tout les registres du thread mit en pause.
3. Si PassContext est vrai, alors on copie (avec NtWriteVirtualMemory) dans la stack du thread distant son CONTEXT pour pouvoir l’utiliser en tant qu’argument.
4. Copie les arguments dans la pile à partir de ESP.
5. Redéfinit l’ESP et l’EIP de la structure CONTEXT puis met a jour le context avec NtSetContextThread.
6. Relance le thread avec NtResumeThread qui va continuer sur le nouvel EIP et sa nouvelle stack.
Par contre si après avoir lancé notre nouvelle fonction celle ci ne se débrouille pas pour reprendre l’exécution du thread ou il avait été stoppé, le programme plante. Avec le paramètre PassContext mit à vrai, on peu récup le CONTEXT du thread et demander à notre fonction de relancer l’exécution, sweet :}
Autrement aller voir sur mon blog le post « Thread Hijacking » vous trouverez la même chose faite à partir d’API documentées. Si j’avais su l’existence ce cette fonction je ne me serais pas fait chier à la recoder mais bon c’est la magie « windows » :]
Le post d’Alex : http://www.alex-ionescu.com/?p=28
février 14th, 2007
Lorsque je m’ennuis, il m’arrive souvent de ressortir les vieilles choses. Me faisant alors chier comme un rat mort, je me suis intéressé à une chose qui me faisait un peu peur, les heap overflows sous windows, de nombreux papers ont déjà été écrit sur le sujet mais au lieu de reprendre leurs contenu, j’ai tenté de déterminer ou et comment leurs auteurs avaient trouvé tant d’infos sur le sujet.
Il faut savoir que la gestion du heap sous windows n’est pas documentée de façon officielle, Microsoft nous fournit quelques API pour créer un heap, l’allouer, le libérer et le détruire mais c’est tout, voyez plutôt :
http://msdn2.microsoft.com/en-us/library/aa366711.aspx
Cependant il est possible à l’aide d’outils comme les symboles de débuggage fournit par Microsoft, IDA et Ollydbg, de reverser certains méchanismes, attention ceci est à relativiser, vouloir reverser tout le fonctionnement du heap demande un travaille énorme que certains ont plus ou moins réalisé, dans le cas présent ceci n’est pas mon but (je suis f0u mais quand même :s).
Depuis l’arrivé du SP2 sur windows XP les routines de management du heap sont devenus plus sûres, exploiter un debordement dans le tas de façon classique est devenu impossible. J’ai donc regarder à quel niveau ce faisait ces vérifications, mais tout d’abord posons quelques définitions sur les structures utilisées pour gérer le tas.
Lorsqu’un bloc mémoire est alloué celui est précédé d’une structure appelé ‘chunk’:
typedef struct _HEAP_ENTRY
{
union
{
struct
{
USHORT Size;
USHORT PreviousSize;
};
PULONG SubSegmentCode;
};
UCHAR SmallTagIndex;
UCHAR Flags;
UCHAR UnusedBytes;
UCHAR SegmentIndex;
} HEAP_ENTRY, *PHEAP_ENTRY
On peut le voir sous olly :
La mémoire alloué est en 0x360640, et est précedée d'une structure, HEAP_ENTRY.
00360640 06 00 08 00 F3 07 18 00 ..ó.
00360650 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Un peu d’info sur les champs de cette structure, je reprend l’article de Kortchinsky paru dans Misc en modifiant 2, 3 choses.
Descriptif des champs :
- « Size » – USHORT, 2 octets : taille du chunk courant. Elle correspond à un nombre de cellules de 8 octets occupées par le chunk. Lors de la demande d’allocation, la taille demandée est arrondie à un multiple de 8 (supérieur ou égal), auquel s’ajoutent les 8 octets necessaires au stockage des champs du chunk. La taille stockée ici répond donc à l’opération (tailledemandee + 7) >> 3 + 1, soit 65 pour 512 octets, 18 pour 129, etc ;
- « PreviousSize » – USHORT, 2 octets : taille du chunk précédant, possède les mêmes caractéristiques que « Self size » ;
- « SmallTagIndex » – UCHAR, 1 octet : uniquement utilisé en mode debug (héhé pas que ca, on le verra plus tard).
- « Flags » – UCHAR, 1 octet : indicateurs des propriétés du chunk, chaque bit positionné à 1 de cet octet faisant état d’une caractéristique particulière du chunk, ils peuvent être trouvés sur le web (les plus utiles seront détaillés par la suite) :
0×01, HEAP_ENTRY_BUSY
0×02, HEAP_ENTRY_EXTRA_PRESENT
0×04, HEAP_ENTRY_FILL_PATTERN
0×08, HEAP_ENTRY_VIRTUAL_ALLOC
0×10, HEAP_ENTRY_LAST_ENTRY
0×20, HEAP_ENTRY_SETTABLE_FLAG1
0×40, HEAP_ENTRY_SETTABLE_FLAG2
0×80, HEAP_ENTRY_SETTABLE_FLAG3
- « UnusedBytes » – UCHAR, 1 octet : nombre d’octets non utilisés dans le buffer alloué, égal à la taille allouée à laquelle est soustraite la taille demandée – information d’une utilité très relative.
- « SegmentIndex » – UCHAR, 1 octet : indice du segment auquel appartient le chunk, en référence au tableau des segments du tas.
La structure d’un chunk libre est en fait une HEAP_ENTRY possédant comme dernier champ une LIST_ENTRY (double liste chaînée) sur les autres chunks libres. Cela donne sous Olly :
00360640 37 01 08 00 F3 14 18 00 7.ó.
00360650 78 01 36 00 94 01 36 00 EE FE EE FE EE FE EE FE x6.x6.îþîþîþîþ
On voit bien les 2 pointeurs Flink et Blink vallant 0×00360178 et 0×00360194, à noté que la structure ne change pas de position en mémoire, les 2 pointeurs sont ajouté à la suite comme on à libéré le Heap.
Lors de l’allocation la fonction HeapAlloc va chercher dans la liste des chunks libre de notre heap celui qui correspond à la taille demandée, le système va donc délinker un chunk pour l’allouer ensuite. C’est à ce niveau que une des vulnérabilités de la gestion du tas apparaît, en effet lors de l’opération de « unlink » le Flink (pointeur sur le chunk libre suivant) du chunk précédent doit être mis à jour pour pointer sur un chunk libre et vise versa. Le système utilise donc les pointeurs du futur chunk alloué pour faire cela.
L’opération correspond en C:
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY;
LIST_ENTRY Chunk; //LIST_ENTRY du chunk qui va être alloué
Chunk.Blink->Flink=Chunk.Flink; // PrevChunk vers le NextChunk
Chunk.Flink->Blink=Chunk.Blink; // NextChunk vers le PrevChunk
En ASM cela donne :
mov eax, Flink
mov ecx, Blink
mov [ecx], eax
mov [eax+4], ecx
Ainsi si par inadvertance le chunk avait été altéré il est possible de d’écrire ou l’on veut en mémoire en controlant les valeurs de Flink et de Blink, en théorie
Mais vous comprendrez dans la suite que sous Windows il n’est pas si simple d’altérer ces chunks sans avoir des petits soucis
Ivanlef0u
février 12th, 2007
Nynaeve ce grand malade commence une série sur les optimisations du compilo. Dans ce premier post on retrouve le fameaux xor eax,eax à la place du mov eax, 0 qui à l’avantage d’être plus rapide et de prendre moins de place en mémoire. Autre chose intéressante l’utilisation de l’instrution lea pour réaliser des opérations arithmétiques avec le moins d’instructions possible, par exemple lea eax, [eax*4+1] permet de mettre dans eax, eax*4+1 tout ca en 1 seule instruction, c’est pas beau ca :]
L’article en anglais ici : http://www.nynaeve.net/?p=64
Moi je dis à suivre.
février 10th, 2007
Yop, après une semaine d’idlage larvaire intensif je me décide à poster quelque chose. Une chose qu’on aime faire lorsque qu’on veut faire mumuse avec le système est de détourner le fonctionnement normal de ses fonctions, pour cela on utilise un « hook », le principe est simple on modifie légèrement le programme pour qu’il appel notre fonction au lieu d’appeler la fonction original. Le plus souvent on détourne une fonction afin de modifier ses résultats, ainsi il est possible de faire croire ce qu’on veut au programme :}
Il existe en gros 2 méthodes pour hooker, la première consiste à modifier la table des imports qui contient les adresses des différentes fonctions importées des DLL. Par exemple si l’on désire détourner la fonction GetModuleHandle(), on modifie dans l’IAT (Import Address Table) le pointeur sur cette fonction par l’adresse de la notre. Evidemment niveau discrétion c’est pas trop ca, il suffit de scanner les entrées de l’IAT et de les comparer aux adresses des vrai fonctions pour détecter se type de hook.
D’ou l’invention d’une seconde méthode, l’inline hook, cette fois plus question de toucher à l’IAT, on va directement modifier le prologue de la fonction. En fait l’on place au début de la fonction un jump sur la notre, le problème c’est qu’on écrase ses premiers bytes et si l’on veut la réutiliser ca pose problème :p
Pour ne pas déteriorer la fonction de départ on sauvegarde ses premieres instructions suivit d’un petit jump, je m’explique, voulant exécuter toutes les instructions de la fonction hooké on doit commencer par appeler ses premiers bytes puis le reste qui n’a pas bougé on l’appel en jumpant dessus, on obtient donc le schéma suivant:
{//buffer en mémoire
[premiers bytes sauvegardés]
[jump @begin]
}
{//fonction hookée
[jump sur notre fonction]
[@begin, instructions inchangées de la fonction hookée]
}
On commence par call sur les premiers bytes saved, on les exécute, puis on jump sur la suite.
Reste encore un petit soucis, la taille d’un jmp en assembleur est des 5 octects, or sauvegarder les 5 premiers bytes de l’API hooké serait une erreur, en effet si par exemple on se retrouve avec 2 instrutions, 1 de 3 bytes et l’autre de 4 bytes, la seconde sera tronqué et lors du jump on exécutera n’importe quoi. La solution est d’utiliser un moteur qui calcul la taille des instructions oO, on calcul la taille des instructions jusqu’à trouver un bloc de taille supérieure ou égale à 5 octets. Si on doit copier 7 bytes alors les 5 premiers seront pour les jump et les 2 autres contiendront des nop (0×90).
Le mieux est évidemment de voir ce que ca donne, dans l’exemple suivant je hook l’api native NtOpenFile :
Avant le hook:
7C91DCFD > B8 74000000 MOV EAX,74
7C91DD02 BA 0003FE7F MOV EDX,7FFE0300
7C91DD07 FF12 CALL NEAR DWORD PTR DS:[EDX]
7C91DD09 C2 1800 RET 18
Après la hook :
7C91DCFD >- E9 374C6E93 JMP 10002939
7C91DD02 BA 0003FE7F MOV EDX,7FFE0300
7C91DD07 FF12 CALL NEAR DWORD PTR DS:[EDX]
7C91DD09 C2 1800 RET 18
A noter que le hasard veut que la première instruction fasse 5 octets ...
On la retrouve sauvegardé ici, suivit du jump vers les suite des instructions
000A9518 B8 74000000 MOV EAX,74
000A951D - E9 E047877C JMP ntdll.7C91DD02
Now reste plus qu’a lancé cela dans la mémoire d’un process, pour cela on utilise une dll qui lors du DLL_PROCESS_ATTACH place le hook, et l’enlève lors du DLL_PROCESS_DETACH. Je ne reviens sur la méthode pour injecter une DLL, il y à assez de doc sur le net pour apprendre ça.
La fonction qui place le hook s’appelle, InstallInlineHook() elle prend 3 arguments :
-L’adresse de l’API à hooker
-L’adresse de notre fonction de substitution
-Un pointeur sur la zone mémoire qui contiendra les instructions sauvegardées.
Pour l’enlever on utilise RemoveInlineHook() avec comme paramètres:
-L’addresse de l’API hooké
-L’adresse des premières instructions sauvegardées
-La taille des instruction sauvegardées
Vous touverez dans les sources:
InlineHook.cpp : le moteur d’inline hook
ZDisasm.h et ZDisasm.C la moteur codé par Z0MBiE qui calcul la taille des instructions.
NtOpenFile_spy.cpp : la dll qui hook l’api native NtOpenFile et qui écrit dans c:\logfile.txt les noms des fichiers passés en paramètre.
Pour l’utiliser il faut d’abord injecter la dll dans le process choisit :
C:\\ProgHack\\c\\InlineHook>inject_dll notepad.exe c:\\proghack\\c\\InlineHook\\NtOpenFile_spy.dll
[*]Ownage du processus: notepad.exe
[*]Handle: 0x7f4
[*]CodeSize: 44
[*]Emplacement memoire: 0x3c0000
[*]Target injected
Après vous pouvez voir dans le fichier c:\logfile.txt les noms des fichers ouverts par le notepad.
Puis pour enlever le hook :
C:\ProgHack\c\InlineHook>unload_dll notepad.exe NtOpenFile_spy.dll
[*]Ownage du processus: notepad.exe
[*]Handle: 0x7f4
[*]Target injected
Voilà j’espère que j’ai été clair, sinon n’hésitez pas à mes poser de questions
Tout est dispo ici:
http://ivanlef0u.fr/repo/InlineHook.rar
Enjoy :}
février 10th, 2007
Vous avez sans doute déjà rencontré cette instruction en tracant sous Olly sans vraiment trop vous y intéressé et pourtant c’est une des plus importante de votre OS. Que peut-elle bien faire pour être si importante et pourquoi elle semble ne rien faire. Ivanlef0u votre détective préféré s’est penché sur cette affaire.
Dans un call à une API native comme par exemple ZwCreateFile on peut voir :
7C91D682 > B8 25000000 MOV EAX,25
7C91D687 BA 0003FE7F MOV EDX,7FFE0300
7C91D68C FF12 CALL NEAR DWORD PTR DS:[EDX] //EDX pointe sur 0x7C91EB8B
7C91D68E C2 2C00 RET 2C
On arrive ici avec le call
7C91EB8B > 8BD4 MOV EDX,ESP
7C91EB8D 0F34 SYSENTER
7C91EB8F 90 NOP
7C91EB90 90 NOP
7C91EB91 90 NOP
7C91EB92 90 NOP
7C91EB93 90 NOP
7C91EB94 > C3 RET
En fait l’instruction SYSINTER à pour rôle de de faire un appel système. Dans notre exemple le code qui sera lancé sera celui de NtCreateFile de ntoskrnl.exe. Bon c’est cool tout ca mais comment on sait qu’on appel NtCreateFile, hé bien vous voyez le « mov eax, 0×25″, 0×25 correspond à l’indice de la fonction dans la SSDT (System Service Descriptor Table), une sorte de table d’exportation pour les fonctions systèmes.
Par contre l’appel à la SSDT n’est pas fait directement par SYSENTER, une routine du noyau (KiSystemService) se charge de retrouver l’adresse de la fonction dans la SSDT. Alors comment fait donc SYSENTER pour jumper sur KiSystemService ? En fait elle utilise les MSR (Model Specific Register), ceux-ci servent à des opérations spéciales sur le processeur. L’intruction SYSENTER utilise les 3 suivants :
- MSR_SYSENTER_CS 0×00000174
- MSR_SYSENTER_ESP 0×00000175
- MSR_SYSENTER_EIP 0×00000176
Voir le manuel « Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 2B Instruction Set Reference, N-Z »
Le MSR_SYSENTER_EIP contient l’adresse de la fonction KiSystemService (0x0804de6f0 chez moi), on trouve dans le MSR_SYSENTER_ESP l’adresse de notre nouvelle Kernel stack (0x0f88ab000 chez moi) enfin le nouveau segment de code MSR_SYSENTER_CS celui-ci n’a pas vraiment d’utilité.
Avant les appels systèmes étaient effectué avec l’interruption int 0x2E, SYSENTER la remplacé pour des raisons de rapididé.
Alors imaginons qu’on puisse modifier ces fameux MSR :), on pourrait donc faire exécuter notre code en ring0 !
He bien cela est possible ! Avec l’API natice NtSystemDebugControl on peut lire et écrire les MSR.
On lui donne une petite structure :
typedef struct _MSR_STRUCT {
DWORD MsrNum; // MSR number
DWORD NotUsed; // Never accessed by the kernel
DWORD MsrLo; // IN (write) or OUT (read): Low 32 bits of MSR
DWORD MsrHi; // IN (write) or OUT (read): High 32 bits of MSR
} MSR_STRUCT, *PMSR_STRUCT;
Structure formée par Reversing du ntoskrnl evidemment …
Apràs il suffit de sauvegarder le MSR_SYSENTER_EIP courant puis on lui dit de pointer sur le morceaux de code que l’on veut lancer en ring0, on appel SYSENTER et hop ! Pour quitter on n’oublie pas de le restaurer bien sur !!!!
Bon clairement il faut codé un shellcode ring0, le mien ne fait que retrouver l’adresse de la structure ETHREAD courante mais au final on peut faire ce qu’on veut dans notre noyau, suffit de s’y mettre :}
Voici le code avec pas mal d’infos dedans et le binaire.
http://ivanlef0u.fr/repo/MSR.rar
Have fun.
février 4th, 2007
Yopla, en direct de la planète RE, en ce moment ce déroule une magnifique rencontre opposant Ivan et son désassembleur, ,le match promet d’être sérré, à la clé, la gloire les filles et l’argent ! L’API NtSystemDebugControl résiste bien mais après avoir été sur des boards chinois et demandé à Alex plein d’infos, Ivan a finit par l’avoir.
L’API Native NtSystemDebugControl permet comme son nom l’indique de réaliser des opérations de débuggage directement au niveau du noyau. Son utilisation requiert l’activation du privilège SeDebugPrivilege. En fait le tout petit ennuit c’est quelle n’est pas documentée
Enfin pas toutes ses possibilitées…
Son prototype est le suivant :
NtSystemDebugControl(
DEBUG_CONTROL_CODE ControlCode,
PVOID InputBuffer,
ULONG InputBufferLength,
PVOID OutputBuffer,
ULONG OutputBufferLength,
PULONG ReturnLength
);
Le champ ControlCode permet de spécifié ce qu’on veut faire, une partie est documenté dans le « Windows 2000 Native API Reference » :
typedef enum _DEBUG_CONTROL_CODE {
DebugGetTraceInformation = 1,
DebugSetInternalBreakpoint,
DebugSetSpecialCall,
DebugClearSpecialCalls,
DebugQuerySpecialCalls,
DebugDbgBreakPoint
} DEBUG_CONTROL_CODE;
Par contre sous IDA, on remarque qu’il y à bien plus que 6 ControlCode possible, voyez plutot :
http://ivanlef0u.fr/repo/NtSystemDebugControl.html
(regardez les ‘case’ du switch)
Après donc avoir chercher un peu partout sur le net et avec l’aide de IDA j’ai pu réunir les 20 possibilitées de cette fonction :
typedef enum _DEBUG_CONTROL_CODE {
DebugSysGetTraceInformation=1,
DebugSysSetInternalBreakpoint,
DebugSysSetSpecialCall,
DebugSysClerSpecialCalls,
DebugSysQuerySpecialCalls,
DebugSysBreakpointWithStatus,
DebugSysGetVersion,
DebugSysReadVirtual,
DebugSysWriteVirtual,
DebugSysReadPhysical,
DebugSysWritePhysical,
DebugSysReadControlSpace,
DebugSysWriteControlSpace,
DebugSysReadIoSpace,
DebugSysSysWriteIoSpace,
DebugSysReadMsr,
DebugSysWriteMsr,
DebugSysReadBusData,
DebugSysWriteBusData,
DebugSysCheckLowMemory,
} DEBUG_CONTROL_CODE;
Pour l’instant j’ai pas encore tout étudié, je me suis intéressé à DebugSysReadVirtual et DebugSysWriteVirtual. Nous permettant de lire et d’écrire partout en mémoire !!!! W00T
On doit pour cela passer à la fonction pour les utiliser une structure SYSDBG_VIRTUAL.
typedef struct _SYSDBG_VIRTUAL {
PVOID Address; //Adresse ou l’on souhaire lire/écrire
PVOID Buffer; //Buffer source ou destination
ULONG Request; //Taille à lire/écrire
} SYSDBG_VIRTUAL, *PSYSDBG_VIRTUAL;
Petit rappel, l’espace mémoire est divisé en 2 grandes zones :
- de 0×00000000 à 0x7FFFFFFF le UserLand, unique à chaque process, diposant ainsi de 2 GO allouable pour chacun.
- de 0×80000000 à 0xFFFFFFFF le KerneLand, la zone ou se situe le noyau et les drivers.
Ainsi j’ai pu codé un petit memory dumper permettant de voir partout partout partout, à noter que si on dump une zone userland, on verra celle du process courant. Un petit exemple sur le KernelBase qui chez moi vaut 0x804D7000 :
C:\\ProgHack\\c\\native>kdump 0x804D7000 150
4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 | MZ..............
b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 | ........@.......
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................
00 00 00 00 00 00 00 00 00 00 00 00 e8 00 00 00 | ................
0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 54 68 | ........!..L.!Th
69 73 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f | is program canno
74 20 62 65 20 72 75 6e 20 69 6e 20 44 4f 53 20 | t be run in DOS
6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00 | mode....$.......
ce 0d 2d e1 8a 6c 43 b2 8a 6c 43 b2 8a 6c 43 b2 | ..-..lC..lC..lC.
49 63 1e b2 8d 6c | Ic...l
Tient le PE Header de Ntoskrnl.exe 
Après libre à vous d’être curieux et d’aller voir ce que vous voulez
Peut-être que Billou vous montrera son cul :]
Cependant la possibilitée la plus intéressante est certainement d’écrire ce que l’on veut dans le noyau :] Pour illustrer rien ne valant un bon exemple, je me suis codé un petit tool qui réalise du DKOM (Direct Kernel Object Manipulation) sur les structures EPROCESS contenant plein d’infos sur les process.
En se basant sur une adresse référence, fs:[0]=0xffdff000, constante qui est en fait le début du KPCR (Kernel Processor Control Region) on peut retrouver la structure EPROCESS courante, celles-ci étant doublement chainée entre elles, il suffit de modifer les pointeurs pour faire croire à sa disparition et donc de son process !!
Le programme s’appel KFist (ouais inspiré hein!) et demande juste le nom du process que vous voulez dégager, exemple avec mon xchat.exe :
C:\\ProgHack\\c\\native>tasklist
Nom de l'image PID Nom de la sessio Numéro d Utilisation
========================= ====== ================ ======== ============
System Idle Process 0 0 16 Ko
System 4 0 212 Ko
smss.exe 512 0 392 Ko
csrss.exe 800 0 4 888 Ko
winlogon.exe 824 0 3 816 Ko
services.exe 868 0 4 424 Ko
winamp.exe 280 0 7 728 Ko
uedit32.exe 1696 0 13 616 Ko
cmd.exe 2036 0 64 Ko
cmd.exe 1744 0 876 Ko
xchat.exe 1684 0 12 940 Ko
wish.exe 1688 0 26 644 Ko
firefox.exe 1792 0 44 412 Ko
FoxitReader.exe 1584 0 10 132 Ko
hh.exe 1380 0 12 016 Ko
tasklist.exe 636 0 4 052 Ko
wmiprvse.exe 1340 0 5 488 Ko
C:\\ProgHack\\c\\native>kfist xchat.exe
KFist : UserLand DKOM Process Hider (xp only)
By Ivanlef0u
BE MAD!
Current ETHREAD : 0x818e1da8
Current EPROCESS : 0x81571da0
Process Name : KFist.exe
EPROCESS : 0x80560450
Process Name :
EPROCESS : 0x81bcb9c8
Process Name : System
EPROCESS : 0x81942da0
Process Name : smss.exe
EPROCESS : 0x818dbda0
Process Name : csrss.exe
EPROCESS : 0x818edd78
Process Name : winlogon.exe
EPROCESS : 0x818b9670
Process Name : services.exe
EPROCESS : 0x818983c0
Process Name : winamp.exe
EPROCESS : 0x8166ea08
Process Name : uedit32.exe
EPROCESS : 0x818c2da0
Process Name : cmd.exe
EPROCESS : 0x81986da0
Process Name : cmd.exe
EPROCESS : 0x818d6da0
Process Name : xchat.exe
Process Found ! Now hiding it...
Prev EPROCESS : 0x818bada0
Next EPROCESS : 0x819bf650
Hiding done, have fun!
EPROCESS : 0x819bf650
Process Name : wish.exe
EPROCESS : 0x8187f3c0
Process Name : firefox.exe
EPROCESS : 0x81aba3c0
Process Name : FoxitReader.exe
EPROCESS : 0x8162ea88
Process Name : hh.exe
EPROCESS : 0x816f8750
Process Name : wmiprvse.exe
EPROCESS : 0x81571da0
C:\\ProgHack\\c\\native>tasklist
Nom de l'image PID Nom de la sessio Numéro d Utilisation
========================= ====== ================ ======== ============
System Idle Process 0 0 16 Ko
System 4 0 212 Ko
smss.exe 512 0 392 Ko
csrss.exe 800 0 4 908 Ko
winlogon.exe 824 0 3 816 Ko
services.exe 868 0 4 432 Ko
winamp.exe 280 0 7 728 Ko
uedit32.exe 1696 0 13 616 Ko
cmd.exe 2036 0 76 Ko
cmd.exe 1744 0 800 Ko
wish.exe 1688 0 26 644 Ko
firefox.exe 1792 0 44 404 Ko
FoxitReader.exe 1584 0 10 132 Ko
hh.exe 1380 0 12 016 Ko
tasklist.exe 1040 0 4 052 Ko
wmiprvse.exe 408 0 5 488 Ko
Voilà mon xchat n’apparaît plus dans la liste des process courant 
Il existe tout de même une facon de le retrouver mais shuuuuut !
Remarque aussi, le fait de virer une structure noyau peut gravement nuire à la santé de votre OS, ainsi si celui-ci tente d’acceder à la structure sans la trouver il risque de mal le prendre et de BSOD …
L’avantage c’est qu’on à plus besoin de loader un driver pour manipuler le KernelSpace, l’inconvénient c’est que c’est plus compliqué car il faut lire la mémoire de facon indirecte en appelant à chaque fois l’API NtSystemeDebugControl.
Ceci n’est qun ‘avant goût des possibilitées de cette fonction, j’essayerais d’explorer les autres plus tard.
En attendant je vous donne le code soure et les binaires :
http://ivanlef0u.fr/repo/DebugControl.rar
Enjoy !
février 1st, 2007