运行环境扮演问题

firmbird 2003-01-20 11:11:54
在调用LsaEnumerateAccountRights获取指定用户的权限时需要调用者有POLICY_LOOKUP_NAMES权限,如果添加该权限给调用者.
...全文
67 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
masterz 2003-01-20
  • 打赏
  • 举报
回复
to enable privilege
Adjusting Process Token Privileges
by Tomas Restrepo

As some of you know, Windows NT is a far more secure OS than Windows 9x, both from a User and Developer standpoint. For a developer, this aspect of NT brings a new challenge: Getting programs to work both under 9x and NT comfortably.

Under NT, many of the Win32 API functions require that the process has a certain level of privilege to execute, which is a good thing, and a great advance on security, compared to 9x. But this also involves more work for the programmer, because some times he's responsible for getting the program to work for user's which do not have Administrator privileges. Don't believe that just because you're not doing low level stuff, or changing user, rights or whatever, you won't need to know this, because you will. A specific example, the SetSystemTime() call: It's simple, just changes the system time, but requires that the process has SE_SYSTEMTIME_NAME privilege on it's tokens, which is not enabled by default.

But, What's an access token? A token is:

A group of security attributes permanently attached to a process when a user logs on to the operating system. An access token contains privileges and security identifiers for a user, global group, or local group. The privileges regulate the use of some system services and the security identifiers regulate access to objects that are protected by access-control lists (ACLs). There are two kinds of access token: primary and impersonation.
When adjusting a process access tokens, you have to be careful to leave the tokens in the state they were in. Don't assume that you can just change them and leave it like that (unless you are enabling the SE_SHUTDOWN_NAME privilege, in which case it doesn't matter), you should always return them to the state you found them. Following this simple rule ensures that your program won't cause security troubles later.

Now, let's go a little deeper. What's the whole purpose of tokens? Tokens are created by the Local Security Authority (lsass.exe), and allow the system to keep track of some information related to the process. The most important information a process token holds is the SID of the user account the process is running under. It also carries the list of SID's for groups the user is member of, and, obviously, which privileges the user has been granted. All this allows the system to easily determine if the process should be granted access to a protected resource. Another important thing the token carries is a default DACL (Discretionary Access Control List) that is used to assign default security settings to objects (i.e. files) created by the process in behalf of the user. Keep in mind that this DACL is only used if the process doesn't explicitly supply a security descriptor when the object is created.

One thing I haven't mentioned so far is that threads can also have tokens associated, which you can get at via the OpenThreadToken() api. However, you'll soon notice that most of the time calling this api will fail. Why? Because usually a thread won't have a token attached, in which case the system will use the token of the process the thread is running on. When a thread does have a token attached to it, it's because it is impersonating another user, in which case it is said that the thread runs under a different security context. Is important to notice that a thread can also impersonate the same user account the process is running under, via the ImpersonateSelf() api.

There are some basic steps to adjusting the tokens, so let's review them, and later I'll present an example.

Call OpenProcessToken() with at least the TOKEN_ADJUST_PRIVILEGE and TOKEN_QUERY flags.
Use LookupPrivilegeValue() to get the LUID (Locally Unique Identifier) of the privilege you want to adjust.
Call AdjustTokenPrivileges() to adjust the tokens.
Do whatever calls you need to do to accomplish your task.
Call AdjustTokenPrivileges() again to set the old privileges back and leave the tokens as found.
Close the Token handle.
Let's see an example: Here we enable the SE_SYSTEMTIME_NAME privilege to be able of setting the system time:


HANDLE hToken; /* process token */
TOKEN_PRIVILEGES tp; /* token provileges */
TOKEN_PRIVILEGES oldtp; /* old token privileges */
DWORD dwSize = sizeof (TOKEN_PRIVILEGES);
LUID luid;

/* now, set the SE_SYSTEMTIME_NAME privilege to our current
* process, so we can call SetSystemTime()
*/
if (!OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken))
{
printf ("OpenProcessToken() failed with code %d\n", GetLastError());
return 1;
}
if (!LookupPrivilegeValue (NULL, SE_SYSTEMTIME_NAME, &luid))
{
printf ("LookupPrivilege() failed with code %d\n", GetLastError());
CloseHandle (hToken);
return 1;
}

ZeroMemory (&tp, sizeof (tp));
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

/* Adjust Token privileges */
if (!AdjustTokenPrivileges (hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES),
&oldtp, &dwSize))
{
printf ("AdjustTokenPrivileges() failed with code %d\n", GetLastError());
CloseHandle (hToken);
return 1;
}

/* Set time */
if (!SetSystemTime (&stCurrentTime))
{
printf ("SetSystemTime() failed with code %d\n", GetLastError());
CloseHandle (hToken);
return 1;
}

