XXE全称是——XML External Entity,也就是XML外部实体注入攻击。该漏洞的核心主要是在对不安全的外部实体数据进行处理时引发的安全攻击问题。如果攻击者可以上传XML文档、或HTTP通信过程中采用XML格式传输数据,并且能够在XML文档中添加恶意内容,他们就能够攻击含有缺陷的XML处理器。XXE漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。
XXE漏洞的危害有很多,比如可以文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等。
概念解析
在讲解XXE之前,我们先来了解几个基础概念。
XML:全称“extensible markup language”可扩展标记语言,XML是一种用于存储和传输数据的语言。XML文档结构包括XML声明+DTD文档类型定义+文档元素,如下图:
(图1)
所有XML文档必须包含一个根节点,根节点是所有其他节点的父节点。
DTD:(document type definition)文档类型定义用于定义XML文档的结构,它作为xml文件的一部分位于XML声明根元素,ELEMENT关键字声明一个XML元素。
实体:是用于定义引用普通文本或特殊字符的快捷方式的变量。按照使用方式可分为内部实体和外部实体。
内部实体:使用!ENTITY关键字声明实体,实体本质是定义了一个变量,以图1为例变量名,值为“123”,后面在 XML 中通过 & 符号进行引用。
外部实体:用来引入外部资源。有SYSTEM和PUBLIC等关键字,表示实体来自本地计算机还是公共计算机。
外部实体可支持http、file等协议。不同程序支持的协议不同:
PCDATA:PCDATA的意思是被解析的字符数据。PCDATA是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。文本中的标签会被当作标记来处理,而实体会被展开。被解析的字符数据不应当包含任何&,字符,需要用& >
CDATA:CDATA意思是字符数据,CDATA 是不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
实体引用:在 XML 中一些字符拥有特殊的意义,如果把字符 < 放在 XML 元素中,便会发生错误,这是因为解析器会把它当作新元素的开始。
hello < world
为了避免这些错误,可以实体引用来代替 < 字符
<message>hello > worldmessage>
XML 中,有 5 个预定义的实体引用,分别为:
典型攻击案例
案例一:读取文件
如果被攻击服务有回显信息,可以直接将读取内容通过回显信息返回。对于没有回显的场景,可以通过blind xxe 漏洞将读取的信息通过指定指定的外带信道传递出去。
开启外部引用的XML解析器实现类:
public class DocumentBuilderFactory { public Document readXML(String path) throws ParserConfigurationException, IOException, SAXException { javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory.newInstance(); //dbf.setExpandEntityReferences(false); dbf.setExpandEntityReferences(true); DocumentBuilder builder = dbf.newDocumentBuilder(); // 读取xml文件内容 FileInputStream fis = new FileInputStream(path); InputSource is = new InputSource(fis); Document document = builder.parse(is); fis.close(); return document; }}
XML外部实例:
<?xml version="1.0" encoding="UTF-8"?> ]><root> <name>&name;name>root>
测试类:
@Testpublic void readXML() throws IOException, SAXException, ParserConfigurationException { Document document = documentBuilderFactory.readXML("src/test/resources/Payload.xml"); NodeList bookList = document.getElementsByTagName("name"); System.out.println(bookList.getLength()); for(int i=0;i if(bookList.item(i).getFirstChild().getNodeValue() != null){ System.out.println("文件被读取"); }else{ System.out.println("文件未被读取"); } }}
案例二:主机探测
对ip地址进行探测或者使用FTP协议探测.
<?xml version="1.0" encoding="utf-8"?> ]><root>&dtd;root>
案例三:执行命令
在安装了expect扩展的环境,可以直接执行攻击命令。
<?xml version="1.0" encoding="utf-8"?> ]><root>&dtd;root>
案例四:DDOS攻击
许多XML解析器在解析XML文档时倾向于将它的整个结构保留在内存中,用以提高解析效率,攻击者可以利用这点构造恶意的XML实体文件耗尽可用内存,最终造成拒绝服务器攻击。
<?xml version="1.0" encoding="utf-8"?> ]><lolz>&lol9;lolz>
该攻击通过创建一项递归的 XML 定义,在内存中生成十亿个”abc”字符串,从而导致 DDoS 攻击。
最佳防御实践:
XXE漏洞利用的是XML解析器支持外部实体引用这一特征,通过修改外部实体注入攻击脚本,实现攻击目的。
尽可能使用简单的数据格式(如:JSON)
禁用XML解析器外部实体引用功能
增加XML文本校验和过滤功能(、)
通过WAF进行XML危险特征语句的过滤和告警
以Java javax.xml.parsers.DocumentBuilderFactory 类库为例,关闭外部实例引用。
public Document readXML(String path) throws ParserConfigurationException, IOException, SAXException { javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory.newInstance(); dbf.setExpandEntityReferences(false); //dbf.setExpandEntityReferences(true); DocumentBuilder builder = dbf.newDocumentBuilder(); // 读取xml文件内容 FileInputStream fis = new FileInputStream(path); InputSource is = new InputSource(fis); Document document = builder.parse(is); fis.close(); return document;}
执行案例一种的测试方法,通过返回接口可判断出文件未被读取。