原理
XML注入攻击和SQL注入攻击的原理一样,利用了XML解析机制的漏洞,如果系统对用户输入"<",">"没有做转义的处理,攻击者可以修改XML的数据格式,或者添加新的XML节点,就会导致解析XML异常,对流程产生影响。
如何注入攻击
<user role="guest">用户输入user>
<user role="guest">user1user>
<user role="admin">user2user>
@Test
public void testDom4j() {
Document document = DocumentHelper.createDocument();
Element user = document.addElement("user");
user.addAttribute("role","guest");
user.setText(REPLACE_XML);
String xml = document.asXML();
System.out.println(xml);
}
<user role="guest">user1</user><user role="admin">user2user>
@Test
public void testTrans() {
String xml = "\n" +
"%s ";
String replace = "user1user2" ;
String format = String.format(xml, xmlConversion(replace));
System.out.println(format);
}
/**
* 转义xml中不支持的特殊字符
*
* @param strXml
* @return
*/
private String xmlConversion(String strXml){
String conversionStr = "";
if(strXml == null){
return null;
}
conversionStr = strXml.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").
replaceAll("'","'").replaceAll("\"",""");
return conversionStr;
}
]>
<updateProfile>
<firstname>joefirstname>
<lastname>&file;lastname>
updateProfile>
具体解析代码
@Test
public void testXXE1() throws DocumentException {
File file = new File("d://xml//demo.xml");
SAXReader reader = new SAXReader();
Document document = reader.read(file);
Element rootElement = document.getRootElement();
String lastname = rootElement.element("lastname").getText();
System.out.println("get the password "+ lastname);// 这边代码会输出password.txt文件里面的内容
}
2. 使用参数实体和避免XML解析语法错误,构造恶意的实体解析
使用参数实体和
XML文件:构造参数实体 % start;% goodies;% end;% dtd 定义一个恶意的combine.dtd
<!ENTITY % end "]]>">
%dtd;
]>
<roottag>&all;roottag>
恶意DTD combine.dtd中定义实体&all;
<!ENTITY all "%start;%goodies;%end;">
甚至可以这样构造恶意的DTD combine.dtd,将结果发送到目标地址,最后会获得file:///etc/fstab文件。
”>
%send;
@Test
public void testXXE2() throws DocumentException, SAXException {
File file = new File("d://xml//demo.xml");
SAXReader reader = new SAXReader();
// 禁止解析DTDS
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); //禁止包含doctype
reader.setFeature("http://xml.org/sax/features/external-general-entities", false); //禁止外部实体解析
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); //禁止外部参数解析
Document document = reader.read(file);
Element rootElement = document.getRootElement();
String lastname = rootElement.element("lastname").getText();
System.out.println("get the password "+lastname);
}
这边对上面列到的testXXE方法做了处理,设置了禁止解析DTDs属性,同时也禁止了参数实体和外部实体的解析,该方式不仅可以防止XML的外部实体攻击也能防止XML内部实体攻击。
具体再运行代码,会抛出如下异常。可以看出DTDs已经被禁止。
@Test
public void testXXE3() throws DocumentException, SAXException {
File file = new File("d://xml//demo.xml");
SAXReader reader = new SAXReader();
// 禁止解析DTDS
reader.setFeature("http://xml.org/sax/features/external-general-entities", false); //禁止外部实体解析
reader.setFeature("http://xml.org/sax/features/external-parameter-entities", false); //禁止外部参数解析
Document document = reader.read(file);
Element rootElement = document.getRootElement();
String lastname = rootElement.element("lastname").getText();
System.out.println("get the password "+lastname);
}
这边具体运行之后是不能获得lastname的值,可以看到外部参数实体没有被解析出来。(此方法可以防止外部实体攻击,不能预防内部实体攻击)
3. 禁止解析外部实体
正确示例方法定义一个CustomResolver类来实现接口org.xml.sax.EntityResolver。在这个类中实现自定义的处理外部实体机制。自定义实体解析函数中使用一个简单的白名单,白名单范围里面则返回对应的文件内容,不在白名单范围里面的则返回一个空的实体解析内容。
// 自定义外部实体处理
private class CustomResolver implements EntityResolver {
// 自定义的一个白名单
String whitePath = "file:///d:/xml/password.txt";
@Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
if (systemId.equals(whitePath)) {
System.out.println("Resolving entity: " + publicId + " " + systemId);
return new InputSource(whitePath);
} else {
// 解析输入恶意的xml内容时,返回空
return new InputSource();
}
}
}
@Test
public void testXXE() throws DocumentException, SAXException {
File file = new File("d://xml//demo.xml");
SAXReader reader = new SAXReader();
// 设置自定义的外部实体处理
reader.setEntityResolver(new CustomResolver());
Document document = reader.read(file);
Element rootElement = document.getRootElement();
String lastname = rootElement.element("lastname").getText();
System.out.println("get the password "+lastname);
}
因为“file:///d:/xml/password.txt”这个路径是加在白名单里面的,所有上面的代码可以运行通过,能够解析到password.txt文件里面的内容。
如果不在白名单里面,会返回空,或者里面可以加上别的具体的业务逻辑。
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;lolz>
@Test
public void testXmlDos() throws SAXException, DocumentException {
File file = new File("d://xml//dos.xml");
SAXReader reader = new SAXReader();
// 这边设置实体个数不超过10000个
reader.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true);
Document document = reader.read(file);
String xml = document.asXML();
System.out.println(xml);
}
参考链接
https://xz.aliyun.com/t/3357#toc-0
https://www.freebuf.com/column/181064.html
项目地址
https://github.com/yzh19961031/blogDemo/tree/master/xml