shellcode ASCII

Si vous aimez la programmation de bas niveau et la bonne grosse bidouille, je pense que la suite pourrais vous intéresser : on vas voir une méthode pour faire des shellcodes composés uniquement de caractères ASCII, et donc imprimables (il en éxiste certainement d’autres, mais je n’ai pas vraiment trouvé beaucoup de documenation à ce sujet, la plupart des articles à propos des shellcodes les évoquent mais ne donnent pas plus d’infos).

On va étudier du code pour plateforme x86 (et plus précisément pour GNU/Linux), il faut donc savoir utiliser le langage d’assemblage de cette plateforme, et surtout comprendre le fonctionnement de la pile (stack). Si c’est le cas, je suppose en plus que vos avez quelques notions en C, et savez faire des calculs avec XOR et AND (si ce n’est pas le cas, vous trouverz plus d’infos en cherchant « bitwise » par exemple wikipedia.)
Si ce n’est pas le cas mais que ça vous semble intéressant, je vous propse de revenir dans quelque temps, je suis en train de préparer une introduction au langage d’assemblage x86 (32bits) qui permettra de comprendre ce qui va suivre.

Pour la suite, on aura besoin de plusieurs outils :
– une calculatrice pouvant utiliser l’hexadecimal, et les opérations XOR et AND, car pour ma part les opérations sur 32 bits de tête j’ai encore un peu de mal. Je suis en train de faire un outil pour les shellcoders, mais n’étant pas encore finis, si vous n’en avez pas je vous propose d’utiliser l’excellent pcalc.
– un debugger que l’on sait utiliser. Ayant eu un peu marre d’utiliser gdb (taper les commandes est trop répétitif), je peut vous proposer the evan’s debugger, qui propose quelque chose d’assez similaire à ollydbg mais sous GNU/Linux, fait avec Qt il est très agréable à utiliser. Cela nous servira principalement à voir l’état de la pile lors de l’éxécution de notre programme.
Une référence quant aux opcodes imprimables. Pour ceux qui connaitraient par coeur les instructions x86 et leurs OPcodes, un man ascii suffit.
une référence quant aux appels sytèmes pour GNU/Linux x86.
de la musique qui monte en l’air.

Avant de commencer, la chose la plus importante est de visser votre casque sur vos oreilles, auguementer le volume, et cliquer sur le play de Youtube.

Cette bonne chose de faite, on peut passer aux choses sérieuses.
Ce qu’on veut faire est un shellcode, qui ne sera composé que d’octets imprimables. Cela peut servir dans certaines circonstances biens précises ( par exemple, une fonction qui pourrait vérifier si le texte entré est du texte valide ? ), ou pour faire des shellcodes stylés.
On aura donc le droit d’utiliser qu’un nombre restreint d’instructions.
Pour commencer, il nous faut un shellcode. Pour s’assurer qu’il sera mis dans la pile, on utilisera le code C suivant pour toute la suite, seul buf changera:
hw.c


#include <stdio.h>

char buf[] =
"\x31\xdb" // xor ecx,ecx
"\xf7\xe3" // mul ecx
"\x68\x72\\x6c\x64\x0a" // push 0x0a646c72
"\x68\x6f\x20\x77\x6f" // push 0x6f77206f
"\x68\x48\x65\x6c\x6c" // push 0x6c6c6548
"\xb2\x0c" // mov dl,0xc
"\x89\xe1" // mov ecx,esp
"\xb3\x01" // move ebx,0x1
"\xb0\x04" // mov al,0x4
"\xcd\x80" // int 0x80
"\x31\xc0" // xor eax,eax
"\x40" // inc eax
"\x89\xc3" // mov ebx,eax
"\xcd\x80" // int 0x80
;

int callShellcode(void) {
char sC[80];

strncpy(sC, buf, sizeof(buf)-1);

void (*shell)() = (void *)&sC;
shell();
}
int main(int argc, char **argv)
{
printf("%d bytes linux/x86 shellcode by YAZn",sizeof(buf)-1);
callShellcode();
printf("It will never happen");
}

il s’agit d’un simple hello world, qui utilise les appels suivants :

write(int fd, const void *buf, size_t count);
exit(int e);

de la manière suivante:

write(0,"Hello world\n" ($esp),12);
exit(1);

On peut voir que ici, déjà on a une « petite astuce » : on n’utilise pas de data, mais le texte à afficher est empilé, et ensuite on fait pointer ECX ( qui est le registre qui doit contenir l’adresse du texte à afficher ) sur le sommet de la pile, soit ESP.

