50

XXE防御笔记

 5 years ago
source link: https://www.tuicool.com/articles/B3maYnr
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

官方防御手册

https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html

DocumentBuilder

javax.xml.parsers.DocumentBuilderFactory

禁用外部实体

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
String FEATURE = null;
try {
    // This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all 
    // XML entity attacks are prevented
    FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
    dbf.setFeature(FEATURE, true);

    // If you can't completely disable DTDs, then at least do the following
    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);

    // Disable external DTDs as well
    FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
    dbf.setFeature(FEATURE, false);

    // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
    dbf.setXIncludeAware(false);
    dbf.setExpandEntityReferences(false);
} catch (Exception e) {
	...
}
DocumentBuilder safebuilder = dbf.newDocumentBuilder();

以上是官方推荐的修复方式,最主要的是第一条feature

"http://apache.org/xml/features/disallow-doctype-decl";

也就是XXE攻击失败时经常跳出来的一条语句

iY7ney7.png!web

以下为个人理解

如果没有特殊需求其实只要开启 http://apache.org/xml/features/disallow-doctype-decl 这一条,就可以禁止外部实体加载。

如果还是有加载外部实体的需求,就可以开启如下几条

> // 禁止加载外部实体
> 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);
> 
> // 禁用外部dtd
> FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
> dbf.setFeature(FEATURE, false);
> 
> dbf.setXIncludeAware(false);
> dbf.setExpandEntityReferences(false);
>

还看到有一种feature,貌似是一种安全的XML加载方式,禁止了http、file等一些协议的加载,具体没深究

FEATURE = "http://javax.xml.XMLConstants/feature/secure-processing";
dbf.setFeature(FEATURE, true);

需要注意的是, 设置feature是需要在 DocumentBuilder safebuilder = dbf.newDocumentBuilder(); 之前

如果在这之后进行操作,还是会存在XXE漏洞。错误示例

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dbf.newDocumentBuilder();
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);
builder.parse(ResourceUtils.getPoc1());

这样的操作是无效的。

SAXParserFactory & DOM4J

这两个的防御方式也是和 DocumentBuilder 一样的

SAXParserFactory spf = SAXParserFactory.newInstance();
// 如果没有特殊需求之开启下面这一句就够了
spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

// 如果不想完全禁用外部实体,可以设置如下,同理于DocumentBuilder
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);
SAXParser parser = spf.newSAXParser();
parser.parse(ResourceUtils.getPoc1(), (HandlerBase) null);

调用方式也是和之前一样,不能颠倒,先 setFeaturenewSAXParser

XMLInputFactory

防御方式

XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
// XMLInputFactory.SUPPORT_DTD = "javax.xml.stream.supportDTD"
xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
// XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES = "javax.xml.stream.isSupportingExternalEntities"
xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
XMLStreamReader parse = xmlInputFactory.createXMLStreamReader(ResourceUtils.getPoc1());
while (parse.hasNext()) {
    parse.next();
}

当设置了防御参数之后,就不会去解析外部实体的内容

neyq63q.png!web

TransformerFactory & Validator & SchemaFactory & SAXTransformerFactory

修复方式都类似,都是设置 XMLConstants.ACCESS_EXTERNAL_DTDXMLConstants.ACCESS_EXTERNAL_SCHEMA

public static final String ACCESS_EXTERNAL_DTD = "http://javax.xml.XMLConstants/property/accessExternalDTD";
public static final String ACCESS_EXTERNAL_STYLESHEET = "http://javax.xml.XMLConstants/property/accessExternalStylesheet"
public static final String ACCESS_EXTERNAL_SCHEMA = "http://javax.xml.XMLConstants/property/accessExternalSchema";

修复后的错误提示也都是一致的

mQJfEfv.png!web

每种修复方式都有设置 XMLConstants.ACCESS_EXTERNAL_DTD

XMLConstants.ACCESS_EXTERNAL_STYLESHEETXMLConstants.ACCESS_EXTERNAL_SCHEMA 根据不同场景开启

TransformerFactory

javax.xml.transform.TransformerFactory

这里是 ACCESS_EXTERNAL_STYLESHEET