/* disable SE_SYSTEMTIME_NAME again */
AdjustTokenPrivileges (hToken, FALSE, &oldtp, dwSize, NULL, NULL);
if (GetLastError() != ERROR_SUCCESS)
{
printf ("AdjustTokenPrivileges() failed with code %d\n", GetLastError());
CloseHandle (hToken);
return 1;
}

CloseHandle (hToken);
As you can see from the example, it's not really a hard thing to do, but requires several non trivial calls just to do a simple task.

Finally, let's consider some linguistic issues. What exactly the difference between a privilege and a right is, that's something most people don't clearly understand. The problem is terminology. The original Windows NT docs referred to privileges as Advanced User Rights, which doesn't help much, either.

One way to clarify things is by understanding that permissions (a.k.a. rights) are always associated to a particular object. You have the permission to open a file, to read from it, etc. There are a set of generic rights, but every object type supplies it's own rights that only make sense for it. The system knows what rights a user have to access an object by looking at the ACL (Access Control List) associated with the object. Usually, ACL's are saved along with the object, so they are persistent if the object itself is. For example, a file's ACL is saved along with it on disk.

Privileges, on the other hand, are associated with particular actions on the system, and are granted to users, not objects. Privileges allow a user to override permissions, and this is why you have to be careful when granting them. A user with SE_RESTORE_NAME privilege could easily overwrite almost any file on the system..
masterz 2003-01-20
  • 打赏
  • 举报
回复
#include <windows.h>
#include <ntsecapi.h>
#include <stdio.h>
#pragma hdrstop



/*******************************************************************
This sample enumerates privileges assigned to an account. There are
two other samples for LSA stuff in the Win32/Platform SDK, under
\mssdk\samples\win32\winnt\security\lsapriv\ and ...\lsasamp\. The
latter directory also contains the help file describing the LSA
functions. There are also a few KB articles -- query for the term
"LsaOpenPolicy" to get them.
********************************************************************/



#define gle GetLastError()



int main( int argc, char *argv[] );
void err( const char *msg, NTSTATUS nts, bool isNtStatus = true );



class LsaUnicodeString: public _LSA_UNICODE_STRING
{
public:
LsaUnicodeString( int maxSize = 512 );
~LsaUnicodeString();
void init( int maxSize = 512 );
operator LSA_UNICODE_STRING *();
operator char *();
const LsaUnicodeString& operator=( const char *s );
};



LsaUnicodeString::LsaUnicodeString( int maxSize /* = 512 */ )
{
Buffer = NULL;
init( maxSize );
}



void LsaUnicodeString::init( int maxSize /* = 512 */ )
{
if ( Buffer )
free( Buffer );
Length = 0;
MaximumLength = maxSize;
Buffer = ( maxSize == 0 )? NULL: (wchar_t *) malloc( maxSize );
if ( Buffer )
*Buffer = L'\0';
}



LsaUnicodeString::operator LSA_UNICODE_STRING *()
{
return static_cast<LSA_UNICODE_STRING *>( this );
}



LsaUnicodeString::operator char *()
{
char *ansiBuf;

ansiBuf = (char *) malloc( Length / 2 + 1 );
int n = WideCharToMultiByte( CP_ACP, 0, Buffer, Length / 2, ansiBuf, Length + 2 / 1, NULL, NULL );
ansiBuf[n] = '\0';
return ansiBuf;
}



LsaUnicodeString::~LsaUnicodeString()
{
if ( Buffer )
free( Buffer );
}



const LsaUnicodeString& LsaUnicodeString::operator=( const char *s )
{
if ( strlen( s ) * 2 >= MaximumLength )
init( strlen( s ) ); // get new buffer
MultiByteToWideChar( CP_ACP, 0, s, -1, Buffer, MaximumLength * 2 );
Length = strlen( s ) * 2;
return *this;
}



void err( const char *msg, NTSTATUS nts, bool isNtStatus /* = true */ )
{
DWORD e;

if ( nts == 0 )
return;

if ( isNtStatus )
{
e = LsaNtStatusToWinError( nts );
printf( "%s: ntstatus %lu [%lXh], gle %lu\n", msg, nts, nts, e );
}
else
{
printf( "%s: gle %lu\n", msg, nts );
}

exit( 1 );
}



