Samir Bousseaden

探索 Windows UAC 绕过:技术与检测策略

在这篇研究文章中,我们将研究一系列 UAC 绕过方法,调查它们所依赖的一些关键原语,并探索检测机会。

阅读 21 分钟安全运营, 检测科学
Exploring Windows UAC Bypasses: Techniques and Detection Strategies

恶意软件通常需要在机器上具有完整的管理权限,才能执行更具影响力的操作,例如添加防病毒排除项、加密安全文件或将代码注入到感兴趣的系统进程中。即使目标用户具有管理权限,用户帐户控制 (UAC) 的普遍存在意味着恶意应用程序通常会默认为中等完整性,从而阻止对具有更高完整性级别的资源的写入访问。要绕过此限制,攻击者需要一种以静默方式且无需用户交互(没有UAC 提示)来提升完整性级别的方法。这种技术被称为用户帐户控制绕过,它依赖于各种原语和条件,其中大部分基于搭载提升的 Windows 功能。

cscript.exe 作为中等完整性运行,通过 UAC 绕过生成具有高完整性的 cmd.exe 实例的示例

大多数 UAC 验证逻辑都在应用程序信息 (AppInfo) 服务中实现。关于提升条件和不同检查的入门资料可以在此处找到。

在这篇博文中,我们将研究一系列 UAC 绕过方法,调查它们所依赖的一些关键原语,并探索检测机会。

UAC 绕过方法

UAC 绕过方法通常会导致劫持提升的应用程序的正常执行流程,方法是生成恶意的子进程或加载恶意模块,从而继承目标应用程序的提升的完整性级别。

还有一些其他边缘情况,但最常见的劫持方法是

注册表项操作

操作注册表项的目的是将提升的程序的执行流程重定向到受控命令。最常被滥用的键值与特定扩展名的 shell 打开命令(取决于目标程序)或 windir/systemroot环境变量操作有关

  • HKCU\Software\Classes\<targeted_extension\>\shell\open\command(默认值或 DelegateExecute 值)
  • HKCU\Environment\windir
  • HKCU\Environment\systemroot

例如,当恶意软件以中等完整性进程启动 fodhelper(一个允许在不需要 UAC 提示的情况下提升的 Windows 二进制文件)时,Windows 会自动将 fodhelper 从中等完整性进程提升为高完整性进程。然后,高完整性 fodhelper 尝试使用其默认处理程序打开 ms-settings 文件。由于中等完整性恶意软件劫持了此处理程序,因此提升的 fodhelper 将以高完整性进程执行攻击者选择的命令。

以下是 Glupteba 恶意软件利用此方法首先从中等完整性进程提升到高完整性进程,然后通过令牌操作从高完整性进程提升到系统完整性进程的示例

一个操作 Windows 环境变量注册表项的 UAC 绕过示例是 byeintegrity5。为了说明这一点,此绕过使用此原语重定向 CDSSync 计划任务(设置为以最高权限运行)的正常执行流程,并提升完整性级别,如下所示。

当 CDSSync 计划任务运行时,taskhostw.exe 将尝试从 %windir%\System32 文件夹加载 npmproxy.dll,但由于恶意软件控制 %windir%,它可以重定向 taskhostw.exe 以从其控制的路径加载名为 npmproxy.dll 的 DLL,如下所示。

当 UAC 设置为始终通知(最高 UAC 级别)时,基于环境变量操作的 UAC 绕过通常会起作用,因为它们通常不涉及将文件写入安全路径或启动自动提升的应用程序。从当前用户注册表到非预期值的 SystemRoot 或 Windir 更改非常可疑,应作为检测的高置信度信号。

DLL 劫持

DLL 劫持方法通常包括查找丢失的 DLL(通常是丢失的依赖项)或通过将恶意 DLL 加载到提升的进程中来赢得 DLL 文件写入竞争。如果启用了 UAC 但未设置为始终通知,则恶意软件可以执行提升的 IFileOperation(无 UAC 提示)以创建/复制/重命名或将 DLL 文件移动到可信路径(即 System32),然后触发提升的程序加载恶意 DLL,而不是预期的 DLL。

IFileOperation 由 dllhost.exe (COM Surrogate) 执行,其中 process.command_line 包含 classId {3AD05575-8857-4850-9277-11B85BDB8E09}。

我们可以使用以下 EQL 相关性来链接 dllhost.exe 的任何文件操作,然后将非 Microsoft 签名的 DLL 加载到以系统完整性运行的进程中

EQL search - UAC bypass via IFileOperation (Medium to System Integrity)