TransformerFactory tf = TransformerFactory.newInstance();
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
StreamSource source = new StreamSource(ResourceUtils.getPoc1());
tf.newTransformer().transform(source, new DOMResult());

Validator

javax.xml.validation.Validator

这里是 ACCESS_EXTERNAL_SCHEMA

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, "");
StreamSource source = new StreamSource(ResourceUtils.getPoc1());
validator.validate(source);

SchemaFactory

javax.xml.validation.SchemaFactory

这里是 ACCESS_EXTERNAL_SCHEMA

SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
StreamSource source = new StreamSource(ResourceUtils.getPoc1());
Schema schema = factory.newSchema(source);

SAXTransformerFactory

javax.xml.transform.sax.SAXTransformerFactory

这里是 ACCESS_EXTERNAL_STYLESHEET

SAXTransformerFactory sf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
sf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
StreamSource source = new StreamSource(ResourceUtils.getPoc1());
sf.newTransformerHandler(source);

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);
reader.parse(new InputSource(ResourceUtils.getPoc1()));

和之前的 DocumentBuilder 是一样的,假如开启了第一句之后,后面的其实是可以省去的。

SAXReader

org.dom4j.io.SAXReader

这里比较特殊, 需要将三个防御全部开启

SAXReader saxReader = new 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);
saxReader.read(ResourceUtils.getPoc1());

在文档中特地标注了这样一句话

Based on testing, if you are missing one of these, you can still be vulnerable to an XXE attack. 

如果少了其中一句,还是会存在XXE攻击,至于具体的利用方法,emmm,目前暂未深究。

SAXBuilder

org.jdom2.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);
Document doc = builder.build(ResourceUtils.getPoc2());

关于这个,测试的话,仅开启后面两个是没用的。 需要将三个选项全部开启

JAXB Unmarshaller & XPathExpression

这两个操作都不支持直接的设置禁用外部实体,它不能单独安全地配置,因此必须首先通过另一个安全的XML解析器解析不受信任的数据。

JAXB Unmarshaller

javax.xml.bind.Unmarshaller 先生成安全的 SAXParserFactory ,再通过它来生成安全的 Unmarshaller

//Disable XXE
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);

//Do unmarshall operation
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);

或者其实这样也是可以的

SAXParserFactory spf = SAXParserFactory.newInstance();
Source xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(), 
                                new InputSource(new StringReader(xml)));
xmlReader.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true);
xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
xmlReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
JAXBContext jc = JAXBContext.newInstance(Object.class);
Unmarshaller um = jc.createUnmarshaller();
um.unmarshal(xmlSource);

然而其实还存在一种默认的写法,但是不会被日, 无需开启防御选项 (感觉正常人应该都是用这种写法的吧)

Class tClass = Some.class;
JAXBContext context = JAXBContext.newInstance(tClass);
Unmarshaller um = context.createUnmarshaller();
Object o = um.unmarshal(ResourceUtils.getPoc1());
tClass.cast(o);

XPathExpression

javax.xml.xpath.XPathExpression ,与 Unmarshaller 类似

DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");

XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
XPathExpression expr = xpath.compile("/person");
DocumentBuilder builder = df.newDocumentBuilder();
String result = expr.evaluate( builder.parse(ResourceUtils.getPoc1()));

XMLDecoder

java.beans.XMLDecoder

无解,等死吧,有解的话weblogic也就没那么多事了。

最后

不同的xml解析器的防御方式不尽相同,但大致可以分为如下几种。

也可以根据当前XXE的报错来知道大概是使用了哪种防御方式。

setFeature

xmlReader.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true);
xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
xmlReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", false);
xmlReader.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

iY7ney7.png!web

XMLConstants

df.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
df.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");

mQJfEfv.png!web

XMLInputFactory

这个比较小众

xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);

neyq63q.png!web

防御策略

由于一些需要开启全部选项才能防御的,而有些只要开启其中一部分选项就能进行防御。

面对这种情况,作为防御方,我们可以采取 security default 原则,让开发者尽可能的开启全部防御选项。

至于其他一些第三方库,比如 org.apache.poi 这种,则也是在内部调用了 SAXReader 这些库,并且没有设置禁用DTD。对于这种只需要升级到最新的版本即可解决。

Reference


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK