Friendly Name |
Programmatic Name |
Admin |
System |
Act as part of the operating system |
SE_TCB_NAME |
|
|
Add workstations to domain |
SE_MACHINE_ACCOUNT_NAME |
|
|
Back up files and directories |
SE_BACKUP_NAME |
|
|
Bypass traverse checking |
SE_CHANGE_NOTIFY_NAME |
|
|
Change the system time |
SE_SYSTEMTIME_NAME |
|
|
Create a pagefile |
SE_CREATE_PAGEFILE_NAME |
|
|
Create a token object |
SE_CREATE_TOKEN_NAME |
|
|
Create permanent shared objects |
SE_CREATE_PERMANENT_NAME |
|
|
Debug programs |
SE_DEBUG_NAME |
|
|
Enable computer and user accounts to be trusted for delegation* |
SE_ENABLE_DELEGATION_NAME |
|
|
Force shutdown from a remote system |
SE_REMOTE_SHUTDOWN_NAME |
|
|
Generate security audits |
SE_AUDIT_NAME |
|
|
Increase quotas |
SE_INCREASE_QUOTA_NAME |
|
|
Increase scheduling priority |
SE_INC_BASE_PRIORITY_NAME |
|
|
Load and unload device drivers |
SE_LOAD_DRIVER_NAME |
|
|
Lock pages in memory |
SE_LOCK_MEMORY_NAME |
|
|
Manage auditing and security log |
SE_SECURITY_NAME |
|
|
Modify firmware environment values |
SE_SYSTEM_ENVIRONMENT_NAME |
|
|
Profile single process |
SE_PROF_SINGLE_PROCESS_NAME |
|
|
Profile system performance |
SE_SYSTEM_PROFILE_NAME |
|
|
Remove computer from docking station* |
SE_UNDOCK_NAME |
|
|
Replace a process-level token |
SE_ASSIGNPRIMARYTOKEN_NAME |
|
|
Restore files and directories |
SE_RESTORE_NAME |
|
|
Shut down the system |
SE_SHUTDOWN_NAME |
|
|
Synchronize directory service data* |
SE_SYNC_AGENT_NAME |
|
|
Take ownership of files or other objects |
SE_TAKE_OWNERSHIP_NAME |
|
|
* Windows 2000 only |
Figure 3 Using SE_BACKUP_NAME
#define UNICODE
#include <windows.h>
#include <stdio.h>
void Quit( const wchar_t* pszMsg, int nExitCode = 1 )
{
wprintf( L"%s\n", pszMsg );
exit( nExitCode );
}
// brain-dead error routine that dumps the last error and exits
void Err( const wchar_t* pszFcn, DWORD nErr = GetLastError() )
{
wchar_t szErr[256];
wchar_t szMsg[512];
if ( FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, 0, nErr, 0,
szErr, sizeof szErr / sizeof *szErr, 0 ) )
swprintf( szMsg, L"%s failed: %s", pszFcn, szErr );
else swprintf( szMsg, L"%s failed: 0x%08X", nErr );
Quit( szMsg );
}
// Useful helper function for enabling a single privilege
bool EnablePrivilege( HANDLE htok, const wchar_t* pszPriv,
TOKEN_PRIVILEGES& tpOld )
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if ( !LookupPrivilegeValue( 0, pszPriv, &tp.Privileges[0].Luid ) )
Err( L"LookupPrivilegeValue" );
// htok must have been opened with the following permissions:
// TOKEN_QUERY (to get the old priv setting)
// TOKEN_ADJUST_PRIVILEGES (to adjust the priv)
DWORD cbOld = sizeof tpOld;
if ( !AdjustTokenPrivileges( htok, FALSE, &tp, cbOld, &tpOld, &cbOld ) )
Err( L"AdjustTokenPrivileges" );
// Note that AdjustTokenPrivileges may succeed, and yet
// some privileges weren't actually adjusted.
// You've got to check GetLastError() to be sure!
return ( ERROR_NOT_ALL_ASSIGNED != GetLastError() );
}
// Corresponding restoration helper function
void RestorePrivilege( HANDLE htok, const TOKEN_PRIVILEGES& tpOld )
{
if ( !AdjustTokenPrivileges( htok, FALSE,
const_cast<TOKEN_PRIVILEGES*>(&tpOld),
0, 0, 0 ) )
Err( L"AdjustTokenPrivileges" );
}
// Brain-dead print routine for brevity
void PrintFile( HANDLE h )
{
DWORD cb = GetFileSize( h, 0 );
void* psz = malloc( cb );
ReadFile( h, psz, cb, &cb, 0 );
WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), psz, cb, &cb, 0 );
free( psz );
}
void wmain( int argc, wchar_t* argv[] )
{
if ( 2 != argc )
Quit( L"Usage: backupfile filename" );
// Enable the backup privilege
HANDLE htok = 0;
if ( !OpenProcessToken( GetCurrentProcess(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &htok ) )
Err( L"OpenProcessToken" );
TOKEN_PRIVILEGES tpOld;
if ( !EnablePrivilege( htok, SE_BACKUP_NAME, tpOld ) )
Quit( L"Sorry, you don't have the backup privilege..." );
// Open the requested file, exercising the backup privilege
HANDLE hfile = CreateFile( argv[1], GENERIC_READ, 0, 0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 );
if ( INVALID_HANDLE_VALUE == hfile )
Err( L"CreateFile" );
// Now restore the privilege back to its prior state.
// This is not strictly necessary, since we're going
// to terminate the process anyway, and the token we've
// adjusted will be destroyed, but in real life you might
// have processes that do a little more than this :-)
RestorePrivilege( htok, tpOld );
// Note that once we've got the file handle opened, we can
// use the permissions we were granted via CreateFile.
// There are no further access checks performed.
PrintFile( hfile );
CloseHandle( hfile );
CloseHandle( htok );
}
Figure 4 Kill2
# #define UNICODE
#define _WIN32_WINNT 0x400
#include <windows.h>
#include <stdio.h>
#include <aclapi.h>
void Quit( const wchar_t* pszMsg, int nExitCode = 1 )
{
wprintf( L"%s\n", pszMsg );
exit( nExitCode );
}
// brain-dead error routine that dumps the last error and exits
void Err( const wchar_t* pszFcn, DWORD nErr = GetLastError() )
{
wchar_t szErr[256];
wchar_t szMsg[512];
if ( FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, 0, nErr, 0,
szErr, sizeof szErr / sizeof *szErr, 0 ) )
swprintf( szMsg, L"%s failed: %s", pszFcn, szErr );
else swprintf( szMsg, L"%s failed: 0x%08X", nErr );
Quit( szMsg );
}
// This helper function simply grants PROCESS_TERMINATE to the WORLD sid
// for simplicity - you can get more sophisticated if you like, but the
// point of this app is to *kill* a process, so we ultimately won't care
// what the DACL looks like, as long as it allows us to terminate it.
void AdjustDacl( HANDLE h )
{
wprintf( L"Attempting to adjust process DACL..." );
// the WORLD Sid is trivial to form programmatically (S-1-1-0)
SID world = { SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY, 0 };
EXPLICIT_ACCESS ea =
{
PROCESS_TERMINATE,
SET_ACCESS,
NO_INHERITANCE,
{
0, NO_MULTIPLE_TRUSTEE,
TRUSTEE_IS_SID,
TRUSTEE_IS_USER,
reinterpret_cast<wchar_t*>( &world )
}
};
ACL* pdacl = 0;
DWORD err = SetEntriesInAcl( 1, &ea, 0, &pdacl );
if ( err )
Err( L"SetEntriesInAcl", err );
err = SetSecurityInfo( h, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION,
0, 0, pdacl, 0 );
if ( err )
Err( L"SetSecurityInfo", err );
LocalFree( pdacl );
}
// Useful helper function for enabling a single privilege
bool EnablePrivilege( HANDLE htok, const wchar_t* pszPriv,
TOKEN_PRIVILEGES& tpOld )
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if ( !LookupPrivilegeValue( 0, pszPriv, &tp.Privileges[0].Luid ) )
Err( L"LookupPrivilegeValue" );
// htok must have been opened with the following permissions:
// TOKEN_QUERY (to get the old priv setting)
// TOKEN_ADJUST_PRIVILEGES (to adjust the priv)
DWORD cbOld = sizeof tpOld;
if ( !AdjustTokenPrivileges( htok, FALSE, &tp, cbOld, &tpOld, &cbOld ) )
Err( L"AdjustTokenPrivileges" );
// Note that AdjustTokenPrivileges may succeed, and yet
// some privileges weren't actually adjusted.
// You've got to check GetLastError() to be sure!
return ( ERROR_NOT_ALL_ASSIGNED != GetLastError() );
}
// Corresponding restoration helper function
void RestorePrivilege( HANDLE htok, const TOKEN_PRIVILEGES& tpOld )
{
if ( !AdjustTokenPrivileges( htok, FALSE,
const_cast<TOKEN_PRIVILEGES*>(&tpOld),
0, 0, 0 ) )
Err( L"AdjustTokenPrivileges" );
}
void wmain( int argc, wchar_t* argv[] )
{
if ( 2 != argc )
Quit( L"Usage: kill2 pid" );
int pid = _wtoi(argv[1]);
if ( !pid )
Quit( L"Usage: kill2 pid" );
// who knows, it might be really easy...
HANDLE hp = OpenProcess( PROCESS_TERMINATE, FALSE, pid );
if ( !hp )
{
// oh well, we need to grant ourselves PROCESS_TERMINATE
HANDLE hpWriteDAC = OpenProcess( WRITE_DAC, FALSE, pid );
if ( !hpWriteDAC )
{
// hmm, we don't have permissions to modify the DACL...
// time to take ownership...
HANDLE htok;
if ( !OpenProcessToken( GetCurrentProcess(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &htok ) )
Err( L"OpenProcessToken" );
TOKEN_PRIVILEGES tpOld;
if ( !EnablePrivilege( htok, SE_TAKE_OWNERSHIP_NAME, tpOld ) )
Err( L"You don't posess SeTakeOwnershipPrivilege." );
// SeTakeOwnershipPrivilege allows us to open objects with
// WRITE_OWNER, but that's about it, so we'll update the owner,
// and dupe the handle so we can get WRITE_DAC permissions.
HANDLE hpWriteOwner = OpenProcess( WRITE_OWNER, FALSE, pid );
if ( !hpWriteOwner )
Err( L"OpenProcess" );
BYTE buf[512]; // this should always be big enough
DWORD cb = sizeof buf;
if ( !GetTokenInformation( htok, TokenUser, buf, cb, &cb ) )
Err( L"GetTokenInformation" );
DWORD err = SetSecurityInfo( hpWriteOwner, SE_KERNEL_OBJECT,
OWNER_SECURITY_INFORMATION,
reinterpret_cast<TOKEN_USER*>(buf)->User.Sid,
0, 0, 0 );
if ( err )
Err( L"SetSecurityInfo", err );
// now that we're the owner, we've implicitly got WRITE_DAC
// permissions, so ask the system to reevaluate our request,
// giving us a handle with WRITE_DAC permissions
if ( !DuplicateHandle( GetCurrentProcess(), hpWriteOwner,
GetCurrentProcess(), &hpWriteDAC,
WRITE_DAC, FALSE, 0 ) )
Err( L"DuplicateHandle" );
// not truly necessary in this app,
// but included for completeness
RestorePrivilege( htok, tpOld );
}
if ( hpWriteDAC )
{
// we've now got a handle that allows us WRITE_DAC permission
AdjustDacl( hpWriteDAC );
// now that we've granted ourselves permission to terminate
// the process, ask the system to reevaluate our request,
// giving us a handle with PROCESS_TERMINATE permissions
if ( !DuplicateHandle( GetCurrentProcess(), hpWriteDAC,
GetCurrentProcess(), &hp,
PROCESS_TERMINATE, FALSE, 0 ) )
Err( L"DuplicateHandle" );
CloseHandle( hpWriteDAC );
}
}
if ( hp )
{
// if all went well, we've now got a handle to the process
// that grants us PROCESS_TERMINATE permissions
if ( !TerminateProcess( hp, 1 ) )
Err( L"TerminateProcess" );
}
wprintf( L"Success!" );
}