Administrator用户直接获取SYSTEM权限
来源:http://www.nsfocus.com
作者:"scz" <scz@nsfocus.com>
标题: MSDN系列(3)--Administrator用户直接获取SYSTEM权限
日期: 2003-06-21 21:51
更新:
--------------------------------------------------------------------------
目录:
☆ 概述
☆ sysproc.c
☆ sysproc_now.c
☆ 参考资源
--------------------------------------------------------------------------
☆ 概述
翻看[5]的时候,其书中例8.1演示如何以SYSTEM身份启动cmd。以前我为了达到同样
目的,利用at命令,但平日里禁用Task Scheduler Service的,感觉操作很绕弯,不
爽得很。网上有现成的可执行程序直接获取SYSTEM权限,不喜欢没源码的此类型小工
具,从未试用过。
麻雀兄 <sparrow@smth>在水木清华MSDN版上共享了一份Ashot Oganesyan K的源代码,
也是直接获取SYSTEM权限。
一下子有了两份现成的短小精悍型源代码,心痒难耐,决定演练。最后发现还是麻雀
兄给的代码有实用价值。Gary Nebbett的代码对于2K/XP实际上失去了实用价值,这
要感谢rain的指点,具体技术讨论见后。
本文提供了两份完整源代码,sysproc.c、sysproc_now.c,前者演示Gary Nebbett的
办法,后者演示Ashot Oganesyan K的办法。
Ashot Oganesyan K的源代码只支持到2K,不清楚这位达人是否自己更新过,google
居然搜不到所提源代码(再次感谢sparrow共享),没办法,我只好自己增加了对XP和
2003 Server的支持。此外为方便使用,sysproc_now.c自己找出winlogon.exe的PID,
不必在命令行上指定拥有SYSTEM权限的PID了。
☆ sysproc.c
Gary Nebbett的中心思想就是利用ZwCreateToken()自己创建一个SYSTEM令牌(Token),
作为CreateProcessAsUser()第一形参使用。这个想法很好,我也费了好大劲去读令
牌相关的各类技术文档,可是到2K/XP环境中实测时却未达到预定效果。
开始完全按照Gary Nebbett所述编码,2K中ZwCreateToken()失败返回,说当前帐号
没有足够的权限。XP中说当前帐号不能指派为所伪造的SYSTEM令牌的所有者(Owner)。
关于这点参sysproc.c/CreateSystemToken()函数中的注释,从中也可看出XP比2K多
做了一些安全检查。后来统一使用sysproc.c中演示的方式,这下都成了权限不够的
错误提示。
知道调用ZwCreateToken()需要SE_CREATE_TOKEN_NAME权限,因此在程序中Enable了
该权限,当时相关的代码路径上并未提示Enable失败。因此对"权限不够"感到非常困
惑。鉴于我对Privilege理解不足,干脆不管三七二十一先将winnt.h中所列权限全部
Enable了,还是同样的下场。由此我才想到Administrator是否缺省并未被Grant某些
权限,AdjustTokenPrivileges()只能Enable/Disable那些已经Grant的权限,比如前
面所提的SE_DEBUG_NAME权限,并不能Grant权限。正好rain来北京,我们聚了两天,
顺带就此问题请教了rain和flier。rain为sysproc.c增加了一段代码,显示当前帐号
所拥有的权限,参CreateSystemToken()函数。从中发现Administrator果然没有所需
的SE_CREATE_TOKEN_NAME权限。rain告诉我,2K/XP中这种权限需要显式指派,也就
是"本地安全策略/用户权利指派"所做的事,NT是否需要显式指派我未证实过。缺省
情况下,CreateProcessAsUser()所需SE_ASSIGNPRIMARYTOKEN_NAME权限也未被指派
给管理员帐号。这两种权限在"本地安全策略/用户权利指派"中对应:
SE_CREATE_TOKEN_NAME - Create a token object
SE_ASSIGNPRIMARYTOKEN_NAME - Replace a process level token
在此加深了对AdjustTokenPrivileges()的记忆。开始对MSDN中的描述并无感性认识,
只判断返回值为FALSE,而未判断GetLastError()为ERROR_NOT_ALL_ASSIGNED的情况,
导致当时相关的代码路径上并未提示Enable失败。
为管理员帐号手工增加这两种权限,注销并重新登录后,Gary Nebbett的目的达到了。
可以不利用"本地安全策略/用户权利指派",而是自己编程实现这个步骤([2])。利用
LsaOpenPolicy()、LsaAddAccountRights()即可,AddPrivilege()演示了这个过程。
但问题是无论如何都必须注销/重登录才会生效。这对我来说,事实上已经失去了利
用价值,我需要的是立即(now)获取SYSTEM权限。
可能有人由ZwCreateToken()联想到LogonUser(),关于后者可以参看[3]、[4],那不
是我需要的东西。
下面给出sysproc.c的完整源代码,秉承一贯风格,以不建工程(Solution/Project)
直接命令行编译为原则。注释相当冗长,那是写给我本人看的,对这些API特别不熟,
不写个注释在附近,下次看不懂是很正常的事,谁让该死的Windows API这么多形参。
--------------------------------------------------------------------------
/*
* For x86/EWindows XP SP1 & VC 7
* cl sysproc.c /Os /G6 /W3
*
* Gary Nebbett
* rain
*/
/************************************************************************
* *
* Head File *
* *
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <ntsecapi.h>
/************************************************************************
* *
* Macro *
* *
************************************************************************/
#pragma comment( linker, "/subsystem:console" )
#pragma comment( lib, "advapi32.lib" )
typedef LONG NTSTATUS;
/*
* you'll find a list of NTSTATUS status codes in the DDK header
* ntstatus.h (/WINDDK/2600.1106/inc/ddk/wxp/)
*/
#define NT_SUCCESS(status) ((NTSTATUS)(status)>=0)
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
/*
*************************************************************************
* ntdef.h
*/
typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
/*
* ntdef.h
*************************************************************************
*/
/*
* 参看DDK文档以及<<Windows NT/2000 Native API Reference>> - Gary Nebbett
* 这些Native API由ntdll.dll输出
*/
typedef ULONG ( __stdcall *RTLNTSTATUSTODOSERROR ) ( IN NTSTATUS Status );
typedef NTSTATUS ( __stdcall *ZWCREATETOKEN ) ( OUT PHANDLE TokenHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN TOKEN_TYPE Type, IN PLUID AuthenticationId, IN PLARGE_INTEGER ExpirationTime, IN PTOKEN_USER User, IN PTOKEN_GROUPS Groups, IN PTOKEN_PRIVILEGES Privileges, IN PTOKEN_OWNER Owner, IN PTOKEN_PRIMARY_GROUP PrimaryGroup, IN PTOKEN_DEFAULT_DACL DefaultDacl, IN PTOKEN_SOURCE Source );
/************************************************************************
* *
* Function Prototype *
* *
************************************************************************/
static BOOL AddCurrentProcessPrivilege
( LPWSTR PrivilegeName );
static BOOL AddPrivilege ( LSA_HANDLE PolicyHandle,
PSID AccountSid, LPWSTR PrivilegeName );
static HANDLE CreateSystemToken ( void );
static BOOL DisableCurrentProcessSomePrivilege
( void );
static BOOL EnableCurrentProcessSomePrivilege
( void );
static PVOID GetFromToken ( HANDLE TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass );
static BOOL LocateNtdllEntry ( void );
static void PrintWin32Error ( char *message, DWORD dwMessageId );
static void PrintZwError ( char *message, NTSTATUS status );
static BOOL RemoveCurrentProcessPrivilege
( LPWSTR PrivilegeName );
static BOOL RemovePrivilege ( LSA_HANDLE PolicyHandle,
PSID AccountSid, LPWSTR PrivilegeName );
static BOOL SetCurrentProcessPrivilege
( LPCTSTR PrivilegeName, BOOL EnableFlag );
static BOOL SetPrivilege ( HANDLE TokenHandle, LPCTSTR PrivilegeName,
BOOL EnableFlag );
/************************************************************************
* *
* Static Global Var *
* *
************************************************************************/
/*
* 由ntdll.dll输出的Native API函数指针
*/
static RTLNTSTATUSTODOSERROR RtlNtStatusToDosError = NULL;
static ZWCREATETOKEN ZwCreateToken = NULL;
/************************************************************************/
static BOOL AddCurrentProcessPrivilege ( LPWSTR PrivilegeName )
{
NTSTATUS status;
BOOL ret = FALSE;
LSA_HANDLE PolicyHandle = NULL;
LSA_OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE CurrentProcessToken = NULL;
PTOKEN_USER token_user = NULL;
ZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );
/*
* NTSTATUS LsaOpenPolicy
* (
* PLSA_UNICODE_STRING SystemName,
* PLSA_OBJECT_ATTRIBUTES ObjectAttributes,
* ACCESS_MASK DesiredAccess,
* PLSA_HANDLE PolicyHandle
* );
*/
status = LsaOpenPolicy( NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &PolicyHandle );
if ( status != STATUS_SUCCESS )
{
PrintWin32Error( "LsaOpenPolicy() failed", LsaNtStatusToWinError( status ) );
goto AddCurrentProcessPrivilege_exit;
}
if ( FALSE == OpenProcessToken( GetCurrentProcess(),
TOKEN_QUERY,
&CurrentProcessToken ) )
{
PrintWin32Error( "OpenProcessToken() failed", GetLastError() );
goto AddCurrentProcessPrivilege_exit;
}
if ( NULL == ( token_user = ( PTOKEN_USER )GetFromToken( CurrentProcessToken, TokenUser ) ) )
{
goto AddCurrentProcessPrivilege_exit;
}
if ( FALSE == AddPrivilege( PolicyHandle,
token_user->User.Sid,
PrivilegeName ) )
{
goto AddCurrentProcessPrivilege_exit;
}
ret = TRUE;
AddCurrentProcessPrivilege_exit:
if ( NULL != token_user )
{
free( token_user );
token_user = NULL;
}
if ( NULL != CurrentProcessToken )
{
CloseHandle( CurrentProcessToken );
CurrentProcessToken = NULL;
}
if ( NULL != PolicyHandle )
{
LsaClose( PolicyHandle );
PolicyHandle = NULL;
}
return( ret );
} /* end of AddCurrentProcessPrivilege */
/*
* 留心第二形参是宽字符串
*/
static BOOL AddPrivilege ( LSA_HANDLE PolicyHandle, PSID AccountSid, LPWSTR PrivilegeName )
{
BOOL ret = FALSE;
LSA_UNICODE_STRING UserRights;
USHORT StringLength;
NTSTATUS status;
if ( PrivilegeName == NULL )
{
goto AddPrivilege_exit;
}
StringLength = wcslen( PrivilegeName );
UserRights.Buffer = PrivilegeName;
UserRights.Length = StringLength * sizeof( WCHAR );
UserRights.MaximumLength = ( StringLength + 1 ) * sizeof( WCHAR );
/*
* Header : Declared in Ntsecapi.h.
* Library: Use Advapi32.lib.
*
* NTSTATUS LsaAddAccountRights
* (
* LSA_HANDLE PolicyHandle,
* PSID AccountSid,
* PLSA_UNICODE_STRING UserRights,
* ULONG CountOfRights
* );
*/
status = LsaAddAccountRights( PolicyHandle, AccountSid, &UserRights, 1 );
if ( status != STATUS_SUCCESS )
{
PrintWin32Error( "LsaAddAccountRights() failed", LsaNtStatusToWinError( status ) );
goto AddPrivilege_exit;
}
ret = TRUE;
AddPrivilege_exit:
return( ret );
} /* end of AddPrivilege */
static HANDLE CreateSystemToken ( void )
{
NTSTATUS status;
HANDLE CurrentProcessToken = NULL;
HANDLE SystemToken = NULL;
SID_IDENTIFIER_AUTHORITY sid_identifier_authority = SECURITY_NT_AUTHORITY;
PTOKEN_PRIVILEGES token_privileges = NULL;
/*
* typedef struct _TOKEN_USER
* {
* SID_AND_ATTRIBUTES User;
* } TOKEN_USER, *PTOKEN_USER;
*
* typedef struct _SID_AND_ATTRIBUTES
* {
* PSID Sid;
* DWORD Attributes;
* } SID_AND_ATTRIBUTES, *PSID_AND_ATTRIBUTES;
*/
TOKEN_USER token_user = { { NULL, 0 } };
/*
* typedef struct _TOKEN_SOURCE
* {
* Char SourceName[8];
* LUID SourceIdentifier;
* } TOKEN_SOURCE, *PTOKEN_SOURCE;
*
* typedef struct _LUID
* {
* D