mercredi 26 mars 2008

Exploitation classique de Buffer overflow

Attention, j'estime que vous connaissez un minimum sur les bof, si ce n'est pas le cas je vous conseilles de lire l'article de wikipédia sur les bof : http://fr.wikipedia.org/wiki/D%C3%A9passement_de_tampon

Suite à mon article sur les fonctions str*cpy/str*cat, 0vercl0k m'a fait remarqué que ça serait sympa que je montre comment exploiter ces failles de sécurités.
Je vous expose donc mes découvertes sur les exploitations de ces failles classiques, on va commencer par quelque chose de simple comme ceci :


#include <stdio.h>
#include <string.h>


void foo(char *str)
{
char buffer[32];

strcpy(buffer, str);

printf("%s\n", buffer);
}

int main(int argc, char *argv[])
{
foo(argv[1]);

return 0;
}


Ce code est très simple mais il n'en ai pas moins vulnérable étant donné qu'on utilise la fonction strcpy et nous savons tous que cette fonction conduit à des bof.
Donc compiler ce prog et voyons ensemble comment nous y prendre.

vuln.exe azerty

azerty


Maintenant provoquons un bof !

vuln.exe AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA


Et puis nous avons une belle fenêtre avec un beau plantage.

Si on regarde bien le rapport de bug, on observe que le prog plante à l'offset 41414141 or 41 correspond à la lettre A on peut donc supposer que eip avez la valeur AAAA et que c'est lui qui a causer le bug.

Il faut savoir qu'après notre buffer ce trouve deux choses importantes la sauvegarde de ebp et la sauvegarde de eip qui servira pour revenir à l'instruction juste après le call.

On peut donc facilement rediriger l'exécution du programme pour en faire ce qu'on veut et même exécuter un shellcode.


Bon ce article touche à ça fin mais j'en posterais plusieurs autres car je n'ai pas encore parler des autres failles avec str*cpy/str*cat mais je voulais le sortir parce que j'ai eu quelques problèmes avec gcc qui mettait des protections un peu partout.
Je me suis finalement rendu compte qu'il suffisait de bourriner pour arriver à faire des bof.

Have fun :)

mardi 11 mars 2008

Detect hidden process

Suite à l'article de lilxam ( Listing all processes ) qui fait lui même suite à l'article de Ivanlef0u ( NtSystemDebugControl Demystified ) j'ai voulu aller un peu plus loin que lilxam. Au lieu d'afficher tout les process en faisant un bf des PIDs, j'ai voulu détecter les process hidden et les afficher.

Il nous suffit donc de bf tout les PIDs comme l'a fait lilxam et puis ensuite de voir si les fonctions First32Process/Next32Process trouvent bien ce PID, si elles ne le trouvent pas alors c'est qu'il a utilisé la technique expliquer par Ivanlef0u pour se cacher.

