长亭百川云 - 文章详情

RASP漏洞防御之 XXE 漏洞

RASP安全技术

68

2024-07-13

简介

当应用是通过用户上传的XML文件或POST请求进行数据的传输,并且应用没有禁止XML引用外部实体,也没有过滤用户提交的XML数据,那么就会产生XML外部实体注入漏洞。

XXE 漏洞在owasp2021中位置:

A05:2021 – Security Misconfiguration CWE-611 Improper Restriction of XML External Entity Reference(XXE)

防护

使用语言中推荐的禁用外部实体的方法

这里以 Java 语言为例子说明。

使用XML库的Java应用程序特别容易受到XXE攻击,因为大多数Java XML解析器的默认设置是启用XXE。为了安全地使用这些解析器,必须在使用的解析器中显式禁用XXE。下面描述如何在最常用的Java XML解析器中禁用XXE。

01.DocumentBuilderFactory

javax.xml.parsers.DocumentBuilderFactory

`DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();``String FEATURE = null;``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);`

02.Dom4j

org.dom4j.io.SAXReader

`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);`

03.Jdom

org.jdom2.input.SAXBuilder、 org.jdom.input.SAXBuilder

`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));`

04.XMLInputFactory

javax.xml.stream.XMLInputFactory

`// This disables DTDs entirely for that factory``xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);``// This causes XMLStreamException to be thrown if external DTDs are accessed.``xmlInputFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");``// disable external entities``xmlInputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);`

05.XMLReader

org.xml.sax.XMLReader

`XMLReader reader = XMLReaderFactory.createXMLReader();``reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);``// This may not be strictly required as DTDs shouldn't be allowed at all, per previous line.``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);`

RASP防护

上面的修复方式在具体实施时存在缺陷:

  • 如果读取xml的代码在第三方包中(或者依赖框架),一般很难直接修改,必须pr框架/包提供者来修复;

这里以dom4j为例子来说RASP如何修复XX漏洞

`package com.example.controller;``   ``import org.dom4j.Document;``import org.dom4j.Element;``import org.dom4j.io.SAXReader;``import org.springframework.http.ResponseEntity;``import org.springframework.web.bind.annotation.PostMapping;``import org.springframework.web.bind.annotation.RequestMapping;``import org.springframework.web.bind.annotation.RestController;``   ``import java.io.ByteArrayInputStream;``   ``@RestController``@RequestMapping("/xxe/dom4j")``public class Dom4jController {`    `    @PostMapping("/post/dom4j.do")`    `public ResponseEntity<String> documentBuilder1(String xml, int fix) throws Exception {`        `return ResponseEntity.ok(dom4j(xml, fix));`    `}``   `    `public static String dom4j(String xml, int fix) throws Exception {`        `SAXReader saxReader = new SAXReader();`        `// 启用修复方式`        `if (fix == 1) {`            `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);`            `saxReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);`        `}`        `ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xml.getBytes());`        `Document document = saxReader.read(byteArrayInputStream);`        `Element element = document.getRootElement();`        `return element.getText();`    `}``}``   `

dom4j版本

 `<dependency>`    `<groupId>org.dom4j</groupId>`    `<artifactId>dom4j</artifactId>`    `<version>2.0.0</version>``</dependency>`

POC

`curl --location --request POST 'http://localhost:8080/xxe/dom4j/post/dom4j.do' \``--header 'Content-Type: application/x-www-form-urlencoded' \``--data-urlencode 'xml=<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><root>&xxe;</root>' \``--data-urlencode 'fix=0'`

读取 /etc/passwd文本内容

返回结果:

`##``# User Database``#` `# Note that this file is consulted directly only when the system is running``# in single-user mode.  At other times this information is provided by``# Open Directory.``#``# See the opendirectoryd(8) man page for additional information about``# Open Directory.``##``nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false``root:*:0:0:System Administrator:/var/root:/bin/sh``daemon:*:1:1:System Services:/var/root:/usr/bin/false``...``   `

模块插件举例

    `/**`     `* read重载方法最终调用 read(InputSource)`     `*`     `* @see org.dom4j.io.SAXReader#read(InputSource)`     `*/`    `public void closeDom4jXXE() {`        `final String className = "org.dom4j.io.SAXReader";`        `final String methdName = "read";`        `new EventWatchBuilder(moduleEventWatcher)`                `.onClass(className)`                `.includeBootstrap()`                `.onBehavior(methdName)`                `.withParameterTypes("org.xml.sax.InputSource")`                `.onWatch(new AdviceListener() {`                    `@Override`                    `public void before(Advice advice) throws Throwable {`                        `if (!enableCheck) {`                            `return;`                        `}`                        `SAXReader saxReader = (SAXReader) advice.getTarget();`                        `saxReader.setFeature(FEATURE_DEFAULTS_1, true);`                        `saxReader.setFeature(FEATURE_DEFAULTS_2, false);`                        `saxReader.setFeature(FEATURE_DEFAULTS_3, false);`                    `}``   `                    `@Override`                    `protected void afterThrowing(Advice advice) throws Throwable {`                        `requestInfoThreadLocal.remove();`                    `}`                `});`    `}``   `

    需要注意的是,JRASP 对Dom4j的hook类做了进一步的优化,hook了更加底层的方法(如下所示)。

public Document read(InputSource in) throws DocumentException

(经过debug和实际dump字节码发现,open-rasp 存在重复插桩、防护逻辑执行两次的问题。相关issue:https://github.com/baidu/openrasp/issues/396)

选取hook类一般是十分慎重,必须了解hook类的功能,选取类的一般性原则如下:

  • 能防护漏洞;

  • 选取更底层方法,避免被被绕过;

  • 在保证功能的基础上hook类尽量少;

目前JRASP已经具备上面5类常用XML解析器的漏洞防护插件。

----------------------------------------------------------------------------

XXE漏洞案例:

https://www.jrasp.com/case/CVE-2018-15531.html

https://www.jrasp.com/case/CVE-2018-1259.html

​​

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2