Pour compiler, puisque vous avez certainement un système mis à jour qui comporte quelques protections contre les Buffer Overflow, il faudra faire ceci:

gcc hw.c -o hw -z execstack -fno-stack-protector

On a notre shellcode. Pour le transformer en shellcode imprimable on va commencer par transformer en caratère tous les octets où c’est possible, pour voir quelles opérations on va devoir bidouiller ce qui nous donne :

char buf[] =
"\x31\xdb" // xor ecx,ecx
"\xf7\xe3" // mul ecx
"hrld\n" // push 0x0a646c72
"ho wo" // push 0x6f77206f
"hHell" // push 0x6c6c6548
"\xb2\x0c" // mov dl,0xc
"\x89\xe1" // mov ecx,esp
"\xb3\x01" // move ebx,0x1
"\xb0\x04" // mov al,0x4
"\xcd\x80" // int 0x80
"\x31\xc0" // xor eax,eax
"@" // inc eax
"\x89\xc3" // mov ebx,eax
"\xcd\x80" // int 0x80
;

Bon… ya du travail.
On va devoir trouver un équivalent imprimable pour chaque opération ci-dessus, où leurs opcodes ne le sont pas.
par exemple, un


"\x31\xdb" // xor ecx,ecx
"\xf7\xe3" // mul ecx

pourrais devenir :


"\x6a\x30" // pushb 0x30
"\x58" // pop eax
"\x34\x30" // xor al, 0x30
"\x50" // push eax
"\x59" // pop ecx

Certe, ça prend plus de place ( et de temps, même si c’est pas très important ici … ), mais ce sont des caractères imprimables. En effet, le code ci-dessus

"\x6a\x30\x58\x34\x30\x50\x59"

peut être indifférement écrit

"j0X40PY"

je vous propose donc ceci


char buf[] =
//"\x31\xdb" // xor ecx,ecx
//"\xf7\xe3" // mul ecx
"j0X40PY" // pushb 0x30 / pop eax / xor al,0x30 / push eax / pop ecx

"hrld\n" // push 0x0a646c72
"ho wo" // push 0x6f77206f
"hHell" // push 0x6c6c6548

//"\xb2\x0c" // mov dl,0xc
"j5X49PZ"// pushb 0x35 / pop eax / xor al,0x39 / push eax, pop edx

//"\x89\xe1" // mov ecx,esp
"TY" // push esp / pop ecx

//"\xb3\x01" // move ebx,0x1
//"\xb0\x04" // mov al,0x4
"j0X41P[" // pushb 0x30 / pop eax / xor al,0x31 / push eax / pop ebx
"@@@" // inc eax / inc eax / inc eax

"\xcd\x80" // int 0x80

//"\x31\xc0" // xor eax,eax
//"@" // inc eax
//"\x89\xc3" // mov ebx,eax
"SX" // push ebx / pop eax

"\xcd\x80" // int 0x80
; // int size = 47

Comme vous pouvez le voir, on a pas (encore) réussi à trouver comment transformer les "\xcd\x80".

Il est grand temps de changer de musique, d’en mettre une de circonstance, de prendre du thé vert, et de faire chauffer votre debugger.

On lance notre debugger,
et on regarde ce qui ce passe. Si vous savez ce que vous faite, vous n’aurez pas besoin de ce qui suit, mais sinon, je vous propose de prendre (stepIn) les appels suivant :
ascii_dbg_1
puis :
ascii_dbg_2
Pour finalement arriver à ce qu’on veut : notre shellcode, qui comme on peut le voir, est éxécuté dans la pile, condition nécessaire à la suite.
ascii_dbg_3

Comment s’y prendre pour inclure dans notre shellcode des octets que l’on à pas le droit d’utiliser ? Certaines personnes habituées à ce genre d’exercie, voient peut être ou je veut en venir : on va faire un code qui va s’auto-modifier.
Sauf qu’ici, c’est un peu spécial, puisque dans le contexte de la pile.
voilà ce que je vous propose :


 -----------------------------------------------
| Header | Shellcode (incomplet) | code inutile |
 -----------------------------------------------

Le Header, va modifier la fin de notre shellcode, en surimprimant le « code inutile » avec les octets que l’on veut.
Pour s’y prendre, on vas faire quelque chose de pas très recommandé, modifier ESP (ici ce n’est pas grave, puisque notre shellcode comporte un « exit(1); » ).
En effet, si on le fait pointer après la fin de notre shellcode, il sera après EIP, et si on fait un push,
on pourra potentiellement placer correctement (de sorte que EIP l’éxécute sans encombre ) du code.

