Los handles en un proceso permiten acceder a diferentes recursos de Windows:
Ya ha habido varios casos de escalada de privilegios donde un proceso privilegiado con handles abiertos e heredables ha ejecutado un proceso no privilegiado dándole acceso a todos esos handles.
Por ejemplo, imagina que un proceso que se ejecuta como SYSTEM abre un nuevo proceso (OpenProcess()) con acceso total. El mismo proceso también crea un nuevo proceso (CreateProcess()) con bajos privilegios pero heredando todos los handles abiertos del proceso principal.
Luego, si tienes acceso total al proceso de bajo privilegio, puedes obtener el handle abierto al proceso privilegiado creado con OpenProcess() y inyectar un shellcode.
Handles Interesantes
Proceso
Como leíste en el ejemplo inicial, si un proceso no privilegiado hereda un handle de proceso de un proceso privilegiado con suficientes permisos, podrá ejecutar código arbitrario en él.
En este excelente artículo puedes ver cómo explotar cualquier handle de proceso que tenga alguno de los siguientes permisos:
PROCESS_ALL_ACCESS
PROCESS_CREATE_PROCESS
PROCESS_CREATE_THREAD
PROCESS_DUP_HANDLE
PROCESS_VM_WRITE
Hilo
Similar a los handles de proceso, si un proceso no privilegiado hereda un handle de hilo de un proceso privilegiado con suficientes permisos, podrá ejecutar código arbitrario en él.
En este excelente artículo también puedes ver cómo explotar cualquier handle de proceso que tenga alguno de los siguientes permisos:
THREAD_ALL_ACCESS
THREAD_DIRECT_IMPERSONATION
THREAD_SET_CONTEXT
Handles de Archivo, Clave y Sección
Si un proceso no privilegiado hereda un handle con permisos equivalentes de escritura sobre un archivo o registro privilegiado, podrá sobrescribir el archivo/registro (y con mucha suerte, escalar privilegios).
Los Handles de Sección son similares a los handles de archivo, el nombre común de este tipo de objetos es "File Mapping". Se utilizan para trabajar con archivos grandes sin mantener todo el archivo en memoria. Eso hace que la explotación sea "similar" a la explotación de un Handle de Archivo.
Cómo ver los handles de los procesos
Process Hacker
Process Hacker es una herramienta que puedes descargar gratis. Tiene varias opciones increíbles para inspeccionar procesos y una de ellas es la capacidad de ver los handles de cada proceso.
Ten en cuenta que para ver todos los handles de todos los procesos, se necesita el SeDebugPrivilege (así que necesitas ejecutar Process Hacker como administrador).
Para ver los handles de un proceso, haz clic derecho en el proceso y selecciona Handles:
Luego puedes hacer clic derecho en el handle y ver los permisos:
Sysinternals Handles
El Handlesbinario de Sysinternals también listará los handles por proceso en la consola:
LeakedHandlesFinder
Esta herramienta te permite monitorear los handles filtrados e incluso autoexplotarlos para escalar privilegios.
Metodología
Ahora que sabes cómo encontrar los handles de los procesos, lo que necesitas verificar es si algún proceso no privilegiado está teniendo acceso a handles privilegiados. En ese caso, el usuario del proceso podría obtener el handle y abusar de él para escalar privilegios.
Se mencionó anteriormente que necesitas el SeDebugPrivilege para acceder a todos los handles. Pero un usuario aún puede acceder a los handles de sus procesos, por lo que podría ser útil si deseas escalar privilegios solo desde ese usuario para ejecutar las herramientas con los permisos regulares del usuario.
Por ejemplo, el siguiente código pertenece a un servicio de Windows que sería vulnerable. El código vulnerable de este binario de servicio se encuentra dentro de la función Exploit. Esta función comienza creando un nuevo proceso de manejo con acceso total. Luego, crea un proceso de bajo privilegio (copiando el token de bajo privilegio de explorer.exe) ejecutando C:\users\username\desktop\client.exe. La vulnerabilidad reside en el hecho de que está creando el proceso de bajo privilegio con bInheritHandles como TRUE.
Por lo tanto, este proceso de bajo privilegio puede obtener el manejo del proceso de alto privilegio creado primero e inyectar y ejecutar un shellcode (ver la siguiente sección).
#include<windows.h>#include<tlhelp32.h>#include<tchar.h>#pragmacomment (lib, "advapi32")TCHAR* serviceName =TEXT("HandleLeakSrv");SERVICE_STATUS serviceStatus;SERVICE_STATUS_HANDLE serviceStatusHandle =0;HANDLE stopServiceEvent =0;//Find PID of a proces from its nameintFindTarget(constchar*procname) {HANDLE hProcSnap;PROCESSENTRY32 pe32;int pid =0;hProcSnap =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);if (INVALID_HANDLE_VALUE == hProcSnap) return0;pe32.dwSize =sizeof(PROCESSENTRY32);if (!Process32First(hProcSnap,&pe32)) {CloseHandle(hProcSnap);return0;}while (Process32Next(hProcSnap,&pe32)) {if (lstrcmpiA(procname,pe32.szExeFile)==0) {pid =pe32.th32ProcessID;break;}}CloseHandle(hProcSnap);return pid;}intExploit(void) {STARTUPINFOA si;PROCESS_INFORMATION pi;int pid =0;HANDLE hUserToken;HANDLE hUserProc;HANDLE hProc;// open a handle to itself (privileged process) - this gets leaked!hProc =OpenProcess(PROCESS_ALL_ACCESS,TRUE, GetCurrentProcessId());// get PID of user low privileged processif ( pid =FindTarget("explorer.exe") )hUserProc =OpenProcess(PROCESS_QUERY_INFORMATION,FALSE, pid);elsereturn-1;// extract low privilege token from a user's processif (!OpenProcessToken(hUserProc, TOKEN_ALL_ACCESS,&hUserToken)) {CloseHandle(hUserProc);return-1;}// spawn a child process with low privs and leaked handleZeroMemory(&si,sizeof(si));si.cb =sizeof(si);ZeroMemory(&pi,sizeof(pi));CreateProcessAsUserA(hUserToken,"C:\\users\\username\\Desktop\\client.exe",NULL,NULL,NULL,TRUE,0,NULL,NULL,&si,&pi);CloseHandle(hProc);CloseHandle(hUserProc);return0;}void WINAPI ServiceControlHandler( DWORD controlCode ) {switch ( controlCode ) {case SERVICE_CONTROL_SHUTDOWN:case SERVICE_CONTROL_STOP:serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;SetServiceStatus( serviceStatusHandle,&serviceStatus );SetEvent( stopServiceEvent );return;case SERVICE_CONTROL_PAUSE:break;case SERVICE_CONTROL_CONTINUE:break;case SERVICE_CONTROL_INTERROGATE:break;default:break;}SetServiceStatus( serviceStatusHandle,&serviceStatus );}void WINAPI ServiceMain( DWORD argc, TCHAR* argv[] ) {// initialise service statusserviceStatus.dwServiceType = SERVICE_WIN32;serviceStatus.dwCurrentState = SERVICE_STOPPED;serviceStatus.dwControlsAccepted =0;serviceStatus.dwWin32ExitCode = NO_ERROR;serviceStatus.dwServiceSpecificExitCode = NO_ERROR;serviceStatus.dwCheckPoint =0;serviceStatus.dwWaitHint =0;serviceStatusHandle =RegisterServiceCtrlHandler( serviceName, ServiceControlHandler );if ( serviceStatusHandle ) {// service is startingserviceStatus.dwCurrentState = SERVICE_START_PENDING;SetServiceStatus( serviceStatusHandle,&serviceStatus );// do initialisation herestopServiceEvent =CreateEvent( 0,FALSE,FALSE,0 );// runningserviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);serviceStatus.dwCurrentState = SERVICE_RUNNING;SetServiceStatus( serviceStatusHandle,&serviceStatus );Exploit();WaitForSingleObject( stopServiceEvent,-1 );// service was stoppedserviceStatus.dwCurrentState = SERVICE_STOP_PENDING;SetServiceStatus( serviceStatusHandle,&serviceStatus );// do cleanup hereCloseHandle( stopServiceEvent );stopServiceEvent =0;// service is now stoppedserviceStatus.dwControlsAccepted &=~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);serviceStatus.dwCurrentState = SERVICE_STOPPED;SetServiceStatus( serviceStatusHandle,&serviceStatus );}}voidInstallService() {SC_HANDLE serviceControlManager =OpenSCManager( 0,0, SC_MANAGER_CREATE_SERVICE );if ( serviceControlManager ) {TCHAR path[ _MAX_PATH +1 ];if ( GetModuleFileName( 0, path,sizeof(path)/sizeof(path[0]) )>0 ) {SC_HANDLE service =CreateService( serviceControlManager,serviceName, serviceName,SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path,0,0,0,0,0 );if ( service )CloseServiceHandle( service );}CloseServiceHandle( serviceControlManager );}}voidUninstallService() {SC_HANDLE serviceControlManager =OpenSCManager( 0,0, SC_MANAGER_CONNECT );if ( serviceControlManager ) {SC_HANDLE service =OpenService( serviceControlManager,serviceName, SERVICE_QUERY_STATUS | DELETE );if ( service ) {SERVICE_STATUS serviceStatus;if ( QueryServiceStatus( service,&serviceStatus ) ) {if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )DeleteService( service );}CloseServiceHandle( service );}CloseServiceHandle( serviceControlManager );}}int_tmain( int argc, TCHAR* argv[] ){if ( argc >1&&lstrcmpi( argv[1], TEXT("install") )==0 ) {InstallService();}elseif ( argc >1&&lstrcmpi( argv[1], TEXT("uninstall") )==0 ) {UninstallService();}else {SERVICE_TABLE_ENTRY serviceTable[]= {{ serviceName, ServiceMain },{ 0,0 }};StartServiceCtrlDispatcher( serviceTable );}return0;}
Ejemplo de Explotación 1
En un escenario real probablemente no podrás controlar el binario que se va a ejecutar por el código vulnerable (C:\users\username\desktop\client.exe en este caso). Probablemente comprometerás un proceso y necesitarás ver si puedes acceder a algún handle vulnerable de algún proceso privilegiado.
En este ejemplo puedes encontrar el código de una posible explotación para C:\users\username\desktop\client.exe.
La parte más interesante de este código se encuentra en GetVulnProcHandle. Esta función comenzará a obtener todos los handles, luego verificará si alguno de ellos pertenece al mismo PID y si el handle pertenece a un proceso. Si se cumplen todos estos requisitos (se encuentra un handle de proceso abierto accesible), intenta inyectar y ejecutar un shellcode abusando del handle del proceso.
La inyección del shellcode se realiza dentro de la Inject función y solo escribirá el shellcode dentro del proceso privilegiado y creará un hilo dentro del mismo proceso para ejecutar el shellcode).