详解AppLocker(Part 1)

 

0x00 前言

AppLocker(以下简称AL)是Windows 7企业版及更高版本中添加的一个功能,是替代SRP(Software Restriction Policies,软件限制策略)的较为复杂的一种应用程序白名单解决方案。经过配置后,AL可以通过各种不同的规则(比如应用路径)来阻止进程创建操作,AL也可以阻止DLL、脚本代码、MSI安装程序等。从技术层面上看,AL正逐渐被功能更为强大的WDAC(Windows Defender Application Control,Windows Defender应用控制)所替代,后者源自于UMCI(User Mode Code Integrity,用户模式代码完整性)。但目前为止,AL在企业环境中部署起来仍然更为方便。根据MSRC的安全服务标准,AL可以当成一种“深度防御”功能。因此,除非其中存在EoP或RCE之类的bug,否则微软不会通过安全公告来替换AL。

现在已经有些资料介绍如何配置AL以及如何绕过AL(大家可以参考Oddvar Moe发表过的一系列文章),然而关于AL内部工作原理的参考资料依然比较少。经过挖掘后,我找到了涉及AL原理的一些文档,如下所示:

然而这些文章依然没有给出足够完整的细节,因此我还是想深入研究一下AL某些的内部原理,重点关注用户访问令牌及应用规则之间的关系。这里我不去介绍如何配置AL(但为了便于演示,会简单设置一下环境),也不会介绍绕过技术。然而我会稍微介绍类似“绕过技术”的一些小技巧,用来应付配置了AL的系统。需要注意的是,本文介绍的内容基于Windows 10 1909企业版系统,在其他版本的Windows上AL的内部原理可能会有所不同。

接下来让我们开始了解各种组件,看一下如何在Windows 10 1909企业版系统中快速设置AL,以便后续技术研究。

 

0x01 各种组件介绍

AL用到了内核驱动(APPID.SYS)以及用户模式下的服务(APPIDSVC)。AL涉及到内核代码,这一点与之前的SRP完全不同。SRP完全基于用户模式,因此绕过起来不是特别困难。内核驱动的主要功能是通过Process Notification Callback(进程通知回调)来阻止进程创建,并且也会提供一些通用的服务。用户模式服务更多是承担辅助角色,可以处理内核中难以处理或者无法处理的事务。然而查看内部实现以及CI(Code Integrity,代码完整性)模块的逻辑后,我认为这里大多数工作可以完全在内核态完成。

对于DLL、脚本及MSI安装程序,各种用户模式组件会访问SAFER API来判断目标代码是否可以运行。SAFER API随后可能会根据具体情况,通过RPC转入内核驱动或者服务。我梳理了一下之间的联系,如下图所示。

 

0x02 设置测试系统

首先我使用MSDN ISO镜像来安装Windows 10 1909企业版系统,如果大家没有MSDN访问渠道,可以通过微软获取试用版的Dev环境虚拟机,该虚拟机承载的也是Windows 10企业版。在本文撰写时,虚拟机的系统版本号为1903,如果有需要大家可以升级到1909。系统安装完后,执行如下操作:

1、启动VM,以管理员登录,然后运行管理员模式下的PowerShell控制台。

2、从GitHub上下载默认的AppLocker策略文件,保存为policy.xml

3、运行PowerShell命令:Set-AppLockerPolicy -XmlPolicy policy.xml

4、运行命令:sc.exe config appidsvc start= auto

5、重启VM。

通过以上操作,我们可以安装一个简单的默认策略并启用Application Identity Service,该策略包含如下规则:

1、EXE规则:

  • 允许Everyone组成员运行%WINDIR%以及%PROGRAMFILES%目录下的任何可执行文件;
  • 允许Administrators组从任意位置运行任何可执行文件。

2、DLL规则:

  • 允许Everyone组成员加载%WINDIR%以及%PROGRAMFILES%目录下的任何DLL文件;
  • 允许Administrators组从任意位置加载DLL文件。

3、APPX规则(Packages Applications,打包应用,例如应用商店中的应用)

  • 允许Everyone加载经过签名的任何打包应用。

当然这些规则比较严格,实际中基本没人会使用这种规则,这里我也是根据这一系列文章主题来采用相应的演示规则。

那么策略配置数据存放在哪里?注册表中含有部分数据,但核心策略配置数据存放于%WINDIR%\SYSTEM32\APPLOCKER目录中,按类型进行区分。比如可执行文件的配置位于EXE.APPLOCKER中,以此类推。当该目录中的文件被修改时,系统就会调用驱动来重载配置策略。如果我们使用十六进制编辑器查看这些文件,可以发现这些文件内容并不像我们输入的XML格式(如下图所示),后面的文章中我们会回到这方面内容。

一旦重启VM,服务就会自动运行,在系统中启用AL。如果我们再次以管理员登录,将可执行文件拷贝到桌面目录(该目录不在允许策略中),运行该文件,可以看到程序能正常运行。大家可能认为这种情况非常正常,因为该用户为管理员,可以从任意位置运行程序。然而默认情况下,管理员使用的是UAC拆分令牌,因此默认的这种“用户”应该不具备Administrators组访问权,不能从任意位置运行代码。我们将在第二篇文章中讨论这一点。

为了检查AL是否正常工作,我们可以创建一个新用户(使用New-LocalUser PowerShell命令),不将该用户加入本地管理员组。使用新用户登录,尝试将可执行文件拷贝到桌面目录并再次运行。这一次我们可以看到一个错误对话框,如下图所示:

需要注意的是,如果我们只是启用APPID驱动,AL也不会被启用,只有每一项都正确启用后,目标服务才能运行。大家可能会猜测,如果我们以管理员权限禁用该服务,是否就能关掉AL?来试一下:

C:\> sc.exe config appidsvc start= demand
[SC] ChangeServiceConfig FAILED 5:

Access is denied.

因此自动启动该服务后,我们似乎无法重新配置服务,将其改成“demand”启动模式(这是初始启动模式)。之所以出现这种情况,原因比较简单:

C:\> sc.exe qprotection appidsvc
[SC] QueryServiceConfig2 SUCCESS
SERVICE appidsvc PROTECTION LEVEL: WINDOWS LIGHT.

在Windows 10上(我没有检查Windows 8.1),AppID服务受PPL(Protected Process Light)技术保护。这意味着SCM(Service Control Manager)会阻止“普通的”管理员修改该服务(比如禁用或者停用服务)。我不清楚为何微软采用这种方式,因为作为管理员用户,我们有各种方法能绕过AppLocker的正常功能,禁用服务应该是最后一种选择。当然这里如果我们一定要在运行时禁用服务,那么可以考虑我之前提过的Task Scheduler技巧,以TrustedInstaller身份运行某些命令(这可能算是SCM中的一个后门)。比如我们可以使用管理员身份来运行如下PowerShell脚本:

$a = New-ScheduledTaskAction -Execute cmd.exe -Argument "/C sc.exe config appidsvc start= demand && sc.exe stop appidsvc"
Register-ScheduledTask -TaskName 'TestTask' -TaskPath \ -Action $a
$svc = New-Object -ComObject 'Schedule.Service'
$svc.Connect()
$user = 'NT SERVICE\TrustedInstaller'
$folder = $svc.GetFolder('\')
$task = $folder.GetTask('TestTask')
$task.RunEx($null, 0, 0, $user)

第一篇文章就暂时介绍这些内容,下篇文章我们将深入分析可执行文件策略的工作原理。

(完)