Java代码审计——XML 外部实体注入(XXE)
创始人
2024-02-20 12:37:58
0

目录

前言:

(一)XML 的常见接口

1.XMLReader

2.SAXBuilder

3.SAXReader

4.SAXParserFactory

5.Digester

6.DocumentBuilderFactory

(二)XXE 漏洞审计

(三)XXE 漏洞修复

(四)小结


前言:

XXE 为 XML 外部实体注入。当应用程序在解析 XML 输入时,在没有禁止外部实体的加载而导致加载了外部文件及代码时,就会造成 XXE 漏洞。XXE 漏洞可以通过 file 协议或是 FTP 协议来读取文件源码,当然也可以通过 XXE 漏洞来对内网进行探测或者功击,如图 2-96 所示。漏洞危害有:任意文件读取、内网探测、攻击内网站点、命令执行、DOS 攻击等。
图 1-0 XXE 漏洞执行流程

 

(一)XML 的常见接口


想要了解 XXE 漏洞,我们首先需要知道常见的能够解析 XML 的方法,在 Java 中我 们一般用以下几种常见的接口来解析 XML 语言。

1.XMLReader


        XMLReader 接口是一种通过回调读取 XML 文档的接口,其存在于公共区域中。XMLReader 接口是 XML 解析器实现 SAX2 驱动程序所必需的接口,其允许应用程序设置和查询解析器中的功能和属性、注册文档处理的事件处理程序,以及开始文档解析。当XMLReader 使用默认的解析方法并且未对 XML 进行过滤时,会出现 XXE 漏洞。
try { XMLReader xmlReader = XMLReaderFactory.createXMLReader();xmlReader.parse(new InputSource(new StringReader(body))); } catch (Exception e) { return EXCEPT; }

2.SAXBuilder


        SAXBuilder 是一个 JDOM 解析器,其能够将路径中的 XML 文件解析为 Document 对象。SAXBuilder 使用第三方 SAX 解析器来处理解析任务,并使用 SAXHandler 的实例侦听 SAX 事件。当 SAXBuilder 使用默认的解析方法并且未对 XML 进行过滤时,会出现XXE 漏洞
