[21636] in bugtraq
RE: W2k: Unkillable Applications
daemon@ATHENA.MIT.EDU (David LeBlanc)
Thu Jul 19 12:03:58 2001
Reply-To: <dleblanc@mindspring.com>
From: "David LeBlanc" <dleblanc@mindspring.com>
To: "'Toomas Kiisk'" <vix@cyber.ee>, "'Frank Breedijk'" <FrankB@InterXion.com>
Cc: <bugtraq@securityfocus.com>
Date: Wed, 18 Jul 2001 10:59:43 -0700
Message-ID: <018e01c10fb4$7c85d4c0$0100a8c0@davenet.local>
MIME-Version: 1.0
Content-Type: text/plain;
charset="US-ASCII"
Content-Transfer-Encoding: 7bit
In-Reply-To: <Pine.LNX.4.33.0107181603170.3831-100000@ondatra.tartu-labor>
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).