sequence by host.id
 [file where event.action in ("creation", "overwrite", "rename",
 "modification") and

  /* IFileOperation are performed by DllHost */
  process.name : "dllhost.exe" and user.id : "S-1-5-21-*" and

  /* executable file dropped via NewItem, Rename, Move or
  Copy IFileOperation */  (file.extension : "dll" or
  file.Ext.header_bytes : "4d5a*") and

  /* protected system paths usually abused via DLL search order hijack */
  file.path : ("?:\\Windows\\system32\\*",
               "?:\\Windows\\syswow64\\*",
               "?:\\Program Files (x86)\\Microsoft\\*",
               "?:\\Program Files\\Microsoft\\*"
               )] by file.path
 [library where
  /* non MS signed DLL loaded by a System Process */
  user.id : "S-1-5-18" and
  process.executable :
              ("?:\\Windows\\system32\\*",
               "?:\\Windows\\syswow64\\*",
               "?:\\Program Files (x86)\\Microsoft\\*",
               "?:\\Program Files\\Microsoft\\*") and
 not (dll.code_signature.subject_name : "Microsoft *" and
      dll.code_signature.trusted == true)] by dll.path

这是一个 UACME 30 侧加载 wow64log.dll 到作为系统运行的 WerFault.exe 实例(这提供了从中等完整性直接跳转到系统完整性的良好方式)的示例,如下所示。

如果 UAC 设置为始终通知,则查找丢失的 DLL 或在由中等完整性进程可写入的路径中赢得文件写入竞争是有效的选择。这是 UAC 绕过劫持 SilentCleanup 计划任务(通过文件写入竞争条件)的示例,它会生成一个高完整性后代进程 DismHost.exe,该进程从 AppData 子文件夹(中等完整性可写入)执行,这是 另一个变体,它滥用相同的任务,但用于缺少依赖项:api-ms-win-core-kernel32-legacy-l1.dll。

可以实现相同目标的另一个 DLL 劫持原语是使用 DLL 加载重定向,方法是在目标提升程序的同一目录中创建一个文件夹(例如 target_program.exe.local),并在其中放置一个 DLL,该 DLL 将被加载而不是预期的 DLL。

在允许将具有宽松访问控制列表的文件夹创建到受控位置的漏洞的情况下,此技术也可以用作本地权限提升的原语,如 Jonas Lykkegård 在此博客 从目录删除到 SYSTEM shell 中所述。

EQL search - Potential Privilege Escalation via DLL Redirection

library where user.id : "S-1-5-18" and
  dll.path : ("?:\\Windows\\system32\\*.exe.local\\*",
              "?:\\Windows\\syswow64\\*.exe.local\\*",
              "?:\\Program Files (x86)\\Microsoft\\*.exe.local\\*",
              "?:\\Program Files\\Microsoft\\*.exe.local\\*") and
 not (dll.code_signature.subject_name : "Microsoft *" and
      dll.code_signature.trusted == true) and
 process.executable :
              ("?:\\Windows\\system32\\*",
               "?:\\Windows\\syswow64\\*",
               "?:\\Program Files (x86)\\Microsoft\\*",
               "?:\\Program Files\\Microsoft\\*")

此查询匹配 UACME 方法 22,该方法以 consent.exe(作为系统执行)为目标,欺骗它从 SxS DotLocal 目录而不是 System32 加载 comctl32.dll

还值得一提的是,大多数通过 DLL 劫持进行的 UAC 绕过也对持久化很有用,并且可能会绕过基于 autoruns(已知文件和注册表持久化位置)的检测。

提升的 COM 接口

此方法与前面的方法略有不同,这意味着不涉及直接操作重定向。相反,它依赖于查找一个提升的 COM 接口,该接口公开某种形式的执行能力(即,CreateProcess / ShellExec 包装器),可以调用该接口来启动通过中等完整性进程的参数传递的特权程序。

从行为角度来看,通常情况下,这些 COM 接口会在 dllhost.exe(COM 代理)的上下文中执行,其 process.command_line 包含目标 COM 对象的 classId,这通常会导致创建高完整性的子进程。

以下是一些恶意软件家族采用此方法进行 UAC 绕过的示例(例如 DarkSideLockBit 勒索软件家族),以便在启动加密和规避功能之前提升完整性级别,这是一个很好的预防瓶颈。

令牌安全属性

James Forshaw 提出了一个有见地的观察,即可以利用进程令牌安全属性来识别作为自动提升应用程序后代启动的进程。

