Aujourd’hui parait un nouvel Uninformed, au programme 3 papers dont 2 de Skape :
Reducing the Effective Entropy of GS Cookie Nous montre comment est calculé le cookie GS visant un éviter le débordement de pile, puis à travers des analyses statistiques nous montre qu’il est possible pour un attaquant local de déterminer un cookie.
Memalyze: Dynamic Analysis of Memory Access Behavior in Software . Super document montrant des techniques permettant de connaître les accès mémoire d’un process. La première le Dynamic Binary Instrumentation (DBI) consiste à un injecter dans le processus un code qui va exécuter les instructions, une sorte de traceur qui va vérifier les accès mémoire, dispo sous Linux sous le nom de DynamoRIO . La seconde méthode repose sur l’utilisation du gestionnaire d’exception, notamment sur le fait qu’en modifiant le Owner des pages mémoire du process afin quelles soient considérées comme des kernel pages, une exception interviendra dès qu’un code y accédera. Skape à implémenter une méthode qui consiste à créer un second mapping contenant la version originale des pages, donc avec le flag Owner normal, tandis que le premier mapping des pages représente les pages lockées. Ansi lorsqu’intervient une exception, il suffit de rediriger le code sur le second mapping. Cela permet d’éviter la modification en permanence du flag Owner des pages et d’échapper à pas mal de problème sur les environnements multithreadés. Enfin, la dernière méthode consiste à utiliser le null segment descriptor, en positionnant les segment CS et ES à 0, on récupère une exception à chaque accès mémoire, l’exception handler restore les segments et activz le trap-flag du registre EFLAGS, un seule instruction est donc exécutée par la suite, on remet les segments 0 et l’exécution repart.
Mnemonic Password Formulas Propose diverses techniques mnémonique pour retenir un password.
mai 14th, 2007
Une explosion ! Vite je cours me mettre à l’abri, je prends mon AK-47 et m’accroupit, respirant calmement j’attends que l’ennemi sorte de son trou, il fait chaud, une goutte de sueur dégouline le long de ma tempe, ma combinaison est un vrai fourneau, j’ai peur ! Il se montre enfin ! Enculé de batard, tu vas la prendre ta rafale, TAC TAC TAC ! Headshot, Terrorists Wins. Ouf quelle partie, ca fait du bien de buter du noob à CS, surtout après une dure journée. Vous l’avez compris en ce moment je fous plus grand chose, petite période d’idle ou j’ai envie de me changer les idées et pour vous décevoir rien de tel qu’un post sans grand-intéret. Rien de bien extraordinaire, je vais juste vous parler d’un anti-debug peu connu.
En lisant de la doc sur l’API native, je suis tombé (entre 2 parties de CS) sur une feature NtSetInformationThread, appelé ThreadHideFromDebugger. L’utilisation est simple on appel NtSetInformationThread avec le ThreadInformationClass mit à ThreadHideFromDebugger (17) et un thread handle.
typedef enum _THREADINFOCLASS {
ThreadHideFromDebugger=17
} THREADINFOCLASS;extern "C" ULONG __stdcall NtSetInformationThread(
__in HANDLE ThreadHandle,
__in THREADINFOCLASS ThreadInformationClass,
__in_bcount(ThreadInformationLength) PVOID ThreadInformation,
__in ULONG ThreadInformationLength
);
ULONG main()
{
ULONG Status;
Status=NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);
if(Status)
printf("Error with NtSetInformationThread : 0x%xn", Status);
__asm {int 3}
return 0;
}
Après si on essaye de lancer le debugger sur ce thread, rien ne se passe, le breakpoint (int 3) n’est pas récupéré par le debugger. En fait toutes les exceptions ne sont plus passées au debugger :]
Pour comprendre il faut savoir que lorsque qu’on trace sous Olly, le debugger positionne le trap-flag du registre EFLAGS à 1, ainsi à chaque exécution d’instruction, le processeur va générer une exception, le débugger va la gérer et nous permettre de continuer sur la prochaine instruction et ainsi de suite. Si par malheur ces exceptions ne sont plus passées au debugger, il n’est plus possible de tracer le thread.
Ainsi dans le code plus haut, si vous lancer Olly sur l’exécutable vous ne verrez jamais le debugger vous signaler l’int 3, mais vous verrez la MessageBox « HideFromDebugger.exe a rencontré un problème et doit fermer. Nous vous prions de nous excuser pour le désagrément encouru. », du gestionnaire d’exception terminal, comme si le debugger n’avait pas été prit en compte. Dans un cas normal celui-ci break et nous propose de continuer l’exécution du programme.
Vu de haut c’est mignon comme feature, mais c’est toujours mieux de voir comment c’est fait dedans, aller hop wormhole trip avec IDA.
Voici le code qui correspond à la feature ThreadHideFromDebugger de NtSetInformationThread.
NtSetInformationThread
[...]
push edi ; HandleInformation
lea eax, [ebp+pETHREAD]
push eax ; Object
push dword ptr [ebp+AccessMode] ; AccessMode
push _PsThreadType ; ObjectType
push 20h ; DesiredAccess
push [ebp+Handle] ; Handle
call _ObReferenceObjectByHandle@24 ; ObReferenceObjectByHandle(x,x,x,x,x,x)
mov esi, eax
test esi, esi
jl loc_49E718
mov ecx, [ebp+pETHREAD]
lea eax, [ecx+248h] ; eax=pETHREAD+0x248
lock or [eax], ebx ; ebx=4
jmp loc_49E713
[...]
Le noyau récupère un pointeur sur la struture ETHREAD de notre thread avec l’api ObReferenceObjectByHandle puis accède au champ situé en 0×248 pour effectuer un « lock or [pETHREAD+0x248], 4. Le préfixe lock permet de verrouiller le bus de données afin que dans un environnement multiprocesseur d’autres threads ne modifient pas la variable en même temps.
En 0×248 de la structure ETHREAD on trouve :
kd>dt nt!_ETHREAD
[...]
+0x248 CrossThreadFlags : Uint4B
+0x248 Terminated : Pos 0, 1 Bit
+0x248 DeadThread : Pos 1, 1 Bit
+0x248 HideFromDebugger : Pos 2, 1 Bit
+0x248 ActiveImpersonationInfo : Pos 3, 1 Bit
+0x248 SystemThread : Pos 4, 1 Bit
+0x248 HardErrorsAreDisabled : Pos 5, 1 Bit
+0x248 BreakOnTermination : Pos 6, 1 Bit
+0x248 SkipCreationMsg : Pos 7, 1 Bit
+0x248 SkipTerminationMsg : Pos 8, 1 Bit
[...]
En faisant un OR avec cette variable, NtSetInformationThread positionne le bit HideFromDebugger à 1. Jusqu’ici rien d’extraordinaire.
Au moment de l’exception int 3, le processeur lance la routine le de l’IDT (Interruption Descriptors Table) appelé KiTrap03 qui va ensuite appelé les fonction suivantes :
KiTrap03->CommonDispatchException->KiDispatchException->DbgkForwardException
Arrivé dans DbgkForwardException on voit :
__stdcall DbgkForwardException(x, x, x)
[...]
PAGE:004AB2C4 64 A1 24 01 00 00 mov eax, large fs:124h ; Current ETHREAD
PAGE:004AB2CA F6 80 48 02 00 00 04 test byte ptr [eax+248h], 4 ;ETHREAD+248 & HideFromDebugger
PAGE:004AB2D1 0F 85 58 7C 09 00 jnz loc_542F2F ; si HideFromDebugger enabled on ne passe pas l'except au debugger
[...]
La fonction DbgkForwardException va vérifier si le champ HideFromDebugger du thread courant est à 1, si oui alors l’exception n’est pas passé au debugger. Dans une situation classique l’exception est transmise au debugger via DbgkpSendApiMessageLpc qui va notifier à l’api WaitForDebugEvent qu’une exception s’est déroulé.
Voilà, vous trouverez le code/binaire ici :
http://ivanlef0u.fr/repo/HideFromDebugger.rar
mai 13th, 2007
Par manque d’inspiration le contenu de ce post racontera la manière dont je me suis prit une grosse baffe dans ma gueule en voulant jouer avec mon OS. Ma péripétie commence à cause d’une simple interrogation, le genre de choses qu’on se demande uniquement sous la douche le matin, après avoir absorbé sa dose vitale journalière de chocapics, des questions auxquelles seul un manuel Intel peut répondre mais qu’on à peur d’ouvrir tellement ce genre de ebooks fait de la peur. Le trip est simple, pourquoi un code userland ne peut-il lire à une adresse supérieur à 0x7FFFFFFF ? Ok vous allez me dire que c’est là qu’est chargé le kernel et ses copines, mais le vrai problème est de savoir par quel méchanisme magique le système nous balance une exception 0xC0000005 (STATUS_ACCESS_VIOLATION) dans la face.
Je ne pars pas complètement apwal, je sais que le passage de ring3 en ring0 se fait à travers l’instruction sysenter. De plus dans un précédent post (SYSENTER, stepping into da ring0) j’avais décrit son fonctionnement et notamment après avoir modifié le MSR_SYSENTER_EIP avec l’API NtSystemDebugControl j’ai pu exécuter du code qui avait accès à toute la mémoire. La clé est donc dans le fonctionnement de sysenter !
Après m’être rappelé que sysenter modifiait l’EIP mais aussi les segments CS et SS, je me suis dit que ceux-ci devaient avoir quelque chose d’intéressant à m’apprendre. Hop je prend un thread userland au hasard et je regarde ses segments.
userland segments
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000
0x1B= 11 0 11b
0x23= 100 0 11b
0x3B= 111 0 11b
En même temps je retrouve la structure décrivant un segment.
typedef struct _X86_SELECTOR
{
union
{
struct
{
WORD wValue; // packed value
WORD wReserved;
};
struct
{
unsigned RPL : 2; // requested privilege level
unsigned TI : 1; // table indicator: 0=gdt, 1=ldt
unsigned Index : 13; // index into descriptor table
unsigned Reserved : 16;
};
};
}
X86_SELECTOR, *PX86_SELECTOR, **PPX86_SELECTOR;
Un segment contient donc, son niveau de privilège requis, un bit désignant une table, soit la gdt ou la ldt et l’indice d’une structure X86_DESCRIPTOR dans cette table. Ici le RPL est bien à 3 (11b), le champ TI vaut 0, on est donc dans la GDT. Pour CS sont descriptor est à l’indice 3 alors que celui de SS est à l’indice 4.
Après je compare les segments avec ceux d’un thread tournant en kerneland.
kernelland segments
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000
0x08= 1 0 00b
0x10= 10 0 00b
0x23= 100 0 11b
0x30= 110 0 00b
Sysenter a fait son boulot, les segments CS et SS sont différents ainsi que FS mais ca c’est la fonction KiSystemService qui le redéfinit. Cette fois ci le descriptor de CS est à l’indice 1 dans la GDT et celui de SS à l’indice 2.
Sachant que un descriptor ressemble à :
typedef struct _X86_DESCRIPTOR
{
union
{
struct
{
DWORD dValueLow; // packed value
DWORD dValueHigh;
};
struct
{
unsigned Limit1 : 16; // bits 15..00
unsigned Base1 : 16; // bits 15..00
unsigned Base2 : 8; // bits 23..16
unsigned Type : 4; // segment type
unsigned S : 1; // type (0=system, 1=code/data)
unsigned DPL : 2; // descriptor privilege level
unsigned P : 1; // segment present
unsigned Limit2 : 4; // bits 19..16
unsigned AVL : 1; // available to programmer
unsigned Reserved : 1;
unsigned DB : 1; // 0=16-bit, 1=32-bit
unsigned G : 1; // granularity (1=4KB)
unsigned Base3 : 8; // bits 31..24
};
};
}
X86_DESCRIPTOR, *PX86_DESCRIPTOR, **PPX86_DESCRIPTOR;
Après avoir vu ca, j’ai pas vraiment eu envie de dumper la GDT à la main, j’ai trouvé une extension ProtMode pour windbg qui permet d’afficher le contenu d’un X86_DESCRIPTOR de manière plus jolie.
A noter que la GDT peut etre retrouvé soit avec l’instruction sgdt, soit dans le KPCR avec la commande !pcr du KD.
kd> !ProtMode.Descriptor GDT 1
------------------- Code Segment Descriptor --------------------
GDT base = 0x8003F000, Index = 0x01, Descriptor @ 0x8003f008
8003f008 ff ff 00 00 00 9b cf 00
Segment size is in 4KB pages, 32-bit default operand and data size
Segment is present, DPL = 0, Not system segment, Code segment
Segment is not conforming, Segment is readable, Segment is accessed
Target code segment base address = 0x00000000
Target code segment size = 0x000fffffkd> !ProtMode.Descriptor GDT 2
------------------- Code Segment Descriptor --------------------
GDT base = 0x8003F000, Index = 0x02, Descriptor @ 0x8003f010
8003f010 ff ff 00 00 00 93 cf 00
Segment size is in 4KB pages, 32-bit default operand and data size
Segment is present, DPL = 0, Not system segment, Data segment
Segment is not conforming, Segment is readable, Segment is accessed
Target code segment base address = 0x00000000
Target code segment size = 0x000fffff
------------------- Code Segment Descriptor --------------------
GDT base = 0x8003F000, Index = 0x03, Descriptor @ 0x8003f018
8003f018 ff ff 00 00 00 fb cf 00
Segment size is in 4KB pages, 32-bit default operand and data size
Segment is present, DPL = 3, Not system segment, Code segment
Segment is not conforming, Segment is readable, Segment is accessed
Target code segment base address = 0x00000000
Target code segment size = 0x000fffff
kd> !ProtMode.Descriptor GDT 4
------------------- Code Segment Descriptor --------------------
GDT base = 0x8003F000, Index = 0x04, Descriptor @ 0x8003f020
8003f020 ff ff 00 00 00 f3 cf 00
Segment size is in 4KB pages, 32-bit default operand and data size
Segment is present, DPL = 3, Not system segment, Data segment
Segment is not conforming, Segment is readable, Segment is accessed
Target code segment base address = 0x00000000
Target code segment size = 0x000fffff
Comme prévu le premier descriptor est de type Code segment et possède un DPL (Descriptor Privilege Level) à 0, notre segment CS en ring0.
Le second descriptor, segment SS ring0.
Le troisième, segment CS ring3
Le quatrième, segment SS ring3.
Ici les segments sont en Flat-model, c’est à dire qu’ils décrivent entièrement l’espace mémoire (segment size*4KB).
Par contre pour le segment FS, indice 7 dans la GDT pour le userland.
kd> !ProtMode.Descriptor GDT 7
------------------- Code Segment Descriptor --------------------
GDT base = 0x8003F000, Index = 0x07, Descriptor @ 0x8003f038
8003f038 ff 0f 00 e0 fd f3 40 7f
Segment size is in bytes, 32-bit default operand and data size
Segment is present, DPL = 3, Not system segment, Data segment
Segment is not conforming, Segment is readable, Segment is accessed
Target code segment base address = 0x7ffde000
Target code segment size = 0x00000fff
L’adresse de base est 0x7ffde000 qui correspond à l’adresse du TEB du thread courant, attention depuis xp sp2 cette adresse est randomisé pour chaque threads, mais c’est pas grave avec fs:[0] on la retrouve :p
Pour un thread ring0 le segment FS vaut :
kd> !ProtMode.Descriptor GDT 6
------------------- Code Segment Descriptor --------------------
GDT base = 0x8003F000, Index = 0x06, Descriptor @ 0x8003f030
8003f030 01 00 00 f0 df 93 c0 ff
Segment size is in 4KB pages, 32-bit default operand and data size
Segment is present, DPL = 0, Not system segment, Data segment
Segment is not conforming, Segment is readable, Segment is accessed
Target code segment base address = 0xffdff000
Target code segment size = 0x00000001
En fs:[0] on retrouve l’adresse de la structure KPCR, ca peut toujours servir, notamment dans un shellcode :]
Lorsque j’essayais de lire une adresse supérieure à 0x7FFFFFFF avec un thread ring3 je me mangeais une exception car j’essayais d’accéder à une kernel page alors que mon CPL (Current Privilege Level) était à 3. La MMU génère une interruption 14 (Page Fault) qui est gérée par la routine de l’IDT KiTrap0E puis par MmAccessFault.
Je me suis dit qu’il suffisait de modif les segments à la main, comme le fait sysenter, pour avoir un thread tournant en ring0 (Il est con le Ivan parfois quand même ….). Sous Olly c’est possible, alors je lance un programme, modif ses segments pour avoir le CS et SS pareil qu’un thread ring0, je relance le thread et là c’est le drame ! Je me mange une exception. Evidemment je m’y attentais quand même un peu, ca paraissait un peu trop facile
Par contre il me fallait comprendre pourquoi l’exception avait été générée.
Arrivé à ca point, pas le choix, faut regarder les man Intel, pénible corvée qui doit se faire en portant une combinaison NBC et des chaussettes marsupilami. Après quelques recherches, j’apprends que depuis tout petit on me ment, les segments ne font pas que 16 bits !! Il existe tout une partie cachée qui contient notamment le DPL du segment ! En fait cela permet d’éviter au processeur des cycles en plus de lecture pour lire la GDT. Ce cache ne peut être modifié directement mais des instructions comme sysenter le mettent à jour, comme le montre le manuel de l’instruction :
CS.SEL <- SYSENTER_CS_MSR (* Operating system provides CS *)
(* Set rest of CS to a fixed value *)
CS.BASE <- 0; (* Flat segment *)
CS.LIMIT <- FFFFFH; (* 4-GByte limit *)
CS.ARbyte.G <- 1; (* 4-KByte granularity *)
CS.ARbyte.S <- 1;
CS.ARbyte.TYPE <- 1011B; (* Execute Read, Accessed *)
CS.ARbyte.D <- 1; (* 32-bit code segment*)
CS.ARbyte.DPL <- 0;
CS.SEL.RPL <- 0;
CS.ARbyte.P <- 1;
CPL <- 0;
Donc même après avoir modifié le segment selector, le système remarquant que le RPL du segment ne correspond pas au DPL contenu dans le cache nous balance une interruption dans la face. Ouinn ….
Bref j’ai prit une baffe mais j’ai compris pourquoi :]
Références :
http://www.awprofessional.com/articles/article.asp?p=167857&rl=1
http://www.codeguru.com/cpp/w-p/system/devicedriverdevelopment/article.php/c8223__1/
http://www.summitsoftconsulting.com/SampleCode/ProtMode.zip
http://www.internals.com/articles/protmode/protmode.htm
http://www.sandpile.org/ia32/sreg.htm
mai 5th, 2007