引言
这篇博文将描述访问令牌操作的概念,以及如何针对winlogon使用这种技术。从管理员上下文模拟系统访问令牌。该技术可以在 MITRE ATT&CK 技术框架中的 访问令牌操作 中找到。
技术交流可以加Q 343202158 个人博客:http://www.Ahacker.org
在通过组策略从本地管理员帐户中取消某些特权的情况下,模拟系统访问令牌非常有用。例如,可以从本地管理员组中删除SeDebugPrivilege,以使攻击者更难转储凭据或与其他进程的内存进行交互。但是,不能从系统帐户撤消特权,因为操作系统需要这些特权才能运行。这使得系统访问令牌对于加固环境中的攻击者非常有价值。
在介绍了访问令牌操作的概念之后,我将展示如何使用系统访问控制列表(SACL)来审计进程对象来检测恶意的访问令牌操作。这种检测的一个缺点是防御者必须主动地了解哪些进程是访问令牌操作的目标。
最后,我将探讨其他系统进程,它们的访问令牌可以像winlogon.exe一样被模拟。我解释了发现这些进程背后的方法,以及我在此过程中学到的东西。
窃取访问令牌
注意:如果你熟悉访问令牌操作并希望深入研究搜索能够模拟访问令牌的备用系统进程背后的方法,请跳过本节。
可以使用以下 Windows API 调用来窃取和滥用访问令牌: OpenProcess(), OpenProcessToken(), ImpersonateLoggedOnUser() , DuplicateTokenEx(), CreateProcessWithTokenW()。
![c058bbb182187d0b8c92481911f58189.png](https://i-blog.csdnimg.cn/blog_migrate/4fb5a17bdb1641984d96a36e789390c3.jpeg)
使用 Windows API 窃取访问令牌
OpenProcess()接受一个进程标识符(PID)并返回一个进程句柄。必须使用PROCESS_QUERY_INFORMATION、PROCESS_QUERY_LIMITED_INFORMATION或PROCESS_ALL_ACCESS访问权限打开进程句柄,才能使用OpenProcessToken()。
![1aa564630b8527508ab0c180c16358b3.png](https://i-blog.csdnimg.cn/blog_migrate/30469780dd2259edc2c305c75639c428.jpeg)
MSDN 文档中针对 OpenProcess的说明
OpenProcessToken()接受一个进程句柄和一个访问权限标志作为输入参数。它将打开与进程关联的访问令牌的句柄。必须使用TOKEN_QUERY和TOKEN_DUPLICATE访问权限打开令牌句柄,才能在ImpersonateLoggedOnUser()中使用。另外,可以使用DuplicateTokenEx()打开令牌句柄,只有TOKEN_DUPLICATE访问权限可以使用。
![fcbc1f7b6666459ef30f978e43090ec3.png](https://i-blog.csdnimg.cn/blog_migrate/bdbf36210da3fecd6b61cad1310166fd.jpeg)
MSDN 文档中针对OpenProcessToken的说明
通过OpenProcessToken()的令牌句柄,我们可以使用ImpersonatedLoggedOnUser()来允许当前线程模拟另一个登录用户。在调用RevertToSelf()或线程退出之前,线程将继续模拟登录用户。
![0a479a8b50315133f1d030f85c957b94.png](https://i-blog.csdnimg.cn/blog_migrate/78f17d102a1b569c8bc88345fb155f3a.jpeg)
MSDN 文档在对于 ImpersonateLoggedOnUser 的说明
如果我们想以另一个用户的身份生成一个进程,我们必须在 OpenProcessToken()的结果令牌句柄上使用 DuplicateTokenEx()来创建一个新的访问令牌。 必须使用 TOKEN_ADJUST_DEFAULT TOKEN_ADJUST_SESSIONID, TOKEN_QUERY, TOKEN_DUPLICATE 和TOKEN_ASSIGN_PRIMARY 访问权限来调用 DuplicateTokenEx() ,才能使用 CreateProcessWithTokenW()。 通过DuplicateTokenEx()创建的访问令牌可以传递给CreateProcessWithTokenW(),从而使用复制的令牌生成一个进程。
![148b5c96c06aa844724fc70be72b4b15.png](https://i-blog.csdnimg.cn/blog_migrate/67ca403b5f2c0cb22af3eb0368507b28.jpeg)
MSDN 文档中对于 DuplicateTokenEx 的说明
![0868125e0fbd8caf0271dc93db400bfe.png](https://i-blog.csdnimg.cn/blog_migrate/64db0faea0db362d4999c76808cf6367.jpeg)
MSDN 文档中对于 CreateProcessWithTokenW 的说明
我编写了一小段代码来演示这一点,大部分代码都参考于kondencuotas写的这篇文章中关于主要令牌操作的内容。
在我的测试中使用的代码可以在这里找到: https://github.com/justinbui/PrimaryTokenTheft
使用 winlogon.exe 升级到系统权限
今年早些时候, Nick Landers 提到一个简单的方法可以将本地管理员提升到 NT AUTHORITYSYSTEM。
作为本地管理员(在高完整性上下文中) ,你可以窃取 winlogon.exe 的访问令牌,以便在当前线程中模拟 SYSTEM 或以 SYSTEM 权限生成新进程。
![542661298bee1738700b96021a9ee36f.png](https://i-blog.csdnimg.cn/blog_migrate/68f1248af41da5e5d8b62692cbee5128.jpeg)
从 winlogon.exe 中窃取SYSTEM令牌
检测
访问控制列表(ACL)是访问控制项(ACE)的列表。 ACL 中的每个 ACE 标识了一个受信者,并为该受信者指定允许、拒绝或审计的访问权限。 安全对象的安全描述符可以包含两种类型的 ACL: DACL 和 SACL。 (资料来源)
我们的检测将基于系统访问控制列表(SACL)。 我们可以应用 SACL 来处理对象,以记录对 Windows 安全日志的成功或失败访问尝试。
我们可以使用 James Forshaw 的 NtObjectManager 轻松地实现这一点。 下面的大部分工作大量改编自 James Forshaw 的博客文章:绕过 SACL 审计 LSASS ,可以点击这里查看。 他的博文让我很好地理解了 SACL,以及它们是如何被 NtObjectManager 操纵的。
auditpol /set /category:"Object Access" /success:enable /failure:enable
$p = Get-NtProcess -name winlogon.exe -Access GenericAll,AccessSystemSecurity
Set-NtSecurityDescriptor $p “S:(AU;SAFA;0x1400;;;WD)” Sacl
让我们分解一下,第一行代码将启用对成功和失败的对象访问的系统审计。
第二行代码将获得具有 GenericAll 和 AccessSystemSecurity 访问权限的 winlogon.exe 进程的句柄。 AccessSystemSecurity 权限是访问 SACL 所必需的。
第三行代码将应用一个审计 ACE 类型(AU) ,它将为 Everyone (WD)组的成功和失败访问(SAFA)生成一个安全事件。 0x1400非常重要,因为它是0x400 (PROCESS_QUERY_INFORMATION)和0x1000 (PROCESS_QUERY_LIMITED_INFORMATION)的按位或运算得到的。 可以使用这两种访问权限中的任何一种从进程对象(以及PROCESS_ALL_ACCESS)获取访问令牌。
太好了! 有了这个 SACL,我们应该能够在使用特定访问权限访问 winlog.exe 时获得警报。
案例1:PROCESS_QUERY_INFORMATION
在运行测试程序时,我们看到生成了 EID (事件 ID)4656,它显示了请求的进程对象、请求访问的进程和请求的访问权限。 0x1400的访问掩码是因为具有PROCESS_QUERY_INFORMATION访问权限的句柄也被自动授予PROCESS_QUERY_LIMITED_INFORMATION访问权限。
![338eccb1ab1dd40229c377afd8cc7259.png](https://i-blog.csdnimg.cn/blog_migrate/360e956a24ea06eeda36f81e464eda7b.jpeg)
案例2:PROCESS_QUERY_LIMITED_INFORMATION
我重新编译了测试程序,只需要 PROCESS_QUERY_LIMITED_INFORMATION 访问权限,然后重新运行测试程序。 我们看到,EID 4656再次生成,显示了请求 winlogon.exe 句柄的进程,该句柄的访问权限为0x1000,表示 PROCESS_QUERY_LIMITED_INFORMATION访问权限。
![0e6ce6f12730b2528a2acf5e1f0e8c5f.png](https://i-blog.csdnimg.cn/blog_migrate/ca56794acac7315195fcba05aef57047.jpeg)
此外,我们看到生成的 EID 4663显示了我们的测试程序在请求句柄之后尝试访问进程对象。 这允许我们通过搜索 EID 4656和 EID 4663来更准确的检测访问令牌操作行为。
![18e8bad278a2492d32c4592cee2dadb8.png](https://i-blog.csdnimg.cn/blog_migrate/ddd54312878023d32510e8610b784d39.jpeg)
案例3: PROCESS_ALL_ACCESS
重新编译测试程序,这次使用 PROCESS_ALL_ACCESS 访问权限,结果得到与案例2相同的 EID,而 EID 4656显示了所请求的额外访问权限。
![58a752b8e122450b9291a8415a0c910f.png](https://i-blog.csdnimg.cn/blog_migrate/02ee3919463a4ca663330c822554ff9e.jpeg)
![e5ede610c14d924c13500b30e0415163.png](https://i-blog.csdnimg.cn/blog_migrate/3987e8046392d54dc2f5710c69af9aeb.jpeg)
有趣的是,EID 4663的访问掩码是0x1000,它表示PROCESS_QUERY_LIMITED_INFORMATION访问权限。此外,当使用PROCESS_QUERY_INFORMATION访问权限运行测试程序时,将生成EID 4656,而不生成EID 4663。
方法:寻找winlogon.exe替代方案
除了 winlogon.exe 之外,我还想知道是否还有其他 以 SYSTEM 权限运行的进程可能成为令牌窃取的目标。 如果有,它们与其他无法进行令牌窃取的 SYSTEM 进程有什么区别?
检验假设
首先,我想看看是否有其他进程可以从中窃取一个系统令牌。 作为在高完整性上下文中运行的本地管理员,我强制使用 SYSTEM 进程(不包括 svchost.exe) ,并发现了一些可以从中窃取 SYSTEM 令牌的其他进程。 这些进程是 lsass.exe、 OfficeClickToRun.exe、 dllhost.exe 和 unsecapp.exe。 我将这些进程称为“已知的好进程”。
![aad0e3c331507f8ac558ad3086075a42.png](https://i-blog.csdnimg.cn/blog_migrate/b5ff29a522317e9d4713e7fd514f9b85.jpeg)
从 unsecapp.exe 进程中窃取 SYSTEM 令牌
当我浏览 SYSTEM 进程列表时,我注意到一些进程在 OpenProcess()期间因访问拒绝而失败(系统错误代码5) ,这将导致程序的其余部分失败。
对于某些 SYSTEM 进程来说,OpenProcess()会成功,但是 OpenProcessToken()会失败,并且存在拒绝访问错误。 我将在以后的研究中了解其中的原因。
![7836d93d73495c50aea343a3eb7cf316.png](https://i-blog.csdnimg.cn/blog_migrate/2ba5e43943c325ab17b27277fa76d283.jpeg)
深入探索
我的目标是找出允许令牌操作成功的系统进程之间的安全设置差异。
我决定对 winlogon.exe 和 spoolsv.exe 进行比较。 这两个都是 SYSTEM 进程,但是,我只能从 winlogon.exe 窃取 SYSTEM 访问令牌。
Session ID
我在 Process Explorer 中打开了这两个进程,并尝试手动发现它们之间的差异。 我记得 Nick 在推特上提到 winlogon.exe 是 ID 为 1的会话中的一个例外。
我将其与其他“已知的好进程”进行了比较,发现它们都在 Session ID 0中运行的。 不幸的是,这并不是我想要找到的不同。
![77ca4ee04652f1c725bc39839b79670e.png](https://i-blog.csdnimg.cn/blog_migrate/750d0b09d6ea19257bd191f130e0076c.jpeg)
比较两个“已知的好进程”之间的会话 ID
进程资源管理器中的高级安全设置
我决定进一步研究 winlogon.exe 和 spoolsv.exe 的高级安全设置。 我注意到Administrators组的权限条目具有略微不同的高级权限。 对于 winlogon.exe 来说,Administrator 组拥有 Terminate、 Read Memory 和 Read Permissions 特权。 Spoolsv.exe 中的 Administrator 组不能使用这些特权。
![e89a94a884965de45069d037fb5092b6.png](https://i-blog.csdnimg.cn/blog_migrate/f3c0864ee0638a2187823d2db478a87b.jpeg)
我试图启用 spoolsv.exe 上的所有权限,并试图窃取访问令牌。 不幸的是,这并没有弹出 SYSTEM 权限的 cmd。
![6ab1ba04b2987e023e1e40cc39a59b3f.png](https://i-blog.csdnimg.cn/blog_migrate/3086d75d809cc5ef9fc14bee5d87311d.jpeg)
我还试图再次启动和停止进程,以查看是否在进程启动时应用权限,结果这也失败了。
Get-ACL
我决定在 PowerShell 中使用 Get-ACL 来查看 winlogon.exe 和 spoolsv.exe 的安全描述符。
![199bd4ac2a2a94c19a3590184f81e64a.png](https://i-blog.csdnimg.cn/blog_migrate/1d4aee55edfda839daae781d658fc17e.jpeg)
使用 Get-ACL 获取 winlogon.exe 和 spoolsv.exe 的进程信息
两个进程之间的所有者、组和访问似乎是相同的。 接下来,我决定使用 ConvertFrom-SddlString 转换安全描述符定义语言(Security Descriptor Definition Language,SDDL)字符串来查找差异。
![5dce0da9dba01eae18bf2d447ad05368.png](https://i-blog.csdnimg.cn/blog_migrate/21f2e27e49115d9215909c4bf20612d3.jpeg)
winlogon.exe 和 spoolsv.exe进程的SDDL
BUILTINAdministrators组的 DiscretionaryAcl 似乎是相同的。 我已经没有什么好主意了,但还是决定最后再看一次进程浏览器。
突破! (TokenUser vs TokenOwner)
在进程资源管理器中再次查看高级安全设置时,我看到所有“已知好”进程中的 Owner 字段都指定了本地 Administrators 组!
![53f43512245e468fb536cedcfbb98df1.png](https://i-blog.csdnimg.cn/blog_migrate/0c9e2eaea02f3f14e7c378d0e16ae39d.jpeg)
winlogon.exe 和 unsecapp.exe进程的TokenOwner
我将其与一些无法从中窃取访问令牌的 SYSTEM 进程进行了比较。 Owner 字段似乎是一个与众不同的因素。
![cae20f666658007bdeef1abd8d5580bc.png](https://i-blog.csdnimg.cn/blog_migrate/c7a823efd53e887e1bc5f901b214d6f8.jpeg)
我的同事@jaredcatkinson 提到过,进程浏览器中指定的 Owner 是 TokenOwner,可以使用 方便的是,我在 GitHub 上找到了一个名为 Get-Token.ps1 的 PowerShell 脚本。 这个脚本可以枚举所有进程和线程令牌。
![3a59f95a752038417f9dbbd978c1f206.png](https://i-blog.csdnimg.cn/blog_migrate/ec1e50d81792556460ab2f35251ec9d5.jpeg)
使用 Get-Token.ps1 获取 winlogon.exe 进程的 Token 信息
查看 winlogon.exe,可以看到 UserName 和 OwnerName 字段不匹配。 查看脚本的实现,我发现这些字段与TOKEN_USER 和 TOKEN_OWNER结构相关。
TOKEN_USER 结构标识的是与访问令牌关联的用户。 TOKEN_OWNER 结构标识的是用访问令牌创建的任何进程的所有者。 这似乎是主要的区别,这个原因似乎就是允许我们从一些 SYSTEM 进程窃取访问令牌,而不是从其他进程!
对于某些 SYSTEM 进程来说,OpenProcess()会成功,但是 OpenProcessToken()会失败,并且存在拒绝访问错误。 我将在以后的研究中了解其中的原因。
现在我可以回答我之前问过自己的这个问题,为什么在某些 SYSTEM 进程上 OpenProcessToken() 会失败并且访问被拒绝? 失败的原因是,因为我不是 TOKEN_OWNER。
下面的一行代码将对 Get-Token 的输出进行分析,其中用户名为 SYSTEM,而 OwnerName 不是 SYSTEM。 然后,它将从每个对象获取 ProcessName 和 ProcessID 属性。
Get-Token | Where-Object {$_.UserName -eq ‘NT AUTHORITYSYSTEM’ -and $_.OwnerName -ne ‘NT AUTHORITYSYSTEM’} | Select-Object ProcessName,ProcessID | Format-Table
![fb5cd35484175b81987d04a851c93401.png](https://i-blog.csdnimg.cn/blog_migrate/01b950e2286d5073329dc6237676044a.jpeg)
太棒了! 列出的这些以 SYSTEM 权限运行的进程就是我们能够访问令牌并模拟一个 SYSTEM 访问令牌的进程。 让我们来验证我们的假设。
我手动浏览了 PID 列表,注意到大多数 PID 都是访问令牌操作的有效候选对象! 然而,有一些进程在实际操作的时候失败了。
![31e922bdb93afe24a4f054b5d0b0294a.png](https://i-blog.csdnimg.cn/blog_migrate/f570bc79694bfa538163f525f74513a6.jpeg)
在对 wininit.exe 和 csrss.exe 进程调用OpenProcess()返回了错误
受保护的进程属性
我看到,在 OpenProcess ()调用期间,由于访问被拒绝,我无法从每个 SYSTEM 进程窃取令牌。 我打开进程资源管理器,发现了一个似乎可能导致这种行为的公共属性: PsProtectedSignerWinTcb-Light。
![f9f35017a53a8af427d737157db84c24.png](https://i-blog.csdnimg.cn/blog_migrate/28635bbdb6a81a92a1c6a5b85cb42d8b.jpeg)
当我浏览 SYSTEM 进程列表时,我注意到一些进程在 OpenProcess ()期间因访问拒绝而失败(系统错误代码是5) ,这将导致程序的其余部分失败。
通过阅读 Alex Ionescu 的博客和这篇 Stack Overflow 的文章,我了解到这个受保护的属性与 Protected Process Light (PPL)有关。
只有在指定的访问权限为PROCESS_QUERY_LIMITED_INFORMATION时,PPL才允许在进程上调用OpenProcess()。 我们的测试程序要求使用 PROCESS_QUERY_INFORMATION 访问权限调用 OpenProcess() ,以使句柄与 OpenProcessToken()兼容,从而导致系统错误ー代码5(访问被拒绝)。 至少我是这么想的..。
在测试检测过程时,我了解到 OpenProcessToken()所需的最小权限是PROCESS_QUERY_LIMITED_INFORMATION 访问权限,这与 微软官方文档的说明是相反的。 我修改了 OpenProcess() API 调用期间请求的访问权限,并获得了一个 SYSTEM 权限的 cm。
![0bcc5a8062e400ca8d84fbe469f91470.png](https://i-blog.csdnimg.cn/blog_migrate/c3c6c87deb6fa3c74a8c0cedbf07f89e.jpeg)
结果
使用PROCESS_QUERY_INFORMATION访问权调用OpenProcess()时,以下系统进程的访问令牌可能被窃取:
· dllhost.exe
· lsass.exe
· OfficeClickToRun.exe
· svchost.exe (only some PIDs)
· Sysmon64.exe
· unsecapp.exe
· VGAuthService.exe
· vmacthlp.exe
· vmtoolsd.exe
· winlogon.exe
当使用PROCESS_QUERY_LIMITED_INFORMATION访问权调用OpenProcess()时,以下带有 Protected Process Light 的系统进程的访问令牌可能被窃取:
· csrss.exe
· Memory Compression.exe
· services.exe
· smss.exe
· wininit.exe
其中一些进程可能特定于我的 Windows 开发环境,但我鼓励你在自己的环境中执行这个测试!
总结
回顾一下本文的内容,winlogon.exe 有一个访问令牌,可以通过盗用该令牌来模拟 SYSTEM 上下文。 我深入研究了如何使用 SACL 和 Windows 安全日志检测访问令牌的操作行为。
我试图搜索具有与 winlogon.exe 类似属性的其他 SYSTEM 进程。 我强调了深入研究这个主题的方法,并且能够找到其他轻易进行访问令牌操作的 SYSTEM 进程。 此外,我还深入研究了为什么某些进程容易执行访问令牌操作,而其他进程不容易执行访问令牌操作的根本原因。
从 SYSTEM 进程中窃取访问令牌的步骤如下:
· BUILTINAdministrator 必须是为进程执行OpenProcessToken()的令牌所有者。
· 将OpenProcess()与PROCESS_QUERY_LIMITED_INFORMATION 访问权限一起用于Protected Process Light(PPL)保护的系统进程。
感谢你花时间阅读这篇文章,我希望你对Windows API、SACL、Windows进程、Windows令牌和访问令牌操作已经有一些了解!