Ce qui donne en un très zolie schéma:
ascii_diagram1
et on veut :
ascii_diagram2

On va maintenant diviser notre shellcode en 2 parties : la partie imprimable :


"j0X40PY" // pushb 0x30 / pop eax / xor al,0x30 / push eax / pop ecx

"hrld\n" // push 0x0a646c72
"ho wo" // push 0x6f77206f
"hHell" // push 0x6c6c6548

"j5X49PZ" // pushb 0x35 / pop eax / xor al,0x39 / push eax, pop edx

"TY" // push esp / pop ecx

"j0X41P[" // pushb 0x30 / pop eax / xor al,0x31 / push eax / pop ebx
"@@@" // inc eax / inc eax / inc eax

et la partie pas entièrement imprimable :


"\xcd\x80" // int 0x80

"SX" // ("\x53\x58") push ebx / pop eax

"\xcd\x80"

Comme on le sait, la plateforme x86 utilise « la forme d’organisation » Little Edian.
Notre header, va donc devoir mettre à la fin de notre shellcode :


 ------------
|   header   |
|------------|
|  shellcode |
|------------|
|  585380cd  |
|  ????80cd  |
 ------------

Puisque les octets avec des « ? » ne sont pas importants (le deuxième « 80cd » provoquera la fin du programme), ce que notre header va faire, c’est :
– positionner ESP « 2 lignes » ( 8 octets ) après notre shellcode. Pour cela on va avoir recours à des POPA (ESP+=32),POP(ESP+=4) et des PUSH (esp-=4).
– grâce aux opérations que nous permettent la liste des octets imprimable mettre dans un registre (ce sera EAX puisque on « a le droit » de ne xorer que lui) « 0x585380CD »
– faire 2 « push EAX » pour, que grâce à la « magie de la pile », qui va monter vers les adresses basses (et donc en direction notre shellcode), le code inutile (qui n’est pas obligatoire), soit remplacé par ce qui se trouvait dans EAX. 2 fois, car on a besoin de 2 syscall (un pour le write, ensuite il faut mettre EAX à 1, on le fait en pushant EBX qui contiendra 0x1, puis en popant EAX. Cela permet de mettre EAX à 1 sur 2 octets (« SX » soit « \x53\x58 »), EBX qui contenait déjà 0x1 le reste et de pouvoir faire ensuite notre deuxième interruption de kernel pour quitter)
– ensuite, il va falloir se « faire de l’espace », en effet, puisque notre shellcode utilise des push, il ne faut que notre footer fraîchement créé ne soit lui même surimprimé.

On remarque donc pour que cela soit possible, il faut que:
1) EIP soit dans la pile (mais ça on le savait déjà) pour pouvoir modifier le code à la volée avant qu’il n’arrive dessus
2) que notre HEADER+SHELLCODE soit composé d’un nombre d’octets multiple de 4, puisque la pile utilise des mots de 32 bits soit 4 octets. Pour aligner la taille de notre header+shellcode, on ne peut pas utliser de NOP (« \x90 ») puisque ce n’est pas imprimable, mais à la place des « NOP effectifs » : des instructions qui n’auront pas d’effets, comme incrémenter on décrémenter un registre, ça tient sur un octet imprimable.

on aura donc :


 ------------
|   header   |
|------------|
|  shellcode |
|------------|
|  585380cd  |
|  585380cd  |
 ------------

Je vous propose de procéder comme ceci pour mettre « 0x585380cd » dans EAX :


// on récupère 0x58545854 dans EAX, ici seulement les 2 premier octets sont importants
- push 0x58545854
- pop eax 

// on met à 0 les 2 autres soit eax = 0x58540000
- xor ax, 0x5854

// pour obtenir 0x5853ffff on décrémente eax
- dec eax

// on veut maintenant mettre le ffff en 80cd en utilisant seulement des octets imprimables
// pour ce faire :
// 0xffff xor
// 0x80cd =
// ----------
// 0x7f32
// il faut maintenant trouver 2 séquences imprimables équivalentes à 7f32 :
// 0xffff xor
// 0x4f65 =
// ----------
// 0xb09a
//
// 0xb09a xor
// 0x3057 =
// ----------
// 0x80cd
- xor ax, 0x4f65
- xor ax, 0x3057

// on surimprime
- push eax
- push eax

// on crée notre espace pour les autres push
- popa

on a donc notre header :


