
我先说个真实场景。你正坐在工位上刚把一台Windows 11的机器加进域正准备装个内部软件结果UAC弹窗又跳出来了你要允许此应用对你的设备进行更改吗你顺手点了“是”。可你有没有想过这个弹窗本身就是Windows安全体系里最微妙的一道关卡——它拦得住普通人却几乎拦不住任何一位有经验的攻击者。而所有绕过UAC的手法里最经典、也最难防的一条路就是利用系统自己对“白名单程序”的信任让恶意代码借着受信任进程的身份悄悄提权。这篇文章我会围绕UAC白名单程序提权这个方向讲清楚UAC到底在防什么、白名单程序为什么能成为突破口、常见的绕过思路长什么样以及更重要的作为防守方你能从哪些痕迹里发现这类攻击。内容面向安全工程师、系统管理员、红蓝队入门学习者也欢迎对Windows内部机制感兴趣的朋友阅读。1. 先说清楚UAC到底在防什么1.1 标准令牌与管理令牌UAC的全称是User Account Control中文叫用户账户控制。从Vista时代引入到现在它的设计目标其实非常朴素让管理员账号平时也用“标准权限”干活只有在需要时才临时把完整的管理员权限“借”出来用一下。这里有个关键概念叫令牌Token。当你用管理员账户登录Windows时系统会同时创建两个访问令牌一个是完整的管理员令牌具有最高权限一个是过滤后的标准令牌权限被大幅削减。正常情况下你的桌面进程、资源管理器、浏览器等大部分进程都拿着标准令牌在跑。当你触发需要管理员权限的操作时UAC弹窗出现你点击“是”系统才会用完整令牌去启动那个需要提升的进程。这套机制的初衷是好的即使你以管理员身份登录日常操作也在标准权限下完成恶意软件想直接获取系统级权限至少得过UAC这一关。值得注意的是UAC并不是真正的安全边界。微软官方文档里其实明确写过UAC不是安全屏障它只是帮助管理员在标准权限和管理员权限之间切换的工具。真正的安全边界是标准用户和管理员用户之间的隔离。这意味着UAC绕过的研究本质上不是突破一个严格的边界而是绕过一道“交互式确认流程”。理解这一点很重要因为很多防守方容易高估UAC的作用以为只要开着UAC就安全了实际上它只解决“用户不小心授权”的问题。1.2 提权触发的典型路径当一个普通进程需要执行管理员操作时大概有这么几条路可以走通过ShellExecute或ShellExecuteEx调用并指定runas动词触发UAC弹窗。通过任务计划程序以最高权限运行某个任务部分情况下也会触发UAC。通过CMSTPLUA COM对象等接口触发提权。从攻击者的视角来看最理想的情况是不触发UAC弹窗或者只触发一次用户很容易点“是”的弹窗就能拿到完整令牌。于是研究重点就落在了两个方向上一个是怎么“骗过”用户点击“是”另一个是怎么“骗过”系统让它认为某个程序本身就值得信任自动提升权限连弹窗都省了。骗过用户点击“是”可以靠社会工程学比如伪造一个看起来很正规的弹窗而骗过系统则要利用Windows自身对“白名单程序”的信任机制。这也就是标题里说的“利用UAC白名单程序提权”。2. 白名单程序为什么能成为突破口2.1 autoElevate隐藏在清单里的信任标记打开任意一个Windows系统目录下的可执行文件用Visual Studio或者Resource Hacker查看它的Manifest清单文件你会发现有的程序里有这么一行requestedExecutionLevel levelhighestAvailable uiAccessfalse /这就是提权级别的声明。正常情况下一个程序如果要提权运行必须经过UAC弹窗确认。但有两种情况可以跳过弹窗。第一种情况如果当前用户是标准用户那么尝试以管理员身份运行时会显示凭据输入框要求输入管理员密码。如果你的账户本身就是管理员只是被UAC过滤成了标准令牌那么当程序清单里声明了requireAdministrator或highestAvailable时系统会直接弹窗询问——这也是一般情况。第二种情况也是关键所在如果可执行文件被标记为autoElevate也就是清单里带有autoElevate属性并且文件本身满足特定条件系统会自动为其赋予完整令牌完全跳过UAC弹窗。这个设计的本意是为了减少微软自家工具频繁弹窗的困扰比如Task Manager、Disk Cleanup、Registry Editor这些程序你运行时从来没有UAC弹窗原因就是它们声明了autoElevate。那么问题来了如果一个普通用户可以往某个autoElevate标记的可执行文件所在的目录里写入内容或者可以操控该程序的启动参数、DLL加载路径是不是就能让这个受信任进程替自己干活答案是可以。这正是白名单程序提权的核心逻辑利用系统对特定签名或特定路径下程序的信任借壳提权。2.2 系统签名與路径信任的双重标准要注意的是并非所有带autoElevate标记的程序都能绕过UAC。微软对这类程序有额外的要求可执行文件必须经过数字签名并且签名必须有效。更深层地说系统在判断时不仅看清单标记还会对文件的可信度做校验包括签名是否有效、程序是否位于受信任的安装目录等。不过这个信任体系并非无懈可击。漏洞往往出现在“信任链的末端”系统信任了一个程序不等于程序的所有行为都安全。如果这个程序本身存在DLL劫持漏洞即启动时会优先从当前目录或特定用户可写目录加载某些DLL那攻击者就可以把恶意DLL放在这些可写目录里然后触发程序启动恶意DLL就以完整令牌的权限被加载进进程。另外还有一类特殊情况部分微软签名工具支持通过命令行参数来指定要执行的操作甚至指定要运行的命令。如果这个工具的参数解析不够严格就可能被滥用来运行任意代码。近年来的研究中有不少这类例子比如通过某些系统自带工具的参数传递功能在无需UAC弹窗的情况下获得高权限。2.3 从设计理念看为什么难以根除回到根源上UAC白名单提权之所以反复出现新变种根本原因在于微软在“用户体验”和“安全”之间选择了前者。试想一下如果所有管理员操作都要弹出确认框那日常运维效率会极低。因此微软不断把自家工具加入白名单、标记autoElevate让常用操作更顺滑。但每多一个白名单程序就多一个潜在的攻击面。防御者必须接受一个事实这套机制不可能完全关闭。只要Windows还需要管理员白名单提权的攻击面就会持续存在。作为防守方能做的事情不是指望UAC拦住所有攻击而是把注意力放到攻击行为的外在特征上谁启动了那个受信任进程、命令行的内容是什么、有没有异常DLL被加载、日志里有没有可疑痕迹。3. 常见的绕过思路与检测视角3.1 利用系统自带工具的提权链路从攻击者的视角看最理想的白名单程序应该满足这三个条件默认存在于系统目录、声明了autoElevate、可以通过参数或文件操作影响它的行为。以“通过autoElevate程序执行指定命令”这种思路为例攻击者需要找到这样一个程序它既能自动提权又能让使用者控制它执行的内容。历史上出现过不少这类程序比如某些微软签名的命令行工具支持调用外部脚本或者某个系统工具会读取指定位置的配置文件而攻击者可以控制这个配置文件的内容。一旦找到这样的程序攻击者只需要一行命令或一次双击就能获取高权限。还有一类思路利用的是“环境变量差异”。有些autoElevate程序在启动时会使用当前用户可控的环境变量例如TMP、TEMP、PATH等。如果程序会调用命令行工具而调用时直接使用了不带完整路径的可执行文件名那么攻击者可以提前在当前用户的某个目录放一个同名的恶意程序并修改PATH环境变量让系统在搜索可执行文件时优先找到恶意版本。当autoElevate程序启动时它就会在完整令牌权限下执行攻击者指定的程序。3.2 竞态条件与文件操作类攻击刚才提到的DLL劫持就属于文件操作类攻击近年热词中出现的applockerfltr竟态提权也与竞态条件有关。这类攻击的共同特点是目标程序会在特定时机访问某个文件或注册表项而这个路径是否可控、是否存在可利用的时间窗口就是攻击成败的关键。举一个典型的场景某个系统服务或者autoElevate工具启动时需要从某个用户可写的临时目录读取配置文件。如果攻击者提前在该目录放置恶意文件并在程序读取前完成替换就能让程序加载到攻击者指定的内容。在防护层这类攻击很难通过简单签名校验来阻止因为程序本身是可信的、签名的恶意内容是从“侧门”混进去的。竞态条件类攻击更考验时机把握。攻击者需要监控目标程序的文件操作行为找到某个短暂的时间窗口在文件被创建之后、被校验之前完成替换。这类攻击成功率高、隐蔽性强但对攻击者的编程能力和耐心有一定要求。从防御角度看这类攻击几乎无法杜绝只能通过加强目录权限、开启受控文件夹访问、监控进程DLL加载行为来降低风险。3.3 从绕过方式反推检测点我把常见的UAC绕过思路梳理成了一张对照表方便大家从攻击手法直接对应到检测思路攻击类型典型手段关键检测点强制访问autoElevate程序利用可传递执行命令的参数接口进程创建日志中的命令行参数DLL劫持将恶意DLL放入目标程序可写目录DLL加载事件、模块列表异常环境变量注入修改PATH/TMP等变量预置同名程序环境变量变更、进程创建路径文件重解析点利用目录链接/Junction指向恶意文件文件系统变更审计、符号链接创建注册表项操控修改启动项或配置键值注册表审计事件很多防守方在收到告警后不知道怎么往下查其实就是因为提前没有做好“攻击行为画像”。比如如果你知道攻击者大概率会利用某个系统工具那你的检测策略就应该包含“该工具被启动时监控它的命令行、它加载的DLL、它访问的目录”。这比单纯盯着漏洞细节更有效因为漏洞可以被修复但行为模式是共通的。4. 如何发现自己被绕过了检测与加固4.1 开启关键的审计与日志检测UAC白名单提权的第一步不是装某个高级EDR而是先确保Windows自身的日志足够完整。你需要确认这几个日志类别已经开启审计进程创建Audit Process Creation对应事件ID 4688。审计命令行进程创建在4688中开启命令行参数记录。审计文件系统访问Audit File System。审计注册表访问Audit Registry。审计对象访问的详细跟踪。在组策略里开启命令行记录的方法是计算机配置 → 管理模板 → 系统 → 审核进程创建 → 勾选“在进程创建事件中包含命令行”。这个选项默认是关闭的不开启的话4688事件里看不到命令行排查时等于失去了一半证据。事件ID 4688是所有进程创建行为的基石建议配合Sysmon一起使用。Sysmon的ProcessCreate事件Event ID 1能提供更丰富的进程信息包括进程哈希、父进程、当前目录等对追踪白名单提权攻击很有帮助。4.2 利用Sysmon补充关键行为监控单纯靠系统日志很难发现DLL劫持和环境变量注入这类隐蔽行为。Sysmon可以有效弥补这块短板。推荐重点关注这几个事件类型Event ID 1进程创建重点看父进程为autoElevate程序的可疑子进程。Event ID 7DLL加载配合签名校验规则标记从用户可写目录加载的DLL。Event ID 11文件创建尤其是系统目录中突然出现的非微软签名文件。Event ID 13注册表值变更特别是Run键、环境变量键的修改。在实际配置时很多管理员嫌Sysmon规则难写直接用了默认配置结果日志量巨大真正有价值的告警被淹没。建议从严格的Include/Exclude规则入手先只监控高价值进程和路径逐步调优。比如对于DLL加载监控可以先只关注系统自带工具的可执行文件名称再添加排除规则过滤掉正常的系统路径。这样告警量会控制在一个可处理的范围内误报率也低很多。4.3 应用白名单AppLocker与WDAC的取舍如果说UAC是“运行时权限控制”那么应用白名单就是“启动前控制”。合理配置应用白名单可以大幅度压缩UAC绕过攻击的生存空间。Windows上主要有两个方案AppLocker和WDACWindows Defender Application Control。AppLocker胜在简单易用适合中小型环境。它按照可执行文件、Windows安装程序、脚本、打包应用等分类进行规则控制。你可以通过路径规则或发布者规则允许特定目录下的文件运行阻止其他路径下的可执行文件启动。WDAC是更新的方案能力更强但配置也更复杂。它支持基于代码完整性Code Integrity的校验即使文件名改掉、路径换掉只要哈希不匹配或签名不受信任就无法运行。对于对抗白名单程序提权WDAC可以做到“即使攻击者想用受信任程序提权也必须在已验证的环境中运行”显著抬高攻击门槛。需要注意应用白名单的威力在于“默认拒绝”不在与“默认允许排除”。如果你只是允许了几个目录但默认策略是允许一切那等于没做。真正有效的策略是默认禁用不认识的程序只允许已明确可执行的程序。当然这套策略的落地需要做充分的兼容性测试建议先在测试环境跑两周收集完整基线后再推广。4.4 应急排查清单速查表如果你怀疑某台机器已经被UAC白名单提权绕过攻击按下面这个顺序查效率最高步骤操作预期发现1检查4688事件中可疑进程的父子关系受信任程序启动异常子进程2用Sysmon Event 7排查高权限进程加载的DLL路径从临时目录或用户目录加载的DLL3检查受信任程序启动瞬间的文件系统活动用户目录下出现对应的DLL/文件4检查环境变量的变更记录PATH、TMP等变量被修改的痕迹5结合威胁情报平台排查进程哈希确认恶意程序家族与行为6审查注册表Run键和计划任务攻击者留下的持久化后门这套清单写起来简单实际操作时最耗时间的往往是第一步和第二步。因为你得从海量日志里筛出“看起来正常但仔细一想不对劲”的记录。比如那种父进程是System32下的正常工具、子进程却是临时目录里的exe的记录通常就是攻击行为。5. 实战复盘一次UAC绕过告警的完整排查5.1 从一条可疑的4688事件说起假设情景某天早上安全设备发出告警指出一台终端出现了可疑的进程创建行为。日志显示某个位于System32目录下的系统工具创建了一个子进程而这个子进程的运行目录是C:\Users\Public\Temp\。拿到告警后先不要急着下结论。打开完整的4688事件重点看几个字段进程命令行、父进程命令行、登录ID、进程运行账户。如果子进程的命令行是powershell.exe -enc ...基本可以判定为异常PowerShell编码命令是攻击行为的典型特征。接着还要确认父进程启动时的账户身份。如果这个账户是本地管理员账户并且父进程是autoElevate签名程序那基本可以认定这是一次UAC白名单提权尝试。攻击者触发受信任程序后该程序以完整令牌运行并执行了攻击者预设的子进程。5.2 顺着DLL加载记录抓到真正的加载体进程创建日志只是第一步。为了搞清楚攻击者到底放了多少东西在这台机器上还需要借助Sysmon的DLL加载记录逐层往下挖。具体做法是找出父进程启动期间加载的所有DLL模块对模块路径做一次排序把不在System32或正常的引用路径中的模块单独列出来。如果发现某个DLL位于C:\Users\Public\Temp\或者C:\ProgramData\某个子目录下那就要重点关注了。对可疑DLL提取哈希丢到威胁情报平台看关联信息往往能确认攻击者使用的载荷家族。在这个排查过程中有两点容易被忽略第一DLL加载记录可能非常庞杂需要先用路径白名单做过滤再在一轮告警里做人工确认第二DLL的创建时间很关键。如果DLL创建时间与父进程的启动时间高度吻合说明是临时释放的载荷如果DLL创建时间很早说明攻击者在更早的时间点就完成了植入。5.3 还原完整攻击链路与修复一个典型的UAC白名单提权攻击链路还原出来大致是这样攻击者通过钓鱼邮件或浏览器漏洞获得初始访问权限此时进程以标准令牌运行。攻击者将恶意DLL写入当前用户可写的一个目录。攻击者在命令行中使用某种方式触发了带有autoElevate签名的工具。该工具以完整令牌启动加载了攻击者放置的DLL。DLL中的代码以完整令牌权限执行攻击者借此关闭安全软件或创建管理员账户。针对这个链路修复的重点也不只是杀掉恶意进程就完事。必须做好这几件事删除所有可疑DLL和可执行文件、检查环境变量是否被持久化修改、审查计划任务和注册表启动项、修改受影响账户的密码、确认攻击者是否建立了新的管理员账户。修复完成后还应该把所有证据整理成时间线回推攻击者的初始入口。这一步做得越细致后续预防工作的针对性就越强。攻击者用哪条路径进来、在哪一步触发了告警、哪一步没被发现都是很有价值的复盘信息。6. 加固UAC环境的几个延伸思路最后补充几个系统层面的加固建议它们不属于某个单个漏洞的修复但对整体安全水位提升非常明显。第一个建议尽早把环境从“管理员权限日常使用”切换到“标准用户”。如果所有员工平时都用标准账户办公需要管理员权限时再通过单独的管理员账户执行操作那UAC提权攻击链的起点就会被直接切断。这也是很多企业安全体系建设到一定阶段后必经的一步。第二个建议关注微软每月补丁日中与Win32k、UAC、AppLocker相关的更新。这类漏洞通常修复周期短、利用公开早及时打补丁是最经济有效的防护手段。当然前面也说过补丁只能堵住已知漏洞行为监控才是应对未知变种的可靠手段。第三个建议做红队演练的时候把UAC绕过检测当成标准化检测项。不要只测漏洞利用的成功率更要测检测系统能不能发现利用行为的痕迹。在很多实战场景中攻击者真正厉害的地方不在于成功绕过了UAC而在于绕过了之后很长一段时间都没被发现。我在实际负责安全运营时最深的感受是UAC在终端用户面前是一个“很烦人”的弹窗但在安全人员眼里它是一个庞大攻击面的浓缩点。理解白名单程序提权不是为了掌握某一条具体的攻击手法生搬硬套而是为了建立起一套关于“Windows如何信任程序”的完整认知。有了这套认知无论是写检测规则、做应急响应还是设计终端加固方案都会有清晰的方向感。下次再遇到UAC弹窗除了点“是”你可能会下意识地想一下这个弹窗背后的信任体系究竟把哪些程序当成了自己人而这些自己人又有没有可能背叛你。