int main( int argc, char *argv[] )
{
if ( argc != 3 )
{
puts( "Usage: lsa_lear <system name> <account name>" );
puts( "Enumerates privileges for the named account on the named machine." );
puts( "<system name> is the machine where the lookup will execute." );
puts( "<account name> is the account to examine, e.g. \"FOO\\felixk\" or \"Administrators\"." );
return 1;
}


// open the policy object on the target computer

static SECURITY_QUALITY_OF_SERVICE sqos = { sizeof SECURITY_QUALITY_OF_SERVICE, SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE };
static LSA_OBJECT_ATTRIBUTES lsaOA = { sizeof LSA_OBJECT_ATTRIBUTES, NULL, NULL, 0, NULL, &sqos };
NTSTATUS nts;
LSA_HANDLE polHandle;
LsaUnicodeString systemName;

systemName = argv[1];

nts = LsaOpenPolicy( systemName, &lsaOA, GENERIC_READ | GENERIC_EXECUTE, &polHandle );
err( "LOP()", nts );

// translate the account name to a RID plus associated domain SID

SID *userSid;
char refDom[MAX_PATH];
SID_NAME_USE sidUse;
DWORD sidSize, domSize;
const char *acctTypeString;

userSid = (SID *) malloc( MAX_PATH );
sidSize = domSize = MAX_PATH;

if ( ! LookupAccountName( argv[1], argv[2], userSid, &sidSize, refDom, &domSize, &sidUse ) )
err( "LAN()", gle, false );

acctTypeString = NULL;
switch ( sidUse )
{
case SidTypeAlias:
case SidTypeUser:
case SidTypeGroup:
case SidTypeWellKnownGroup:
break;
case SidTypeDomain:
if ( acctTypeString == NULL )
acctTypeString = " domain";
// fall-through
case SidTypeInvalid:
if ( acctTypeString == NULL )
acctTypeString = "n invalid";
// fall-through
case SidTypeUnknown:
if ( acctTypeString == NULL )
acctTypeString = "n unknown";
// fall-through
case SidTypeDeletedAccount:
if ( acctTypeString == NULL )
acctTypeString = " deleted";
printf( "Don't know how to handle a%s account.\n", acctTypeString );
return 1;
}

LsaUnicodeString *userRights;
ULONG count;

userRights = NULL;
count = 0;
nts = LsaEnumerateAccountRights( polHandle, userSid, (LSA_UNICODE_STRING **) &userRights, &count );
err( "LEAR()", nts );

DWORD i;
char *p;

printf( "%d privileges for user \"%s\" in domain \"%s\":\n", count, argv[2], refDom );
for ( i = 0; i < count; ++ i )
{
p = (char *) userRights[i]; // must free() later
printf( "priv %u: %s\n", i, p );
free( p );
}

LsaClose( polHandle );
free( userSid );

return 0;
}

firmbird 2003-01-20
  • 打赏
  • 举报
回复
礼貌地说声“谢谢你”,不过请不要把这么大段大段的文章贴上来。如果我没有看过MSDN的话,我是不会先到CSDN上来问的。
我现在的问题不是要修改访问令牌的权限,这些在我以管理员身份登录时都可以实现,现在是我想以管理员身份登录,然后用程序修改某一帐号的策略对象访问权限,是Policy Object Access Rights而不是WindowNT Privileges
Frida是一款免费的,基于Python和JavaScript来实现的,面向开发人员、逆向工程师和安全研究人员的动态检测工具包。 Frida拥有一套全面的测试套件,不但调试效率极高,而且在广泛的使用中经历了多年严格的测试。 尤其是,移动应用安全测试和服务巨头NowSecure对齐钟爱有加,在NowSecure内部,安全人员通过Frida这个工具套装,已经完成对大量的移动应用程序大规模深度的安全分析测试。目前依然在该公司的安全测试中扮演重要的角色。 基于Python和JavaScript的Frida,天生就是跨平台的动态调试工具,不但可以运行在Windows、Linux、macOS之上,而且还可以调试Windows应用程序、Linux应用程序,macOS、iOS、Andriod和QNX等几乎全平台的应用程序。可以说,一旦掌握Frida这套工具,就可以在全平台,对全平台的应用程序进行动态调试和分析。 Frida使用极其方便,在使用过程中,只需将你编写的JavaScript脚本通过Frida自身的工具注入到目标进程中,就可以HOOK任何功能,其中包括但不限于监视加密API或跟踪应用程序关键代码等。在使用过程中,无需知道被“研究”程序的源代码。 尤其是可以一边编辑JavaScript脚本,一边运行JavaScript脚本的功能对于调试分析来说极为友好。只需“保存”正在编辑的JavaScript脚本,就立即就能看到该脚本执行的结果,全称无需其它人工介入,也无需重新启动被“研究”的应用程序,极大地简化了分析流程,同时也极大地提高了工作效率。因此,得到了众多安全分析人士的青睐。 本课程从最基本的调试环境搭建开始,基于经典的Windows“扫雷”游戏的动态调试分析,编码等,循序渐进演示Firda在分析调试Windows应用程序中基本使用方法和技巧。拥有这些知识储备之后,在加上官方的参考文档,你就可以轻松地将这些知识“迁移”至分析和调试其他平台的应用程序。 课程资料,请看第一课中github链接。

3,245

社区成员

发帖
与我相关
我的任务
社区描述
ATL,Active Template Library活动(动态)模板库,是一种微软程序库,支持利用C++语言编写ASP代码以及其它ActiveX程序。
社区管理员
  • ATL/ActiveX/COM社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