Bugtraq mailing list archives

RE: W2k: Unkillable Applications


From: "David LeBlanc" <dleblanc () mindspring com>
Date: Wed, 18 Jul 2001 10:59:43 -0700

I don't have time to fix it this morning, but there's several problems in
this code - inline -

-----Original Message-----
From: vix () tartu cyber ee [mailto:vix () tartu cyber ee]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).



Current thread: