windows的安全模型学习笔记
简介
windows的安全模型主要由令牌和安全描述符组成,令牌定义了当前进程的安全上下文,安全描述符则决定了对象的所有者和访问名单,符合条件的才能访问。
当线程访问一个安全对象时,默认使用进程的主令牌,也可以创建一个模拟令牌或受限命牌来进行权限检查。如果访问对象的安全描述符为空,则授于完全的访问权限,如果对象的dacl为空,则不授权,其它情况则查询acl中的ace,除非有明确的许可,否则一般是拒绝授权。可以用AccessCheck函数检查访问权限。
访问令牌
一个描述进程或者线程安全上下文的一个对象。
GetTokenInformation函数可以获取一个进程的令牌信息
_EPROCESS +0x358 Token : _EX_FAST_REF
Token组成
- 用户帐户的安全标识符(SID)
- 用户所属的组的SID
- 用于标识当前登录会话的登录SID
- 用户或用户组所拥有的权限列表
- 所有者SID
- 主要组的SID
- 访问控制列表
- 访问令牌的来源
- 令牌是主要令牌还是模拟令牌
- 限制SID的可选列表
- 目前的模拟等级
- 其他统计数据
nt!_TOKEN
+0x000 TokenSource : _TOKEN_SOURCE //令牌的来源(所属进程名和Source Identifier)
+0x010 TokenId : _LUID
+0x018 AuthenticationId : _LUID
+0x020 ParentTokenId : _LUID
+0x028 ExpirationTime : _LARGE_INTEGER
+0x030 TokenLock : Ptr64 _ERESOURCE //同步锁
+0x038 ModifiedId : _LUID
+0x040 Privileges : _SEP_TOKEN_PRIVILEGES //Privileges位图
+0x058 AuditPolicy : _SEP_AUDIT_POLICY //Specifies the per user audit policy for the token.
+0x078 SessionId : Uint4B //当前的登陆会话id
+0x07c UserAndGroupCount : Uint4B //UserAndGroup元素数量
+0x080 RestrictedSidCount : Uint4B // RestrictedSidCount元素数量
+0x084 VariableLength : Uint4B
+0x088 DynamicCharged : Uint4B
+0x08c DynamicAvailable : Uint4B
+0x090 DefaultOwnerIndex : Uint4B
+0x098 UserAndGroups : Ptr64 _SID_AND_ATTRIBUTES //代表着主体的普通用户身份和组身份,是不可或缺的成员;
+0x0a0 RestrictedSids : Ptr64 _SID_AND_ATTRIBUTES //可选成员,如果不为空,则代表着当前的令牌是受限令牌,受限令牌通过从普通令牌中过滤掉一些比较敏感的身份转化而来,
+0x0a8 PrimaryGroup : Ptr64 Void //用户所在的组的安全标识符
+0x0b0 DynamicPart : Ptr64 Uint4B
+0x0b8 DefaultDacl : Ptr64 _ACL //新创建对象的默认dacl
+0x0c0 TokenType : _TOKEN_TYPE //令牌类型(主令牌或模拟令牌)
+0x0c4 ImpersonationLevel : _SECURITY_IMPERSONATION_LEVEL //令牌的模拟级别
+0x0c8 TokenFlags : Uint4B
+0x0cc TokenInUse : UChar
+0x0d0 IntegrityLevelIndex : Uint4B //完整性级别,4为系统级别
+0x0d4 MandatoryPolicy : Uint4B //令牌的强制完整性访问策略
+0x0d8 LogonSession : Ptr64 _SEP_LOGON_SESSION_REFERENCES
+0x0e0 OriginatingLogonSession : _LUID
+0x0e8 SidHash : _SID_AND_ATTRIBUTES_HASH //SID哈希信息
+0x1f8 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH //RestrictedSid哈希信息
+0x308 pSecurityAttributes : Ptr64 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
+0x310 Package : Ptr64 Void
+0x318 Capabilities : Ptr64 _SID_AND_ATTRIBUTES //可选成员,仅用于AppContainer,其中的Sid代表着与App相关的身份,比如拥有连接网络、访问当前位置等权限的身份。
+0x320 CapabilityCount : Uint4B //Capability元素数量
+0x328 CapabilitiesHash : _SID_AND_ATTRIBUTES_HASH //Capabilities哈希信息
+0x438 LowboxNumberEntry : Ptr64 _SEP_LOWBOX_NUMBER_ENTRY
+0x440 LowboxHandlesEntry : Ptr64 _SEP_LOWBOX_HANDLES_ENTRY
+0x448 pClaimAttributes : Ptr64 _AUTHZBASEP_CLAIM_ATTRIBUTES_COLLECTION
+0x450 TrustLevelSid : Ptr64 Void //见下面
+0x458 TrustLinkedToken : Ptr64 _TOKEN //关联令牌
+0x460 IntegrityLevelSidValue : Ptr64 Void
+0x468 TokenSidValues : Ptr64 _SEP_SID_VALUES_BLOCK
+0x470 VariablePart : Uint8B
// SourceIdentifier: Specifies a locally unique identifier (LUID) provided by the source component named by the SourceName member. This value aids the source component in relating context blocks, such as session-control structures, to the token. This value is typically, but not necessarily, an LUID.
//MandatoryPolicy (令牌的强制完整性访问策略)
MEMBERS/Value Meaning
TOKEN_MANDATORY_POLICY_OFF(0x0) No mandatory integrity policy isenforced for the token.
TOKEN_MANDATORY_POLICY_NO_WRITE_UP(0x1) A process associated with the token cannot write to objects that have a greater mandatory integrity level.
TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN(0x2) A process created with the token has an integrity level that is the lesser of the parent-process integrity level and the executable-file integrity level.
TOKEN_MANDATORY_POLICY_VALID_MASK(0x3) A combination of TOKEN_MANDATORY_POLICY_NO_WRITE_UP and TOKEN_MANDATORY_POLICY_NEW_PROCESS_MIN.
//TrustLevelSid 字段
对于Signer为PsProtectedSignerWindows(5)和PsProtectedSignerTcb(6)的保护进程,其Type和Signer信息会被抽取出来,组装成一个Sid,代表着该进程的可信赖级别,保存到基本令牌中的TrustLevelSid成员中。
当一个令牌中的TrustLevelSid被使用时,需要保证与当前进程的Protection信息保持同步,这主要是为了应对令牌在不同进程间传递时(比如子进程继承父进程的令牌)导致的TrustLevelSid成员过时的情形。
//令牌的模拟级别
nt!_SECURITY_IMPERSONATION_LEVEL
SecurityAnonymous = 0n0
SecurityIdentification = 0n1
SecurityImpersonation = 0n2
SecurityDelegation = 0n3
SID
安全标识符(Security Identifiers,SID),是标识用户、组和计算机帐户的唯一的号码。
一个完整的SID包括:
• 用户和组的安全描述
• 48-bit的ID authority
• 修订版本
• 可变的验证值Variable sub-authority values
SID字符串
例:S-1-5-21-310440588-250036847-580389505-500
第一项S表示该字符串是SID;第二项是SID的版本号,对于2000来说,这个就是1;然后是标志符的颁发机构(identifier authority),对于2000内的帐户,颁发机构就是NT,值是5。然后表示一系列的子颁发机构,前面几项是标志域的,最后一个标志着域内的帐户和组。
Privilege
所谓特权,是指那些不与某一具体对象相关的、影响整个系统的访问权限。
//用户层
typedef struct _TOKEN_PRIVILEGES {
DWORD PrivilegeCount; 数组元素的个数
LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; 每个元素都描述了一个特权及属性
} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
typedef struct _LUID_AND_ATTRIBUTES {
LUID Luid; 特权
DWORD Attributes; 属性
} LUID_AND_ATTRIBUTES, * PLUID_AND_ATTRIBUTES;
/*
SE_PRIVILEGE_ENABLED The privilege is enabled.
SE_PRIVILEGE_ENABLED_BY_DEFAULT The privilege is enabled by default.
SE_PRIVILEGE_REMOVED Used to remove a privilege. For details, see AdjustTokenPrivileges.
SE_PRIVILEGE_USED_FOR_ACCESS The privilege was used to gain access to an object or service. This flag is used to identify the relevant privileges in a set passed by a client application that may contain unnecessary privileges.
*/
//locally unique identifier 和GUID的要求保证全局唯一不同,LUID只要保证局部唯一,指在系统的每一次运行期间保证是唯一的
typedef struct _LUID {
DWORD LowPart;
LONG HighPart;
} LUID, *PLUID;
//特权种类详见https://docs.microsoft.com/en-us/windows/win32/secauthz/privilege-constants
windbg查看system进程的token信息
0: kd> !process 0 1 system
PROCESS ffffe0007aab1040
SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 001ab000 ObjectTable: ffffc00051014000 HandleCount: <Data Not Accessible>
Image: System
VadRoot ffffe0007aac4360 Vads 6 Clone 0 Private 19. Modified 9191. Locked 0.
DeviceMap ffffc00051016620
Token ffffc00051016a60
ElapsedTime 00:13:13.814
UserTime 00:00:00.000
KernelTime 00:00:16.656
QuotaPoolUsage[PagedPool] 0
QuotaPoolUsage[NonPagedPool] 0
Working Set Sizes (now,min,max) (42, 50, 450) (168KB, 200KB, 1800KB)
PeakWorkingSetSize 335
VirtualSize 3 Mb
PeakVirtualSize 10 Mb
PageFaultCount 957
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 30
0: kd> !token ffffc00051016a60
_TOKEN 0xffffc00051016a60
TS Session ID: 0
User: S-1-5-18
User Groups:
00 S-1-5-32-544
Attributes - Default Enabled Owner
01 S-1-1-0
Attributes - Mandatory Default Enabled
02 S-1-5-11
Attributes - Mandatory Default Enabled
03 S-1-16-16384
Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-18
Privs:
02 0x000000002 SeCreateTokenPrivilege Attributes -
03 0x000000003 SeAssignPrimaryTokenPrivilege Attributes -
04 0x000000004 SeLockMemoryPrivilege Attributes - Enabled Default
05 0x000000005 SeIncreaseQuotaPrivilege Attributes -
07 0x000000007 SeTcbPrivilege Attributes - Enabled Default
08 0x000000008 SeSecurityPrivilege Attributes -
09 0x000000009 SeTakeOwnershipPrivilege Attributes -
10 0x00000000a SeLoadDriverPrivilege Attributes -
11 0x00000000b SeSystemProfilePrivilege Attributes - Enabled Default
12 0x00000000c SeSystemtimePrivilege Attributes -
13 0x00000000d SeProfileSingleProcessPrivilege Attributes - Enabled Default
14 0x00000000e SeIncreaseBasePriorityPrivilege Attributes - Enabled Default
15 0x00000000f SeCreatePagefilePrivilege Attributes - Enabled Default
16 0x000000010 SeCreatePermanentPrivilege Attributes - Enabled Default
17 0x000000011 SeBackupPrivilege Attributes -
18 0x000000012 SeRestorePrivilege Attributes -
19 0x000000013 SeShutdownPrivilege Attributes -
20 0x000000014 SeDebugPrivilege Attributes - Enabled Default
21 0x000000015 SeAuditPrivilege Attributes - Enabled Default
22 0x000000016 SeSystemEnvironmentPrivilege Attributes -
23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
25 0x000000019 SeUndockPrivilege Attributes -
28 0x00000001c SeManageVolumePrivilege Attributes -
29 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default
30 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default
31 0x00000001f SeTrustedCredManAccessPrivilege Attributes -
32 0x000000020 SeRelabelPrivilege Attributes -
33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes - Enabled Default
34 0x000000022 SeTimeZonePrivilege Attributes - Enabled Default
35 0x000000023 SeCreateSymbolicLinkPrivilege Attributes - Enabled Default
Authentication ID: (0,3e7)
Impersonation Level: Anonymous
TokenType: Primary
Source: *SYSTEM* TokenFlags: 0x2000 ( Token in use )
Token ID: 3eb ParentToken ID: 0
Modified ID: (0, 3ec)
RestrictedSidCount: 0 RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 0
PackageSid: (null)
CapabilityCount: 0 Capabilities: 0x0000000000000000
LowboxNumberEntry: 0x0000000000000000
Security Attributes:
Invalid AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION with no claims
Process Token TrustLevelSid: S-1-19-1024-8192
安全描述符
包含和被保护对象相关联的安全信息的数据结构。安全描述符包括谁拥有对象,以何种方式访问以及何种审查访问类型等信息。
可以用GetsecurityInfo函数取得一个对象的安全信息
_OBJECT_HEADER +0x028 SecurityDescriptor : Ptr64 Void
SECURITY DESCRIPTOR组成
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength; 结构体长度
LPVOID lpSecurityDescriptor; 安全描述符指针
BOOL bInheritHandle; 是否被子进程继承
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
typedef struct _SECURITY_DESCRIPTOR {
BYTE Revision; 修订版本
BYTE Sbz1;
SECURITY_DESCRIPTOR_CONTROL Control; 控制位
PSID Owner; 所有者sid
PSID Group; 所属用户组sid
PACL Sacl; 系统访问控制链表( SACL )
PACL Dacl; 目录访问控制链表( DACL )
} SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;
目录访问控制链表
一个目录访问控制链表( DACL )标示允许或拒绝访问一个安全对象的托管。当一个进程尝试访问一个安全对象的时候,系统检查对象的 DACL 中的ACE(注:DACL就是ACE链表,一种数据结构,每一个节点存放着访问权限等信息) 来决定是否赋予访问权限。如果对象没有 DACL ,系统赋予完全的访问权限,如果对象的 DACL 没有 ACE,那么系统拒绝所有访问对象的尝试。
//详见sdk中winnt.h头文件的10782行
typedef struct _ACL {
BYTE AclRevision; acl修订版本
BYTE Sbz1;
WORD AclSize;
WORD AceCount;
WORD Sbz2;
} ACL;
typedef ACL *PACL;
typedef struct _ACE_HEADER {
BYTE AceType; //ACE类型
BYTE AceFlags;
WORD AceSize;
} ACE_HEADER;
typedef struct _ACCESS_ALLOWED_ACE {
ACE_HEADER Header; //ACE信息
ACCESS_MASK Mask; //访问掩码
DWORD SidStart; //SID的第一个DWORD。SID 的剩余字节存储在SidStart成员之后的连续内存中
} ACCESS_ALLOWED_ACE;
typedef struct _ACCESS_DENIED_ACE {
ACE_HEADER Header;
ACCESS_MASK Mask;
DWORD SidStart;
} ACCESS_DENIED_ACE;
SDDL (Security Descriptor Definition Language)
安全描述符字符串格式 这个字符串是存储安全描述符(那个结构体)所需要记录的文本格式.
SDDL结构
O:owner_sid
G:group_sid
D:dacl_flags(string_ace1)(string_ace2)… (string_acen)
S:sacl_flags(string_ace1)(string_ace2)… (string_acen)
可以用ConvertSecurityDescriptorToStringSecurityDescriptor 和ConvertStringSecurityDescriptorToSecurityDescriptor 在两种格式问相互转化
详见: https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-strings
windbg查看一个内核对象的安全描述符信息
0: kd> dt _OBJECT_HEADER SecurityDescriptor ffffe0007aab1040-0x30
nt!_OBJECT_HEADER +0x028 SecurityDescriptor : 0xffffc000`5101651f Void
0: kd> !sd 0xffffc000`51016510
->Revision: 0x1
->Sbz1 : 0x0
->Control : 0x8814
SE_DACL_PRESENT
SE_SACL_PRESENT
SE_SACL_AUTO_INHERITED
SE_SELF_RELATIVE
->Owner : S-1-5-32-544
->Group : S-1-5-18
->Dacl :
->Dacl : ->AclRevision: 0x2
->Dacl : ->Sbz1 : 0x0
->Dacl : ->AclSize : 0x3c
->Dacl : ->AceCount : 0x2
->Dacl : ->Sbz2 : 0x0
->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[0]: ->AceFlags: 0x0
->Dacl : ->Ace[0]: ->AceSize: 0x14
->Dacl : ->Ace[0]: ->Mask : 0x001fffff
->Dacl : ->Ace[0]: ->SID: S-1-5-18
->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[1]: ->AceFlags: 0x0
->Dacl : ->Ace[1]: ->AceSize: 0x18
->Dacl : ->Ace[1]: ->Mask : 0x00121411
->Dacl : ->Ace[1]: ->SID: S-1-5-32-544
->Sacl :
->Sacl : ->AclRevision: 0x2
->Sacl : ->Sbz1 : 0x0
->Sacl : ->AclSize : 0x1c
->Sacl : ->AceCount : 0x1
->Sacl : ->Sbz2 : 0x0
->Sacl : ->Ace[0]: ->AceType: SYSTEM_MANDATORY_LABEL_ACE_TYPE
->Sacl : ->Ace[0]: ->AceFlags: 0x0
->Sacl : ->Ace[0]: ->AceSize: 0x14
->Sacl : ->Ace[0]: ->Mask : 0x00000003
->Sacl : ->Ace[0]: ->SID: S-1-16-16384
保护进程
保护进程(Protected Process)是Windows操作系统为了保护某些关键进程,防止其被普通进程调试、注入、甚至是读取内存信息而建立起来的一种安全机制。保护进程与其他普通进程的区别在于,保护进程的Protection成员不为0。
_EPROCESS +0x6aa Protection : _PS_PROTECTION
nt!_PS_PROTECTION
+0x000 Level : UChar
+0x000 Type : Pos 0, 3 Bits //Protected Process(PP),Protected Process Lite(PPL)
+0x000 Audit : Pos 3, 1 Bit
+0x000 Signer : Pos 4, 4 Bits //进程的签名发布者的类别
完整性级别(Integrity Level)
从 Windows Vista 开始,进程在创建的时候,可以得到一个访问令牌(Access Token),这个令牌有四个完整性级别:
- System(系统)
- High(高)
- Medium(中)
- Low(低)
除了标准的访问控制检查,Windows还会检查IL的安全等级标识与目标资源的IL等级一致。例如,一个中等IL进程也许会被"向上"禁止访问、修改和执行一个高等级IL的对象。
打印线程所用户帐户名和特权情况
#include<Windows.h>
#include<tchar.h>
#include<locale.h>
#define ERROR_CHECK(expr) {if(expr) return PrintWinError(GetLastError(),__LINE__);}
const WCHAR* ATTRIBUTE_NAME[] = {
L"SE_PRIVILEGE_ENABLED_BY_DEFAULT",
L"SE_PRIVILEGE_ENABLED",
L"SE_PRIVILEGE_REMOVED",
L"SE_PRIVILEGE_USED_FOR_ACCESS"
};
//输出错误信息
BOOL PrintWinError(DWORD dwError, DWORD dwLine)
{
static char* useless = setlocale(LC_ALL, "zh-CN");
LPWSTR lpErrorString = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&lpErrorString, 0, NULL);
_tprintf_s(L"ErrorCode:%d \nErrorString:%ws \nLine: %d", dwError, lpErrorString, dwLine);
LocalFree((HLOCAL)lpErrorString);
return lpErrorString != NULL;
}
//打印进程所用户帐户名
BOOL PrintProcessAccountName(HANDLE hProcess)
{
HANDLE hProcessToken = NULL;
BOOL bResult = FALSE;
DWORD dwWriten = 0;
bResult = OpenProcessToken(hProcess, TOKEN_QUERY, &hProcessToken);
ERROR_CHECK(bResult == FALSE);
bResult = GetTokenInformation(hProcessToken, TokenUser, NULL, 0, &dwWriten);
TOKEN_USER* lpTokenUser = (TOKEN_USER*)LocalAlloc(LMEM_ZEROINIT | LMEM_FIXED, dwWriten);
ERROR_CHECK(lpTokenUser == NULL);
bResult = GetTokenInformation(hProcessToken, TokenUser, lpTokenUser, dwWriten, &dwWriten);
ERROR_CHECK(bResult == FALSE);
WCHAR lpDomainName[256], lpAccountName[256];
SID_NAME_USE accountType;
bResult = LookupAccountSid(NULL, lpTokenUser->User.Sid,
lpAccountName, &dwWriten,
lpDomainName, &dwWriten, &accountType);
ERROR_CHECK(bResult == FALSE);
_tprintf_s(L"AccountName: %ws \nDomainName: %ws \n",
lpAccountName, lpDomainName);
LocalFree(lpTokenUser);
return bResult == ERROR_SUCCESS;
}
//打印进程所用户特权情况
BOOL PrintProcessPrivileges(HANDLE hProcess)
{
HANDLE hProcessToken = NULL;
BOOL bResult = FALSE;
DWORD dwWriten = 0;
TOKEN_PRIVILEGES* lpPrivileges = NULL;
WCHAR wszPrivilegeName[256] = { 0 };
int index = 0;
bResult = OpenProcessToken(hProcess, TOKEN_QUERY, &hProcessToken);
ERROR_CHECK(bResult == FALSE);
bResult = GetTokenInformation(hProcessToken, TokenPrivileges, NULL, 0, &dwWriten);
lpPrivileges = (TOKEN_PRIVILEGES*)LocalAlloc(LMEM_ZEROINIT | LMEM_FIXED, dwWriten);
ERROR_CHECK(lpPrivileges == NULL);
bResult = GetTokenInformation(hProcessToken, TokenPrivileges, lpPrivileges, dwWriten, &dwWriten);
ERROR_CHECK(bResult == FALSE);
for (int i = lpPrivileges->PrivilegeCount - 1; i > 0; --i)
{
dwWriten = 256;
ZeroMemory(wszPrivilegeName, 256 * sizeof(WCHAR));
bResult = LookupPrivilegeName(NULL, &(lpPrivileges->Privileges[i].Luid), wszPrivilegeName, &dwWriten);
ERROR_CHECK(bResult == FALSE);
index = lpPrivileges->Privileges[i].Attributes != 0x80000000L ? lpPrivileges->Privileges[i].Attributes / 2 : 3;
_tprintf(L"privilrgeName: %ws attribute: %ws\n", wszPrivilegeName, ATTRIBUTE_NAME[index]);
}
LocalFree(lpPrivileges);
return bResult == ERROR_SUCCESS;
}
参考资料
- http://blog.nsfocus.net/analysis-windows-access-authority-inspection-mechanism/
- https://docs.microsoft.com/en-us/windows/win32/security