"hTXTX" // "\x68\x54\x58\x54\x58" | push 0x58545854
"X" // "\x58" | pop eax
"f5TX" // "\x66\x35\x54\x58" | xor ax, 0x5854
"H" // "\x48" | dec eax
"f5eO" // "\x66\x35\x65\x4f" | xor ax, 0x4f65
"f5W0" // "\x66\x35\x57\x30" | xor ax, 0x3057
"P" // "\x50" | push eax
"P" // "\x50" | push eax
"a" // "\x61" | popa
// size = 22

et notre shellcode :


"j0X40PY" // pushb 0x30 / pop eax / xor al,0x30 / push eax / pop ecx

"hrld\n" // push 0x0a646c72
"ho wo" // push 0x6f77206f
"hHell" // push 0x6c6c6548

"j5X49PZ" // pushb 0x35 / pop eax / xor al,0x39 / push eax, pop edx

"TY" // push esp / pop ecx

"j0X41P[" // pushb 0x30 / pop eax / xor al,0x31 / push eax / pop ebx
"@@@" // inc eax / inc eax / inc eax
// size = 41

ce qui nous fait un total de 41+22 = 63 octets.

Comme on a pu le voir lors du passage au debugger, lorsque EIP saute dans la pile, ESP est (4* »8lignes ») 32 octets plus haut.

on va donc rajouter en haut de notre header :


// on aligne ESP 8 octets en dessous de notre shellcode
- popa
- popa
- popa
- pop edx
- pop edx
- pop edx
- pop edx
// on rajoute des nop effectifs pour que (63 + ça) % 4 == 0; ici 72
- inc ebx
- inc ebx
// size = 9

Ce qui nous donne au finale :


char buf[] =
// on aligne esp
"aaa" // popa * 3
"ZZZZ" // pop edx * 4

// on aligne la taille avec des nop effectifs
"CC"
// size = 9

// header
"hTXTX" // "\x68\x54\x58\x54\x58" | push 0x58545854
"X" // "\x58" | pop eax
"f5TX" // "\x66\x35\x54\x58" | xor ax, 0x5854
"H" // "\x48" | dec eax
"f5eO" // "\x66\x35\x65\x4f" | xor ax, 0x4f65
"f5W0" // "\x66\x35\x57\x30" | xor ax, 0x3057
"P" // "\x50" | push eax
"P" // "\x50" | push eax
"a" // "\x61" | popa
// size = 22

// shellcode
"j0X40PY" // pushb 0x30 / pop eax / xor al,0x30 / push eax / pop ecx

"hrld\n" // push 0x0a646c72
"ho wo" // push 0x6f77206f
"hHell" // push 0x6c6c6548

"j5X49PZ" // pushb 0x35 / pop eax / xor al,0x39 / push eax, pop edx

"TY" // push esp / pop ecx

"j0X41P[" // pushb 0x30 / pop eax / xor al,0x31 / push eax / pop ebx
"@@@" // inc eax / inc eax / inc eax
// size = 41
;

on a bien 9 + 22 + 41 = 72
et voici notre shellcode final en version imprimale:

aaaZZZZCChTXTXXf5TXHf5eOf5W0PPaj0X40PYhrld\nho wohHellj5X49PZTYj0X41P[@@@

ce qui donne avec le code C :


#include <stdio.h>

char buf[] =
"aaaZZZZCChTXTXXf5TXHf5eOf5W0PPaj0X40PYhrld\nho wohHellj5X49PZTYj0X41P[@@@";

int callShellcode(void) {
char sC[80];

strncpy(sC, buf, sizeof(buf)-1);

void (*shell)() = (void *)&sC;
shell();
}
int main(int argc, char **argv)
{
printf("%d bytes linux/x86 shellcode by YAZ\n",sizeof(buf)-1);
callShellcode();
printf("It will never happen");
}

On peut rajouter ce qu’on veut après @@@ pusique ce sera effacé (mais ça auguement la taille du shellcode).

Si vous n’êtes pas sûr d’avoir tout compris concernant l’utilisation de ESP, et des pop(a) d’alignement, je vous propose de faire éxécuter le code ci-dessus dans un debugger, pas à pas à partir du saut de EIP dans la pile, afin de bien voir ce qui ce passe ( pour cela, edbg est vraiment top ! ).

Ceci d’expliqué, il devrait vous être facile de faire un vrai shellcode ( qui spawn un shell ).

Sources / Plus d’infos

http://skypher.com/wiki/index.php?title=X86_alphanumeric_opcodes

http://www.blackhatlibrary.net/Ascii_shellcode

dia, pour faire des supers schémas trop oufs comme sur le sdz

Publicités
Cet article, publié dans GNU/Linux, insomnia, Non classé, programmation, est tagué , , , , , , , , , . Ajoutez ce permalien à vos favoris.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion /  Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s