ProcessHacker 也会捕获此类信息。以下是通过 fodhelper UAC 绕过启动的 notepad.exe 实例的令牌属性示例。

LUA://HdAutoAp 属性表示它是一个自动提升的应用程序(也为提升的 COM 对象和 AppInfo 硬编码白名单进程填充)。LUA://DecHdAutoAp 表示它是自动提升应用程序的后代,这在跟踪通过 UAC 绕过生成的进程树时非常有用。

Elastic Endpoint security 7.16 及以上版本通过进程执行事件 (process.Ext.token.security_attributes) 捕获此信息,这为我们提供了机会去搜索和检测 UAC 绕过,这些绕过劫持了自动提升程序或 COM 接口的执行流程,而无需事先了解绕过的具体细节(目标二进制文件、COM 接口、重定向方法和其他重要细节)。

可疑的自动提升程序子进程

EQL search - Detecting UAC bypass via Token Security Attributes

process where event.action == "start" and
   process.Ext.token.integrity_level_name : ("high", "system") and
   process.parent.command_line != null and
   /* descendant of an auto-elevated application or COM object */
   process.Ext.token.security_attributes : "LUA://DecHdAutoAp" and
    (
      /* common lolbins, evasion and proxy execution programs */
      process.pe.original_file_name :
                 ("rundll32.exe",
                  "cmd.exe",
                  "pwsh*",
                  "powershell.exe",
                  "mshta.exe",
                  "msbuild.exe",
                  "regsvr32.exe",
                  "powershell.exe",
                  "cscript.exe",
                  "wscript.exe",
                  "wmic.exe",
                  "installutil.exe",
                  "msxsl.exe",
                  "Microsoft.Workflow.Compiler.exe",
                  "ieexec.exe",
                  "iexpress.exe",
                  "RegAsm.exe",
                  "installutil.exe",
                  "RegSvcs.exe",
                  "RegAsm.exe",
                  "javaw.exe",
                  "reg.exe",
                  "schtasks.exe",
                  "sc.exe",
                  "net.exe",
                  "net1.exe",
                  "vssadmin.exe",
                  "bcdedit.exe",
                  "wbadmin.exe",
                  "msiexec.exe") or

       /* suspicious or unusual paths */
       process.executable : ("?:\\Windows\\Microsoft.NET\\*",
                             "?:\\Users\\Public\\*",
                             "?:\\Programdata\\*",
                             "?:\\Windows\\Temp\\*",
                             "?:\\Windows\\Tasks\\*",
                             "?:\\Windows\\System32\\Tasks\\*") or

       /* MS signed but from unusual paths */
       (process.code_signature.trusted == true and
        process.code_signature.subject_name : "Microsoft *" and
        not process.executable : ("?:\\Windows\\system32\\*.exe",
                                  "?:\\Windows\\SysWOW64\\*.exe",
                                  "?:\\Program Files\\*.exe",
                                  "?:\\Program Files (x86)\\*",
                                  "?:\\ProgramData\\Microsoft\\*",
                      "\\Device\\HarddiskVolume*\\Windows\\System32\\*.exe",
                      "\\Device\\HarddiskVolume*\\Windows\\SysWOW64\\*.exe") and

        /* runs from temp folder and invoked by different elevated processes */
        not process.pe.original_file_name == "DismHost.exe"
       ) or

    /* elevated and unsigned or untrusted programs excluding
      third party uninstallers executed via appwiz.cpl */
      ((process.code_signature.trusted == false or
      process.code_signature.exists == false) and
        not (process.parent.name : "dllhost.exe" and
          process.parent.command_line :
          "*FCC74B77-EC3E-4DD8-A80B-008A702075A9*"))) and

  /* Rundll32 FPs */
  not (process.name : "rundll32.exe" and
       process.args :
         ("devmgr.dll,DeviceProperties_RunDLL",
         "?:\\Windows\\system32\\iesetup.dll,IEShowHardeningDialog") and
       process.parent.name : ("dllhost.exe", "ServerManager.exe")) and

  /* uninstallers executed via appwiz.cpl */
  not (process.args : "/uninstall" and
       process.parent.name : "dllhost.exe" and
       process.parent.command_line : "*FCC74B77-EC3E-4DD8-A80B-008A702075A9*")
       and

  /* server manager may spawn interactive powershell commands */
  not (process.name : "powershell.exe" and
       process.parent.executable : "?:\\Windows\\System32\\ServerManager.exe")
       and

 /* Windows Installer service descendants */
 not (process.parent.executable : "?:\\Windows\\System32\\msiexec.exe" and
      process.parent.args : "/V")

