-
SRM(Security reference monitor 安全引用监视器)
Ntoskrnl.exe中的一个组件,工作在内核模式下,它负责:定义访问令牌的数据结构来表示一个安全环境、执行对象的访问检查、管理特权,以及生成所有的结果安全审计消息。
-
LSASS(Local Security Authority Subsystem本地安全权威子系统)
工作在用户模式下,它负责:本地系统安全策略(比如允许哪些用户登录到本地机器上、密码策略、授予用户和用户组特权,以及系统安全审计设置等)、用户认证、以及发送安全审计信息到Event Log中。它在启动后,会与SRM建立私有通信端口。
Windows使用SID来标识系统中执行各种动作的实体(安全主体),用户有SID、本地用户组、域用户组、本地计算机、域、域成员和服务都有SID。
例如,windows用唯一的SID标识账户及账户组,此后该账户或账户组所做的操作(比如访问某个文件夹),windows都要去验证其身份,确认它是否有权限操作。本地帐户或组的SID 由计算机上的本地安全机构(LSA)生成,并与其他帐户信息一起存储在注册表安全区域中;域帐户或组的SID由域安全机构
(域控上的LSA)生成,并以用户或组对象的属性的形式存储在 Active Directory中。
常见SID的格式如下(RID不一定都有):
(1) procexp查看SID:
(2) 注册表查看本地service account SID:
(3) 注册表查看本地user account RID:
一般RID 500是Administrator, 501是Guest, 503是DefalutAccount,自定义用户账户和组的RID是从1000开始。
此外,Windows会预定义一些account和group,如NT AUTHORITY\Local Service S-1-5-19,在这里可以查看常见SID:
同一用户,打开chrome.exe下载未知程序和打开word.exe,这两个操作危险性不一样,显然进程权限也应不一样;为此,windows使用强制完整性控制(MIC) 来控制同一用户下的不同进程对安全对象的访问。
MIC机制包含两部分:完整性级别和强制策略,此机制是对自由访问控制的补充,发生在自由访问控制列表(DACL)访问检查之前。
安全主体和安全对象都被分配了完整性级别,用于描述访问者与被访问者的访问级别。
1. 进程的完整性级别与令牌强制策略(TOKEN_MANDATORY_POLICY)
每个进程在它的访问令牌中有一个完整性级别,它根据以下规则传播:
进程除了完整性级别,还有一个强制策略:
2. 对象的完整性级别与系统强制策略
安全对象在其安全描述符的系统访问控制列表(SACL)中有一个完整性级别
- 为了兼容老版本window和方便开发者,所有的对象都有一个隐含的完整性级别"中"
- 当进程创建一个对象时,如果没有指定完整性级别,系统会根据进程令牌中的完整性来决定对象的完整性。如果进程完整性级别是"中"或更高,则对象隐含的完整性级别仍是"中";如果进程的完整性级别低于"中",则会显示地以与令牌中相匹配的完整性级别创建对象。
为什么对象不能继承创建者的"高"完整性级别? 如果管理员禁用UAC并创建一个对象,此时进程是"高"完整性级别,若对象也继承"高"完整级别,当再次启用UAC时管理员会无法修改这个对象。
- 对象也可以有一个由系统或对象创建者显示设置的完整性级别。当进程、线程、令牌、作业这几类对象被创建时,内核会赋予一个显示的完整性级别,使得同一用户下低完整性级别的进程无法访问或修改这些对象。
安全对象除了完整性级别,也有一个强制策略:
可以用accesschk.exe -v查看进程、文件、注册表等对象的完整性级别和强制策略:
三、访问令牌 Access Token
SRM使用令牌对象来标识一个进程或线程的安全上下文环境,令牌中包含的最重要的内容如下所示:
-
令牌源,描述创建此令牌的实体
-
主令牌还是模仿令牌,模仿令牌的模仿级别(后面讲述)
-
令牌ID,相当于令牌标识符,每个令牌唯一。
-
认证ID,LSASS为所有从同一个初始登录令牌继承下来的令牌,拷贝其认证ID,用于程序检查该令牌与其他令牌是否属于同一个登录会话。
-
修改ID,每次当一个令牌的特征被修改时,会刷新修改ID。
-
过期时间,令牌过期时间
-
会话ID
-
各种标志,决定了UAC和UIPI机制的某些行为(后面讲述)
-
登录会话
-
强制策略,对应第二部分讲到的令牌强制策略
-
默认的主组和默认的DACL,进程所创建对象的默认主组和默认DACL,这样不需每次创建对象都显示指定这些信息。
-
用户账户SID, 进程所属账户
-
组SID,用户账户属于哪些组,完整性级别SID也存储在这里。
-
受限制的SID,将此SID标记为deny-only、restricted等(后面讲述)
-
特权 (后面讲述)
登录过程中,LSASS会确定本次登录的用户是否属于一个权力较大的组、或拥有一个权力较大的特权。如果是,且UAC未被禁用,则会为用户创建一个已过滤的管理员令牌(也称标准用户令牌),并为两者创建一个登录会话。之后,此标准用户令牌就会被附载到Winlogon启动的一个或多个初始进程进程上(默认是Uerinit.exe)。默认情况下,子进程继承其创建者令牌的一份拷贝,也可以利用Windows的LogonUser函数来生成一个令牌,然后把令牌传递给CreateProcessAsUser函数,从而用它来创建一个进程。CreateProcessWithLogon函数把这两步并成一个调用,这正是Runas命令在其他令牌下启动进程的做法。
受限制的令牌
受限制的令牌是通过CreateRestrictedToken创建的,受限制令牌是其来源令牌的一份拷贝,但可能有下面的修改:
- 从该令牌的特权组中可以删除一些特权
- 令牌中的SID可以标记为deny-only,后面做访问检查的时候,只匹配那些access-denied的ACE,略过access-allowed的ACE,即只用于拒绝。
假如某个安全对象拒绝administrators组访问,如果令牌中只是将administrators组移除,而安全对象又允许everyone组访问,此令牌还是能够访问此对象。此时,将令牌中administrators组SID标记为deny_only,就可以避免这种不确定的情况。
- 令牌中的SID可以标记为restricted,when the OS compares a restricted token against an ACL it first compares the ACL against the token "normal" SIDs and, if that check succeeds, it will then compare the ACL against the list of restricted SIDS and the access that will be eventually granted will consists of the union of both。
已过滤的管理员令牌:
UAC通过CreateRestrictedToken来创建一个已过滤的管理员令牌,使得即使管理员登录也使用一般权限来启动绝大部分应用程序。已过滤的管理员令牌有以下特性:
- 令牌中完整性级别为“中”
- Administrators组SID被标记为deny_only
- 除了Change Notify、Shutdown、Undock、Increase Working Set、TimeZone,其他特权都被移除
2. 模仿令牌
令牌有主令牌和模仿令牌两种:
-
一个进程有一个LSASS赋予的主令牌来描述自己的安全上下文,通过OpenProcessToken可获取当前进程主令牌。
-
此外,线程可以创建模仿令牌来临时模拟一个用户,一般是服务器来模拟客户端。例如,某用户想删除网络共享文件夹下的一个文件,负责网络共享的服务需要检查该用户是否有权限执行删除操作。如果由服务本身去实现用户及用户组权限检查会很复杂,这时由服务端临时创建一个客户端上下文,由它来代替客户端发出资源访问请求,访问验证过程仍然由网络共享文件夹所在机器上的SRM去做。这样,就简化了服务器的工作。通过 OpenThreadToken可获取当前线程的模拟令牌,这里简要列出了Windows提供的几种使用模仿令牌的机制。一个低完整性级别的进程可能创建一个用户界面来捕获用户的登录凭证,然后调用LogonUser来获取该用户的令牌。为了避免这种假冒身份的情况出现,在模仿的情形下,一个特殊的完整性策略会起作用: 从LSALogonUser(LogonUser调用了LSALogonUser)返回的访问令牌,其完整性级别不得高于发出调用的进程的完整性级别。
接下来,简单研究了下命名管道中,命名管道server端使用模仿令牌模拟客户端的方法,MSF中的getsystem命令就是基于此原理。
server端使用CreateNamedPipe和ConnectNamedPipe创建命名管道及等待client端连接,客户连接上命名管道,此时Server端可使用ImpersonateNamedPipeClient来模仿客户端。如果执行成功,调用进程的线程会变成客户端的安全上下文,那么就可以使用OpenThreadToken来获取客户端令牌。
MSF中的getsystem原理就是创建了一个命名管道和一个windows服务来连接此命名管道,windows服务启动时是system权限,当其连接上命名管道后,通过ImpersonateNamedPipeClient和OpenThreadToken获取其令牌,再通过DuplicateTokenEx和CreateProcessWithTokenW复制令牌并创建新进程,新的子进程是system权限。
以下代码摘自:https://www.anquanke.com/post/id/190207#h2-8
if (ImpersonateNamedPipeClient(hPipe) == 0) {
printf("[!] Error impersonating client %d\n", GetLastError());
return 0;
}
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &token)) {
printf("[!] Error opening thread token %d\n", GetLastError());
return 0;
}
if (!DuplicateTokenEx(token, TOKEN_ALL_ACCESS, NULL, SecurityDelegation, TokenPrimary, &newtoken)) {
printf("[!] Error duplicating thread token %d\n", GetLastError());
return 0;
}
printf("[*] Impersonated SYSTEM user successfully\n");
if (!CreateProcessWithTokenW(newtoken, LOGON_NETCREDENTIALS_ONLY, L"", L"C:\\windows\\system32\\cmd.exe", NULL, NULL, NULL, (LPSTARTUPINFOW)&si, &pi)) {
printf("[!] CreateProcessWithToken failed (%d).\n", GetLastError());
return 0;
}
为了防止滥用模仿机制,Windows不允许服务端在没有客户端同意的情况下执行模仿,客户端在连接服务端的时候可以指定一个模仿级别,以此来限制服务端的操作。如果没有指定,Windows默认是SecurityImpersonation级别。
上面的命名管道例子,CreateFile的dwFlagsAndAttributes参数就用于指定模仿级别,除此之外还有两个额外的设置选项。
四、安全描述符与访问控制
上面介绍的安全标示符SID和访问令牌Access Token描述的是访问者,接下来安全描述符描述的是被访问资源,官方叫法是安全对象。
安全描述符包括安全对象所有者,哪些用户可以访问该对象、以何种方式访问以及审核哪些类型的访问权限。 安全描述符包含对象的访问控制列表(ACL),其中包括应用于该对象的所有安全权限。 对象的安全描述符可以包含两种类型的 Acl:
文件夹属性-安全这里,展示的就是其安全描述符,SysinternalsSuite下的accesschk.exe,以及powershell的Get-Acl命令都可以用来查看对象的安全描述符。如果在一个安全描述符中没有出现DACL(即null DACL),则任何人对于该对象都有完全的访问权限。如果DACL为空(即0个ACE),则没有用户对该对象有访问权限。
一个文件对象及其DACL的简化图如下:
当某进程想对某个文件夹进行写操作时,SRM会将进程中Access Token中的信息与安全描述符中的ACL比较,决定允许还是拒绝该操作,完成这一功能的是内核的SeAccessCheck函数。访问控制包括两部分:
-
强制完整性检查,根据安全主体和安全对象的完整性级别、完整性策略,来确定安全主体是否能够访问安全对象。这一过程发生在DACL检查之前,因为它执行起来更快。
-
自主访问检查,强制完整性检查通过后,它通过比较DACL中的ACE来决定访问检查结果。这一步ACE顺序很重要,因此windows一些函数就按"显示拒绝的ACE总是在显示允许的ACE"之前来应用ACE,注册表编辑所使用的对话框就使用了这些函数。
账户权限指允许或拒绝某一个账户或账户组以某种方式登录到一台计算机。账户权限检查不是由SRM做的,也不存储在令牌中,而是在登录过程中LsaLogonUser函数负责,如果组策略允许该账户登录,才会创建令牌返回句柄给Winlogon。
通过查看组策略,可以看到当前设置账户权限。
特权指一个账户执行某个与系统相关的操作的权限,如shutdown计算机、更改时间、调试特权等。特权放在令牌中,不同的特权由不同的组件来定义、也由这些组件来强制使用。例如,调试特权是由进程管理器来检查的,具有此特权的用户可以打开系统中的任何一个进程(除了受保护进程以外),而不用考虑该进程上的安全描述符。
在系统初始化过程中,在任何用户应用程序被激活起来以前,一旦系统准备好与用户进行交互了,Winlogon就执行下面的步骤来确保它控制着整个工作站:
- 1)创建并打开一个交互式窗口站(\Sessions\1\Windows\WindowStations\WinSta0)
- 2)创建和打开两个桌面:一个应用程序桌面(\Sessions\1\Windows\WindowStations\WinSta0\Default,也称为交互式桌面)和一个Winlogon桌面(\Sessions\1\Windows\WindowStations\WinSta0\Winlogon, 也称为安全桌面)。按Ctrl+Alt+Delete所见的桌面就是Winlogon桌面,不管任何时候,当Winlogon桌面是活动桌面时,没有任何其他的进程可以访问与该桌面相关联的任何活动代码或数据。Windows使用此特性来保护与口令相关的安全操作,以及锁定桌面和解除桌面锁定这样的操作;只有Winlogon进程才可以锁定一个桌面,或者解除桌面的锁定。
- 3)与LSASS建立一个ALPC连接,用于在登录、注销和口令操作过程中交换信息。
- 4)为Winlogon登记RPC消息服务器,该服务器会监听从Win32k发送的SAS、注销和工作站锁定的通知。该种方法可防止特洛伊木马在用户按下SAS时获得对屏幕的控制。
SAS是安全的,没有应用程序能够截取Ctrl+Alt+Delete组合,也无法阻止Winlogon接受此键击组合:
- Winlogon会注册RPC消息服务器,当Win32k监测到SAS时,会给Winlogon的消息服务器发送一个RPC消息,其他应用程序不可能注销Winlogon对SAS的所有权。
- 其次,虽然Windows SetWindowsHooks函数允许应用程序安装一个按键Hook,但Windows的热键处理代码对Ctrl+Alt+Delete作了特殊处理,不允许设置针对它的Hook,所以SAS不会被其他程序监听截取到。
- 最后,如果交互式桌面被锁住,只有Winlogon拥有的热键才被处理。
当SAS被按下以后,Winlogon启动LogonUI,后者会调用凭证提供者来获取用户名和口令。Winlogon也为该用户创建一个唯一的本地登录SID,这是它分配给此桌面实例的SID。Winlogon在LsaLogonUser调用中将此SID传递给LSASS,如果登陆成功,则此SID会包含在LSASS返回的令牌中。这一步骤保护了对桌面的访问。
1)当登录信息被输入,Winlogon调用LSASS的函数来获取一个SSP认证包的句柄,Winlogon通过LsaLogonUser将登录信息传递给认证包。如果没有一个认证包指示这是一次成功的登录,则登录过程终止。
2)对于交互式登录,Windows使用两个标准的认证包:Kerberos和Msv1_0(NTLM认证)。已加域的计算机用Kerberos认证,未加域、Windows 2000以前的域、以及未能找到域控制器的计算机(如与域控机网络连接断开)使用NTLM认证。Kerberos认证笔者暂时未研究
MSV1_0将用户名、NLTM Hash与SAM数据库中用户名、NTLM Hash比较;如果这是一次缓存的域登录,则LSASS会从HKLM\security下获取已缓存的口令信息。 如果用户登录到一个Windows 2000以前的可信域中,MSV1_0使用Netlogon服务与远程系统上的Netlogon实例通信。远程系统上的Netlogon与该系统上的MSV1_0认证包进行交互,将认证结果送回到当前正在执行登录的系统上。如果认证通过,LSASS会为此用户创建一个登录会话,并生成一个LUID与该会话关联。
3)认证通过以后,LSASS会对该账户做账户权限认证,查看本地组策略中是否允许该账户所请求的访问方式(见上面账户权限章节)。如果不允许,LSASS会删除新建的登录会话、向Winlogon返回登录失败;如果允许登录,LSASS会依据此账户的信息,如组SID、特权、会话ID等,创建访问令牌。对于交互式登录或服务方式登录,创建一个主令牌;对于网络登录方式,创建一个模仿令牌。在成功创建令牌后,LSASS复制此令牌,返回一个访问句柄给Winlogon。
4)然后,Winlogon会调用注册表中 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit 下指定的可执行程序(默认是userinit.exe)用于初始化用户环境;Userinit.exe会调用HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\shell(默认不存在) 和 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\shell下指定的可执行程序(默认是explorer.exe),然后Userinit.exe退出,这也是为什么刚登陆时explore.exe没有父进程的原因。
使用logonsessions.exe可以查看当前活动会话。
Local Security Authority Subsystem本地安全权威子系统,开机后由wininit.exe启动,承载了很多核心服务
Get-WmiObject win32_service | ?{$_.PathName -like '*lsass*'} | select Name, DisplayName, State, description | Format-Table
Domain Controller
域内主机
工作组机器