Donc on commence par faire une liste de tout les process avec First32Process/Next32Process, que l'on gardera bien au chaud, ensuite on fait un bf PIDs comme l'a montré lilxam ( on s'arrête à 0x4E1C ) et on recherche dans la liste des process qu'on avait garder si ce PID existe ensuite deux cas s'offre à nous :
  • soit il n'existe pas donc on sort de la boucle et on l'affiche ( je passe les détails pour afficher le nom d'un process avec son handle, merci lilxam )
  • soit il existe au quel cas il ne faut pas oublier de comptabiliser ces threads ( windows ne sait pas trop compter apparement )
Et bien voila pas très dur mais windows m'a bien fais chier avec ces threads qui sont considérer comme des process mais qui n'apparaissent pas avec First32Process/Next32Process. Et je n'ai pas trop compris pourquoi tous les Threads n'étaient pas comptabilisés si quelqu'un a la réponse ?

On cache le process avec l'outil de Ivanlef0u ( xchat.exe par exemple ) :
KFist.exe calc.exe


Puis on regarde la liste des process avec le gestionnaire de tâches et on remarque que xchat à disparu.

Donc on lance notre petit programme et il nous affiche ceci :
[+]Process Hidden : xchat.exe
[+]Process Hidden : xchat.exe
[+]Process Hidden : xchat.exe
[+]Process Hidden : xchat.exe


Bon il nous affiche plusieurs fois xchat.exe parce qu'il nous affiche tout les threads mais en tout cas ça marche et l'avantage c'est qu'on a le résultat directement.

Un bon moyen de détecter un process qui s'est hidden.

Have fun ;)


Voici le code : DetectProcessHidden.c

PS : n'oublier pas de linker la lib psapi

dimanche 9 mars 2008

Les erreurs de codages avec str*cpy et str*cat

Dans cet article je vais vous parlez des erreurs de codage fréquemment rencontrés avec les fonctions str*cpy et str*cat, ces erreurs sont généralement dû à une mauvaise utilisation des fonctions en question et amènent à un stack overflow.

Tout le monde sait que la meilleure façon de faire un stack overflow, c'est d'utilisé strcpy() ou strcat() car elle ne vérifie pas la longueur des variables et écrivent sans aucun remord dans des zones ne leur appartenant pas généralement une sauvegarde de ebp et eip mais si ils continuent ils peuvent écraser encore plus de données. C'est généralement l'écrasement de eip qui conduit à une redirection des instructions vers un shellcode seulement un écrasement de ebp est tout aussi dangereux.

Les programmeurs pour éviter les stack overflow utilisent donc leur équivalent un peu plus sécurisé ( pas entièrement sinon c'est pas marrant ) strncpy() et strncat() en leur passant en troisième paramètre la taille du buffer.
Cependant les deux fonctions n'ont pas le même comportement, dans la page de man de strncpy() on nous dit que le \0 final n'est pas compter et il faut donc en tenir compte alors que dans la page de man de strncat() on nous dis que le \0 final est rajouter par la fonction elle même.
Cette différence entraine généralement des erreurs de codage.

strncpy(dest, src, sizeof(dest)-1);

au lieu de
strncpy(dest, src, sizeof(dest)-1);
dest[sizeof(dest)-1] = 0;


En général, le stack overflow ne vient pas de cette ligne mais d'un éventuel str*cat() qui suit, imaginez ce code:

strncpy(dest, src, sizeof(dest)-1);
strncat(dest, src2, sizeof(dest)-strlen(dest)-1);


Ici strncat est bien utilisé mais le strncpy() va provoquer deux choses d'abord un integer overflow et après un buffer overflow.

strncat à la prototype suivant char * strncat ( char * destination, char * source, size_t num );

Ici l'absence de \0 dans dest à la fin du strncpy va faire que strlen(dest) va être beaucoup plus grand que sizeof(dest) et donc que le résultat sera négatif or la fonction accepte un size_t qui est unsigned donc cela fera un nombre extrêmement grand d'où le possible overflow.

Ensuite il faut savoir que strncat met un \0 à la fin de la chaine de destination à tout les coups donc cet appel provoquera un overflow ( en supposant qu'il y ai bien un \0 dans dest pour ne pas revenir dans le même cas que précédemment ) :

strncat(dest, src, sizeof(dest)-strlen(dest));


Ici la fonction strncat va bien copier sizeof(dest)-strlen(dest) et le buffer sera rempli mais la fonction va rajouter un \0 et provoquer un overflow qui va écraser le premier octet de la sauvegarde de ebp.

C'est quoi un handle ?

On utilise des HANDLE un peu partout, pour manipuler des fichiers après un CreateFile() par exemple, pour le chargement des dlls et leur manipulation vu que HMODULE n'est qu'un simple typedef de HANDLE ( en passant par l'intermédiaire de HINSTANCE qui est aussi un typedef de HANDLE ) avec l'API LoadLibrary().

Mais je vais essayer de vous expliquer ce qu'est concrètement un HANDLE.
Bon alors on fait un petit tours du côté de winnt.h et on recherche le typedef HANDLE et on tombe sur ceci vers les lignes 150 chez moi :
#ifdef STRICT
typedef void *HANDLE;
#define DECLARE_HANDLE(n) typedef struct n##__{int i;}*n
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(n) typedef HANDLE n
#endif


Et c'est la qu'on ce rend compte qu'en fait un HANDLE ce n'est pas grand chose ( void qui signifie vide ).

Bon ça ne nous avance pas beaucoup mais on sait au moins que c'est un pointeur sur void. Dans la plus part des cas un pointeur sur void n'est rien d'autres qu'une adresse mémoire, on va donc essayer de l'afficher.

- DumpHMod.c

Hum tiens intéressant, tiens l'adresse nous dis quelque chose, elle correspond à l'adresse de kernel32 en mémoire. Quand au Dump pas très bavard il n'y a que trois octets ( il y a surement un \0 en quatrième octets ) cependant on reconnait bien le début d'un MZ header.

Pour conclure, je dirais juste qu'un HANDLE n'est en fait qu'un pointeur sur un fichier mapper en mémoire et c'est donc pour ça qu'on utilise autant de HANDLE et que ceci sont aussi utile.

dimanche 2 mars 2008

L'interface homme-machine du futur ?

Johnny Chung Lee est un chercheur à la Carnegie Mellon University à Pittsburgh, il travaille sur les interactions homme-machine et grâce à lui on a peut être notre interface du futur.
Grâce à la dernière invention de Nitendo, la Wiimote, Johnny a brillamment su réutiliser cette technologie bon marché ( environ 40 € ). Il a grâce a un montage très simple et peu couteux réussi à piloter son ordinateur à distance en remplacement de sa souris. Cette technologie pourrait être utilisé lors de conférence pour plus de simplicité, plus besoin d'une personne devant l'ordinateur et une autre en train de parler. Avec cette technologie de nouvelle génération, la même personne peut contrôler l'ordinateur pour afficher le résultat des travaux comme une présentation.

Voici les vidéos montrant son œuvre très intéressantes :
Bien que le concept ne soit pas révolutionnaire puisqu'il existe déjà le crayon optique et l'écran tactile depuis très longtemps. La Wiimote reste a un prix abordable et avec un dispositif très simple on arrive à faire quelque chose de très imprésionnant.
De plus, ceci fonctionne sur tout les supports contrairement au crayon optique qui ne fonctionne qu'avec un écran cathodique.

Voici la page de son projet http://www.cs.cmu.edu/~johnny/projects/wii/ .

Il a aussi crée un projet sur sourceforge, il a déjà commencé à dev une version pour mac, une version pour windows est déjà dispo et il a fait un appel à la communauté pour coder un version linux.

Ce projet promet de bonne chose dans un futur ( proche ? ) pour un meilleur interfaçage homme-machine.

Voici trois autres vidéos de projets intéressants même si ils ne sont pas aussi prometteur que ceux de Johnny :

mardi 19 février 2008

Reverse Msnmsgr.exe

Bon aujourd'hui on va s'attaquer à ce cher petit msnmsgr.exe, ce petit outil utilisé par le monde entier et qui connait un succès grandissant.

On va voir comment arriver à lancer plusieurs fois MSN alors qu'à vu d'œil il refuse de se lancer plusieurs fois, à la place il donne le focus à la fenêtre déjà lancé.

Bien pour tout vous dire je me suis longtemps demander comment faisait beaucoup de programme pour détecter si ils sont déjà lancés ou pas.

Ils auraient pût lister la liste des processus et vérifier si le nom du prog ni figurait pas mais non ce n'est pas assez flexible, il suffit de renommer l'exe pour pouvoir le lancer plusieurs fois et la solution n'est donc pas très bonne au final.
Et puis je me suis demander si ce n'était pas une sorte de variable globale qui était déclaré lors du lancement de l'exe et qui été détruit en même tant que l'exe, comme ça lorsque l'exe été relancé il lui suffisait de vérifier si la variable existait et si tel était le cas bim on lui balance un MessageBox comme c'est le cas pour CodeBlocks ou alors on donne le focus au premier exe.

Et puis la ça fait bingo parce que justement Windows a une API qui nous permet de déclarer des variables globales enfin c'est pas tout à fait des variables globales c'est plutôt des events grâce à CreateEvent.

Reverse de MsnMsgr

Bon alors c'est parti pour le Reverse de MsnMsgr, maintenant qu'on sait ( on suppose en fait ) quelle fonction MsnMsgr va utilisé pour y parvenir ça va être très simple.

Alors on lance notre cher olly ( moi j'utilise olly parce que c'est le seul que j'utilise mais vous pouvez bien entendu utiliser votre debogueur préférer ) et on ouvre MsnMsgr ( chez moi il se trouve dans C:\Program Files\MSN Messenger\ ).

Et on laisse olly analyser le code parce que MsnMsgr c'est un sacré morceau ... 5 min plus tard olly à fini ^^

Donc comme on suppose que MsnMsgr va utiliser l'API CreateEvent() on va faire un clic droit -> Search For -> Name (label) in current module et on va rechercher CreateEvent(), il n'y a pas de symbole CreateEvent() mais il y a CreateEventA() et CreateEventW() d'après la msdn la seule différence entre ces deux fonctions est que CreateEventA() utilise un encodage ANSI alors que CreateEventW() utilise un encodage Unicode ( http://msdn2.microsoft.com/en-us/library/ms682396(VS.85).aspx Section Requirements -> Unicode).
On va donc poser des breakpoints sur les deux API à coup de clic droit -> Set breakpoint on every reference.

Maintenant un petit coup de F9 pour lancer le prog, on reconnait bien la lenteur de microsoft ... 5 min après boum break sur CreateEventW()

On regarde l'argument EventName, il est à NULL donc ça ne nous intéresse pas aller re F9, encore un appel qui ne nous concerne pas re F9 et puis la un CreateEventW qui pourrait nous servir sauf qu'après analyse aucune vérification n'est faite et donc ça ne nous intéresse pas non plus.
Après avoir relancer encore une fois à coup de F9, on tombe sur un CreateEventA avec comme EventName "MSNMSGR".
On va analyser un peu ce code pour voir de quoi il retourne.

...

00543CCB > 68 78D75500 PUSH msnmsgr.0055D778 ; /EventName = "MSNMSGR"
00543CD0 . 57 PUSH EDI ; |InitiallySignaled
00543CD1 . 6A 01 PUSH 1 ; |ManualReset = TRUE
00543CD3 . 57 PUSH EDI ; |pSecurity
00543CD4 . FF15 3C144000 CALL DWORD PTR DS:[<&KERNEL32.CreateEventA>] ; \CreateEventA
00543CDA . 3BC7 CMP EAX,EDI
00543CDC . 8B5D E8 MOV EBX,DWORD PTR SS:[EBP-18]
00543CDF . 8943 24 MOV DWORD PTR DS:[EBX+24],EAX
00543CE2 . 0F84 EA4B0000 JE msnmsgr.005488D2
00543CE8 . FF15 8C154000 CALL DWORD PTR DS:[<&KERNEL32.GetLastError>] ; [GetLastError
00543CEE . 3D B7000000 CMP EAX,0B7
00543CF3 . 0F84 2F4B0000 JE msnmsgr.00548828

...

Donc il appelle CreateEventA avec comme EventName MSNMSGR puis il compare EAX avec EDI or on peut remarquer que EDI est à 0 comme le témoigne le InitiallySignaled et pSecurity à FALSE donc il va tester si la fonction renvoie 0 donc hop un coup de msdn sur CreateEvent et on nous dis que CreateEvent renvoie un handle si la fonction est un succès sinon elle renvoie NULL donc il ne fait que tester si la fonction à bien marcher.
Si la fonction échoue on sort sinon on continue avec un appel à GetLastError().
Comme nous le dis la msdn GetLastError retourne ERROR_ALREADY_EXISTS si jamais l'event existe déjà, on peut donc supposer que le CMP EAX, 0B7 va vérifier si GetLAstError() renvoie ERROR_ALREADY_EXISTS.
Donc on fait un petit coup de msdn sur GetLastError pour trouver à quoi correspond ERROR_ALREADY_EXISTS, elle nous indique une page avec tout les codes d'erreur ( http://msdn2.microsoft.com/en-us/library/ms681381(VS.85).aspx )

On va donc chercher à quoi correspond 0B7h soit 183 et on tombe sur
ERROR_ALREADY_EXISTS         Cannot create a file when that file already exists.
183
0xB7
Tiens on ne s'en serait jamais douté ^^

Donc il vérifie si l'event existe déjà et si il existe il jmp en 00548828

On regarde ce qu'il fait en 00548828 et on s'aperçoit qu'il va chercher la fenêtre de MsnMsgr grâce à FindWindowA() et va lui envoyer un message pour qu'il prenne le focus avec PostMessageW().

Patch MsnMsgr

Donc pour finir cet article, il vous suffit de changer le 0B7 ( ERROR_ALREADY_EXISTS ) de "CMP EAX, 0B7" en 00543CEE par autre chose comme 0B6 ( ERROR_INVALID_ORDINAL ), ça fera très bien l'affaire.

Et bien j'espère que vous avez appris quelque chose aujourd'hui ( en tout cas moi oui ) d'intéressant et puis au moins ça m'aura permis de patcher mon msn pour ouvrir plusieurs compte en même temps ( plus besoin de aMsn )

Have fun :)

vendredi 15 février 2008

La fin du monde de Unix ?

J'imagine que beaucoup d'entre vous connaissent le timestamp, pour ceux qui ne savent pas ce que c'est, c'est un nombre qui représente le nombre de secondes écoulées depuis le 1er Janvier 1970.
Le timestamp est utilisé un peu partout dans les codes Unix notamment dans Linux et malheureusement il subsiste une petite imperfection dans ce mode de fonctionnement.
Certes de prendre une référence commune pour une meilleure synchronisation des informations est une très bonne chose mais le timestamp est un nombre codée sur 32 bits dont seuls les 31 premiers sont utilisables vu que le dernier est réservé le signe ( hum je ne comprend pas trop pourquoi d'ailleurs mais bon ... ).

On peut donc penser qu'il arrivera un moment où on aura attend ces 31 bits et qu'il n'y auras plus de place pour des nombres plus grand.

Voici un petit calcul d'estimation de la fin du monde de Unix :
Fin du monde de Unix le 19/01/2038 a 04:14:07

Donc voila si rien ne change d'ici 2038 Unix a du soucis à se faire pour son futur.
Il se produira comme ce qu'il c'est produit pour le bug de l'an 2000, les programmeurs du monde entier se demander ce qu'il se passerait quand on passerait à l'an 2000 car beaucoup de programme représentaient la date avec seulement 2 chiffres et ils allaient passer de 99 à 00 ce qui aurait pu dérégler les ordinateurs du monde entier mais en fait rien de cela ne c'est produit avec quelque petit bug mineur mais rien de grave.


Référence :

Code de l'estimation de la fin du monde de Unix :
http://akhenathon2.free.fr/coding/FinUnix.c

C'est dommage quand même ^^