上述查询还可以匹配 UAC 绕过的所有后代,而不仅仅是直接子进程。

在这里,我们可以看到这种方法通过注册表键操作检测 fodhelper 执行流劫持。

这是通过模拟受信任目录匹配 UAC 绕过的示例。

以下是通过提升的 COM 接口匹配 3 种不同 UAC 绕过的示例。

检测规避

hFireF0X博客文章中讨论了许多规避技术,这些技术不仅限于 UAC 绕过,例如重命名文件夹或注册表项、注册表符号链接以破坏基于特定文件路径/注册表项更改的检测逻辑,或者通过同一进程关联不同的事件。尽管大多数恶意软件家族并不费心修改和调整这些技术,但考虑这些规避机会对于提高弹性是必不可少的。

以下是通过目录重命名进行文件监控规避的示例(UACME 22)。

这是通过键重命名进行注册表项路径监控规避的示例(byeintegrity8)。

最近添加到UACME v.3.5.7的另一个有趣的规避技巧是CurVer子项,它可以用于重定向 shell 默认处理程序。这有效地绕过了查找硬编码的可疑注册表路径/值的检测。

对于与 DLL 劫持相关的文件检测,最好使用 DLL 加载事件(Elastic Endpoint Security 7.16 记录非 Microsoft 签名的 DLL)。对于注册表检测,注册表.data.strings 和值名称的混合使用应该比完整的键路径更具弹性。

下面的 EQL 相关性示例显示了如何检测从伪装成 System32 的目录加载 DLL(即由于 windir/systemroot 环境变量修改)

EQL search - Detect redirection via rogue Windir/SystemRoot

sequence by process.entity_id with maxspan=1m
  [process where event.action == "start" and
    /* any process running as high or system integrity */
    process.Ext.token.integrity_level_name : ("high", "system")]
  [library where dll.path :
    /* masquerading as windir/system root */
    ("?:\\*\\System32\\*.dll", "?:\\*\\SysWOW64\\*.dll") and
    not dll.path :
           ("?:\\Windows\\System32\\*.dll","?:\\Windows\\Syswow64\\*.dll") and
    not (dll.code_signature.subject_name : "Microsoft *" and
         dll.code_signature.trusted == true)]

此示例显示了针对 2 种不同技术的匹配(注册表键操作和通过伪造的 Windir 进行 DLL 劫持)

下一个示例结合了注册表符号链接和注册表项重命名,以规避基于注册表项更改监控 (ms-settings 或 shell\open\command) 的 fodhelper UAC 绕过检测。

UACME v.3.5 及以上版本实现了这种针对涉及注册表键操作的方法的规避。

您可以通过查找值名称等于 SymbolicLinkValue 的注册表修改来使用 Elastic Endpoint 或 Sysmon 日志搜索注册表符号链接创建。

用于检测此规避的 KQL 查询示例为:registry.value :"SymbolicLinkValue" and registry.key :S-1-5-21-15_Classes_*`

最常见的 UAC 绕过

在野外使用的恶意软件家族不断变化。下面您可以快速概览恶意软件家族使用的最常见的 UAC 绕过方法。

方法恶意软件家族
通过 ICMLuaUtil 提升的 COM 接口进行 UAC 绕过DarkSide, LockBit, TrickBot
通过 ComputerDefaults 执行劫持进行 UAC 绕过ClipBanker, Quasar RAT
通过控制面板执行劫持进行 UAC 绕过AveMaria, Trojan.Mardom
通过 DiskCleanup 计划任务劫持进行 UAC 绕过RedLine Stealer, Glupteba
通过 FodHelper 执行劫持进行 UAC 绕过Glupteba, BitAT dropper
通过 Windows 目录伪装进行的 UAC 绕过尝试Remcos RAT

通过 UAC 绕过执行的最常见命令是恶意软件以高完整性重新执行自身或防御规避技术,例如:

  • 篡改 AV 排除项或状态
  • 写入 HKLM 受保护的注册表项
  • 篡改系统恢复设置

结论

通过关注攻击性技术的关键构建块来设计检测比试图覆盖无休止的实现和潜在的规避调整更具成本效益。在这篇文章中,我们介绍了用于 UAC 绕过的主要方法以及如何检测它们,以及如何通过使用令牌安全属性丰富进程执行事件,使我们能够创建更广泛的检测逻辑,以匹配未知的绕过。

除了此博客文章中强调的更广泛的检测之外,Elastic Endpoint Security 还为 UAC 绕过提供了 26 个预构建的端点行为保护。

参考资料