try { String body = WebUtils.getRequestBody(request); logger.info(body); SAXBuilder builder = new SAXBuilder(); // org.jdom2.Document document builder.build(new InputSource(new StringReader(body))); // cause xxe return "SAXBuilder xxe vuln code"; } catch (Exception e) { logger.error(e.toString()); return EXCEPT; }

3.SAXReader


DOM4J 是 dom4j.org 出品的一个开源 XML 解析包,使用起来非常简单,只要了解基本的 XML-DOM 模型,就能使用。DOM4J 读/写 XML 文档主要依赖于 org.dom4j.io包,它有 DOMReader 和 SAXReader 两种方式。因为使用了同一个接口,所以这两种方式的调用方法是完全一致的。同样的,在使用默认解析方法并且未对 XML 进行过滤时,其也会出现 XXE 漏洞。
try { String body = WebUtils.getRequestBody(request); logger.info(body); SAXReader reader = new SAXReader(); // org.dom4j.Document document reader.read(new InputSource(new StringReader(body))); // cause xxe } catch (Exception e) { logger.error(e.toString()); return EXCEPT; }

4.SAXParserFactory


SAXParserFactory 使应用程序能够配置和获取基于 SAX 的解析器以解析 XML 文档。其受保护的构造方法,可以强制使用 newInstance()。跟上面介绍的一样,在使用默认解析方法且未对 XML 进行过滤时,其也会出现 XXE 漏洞。
try { String body = WebUtils.getRequestBody(request); logger.info(body); SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser parser = spf.newSAXParser(); parser.parse(new InputSource(new StringReader(body)), new DefaultHandler()); // parse xmlreturn "SAXParser xxe vuln code"; } catch (Exception e) { logger.error(e.toString()); return EXCEPT; }

5.Digester


Digester 类用来将 XML 映射成 Java 类,以简化 XML 的处理。它是 Apache Commons库中的一个 jar 包:common-digester 包。一样的在默认配置下会出现 XXE 漏洞。其触发的 XXE 漏洞是没有回显的,我们一般需通过 Blind XXE 的方法来利用:
 try { String body = WebUtils.getRequestBody(request); logger.info(body); Digester digester = new Digester(); digester.parse(new StringReader(body)); // parse xml } catch (Exception e) { logger.error(e.toString()); return EXCEPT; }

6.DocumentBuilderFactory


       javax.xml.parsers 包中的 DocumentBuilderFactory 用于创建 DOM 模式的解析器对象, DocumentBuilderFactory 是一个抽象工厂类,它不能直接实例化,但该类提供了一个 newInstance()方法,这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对 象并返回。
try { String body = WebUtils.getRequestBody(request); logger.info(body); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); StringReader sr = new StringReader(body); InputSource is = new InputSource(sr); Document document = db.parse(is); // parse xml // 遍历 xml 节点 name 和 value StringBuffer buf = new StringBuffer(); NodeList rootNodeList = document.getChildNodes(); for (int i = 0; i < rootNodeList.getLength(); i++) { Node rootNode = rootNodeList.item(i); NodeList child = rootNode.getChildNodes(); for (int j = 0; j < child.getLength(); j++) { Node node = child.item(j); buf.append(node.getNodeName() + ":" + node.getTextContent() + "\n");} } sr.close(); return buf.toString(); }catch (Exception e) { logger.error(e.toString()); return EXCEPT; }

(二)XXE 漏洞审计


        XXE 漏洞的审计方法和其他漏洞类似,只是搜索的关键字有所不同。这次我们采用了 GitHub 开源的 XXE 靶场:XXE-Lab 来进行审计。通过搜索 XML 的常见关键字,可以快速地对关键代码进行定位,如图 2-1 所示。
图 2-1 搜索关键代码

 

        根据搜索结果可知,使用了“DocumentBuilderFactory.newInstance()”,因此可通过双击跟进对应代码段查看 XML 是否可控。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, 
IOException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; String result=""; try { db = dbf.newDocumentBuilder(); Document doc = db.parse(request.getInputStream()); String username = getValueByTagName(doc,"username"); String password = getValueByTagName(doc,"password"); if(username.equals(USERNAME) && password.equals(PASSWORD)){ result = String.format("%d%s        ",1,username); }else{ result = String.format("%d%s",0,username); } } catch (ParserConfigurationException e) { e.printStackTrace(); result = String.format("%d%s",3,e.getMessage()); } catch (SAXException e) { e.printStackTrace();result = String.format("%d%s",3,e.getMessage()); } response.setContentType("text/xml;charset=UTF-8"); response.getWriter().append(result); 
}
通过通读源码可以发现:程序首先通过“DocumentBuilderFactory.newInstance()”获 取一个“DocumentBuilderFactory”的实例,随后通过“DocumentBuilder::parse”来解析 通过“request.getInputStream()”传入的 body 内容,并返回一个 Document 实例,漏洞关 键代码如图 2-2 所示。
图 2-2 漏洞关键代码
继续跟进程序可以发现,其通过 getValueByTagName 函数获取了 username 节点的内容,并在对比数据后将其输出到页面中。也就是说,这里可以通过控制 username 的值来达到回显 XXE 的目的,如图 2-3 所示。
图 2-3 username 值可控

 

        审计到这里,我们已经清楚了漏洞的整个触发流程及原理。接下来,可以构造一个用于 XXE 漏洞审计的常见的 Payload。常见的 XXE Payload 如下所示:
 
 ]> 
&file;
        该源码中会将 username 节点的内容进行打印,因此我们只需将上面的 XXE 节点换为 username 即可被成功解析并输出,XXE 执行效果如图 2-4 所示。最终的 Payload 如下
 
 ]> 
&file;admin
图 2-4 XXE 执行效果

(三)XXE 漏洞修复


        对于 XXE 漏洞的防御比较简单,只需在使用 XML 解析器时设置其属性,禁用 DTD或者禁止使用外部实体,一般常见的修复方案如下所示:
//实例化解析类之后通常会支持三个配置
obj.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 
obj.setFeature("http://xml.org/sax/features/external-general-entities", false); 
obj.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        当然,在使用不同的解析库时修复方案也会略有不同。值得注意的是, “DocumentBuilder builder = dbf.newDocumentBuilder();”这行代码需要写在 dbf.setFeature() 之后安全措施才能够生效,否则将无法防范 XXE 漏洞。以前面的实例代码为例,我们可以通过如下方法进行修复:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, 
IOException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db; String result=""; try { String FEATURE = null; FEATURE = "http://javax.xml.XMLConstants/feature/secure-processing"; dbf.setFeature(FEATURE, true); FEATURE = "http://apache.org/xml/features/disallow-doctype-decl"; dbf.setFeature(FEATURE, true); FEATURE = "http://xml.org/sax/features/external-parameter-entities"; dbf.setFeature(FEATURE, false); FEATURE = "http://xml.org/sax/features/external-general-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); db = dbf.newDocumentBuilder(); Document doc = db.parse(request.getInputStream()); String username = getValueByTagName(doc,"username"); String password = getValueByTagName(doc,"password"); if(username.equals(USERNAME) && password.equals(PASSWORD)){ result = String.format("%d%s",1,username); }else{ result = String.format("%d%s",0,username); } } catch (ParserConfigurationException e) { e.printStackTrace(); result = String.format("%d%s        ",3,e.getMessage()); } catch (SAXException e) { e.printStackTrace(); result = String.format("%d%s",3,e.getMessage()); } response.setContentType("text/xml;charset=UTF-8"); response.getWriter().append(result); 
}

(四)小结


        为了防御 XXE 漏洞,较为有效的处理方式是开发者在不影响系统业务的前提下做好安全配置。如果系统业务需要引用外部实体,应在后端做好参数校验。         此外,有许多第三方组件曾被爆出 XXE 漏洞(例如,Spring-data-XMLBean、 JavaMelody 组件 XXE、Apache OFBiz、微信支付 SDK-XXE),建议读者朋友关注这类安全问题,避免因为使用第三方组件而引入 XXE 漏洞。

 

相关内容

热门资讯

【法规】四川省村民委员会选举条... 欢迎关注“方志四川”! 四川省第十四届人民代表大会 常务委员会公告 第92号 《四川省村民委员会选举...
美欧就动用俄被冻结资产博弈升级... 欧盟于18日在比利时布鲁塞尔召开为期两天的峰会,欧盟领导人就是否动用俄被冻结资产为乌克兰提供资金援助...
江宁打造“法援惠民”闪亮名片 □本报记者 郑弋 本报通讯员 何郁 近年来,南京市江宁区法律援助中心紧扣“应援尽援、应援优援”目标,...
积极发展股权、债券等直接融资 ... “提高资本市场制度包容性、适应性”是“十五五”规划建议对资本市场作出的重要部署。中央经济工作会议要求...
欧洲两大央行公布2025年收官... 来源:财联社 北京时间周四晚间,英国央行和欧洲央行先后公布2025年最后一份议息决议,结果均符合事前...
泰国总检察长:与中方共同打击电... 原标题:驻泰国大使张建卫会见泰国总检察长易提蓬 12月18日,驻泰国大使张建卫会见泰国总检察长易提蓬...
视频丨美欧就动用俄被冻结资产博... 欧盟于18日在比利时布鲁塞尔召开为期两天的峰会,欧盟领导人就是否动用俄被冻结资产为乌克兰提供资金援助...
丝芭传媒披露鞠婧祎8年收入近1... 近日,艺人鞠婧祎与此前合作的上海丝芭文化传媒集团有限公司(后简称“丝芭传媒”)之间的合同纠纷事件引发...
骑手“拉黑权”上线,配送纠纷解... 本报记者 姜雨晴 阅读提示 经过前期调研和试点,“骑手屏蔽恶意顾客”功能近日已在全国上线。记者采访多...
海淀检方公益诉讼为高梁桥正名 北京青年报2月28日相关报道版面 在高梁桥文保牌旁,存在多年的错牌已被移走 被移走的水泥牌 ...