RE: W2k: Unkillable Applications

From: David LeBlanc (dleblancat_private)
Date: Wed Jul 18 2001 - 10:59:43 PDT

  • Next message: Cole, Timothy D.: "RE: Linux, too, sot of (Windows MS-DOS Device Name DoS vulnerabil ities)"

    I don't have time to fix it this morning, but there's several problems in
    this code - inline -
    
    > -----Original Message-----
    > From: vixat_private [mailto:vixat_private]On Behalf
    > Of Toomas Kiisk
    
    > There's no need for a debugger. SE_DEBUG privilege is simply
    > disabled by default, and it must be enabled using
    > AdjustTokenPrivileges(). Here's the source of a small
    > utility I posted few years ago to ee.arvutid.microsoft,
    > hopefully it is self-explanatory. The source has undergone
    > some "formatting" by google archive, so there may be few
    > underscores missing.
    
    > --------------begin kill.c----------
    > #include <windows.h>
    > #include <malloc.h>
    > #include <stdio.h>
    > #include <stdarg.h>
    > #include <assert.h>
    >
    >
    > void usage_exit( void );
    > void w32_error( const char *blah, ... );
    >
    >
    > int main( int argc, char **argv )
    > {
    > 	HANDLE proc, token;
    > 	TOKEN_PRIVILEGES *p = NULL, *dummy = NULL;
    > 	DWORD psize = 0, i = 0;
    >
    > 	if ( argc < 2 )
    > 		usage_exit();
    >
    > 	assert( OpenProcessToken( GetCurrentProcess(),
    > 			TOKEN_ALL_ACCESS, &token ) );
    
    This won't get executed in a release build - proper way to do this would be:
    
    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token ) )
    {
      printf("horrible error\n");
      assert(false);
      return -1;
    }
    
    This both executes properly and handles errors correctly under debug and
    release builds.
    
    > 	while ( ! GetTokenInformation( token, TokenPrivileges, p,
    > 				psize, &psize ) ) {
    > 		if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) {
    > 			w32_error( "GetTokenInformation()" );
    > 			exit( 1 );
    > 		}
    > 		if ( ! (p = alloca( psize ) ) ) {
    > 			w32_error( "alloca( %u )", psize );
    > 			exit( 1 );
    > 		}
    > 	}
    
    The buffer you're pulling back is a structure containing a DWORD and an
    array of structs consisting of a LUID (almost the same as a large integer)
    and a DWORD. The size of the required buffer can be calculated by:
    
    size = sizeof(DWORD) + sizeof(LUID_AND_ATTRIBUTES) * MaxPrivs;
    
    It's still good to check for an inadequate buffer, as future versions of the
    OS might have new privileges, but you can use it to pre-allocate a
    reasonable buffer and save yourself a call.
    
    > 	for ( i=0; i<p->PrivilegeCount; i++ )
    > 		p->Privileges[ i ].Attributes |= SE_PRIVILEGE_ENABLED;
    
    You're going to enable every possible privilege?!? Just to get SE_DEBUG???
    The correct way to do this is to use LookupPrivilegeValue() to find the LUID
    for SE_DEBUG, and flip that bit for only that privilege. If you don't flip
    the bit, then you can only terminate your own processes.
    
    > 	while ( ! AdjustTokenPrivileges( token, FALSE, p, psize,
    > 				dummy, &psize ) ) {
    > 		if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) {
    > 			w32_error( "AdjustTokenPrivileges()" );
    > 			exit( 1 );
    > 		}
    > 		if ( ! (dummy = alloca( psize ) ) ) {
    > 			w32_error( "alloca( %u )", psize );
    > 			exit( 1 );
    > 		}
    > 	}
    
    If you're not interested in restoring this process' privileges to a previous
    state, there is no need to store the previous privileges, and no need to
    allocate anything. The new number of privileges will also be the same as the
    old, so the allocation size would be the same. Once you know how much you
    needed before, you can use the same buffer size.
    
    Additionally, AdjustTokenPrivileges() has a very annoying behavior - it has
    a trinary return - it can either fail, it can adjust some of the privileges,
    or it can adjust all of the privileges. It can have a situation where you
    ask it to adjust ONE privilege, it returns success, but GetLastError()
    throws ERROR_NOT_ALL_ASSIGNED.
    
    > 	while ( --argc ) {
    > 		proc = OpenProcess( PROCESS_TERMINATE, FALSE,
    > 				(DWORD)atoi( argv[ argc ] ) );
    
    If atoi fails, you'll try and terminate the 0 PID (system idle) process. I
    don't think it will die (or open - not sure, never tried).
    



    This archive was generated by hypermail 2b30 : Thu Jul 19 2001 - 09:02:54 PDT