🛫 导读
需求
授权是授予个人使用系统及其上存储的数据的权利。 授权通常由系统管理员设置,并由计算机根据某种形式的用户标识(如代码号或密码)进行验证。
今天我们将对windows授权相关知识进行简单的介绍,通过C++对其进行控制。
开发环境
版本号 | 描述 | |
---|---|---|
文章日期 | 2023-05-30 | |
操作系统 | Win11 - 21H2 - 22000.1335 | |
1️⃣ 常见名词及缩写汇总
以下是常见的 Windows 权限关键名词及它们之间的关系:
主体
主体
可以是用户、组或应用程序。
用户账户
: Windows 中,每个用户都必须有一个账户才能登录系统。每个帐户都有一个唯一的名称和密码来标识该用户。
组
:组是一组用户账户的集合,它们通常具有相似的访问需求。将用户分配到组中,可以更方便地为它们分配权限和访问控制。
权限
:权限是授予特定用户或组进行某些操作或访问某些资源的权利。在 Windows 中,权限可以授予给文件、文件夹、注册表项和其他资源。
访问令牌(Access tokens)
:描述进程或线程的安全上下文的对象。令牌中的信息包括与进程或线程关联的用户帐户的标识和特权信息。
安全标识符(SID)
:安全标识符是一种唯一标识符,用于表示用户、组和计算机对象。SID 也用于授权和访问控制。
对象
对象
可以是文件、文件夹、注册表项等资源。
安全描述符(SD)
:安全描述符是一个数据结构,用于描述对象的安全性。SD 通常包含对象的安全标识符、ACL 和其他安全相关信息。安全对象(SO)
权限(规则)
权限
则代表对这些资源的操作,例如读取、写入、删除等。
访问控制列表(ACL)
:ACL 是一组权限,用于定义谁可以访问资源以及他们可以执行哪些操作。ACL 通常与文件、文件夹、注册表项和其他资源相关联。
访问控制项(ACE)
:ACL 是一组权限,用于定义谁可以访问资源以及他们可以执行哪些操作。ACL 通常与文件、文件夹、注册表项和其他资源相关联。
访问控制项(AES)
:ACE复数形式。
访问控制模型(Access Control Model)
用户帐户控制(UAC)
:通过此功能,用户可以以非管理程序、称为标准用户和管理员身份执行常见任务,而无需切换用户、注销或使用 运行方式。
2️⃣ 常见概念
访问控制及安全级别
Windows访问控制模型(Windows Access Control Model)是Windows操作系统中实现访问控制的一种机制。它基于
对象
、主体
和权限
的概念来管理资源的访问权限。
主体
可以是用户、组或应用程序。对象
可以是文件、文件夹、注册表项等资源。权限
则代表对这些资源的操作,例如读取、写入、删除等。
Windows访问控制模型采用一种称为
访问控制列表(Access Control List,ACL)
的数据结构来存储对象的权限信息。ACL包含了一组访问控制条目(Access Control Entry,ACE)
,每个ACE描述了一种访问权限以及被授予该权限的主体。
当主体试图访问一个对象时,Windows会检查该主体的身份以及该主体被授予的权限。如果主体拥有足够的权限,它则可以访问该对象,否则就会被拒绝访问。
总体来说,Windows访问控制模型提供了一个可扩展、灵活、精细的访问控制机制,可以满足不同场景下对资源的安全性需求。
- 更多关于访问控制的内容,可以参考视频《计算机安全-第4章 访问控制 https://www.bilibili.com/video/BV1wy4y1Y7fo/》。
安全级别被定义为ABCD四个级别,如下图:
WindowsNT系列属于C2 级,满足下面要求:
- 必须通过授予或拒绝对单个用户或命名用户组的访问权限来控制对资源的访问。
- 内存必须受到保护,以便在 进程 释放内存后无法读取其内容。 同样,安全文件系统(如 NTFS)必须保护已删除的文件不被读取。
- 用户登录时必须以唯一的方式(例如按密码)标识自己。 所有可审核操作都必须标识执行该操作的用户。
- 系统管理员必须能够审核与安全相关的事件。 但是,访问安全相关事件审核数据必须仅限于授权管理员。
- 必须保护系统免受外部干扰或篡改,例如修改正在运行的系统或存储在磁盘上的系统文件。
访问令牌(Access tokens)
访问令牌是描述进程或线程的
安全上下文
的对象。
令牌中的信息包括与进程
或线程
关联的用户帐户的标识和特权信息。
当用户登录时,系统通过将用户密码与安全数据库(如域认证中的NTDS或本地认证中的SAM文件)中存储的信息进行比较来验证用户密码。
如果密码经过验证,则系统将生成访问令牌。代表该用户执行的每个进程都有此访问令牌的副本
。(通常我们在输入密码登陆进入Windows界面时就是一个生成访问令牌的过程)
当线程与安全对象进行交互或尝试执行需要特权的系统任务时,系统使用访问令牌来标识用户。
访问令牌包含以下信息:
Windows下有两种类型的Token令牌:授权令牌和模拟令牌。
授权令牌(Delegation Token)
也叫主令牌,是由windows内核创建并分配给进程的默认访问令牌,每一个进程有一个主令牌,它描述了与当前进程相关的用户帐户的安全上下文。用于交互式登录(如rdp登录访问)。
模拟令牌(Impersonation Token)
所在进程的主令牌会自动附加到当前的线程上,作为线程的安全上下文。而线程可以运行在另一个非主令牌的访问令牌下执行,而这个令牌被称为模拟令牌。用于非交互式的会话(利用WMI进行远程访问)。
特权 (Privilege) - 线程相关的
特权是帐户(如用户或组帐户)在本地计算机上执行各种
系统相关操作的权限
,例如关闭系统、加载设备驱动程序或更改系统时间。
系统有一个帐户数据库,存储了用户或用户群的特权。当用户登录后,系统产生一个访问令牌(access token)包含了用户的特权清单,这包含用户所在群的特权。注意特权仅限于本地计算机,域帐户在不同计算机上有不同特权。
当用户试图执行一个特权操作,系统检查用户的访问令牌以确定使用是否具有必要的特权。调用GetTokenInformation函数可以检查特权。
另外,还有令牌中的权限列表,权限与对象访问无关,所以与安全描述符,SID等无关。
当线程
执行一些管理相关的操作时,系统会检查该线程的令牌中是否含有特定的权限,如果有,则允许线程执行该操作,否则拒绝。
安全描述符(Security Descriptors,SD)
安全描述符
包含
与安全对象关联的安全信息。 安全描述符由 SECURITY_DESCRIPTOR 结构和关联的安全信息组成。 安全描述符可以包含以下安全信息:
- 对象
所有者
和主组
的安全标识符 (SID) 。DACL
:指定允许或拒绝特定用户或组的访问权限的。SACL
:指定为对象生成审核记录的访问尝试的类型。一组控制位
:用于限定安全描述符或其单个成员的含义。
访问控制列表(Access Control List,ACL)
ACL 中的每个 ACE 标识一个受信者,并指定该受信者访问规则。
安全对象的安全描述符可以包含两种类型的 ACL:DACL 和 SACL。
DACL
:自主访问控制列表(DACL),是安全描述符中最重要的,它里面包含零个或多个访问控制项(ACE,Access Control Entry),每个访问控制项的内容描述了允许或拒绝特定账户对这个对象执行特定操作。
SACL
:系统访问控制列表(SACL), 主要是用于系统审计的,它的内容指定了当特定账户对这个对象执行特定操作时,记录到系统日志中。
授权参数为空 (NULL DACL && 空 DACL)
如果属于对象安全描述符 (DACL) 自由访问控制列表设置为 NULL, 则创建 null DACL。
NULL DACL 向请求它的用户授予完全访问权限;不会对 对象执行正常的安全检查。
空 DACL 是正确分配和初始化的 DACL,它不包含AES (访问控制) 。 空 DACL 不授予对它所分配到的对象的访问权限。
null DACL 不应与空的 DACL 混淆。
DACL|SACL 列表显示顺序
1、拒绝(deny)有优先排在允许(allow)前面
2、继承的ACE会排在程序或者手动的添加ACE顺序之后;
访问掩码
所有安全对象都使用下图所示的访问掩码格式来安排其访问权限。
文件安全和访问权限
由于文件是
安全对象
,因此对它们的访问由访问控制模型
控制,该控制模型对 Windows 中所有其他安全对象的访问。
- 文件或目录的默认安全描述符中的访问控制列表 (ACL)
继承自其父目录
。- 默认安全描述符仅在新创建文件或目录时分配,而不是在重命名或移动文件或目录时分配。
FileSystemRights映射到访问掩码上
参考资料:
- https://learn.microsoft.com/en-us/windows/win32/fileio/file-security-and-access-rights
AccessCheck 的工作原理
当一个线程想访问一个安全对象时候,系统
要么允许访问,要拒绝访问
。
系统现成序列中的每个ACE,直到下面的情况发生:
- 拒绝访问的 ACE
显式拒绝
对线程的访问令牌中列出的受信者之一的任何请求访问权限。- 线程访问令牌中列出的受信者允许访问的一个或多个 AES
显式授予
所有请求的访问权限。- 已检查所有 AES,并且仍有至少一个请求的访问权限未显式允许,在这种情况下,将
隐式拒绝
访问。
示例:
下图描述一个DACL对象允许访问一个线程而拒绝访问另一个线程。
- 对于线程A,系统读ACE1并且立即拒绝访问,因为访问拒绝ACE应用到线程访问标记中的用户。这种情况下,系统并没有检查ACE2和3。
- 对于线程B,ACE1没有应用,因此系统处理ACE2,其允许写操作,并且ACE3允许读和执行操作。
因为系统在请求访问并显式允许或拒绝的时候停止检查,DACL中的ACE顺序非常重要。注意,如果ACE顺序与例子中的不同,系统可能分配访问权限给A。对于系统对象,操作系统定义一个首选的DACL中ACE的顺序。
附赠一张Window权限控制如何运行的流程图:
安全描述符定义语言(SDDL)
安全描述符定义语言(SDDL)定义了ConvertSecurityDescriptorToStringSecurityDescriptor和ConvertStringSecurityDescriptorToSecurityDescriptor函数用来将安全描述符描述为文本字符串的字符串格式。该语言还定义了字符串元素,用于描述安全描述符的组成部分中的信息。
AccessChk工具可以直接展示出指定对象的权限信息,并且能从SDDL到已解析的权限全面覆盖。
3️⃣ 常见工具及用法
whoami
获取 当前登录的用户的访问令牌信息:
whoami /all
runas
- 创建一个名为foo的账号
- 执行
runas /user:foo cmd
,并输入密码- 新的cmd窗口标题变了,而且执行
whoam /user
显示信息为账号foo
icacls
- 查看指定文件的ACL
icacls C:\Windows\SYSVOL\sysvol\test.com
2、备份指定文件(包括当前目录及其子目录中的文件)的ACL
icacls C:\Windows\SYSVOL\sysvol\test.com /save AclFile /t
3、还原指定文件(包括当前目录及其子目录中的文件)的ACL
icacls C:\Windows\SYSVOL\sysvol\ /restore AclFile /t
注:还原时,路径需要设置为上级目录。
4、添加用户test1对指定文件(包括当前目录及其子目录中的文件)的完全访问权限
icacls C:\Windows\SYSVOL\sysvol\test.com /grant test1:(OI)(CI)(F) /t
注:(OI)代表对象继承 (CI)代表容器继承 (F)代表完全访问。
5、移除用户test1对指定文件(包括当前目录及其子目录中的文件)的完全访问权限
icacls C:\Windows\SYSVOL\sysvol\test.com /remove test1 /t
Repacls
开源的文件安全工具,代码写的挺好的,效率很高。
- github地址:https://github.com/NoMoreFood/Repacls
- 文档: https://nomorefood.github.io/Repacls/
- Replace all instances of DOM\jack to DOM\jill in C:\test:
repacls.exe /Path C:\Test /ReplaceAccount "DOM\jack:DOM\jill"
- Migrate all permissions for all accounts with matching
names in DOMA with DOMB:
repacls.exe /Path C:\Test /MoveDomain DOMA:DOMB
- Update old SID references, remove any explicit permissions that are already
granted by inherited permissions, and compact all ACLs if not compacted:
repacls.exe /Path C:\Test /UpdateHistoricalSids /RemoveRedundant /Compact
4️⃣ C++实战
修改对象的ACL(访问控制列表)
以下示例将 访问控制项 (ACE) 添加到对象的 任意访问控制列表 (DACL) 。主要流程为:
GetNamedSecurityInfo
:获取DACL(pOldDACL)EXPLICIT_ACCESS
:创建并初始化一个ACE(ea)SetEntriesInAcl
:将ea和pOldDACL,合并成新的DACL(pNewDACL)SetNamedSecurityInfo
:将pNewDACL设置到对象上
#include <windows.h>
#include <stdio.h>
DWORD AddAceToObjectsSecurityDescriptor (
LPTSTR pszObjName, // name of object
SE_OBJECT_TYPE ObjectType, // type of object
LPTSTR pszTrustee, // trustee for new ACE
TRUSTEE_FORM TrusteeForm, // format of trustee structure
DWORD dwAccessRights, // access mask for new ACE
ACCESS_MODE AccessMode, // type of ACE
DWORD dwInheritance // inheritance flags for new ACE
)
{
DWORD dwRes = 0;
PACL pOldDACL = NULL, pNewDACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
EXPLICIT_ACCESS ea;
if (NULL == pszObjName)
return ERROR_INVALID_PARAMETER;
// Get a pointer to the existing DACL.
dwRes = GetNamedSecurityInfo(pszObjName, ObjectType,
DACL_SECURITY_INFORMATION,
NULL, NULL, &pOldDACL, NULL, &pSD);
if (ERROR_SUCCESS != dwRes) {
printf( "GetNamedSecurityInfo Error %u\n", dwRes );
goto Cleanup;
}
// Initialize an EXPLICIT_ACCESS structure for the new ACE.
ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
ea.grfAccessPermissions = dwAccessRights;
ea.grfAccessMode = AccessMode;
ea.grfInheritance= dwInheritance;
ea.Trustee.TrusteeForm = TrusteeForm;
ea.Trustee.ptstrName = pszTrustee;
// Create a new ACL that merges the new ACE
// into the existing DACL.
dwRes = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL);
if (ERROR_SUCCESS != dwRes) {
printf( "SetEntriesInAcl Error %u\n", dwRes );
goto Cleanup;
}
// Attach the new ACL as the object's DACL.
dwRes = SetNamedSecurityInfo(pszObjName, ObjectType,
DACL_SECURITY_INFORMATION,
NULL, NULL, pNewDACL, NULL);
if (ERROR_SUCCESS != dwRes) {
printf( "SetNamedSecurityInfo Error %u\n", dwRes );
goto Cleanup;
}
Cleanup:
if(pSD != NULL)
LocalFree((HLOCAL) pSD);
if(pNewDACL != NULL)
LocalFree((HLOCAL) pNewDACL);
return dwRes;
}
为新对象创建安全描述符
以下示例使用以下过程为新注册表项创建 安全描述符 。 类似的代码可用于为其他对象类型创建安全描述符。主要流程为:
GetNamedSecurityInfo
:获取DACL(pOldDACL)AllocateAndInitializeSid
:创建两个sid(Everyone && Administrators group)EXPLICIT_ACCESS
:创建并初始化两个个ACE(ea)SetEntriesInAcl
:根据ea创建DACL(pACL)InitializeSecurityDescriptor
、SetSecurityDescriptorDacl
:根据pACL创建并初始化SD(pSD)SECURITY_ATTRIBUTES
:根据pSD创建安全属性(sa)RegCreateKeyEx
:根据sa调用注册表创建接口,这样得到的注册表就是我们指定的权限的注册表项了。
效果图:
#pragma comment(lib, "advapi32.lib")
#include <windows.h>
#include <stdio.h>
#include <aclapi.h>
#include <tchar.h>
void main()
{
DWORD dwRes, dwDisposition;
PSID pEveryoneSID = NULL, pAdminSID = NULL;
PACL pACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
EXPLICIT_ACCESS ea[2];
SID_IDENTIFIER_AUTHORITY SIDAuthWorld =
SECURITY_WORLD_SID_AUTHORITY;
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
SECURITY_ATTRIBUTES sa;
LONG lRes;
HKEY hkSub = NULL;
// Create a well-known SID for the Everyone group.
if(!AllocateAndInitializeSid(&SIDAuthWorld, 1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&pEveryoneSID))
{
_tprintf(_T("AllocateAndInitializeSid Error %u\n"), GetLastError());
goto Cleanup;
}
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow Everyone read access to the key.
ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESS));
ea[0].grfAccessPermissions = KEY_READ;
ea[0].grfAccessMode = SET_ACCESS;
ea[0].grfInheritance= NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName = (LPTSTR) pEveryoneSID;
// Create a SID for the BUILTIN\Administrators group.
if(! AllocateAndInitializeSid(&SIDAuthNT, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pAdminSID))
{
_tprintf(_T("AllocateAndInitializeSid Error %u\n"), GetLastError());
goto Cleanup;
}
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow the Administrators group full access to
// the key.
ea[1].grfAccessPermissions = KEY_ALL_ACCESS;
ea[1].grfAccessMode = SET_ACCESS;
ea[1].grfInheritance= NO_INHERITANCE;
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea[1].Trustee.ptstrName = (LPTSTR) pAdminSID;
// Create a new ACL that contains the new ACEs.
dwRes = SetEntriesInAcl(2, ea, NULL, &pACL);
if (ERROR_SUCCESS != dwRes)
{
_tprintf(_T("SetEntriesInAcl Error %u\n"), GetLastError());
goto Cleanup;
}
// Initialize a security descriptor.
pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (NULL == pSD)
{
_tprintf(_T("LocalAlloc Error %u\n"), GetLastError());
goto Cleanup;
}
if (!InitializeSecurityDescriptor(pSD,
SECURITY_DESCRIPTOR_REVISION))
{
_tprintf(_T("InitializeSecurityDescriptor Error %u\n"),
GetLastError());
goto Cleanup;
}
// Add the ACL to the security descriptor.
if (!SetSecurityDescriptorDacl(pSD,
TRUE, // bDaclPresent flag
pACL,
FALSE)) // not a default DACL
{
_tprintf(_T("SetSecurityDescriptorDacl Error %u\n"),
GetLastError());
goto Cleanup;
}
// Initialize a security attributes structure.
sa.nLength = sizeof (SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
// Use the security attributes to set the security descriptor
// when you create a key.
lRes = RegCreateKeyEx(HKEY_CURRENT_USER, _T("mykey"), 0, _T(""), 0,
KEY_READ | KEY_WRITE, &sa, &hkSub, &dwDisposition);
_tprintf(_T("RegCreateKeyEx result %u\n"), lRes );
Cleanup:
if (pEveryoneSID)
FreeSid(pEveryoneSID);
if (pAdminSID)
FreeSid(pAdminSID);
if (pACL)
LocalFree(pACL);
if (pSD)
LocalFree(pSD);
if (hkSub)
RegCloseKey(hkSub);
return;
}
🛬 文章小结
以上只是对Windows权限的简单总结,其内容无比庞杂,资料又相对匮乏。
小编花了一周时间,反复看《参考资料》中的各个资料,尤其是微软官网资料
,看的多了,略有体会,故记录下来,为以后学习做个阶段性的总结。文章来源:https://www.toymoban.com/news/detail-474745.html
📖 参考资料
- 微软官网资料:授权 https://learn.microsoft.com/zh-cn/windows/win32/secauthz/authorization-portal
- Windows访问控制系列文章 https://github.com/rootclay/Windows-Access-Control.git
- 【windows安全性 之访问控制】 访问控制 详细解说 https://www.cnblogs.com/cdaniu/p/15582260.html
- 【windows 访问控制】系列文章 https://www.cnblogs.com/cdaniu/p/15630322.html
- 第七章:访问控制技术原理和应用 https://blog.csdn.net/xjc2998310890/article/details/112809763
- 渗透技巧——Windows下的Access Control List https://zhuanlan.zhihu.com/p/54426112
ps: 文章中内容仅用于技术交流,请勿用于违规违法行为。文章来源地址https://www.toymoban.com/news/detail-474745.html
到了这里,关于【Windows安全】授权初探:访问控制基础及应用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!