Windows下如何创建低权限进程
2021-05-07 12:26
标签:基本 main eof lock 参数 ups 针对 last 信任 1. 前言 在使用 Sysinternals 出品的 Process Explorer 过程中,对 “Run as Limited User” 功能的实现方式颇感兴趣,一番搜寻之下发现Mark大神在《Running as Limited User – the Easy Way》中对该功能的实现做了相关的阐述: use the CreateRestrictedToken API to create a security context, called a token, that’s a stripped-down version of its own, removing administrative privileges and group membership. After generating a token that looks like one that Windows assigns to standard users Process Explorer calls CreateProcessAsUser to launch the target process with the new token. 使用 CreateRestrictedToken API来创建安全上下文,降低令牌(Token)的管理员权限和组成员资格,使其创建的令牌看起来像Windows赋予普通用户时一样,然后使用此令牌作为传入参数调用CreateProcessAsUser来创建新的子进程。 Process Explorer queries the privileges assigned to the Users group and strips out all other privileges, including powerful ones like SeDebugPrivilege, SeLoadDriverPrivilege and SeRestorePrivilege. 查询赋予用户组的特权并从当前进程权限中剔除这些权限比如SeDebugPrivilege、SeLoadDriverPrivilege和SeRestorePrivilege。 刚好最近有个项目需要实现降低进程权限的功能,在一翻折腾下就将其实现了,下面将谈谈实现的历程,如果纰漏之处,不吝指出。 2. 知识背书 在列出代码前需要了解一下一些实现原理,下面是一些相关的知识点,如果无耐心往下看,可以直接点击这里跳到代码实现处。 有资格拥有安全描述符的对象如文件、管道、进程、进程间同步对象等。所有已命名的Windows对象都是安全的,那些未被命名的对象比如线程或进程对象也可以拥有安全描述符。 对于大多数的安全对象,当创建该对象时可以指定它的安全描述符。当一个安全对象被创建时,系统会对其赋予一个安全描述符,安全描述符包含由其创建者指定的安全信息,或者缺省的安全信息(如果没有特意进行指定的话)。 更详细内容见:https://msdn.microsoft.com/en-us/library/windows/desktop/aa379557(v=vs.85).aspx 包含用于保护安全对象的安全信息。 安全描述符描述 对象的所有者(SIDs) 和 以下的访问控制列表: 一定长度用来表示托管的唯一值。 安全标识主要运用于如下几个方面: 包含登录用户的信息。用来描述一个进程或线程的安全上下文的对象,令牌的信息包含关联到进程或线程的账号的标识和特权。 当一个用户登录时,系统对用户的账号和密码进行认证,如果登录成功,系统则创建一个访问令牌,每个进程运行时都有一个访问令牌代表当前的用户,访问令牌中的安全描述符指明当前用户所属的账号和所属的组账号,令牌也包含一系列由用户或用户所在组进行维护的权限,在一个进程试图进行访问安全对象或执行系统管理员任务过程中需要权限时,系统通过这个令牌来确认关联的用户。 自由访问控制列表(DACL)包含若干个访问控制项(ACEs)。 约定的执行规则如下: 需要注意的是访问控制项的排序很重要。因为系统按照队列的方式读取访问控制项,直到访问被拒绝或允许。用户的访问拒绝ACE必须放在访问允许ACE的前头,否则当系统读到对组的访问允许ACE时,它会给当前限制的用户赋予访问的权限。系统在检测到请求访问被允许或拒绝后就不再往下检查。 你可以通过标识允许访问的ACE来控制对对象的访问,你无需显式地拒绝一个对象的访问。 当一个线程想要使用一个安全对象时,系统在线程执行前会进行访问审核,在访问审核中,系统将线程访问令牌中的安全信息与对象安全描述符中的安全信息进行比对。 访问令牌中包含的安全标识(SIDs)可以指明与线程关联的用户,系统查看线程访问令牌中用户或组的SID,同时检查对象的自由访问控制列表(DACL),自由访问控制列表(DACL)中包含存储有指明对指定的用户或组的访问权限是允许或拒绝信息的访问控制项(ACE),系统检查每个访问控制项(ACE)直至出现指明针对此线程(的用户或组的SID)的访问权限是允许还是拒绝的ACE,或者到最终都没有对应的ACEs可以检查。 (图片出处:https://msdn.microsoft.com/en-us/library/windows/desktop/aa378890(v=vs.85).aspx) 系统按照序列检查每个ACE,查询ACE中的托管与定义在线程中的托管(根据托管的SID)一致的ACE,直到如下的情况出现: 一个访问控制列表(ACL)可以有多个的访问控制项(ACE)针对令牌的(同一个)SIDs,这种情况下每个ACE授予的访问权限是可以进行累积叠加,比如,如果一个ACE对一个组允许读的访问权限,而另一个ACE对该组内的一个用户允许写的访问权限,那么该用户对于当前对象就拥有了读和写的访问权限。 (图片出处:https://msdn.microsoft.com/en-us/library/windows/desktop/aa446597(v=vs.85).aspx) 如上图所示,对于线程A,尽管在ACE@3中允许写权限,但因为在ACE@1中已经显式拒绝“Andrew”用户的访问权限,所以该安全对象对于线程A是不可访问的;对于线程B,在ACE@2中显式指明A组用户可以有写的权限,并且在ACE@3中允许任何用户读和执行的权限,所以线程B对这个安全对象拥有读、写以及执行的权限。 Windows完整性机制是对Windows安全架构的扩展,该完整性机制通过添加完整性等级到安全访问令牌和添加强制性标记访问控制项到安全描述符中的系统访问控制列表(SACL) 进程在安全访问令牌中定义完整性等级,IE在保护模式下的完整性等级为低,从开始菜单运行的应用程序的等级为中等,需要管理员权限并以管理员权限运行的应用程序的等级为高。 保护模式能够有效地减少IE进程附带的攻击行为如篡改和摧毁数据、安装恶意程序;相比其他程序,连接网络的程序更容易遭受网络的攻击因为它们更可能从未知源地址下载未受信任的内容,通过降低完整性等级或限制对其的允许权限,可以减少篡改系统或污染用户数据文件的风险。 在系统访问控制列表(SACL)中有一个称为强制标识的访问控制项(ACE),该控制项的安全描述符定义完整性等级或允许访问当前对象需要达到的等级,安全对象如果没有该控制项则默认拥有中等的完整性等级; 即使用户在自由访问控制列表(DACL)中已经明确授予相应的写权限,低等级的进程也不能获取比其高等级的安全对象的写权限,完整性等级检验在用户访问权限审查之前完成。 所有的文件和注册表键在缺省下的完整性等级为中,而由低等完整性进程创建的安全对象,系统会自动地赋予其低等完整性强制标志,同样,由低等完整性进程创建的子进程也是在低完整性等级下运行。 完整性访问等级(IL) 系统权限 安全标识 System System S-1-16-16384 High Administrative S-1-16-12288 可安装文件到Program Files文件夹、往敏感的注册表中如HKEY_LOCAL_MACHINE写数据 Medium User S-1-16-8192 创建和修改用户文档中的文件、往特定用户的注册表如HKEY_CURRENT_USER中写数据 Low Untrusted S-1-16-4096 仅能往低等级位置写数据如临时网络文件夹和注册表 HKEY_CURRENT_USER\Software\LowRegistry 低完整性进程可以往用户存档文件下写文件,通常为%USER PROFILE%\AppData\LocalLow,可以通过调用SHGetKnownFolderPath 函数并传入FOLDERID_LocalAppDataLow参数来获取完整的路径名称 SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, NULL, szPath, ARRAYSIZE(szPath)); 同样低完整性进程可以往指定的注册表下创建和修改子键,该注册表路径通常为HKEY_CURRENT_USER\Software\AppDataLow 3. 代码实现 实现思路 代码实现 下图是普通创建子进程效果(使用 Process Explorer 获取的进程信息,下同),可以看到: 通过执行上方的代码对新进程的创建令牌进行一系列的限制后,可以看到: 4. 扩展延伸 一个托管可以是用户账号、组账号或者登陆会话。是由访问控制项(ACE)赋予的,每个访问控制项(ACE)中都有一个安全标识(SID)用来表明特定的托管。 特权用于对一个对象或服务的访问控制,比自由访问控制更为严格,一个系统管理员通过使用特权控制那些用户可以操纵系统资源,一个应用程序在修改系统层级的资源需要使用到特权,比如修改系统时间和关闭系统。 更多内容请查看如下链接: Windows Integrity Mechanism Design Designing Applications to Run at a Low Integrity Level Understanding and Working in Protected Mode Internet Explorer Browsing the Web and Reading E-mail Safely as an Administrator(DropMyRight.exe的实现) Windows下如何创建低权限进程 标签:基本 main eof lock 参数 ups 针对 last 信任 原文地址:http://www.cnblogs.com/liaoguifa/p/lower-process-integrity.html
安全对象
安全描述符(security descriptor)
安全标识(Security Identifiers)
访问令牌
访问控制列表及其访问控制项
线程和安全对象间的交互
完整性级别
1 void CreateRestrictedProcess()
2 {
3 SECURITY_ATTRIBUTES sa;
4 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
5 sa.bInheritHandle = TRUE;
6 sa.lpSecurityDescriptor = NULL;
7
8 TCHAR szCmdLine[CMDLINE_SIZE] = {0};
9 HANDLE hToken = NULL;
10 HANDLE hNewToken = NULL;
11 HANDLE hNewExToken = NULL;
12
13 CHAR szIntegritySid[20] = "S-1-16-4096";
14 PSID pIntegritySid = NULL;
15 PSID pUserGroupSID = NULL;
16 PSID pAdminSID = NULL;
17
18 TOKEN_MANDATORY_LABEL tml = {0};
19
20 PROCESS_INFORMATION pi;
21 STARTUPINFO si;
22
23 BOOL bSuc = FALSE;
24 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
25
26 ZeroMemory(&si, sizeof(STARTUPINFO));
27 si.cb = sizeof(STARTUPINFO);
28 GetStartupInfo(&si);
29 DWORD fdwCreate = 0;
30
31 __try
32 {
33
34 if (!OpenProcessToken(GetCurrentProcess(),
35 //MAXIMUM_ALLOWED,
36 TOKEN_DUPLICATE |
37 TOKEN_ADJUST_DEFAULT |
38 TOKEN_QUERY |
39 TOKEN_ASSIGN_PRIMARY,
40 &hToken))
41 {
42 char szMsg[DEFAULT_MSG_SIZE] = {0};
43 Dbg("OpenProcessToken failed, GLE = %u.", GetLastError());
44 __leave;
45 }
46
47 Dbg("Using RestrictedTokens way !!!");
48 DWORD dwSize = 0;
49 DWORD dwTokenInfoLength = 0;
50 SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
51 SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
52 if(!AllocateAndInitializeSid(
53 &SIDAuthNT,
54 0x2,
55 SECURITY_BUILTIN_DOMAIN_RID/*0×20*/,
56 DOMAIN_ALIAS_RID_USERS,
57 0, 0, 0, 0, 0, 0,
58 &pUserGroupSID))
59 {
60 Dbg("AllocateAndInitializeSid for UserGroup Error %u", GetLastError());
61 __leave;
62 }
63
64 // Create a SID for the BUILTIN\Administrators group.
65 if(! AllocateAndInitializeSid( &SIDAuth, 2,
66 SECURITY_BUILTIN_DOMAIN_RID,
67 DOMAIN_ALIAS_RID_ADMINS,
68 0, 0, 0, 0, 0, 0,
69 &pAdminSID) )
70 {
71 Dbg("AllocateAndInitializeSid for AdminGroup Error %u", GetLastError());
72 __leave;
73 }
74
75 SID_AND_ATTRIBUTES SidToDisable[1] = {0};
76 SidToDisable[0].Sid = pAdminSID;
77 SidToDisable[0].Attributes = 0;
78
79 PTOKEN_PRIVILEGES pTokenPrivileges = NULL;
80 PTOKEN_PRIVILEGES pTokenPrivilegesToDel = NULL;
81 if(!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize))
82 {
83 if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
84 {
85 pTokenPrivileges = (PTOKEN_PRIVILEGES)LocalAlloc(0, dwSize);
86 pTokenPrivilegesToDel = (PTOKEN_PRIVILEGES)LocalAlloc(0, dwSize);
87 if(pTokenPrivileges != NULL && pTokenPrivilegesToDel != NULL)
88 {
89 if(!GetTokenInformation(hToken, TokenPrivileges, pTokenPrivileges, dwSize, &dwSize))
90 {
91 Dbg("GetTokenInformation about TokenPrivileges failed GTE = %u.", GetLastError());
92 __leave;
93 }
94 }
95 else
96 {
97 char szMsg[DEFAULT_MSG_SIZE] = {0};
98 Dbg("LocalAlloc for pTokenPrivileges failed GTE = %u.", GetLastError());
99 __leave;
100 }
101 }
102 }
103
104 LUID_AND_ATTRIBUTES *pTokenLUID = pTokenPrivileges->Privileges;
105 Dbg("CurrentToken‘s TokenPrivileges Count: %u", pTokenPrivileges->PrivilegeCount);
106 DWORD dwLuidCount = 0;
107 PLUID pPrivilegeLuid = NULL;
108 if(!CTWProcHelper::GetPrivilegeLUIDWithSID(pUserGroupSID, &pPrivilegeLuid, &dwLuidCount))
109 {
110 Dbg("GetPrivilegeLUIDWithSID failed GTE = %u.", GetLastError());
111 if(pPrivilegeLuid)
112 {
113 //HeapFree(GetProcessHeap(), 0, pPrivilegeLuid);
114 LocalFree(pPrivilegeLuid);
115 pPrivilegeLuid = NULL;
116 }
117 __leave;
118 }
119 Dbg("UserGroup‘s TokenPrivileges Count: %u", dwLuidCount);
120
121 DWORD dwDelPrivilegeCount = 0;
122 for(DWORD dwIdx=0; dwIdxPrivilegeCount); dwIdx++)
123 {
124 BOOL bFound = FALSE;
125 DWORD dwJdx = 0;
126 for(; dwJdx
其中 GetPrivilegeLUIDWithSID 函数的实现如下: 1 BOOL GetPrivilegeLUIDWithSID(PSID pSID, PLUID *pLUID, PDWORD pDwCount)
2 {
3 LSA_OBJECT_ATTRIBUTES ObjectAttributes;
4 NTSTATUS ntsResult;
5 LSA_HANDLE lsahPolicyHandle;
6
7 // Object attributes are reserved, so initialize to zeros.
8 ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
9
10 // Get a handle to the Policy object.
11 ntsResult = LsaOpenPolicy(
12 NULL, //Name of the target system.
13 &ObjectAttributes, //Object attributes.
14 POLICY_ALL_ACCESS, //Desired access permissions.
15 &lsahPolicyHandle //Receives the policy handle.
16 );
17
18 if (ntsResult != STATUS_SUCCESS)
19 {
20 printf("OpenPolicy failed returned %lu", LsaNtStatusToWinError(ntsResult));
21 return FALSE;
22 }
23
24 PLSA_UNICODE_STRING UserRights = NULL;
25 ULONG uRightCount;
26 ntsResult = LsaEnumerateAccountRights(lsahPolicyHandle, pSID, &UserRights, &uRightCount);
27 if (ntsResult != STATUS_SUCCESS)
28 {
29 printf("LsaEnumerateAccountRights failed returned %lu", LsaNtStatusToWinError(ntsResult));
30 LsaClose(lsahPolicyHandle);
31 return FALSE;
32 }
33
34 printf("LsaEnumerateAccountRights returned Right count: %lu", uRightCount);
35
36 (*pDwCount) = 0;
37 //pLUID = (PLUID)HeapAlloc(GetProcessHeap(), 0, uRightCount*sizeof(LUID));
38 (*pLUID) = (PLUID)LocalAlloc(LPTR, uRightCount*sizeof(LUID));
39 if((*pLUID) == NULL)
40 {
41 printf("HeapAlloc for PLUID failed returned %u", GetLastError());
42 LsaClose(lsahPolicyHandle);
43 return FALSE;
44 }
45
46 for(ULONG uIdx=0; UserRights != NULL && uIdx
托管
特权(Privilege)
上一篇:Windows下安装pip
下一篇:C#的扩展方法简介