J2EE MVC模式框架中,表單資料繫結功能不安全實現在Tomcat下造成的DoS及RCE

wyzsk發表於2020-08-19

Nebula · 2014/04/10 11:27

0x00 背景


當這個《Struts2 Tomcat class.classLoader.resources.dirContext.docBase賦值造成的DoS及遠端程式碼執行利用!》在Tomcat下的利用出來以後,它其實秒殺的不是一個框架,而是所有表單資料繫結功能不安全實現的J2EE MVC模式框架(因為國內運營商共享協議的限制,遠端程式碼執行漏洞在國內難以大規模實現.但DoS漏洞還是存在的!).

0x01 細節


稍微列一下清單:

Struts1框架:

2013年已經停止更新了,所以不會有補丁,出於節操,報了一下官方,Struts2專案leader 波蘭小胖子 Lukasz Lenar 是這樣回覆的:

2014040921474585720.png

如圖:

2014040922063313735.png

webWork框架:

struts2框架的前身,由於與struts2框架合併,所以不會有補丁!

2014040922091186766.png

Struts2框架:

前面文章已經介紹了(不過發現,到現在很多重要廠商都沒修復!)

Spring 框架:

已經在cve-2010-1622中,補丁對class.classLoader的限制.但利用從此可以簡化!

還有就是,自己早年學習java時,寫的簡易框架,也可以打(只是表單填充需要多寫一行程式碼手動呼叫填充,而其他常規框架是自動的)40.gif

就以自己寫的框架說明問題:

表單引數繫結功能是MVC模式的框架一項非常重要的功能,比如:

沒有表單引數繫結功能的時代,我們填充javabean的屬性都需要:

#!java
... 
dto.setUserName(request.getParameter("userName"));  
dto.setPassWord(request.getParameter("passWord"));  
.... 
複製程式碼

框架表單引數繫結出現以後,就節約了很多硬編碼(這是框架的主要作用之一!)

通常實現表單引數繫結需要用到Java的兩個重要機制:

內省(introspector)與反射(reflection)

而Apache commons-beanutils元件就提供了javaBean的內省與反射操作簡化API

哥的框架使用commons-beanutils,實現表單引數繫結的部分程式碼塊:

#!java
... 
public static Object parseRequest(HttpServletRequest request, Object bean) throws ServletException, IOException {
        Enumeration enums = request.getParameterNames();
        while (enums.hasMoreElements()) {
            Object obj = enums.nextElement();
            try {
                Class cls = PropertyUtils.getPropertyType(bean, obj.toString());
                Object beanValue = ConvertUtils.convert(request.getParameter(obj.toString()), cls);
                HashMap map = new HashMap();
                BeanUtils.populate(bean, map);
                PropertyUtils.setProperty(bean, obj.toString(), beanValue);
            } catch (Exception e) {
                // e.printStackTrace();
            }
        }
        return bean;
    }
... 
複製程式碼

這裡不會框架的可以用servlet測試一下漏洞!

這裡有一個重要的問題,就是為什麼能訪問到基類Object.class ?

看內省機制,內省是 Java 語言對 Bean 類屬性、事件的一種預設處理方法。例如類 A 中有屬性 name, 那我們可以通過 getName,setName 來得到其值或者設定新的值。通過 getName/setName 來訪問 name 屬性,這就是預設的規則(*但是隻要有 getter/setter 方法中的其中一個,那麼 Java 的內省機制就會認為存在一個屬性)。

本身就業務邏輯講,訪問javaBean的屬性就夠了,是不需要訪問其他基類的Object.class,但是內省機制使用不當,就會造成這個問題.

問題非常簡單,在內省機制獲取javaBean屬性的一個小細節,測試程式碼:

#!java
public class TestBean {

    private String id;
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}



public class Test {

    public static void main(String[] args) throws IntrospectionException,
            IllegalArgumentException, IllegalAccessException,
            InvocationTargetException {

        TestBean dto = new TestBean();

        BeanInfo bi = Introspector.getBeanInfo(dto.getClass());

        // BeanInfo bi = Introspector.getBeanInfo(dto.getClass(),Object.class);

        PropertyDescriptor[] props = bi.getPropertyDescriptors();
        for (int i = 0; i < props.length; i++) {
            System.out.println(props[i].getName());
        }
        BeanInfo bi = Introspector.getBeanInfo(dto.getClass());
    }

}
複製程式碼

獲取包括父類的所有屬性,如圖:

2014040922595023935.png

如果不想獲取父類的屬性.可以使用

#!java
Introspector.getBeanInfo(dto.getClass(),Object.class);
複製程式碼

其中第二個引數就是終止遍歷到的父類(如:終止到基類Object),如圖(這才是表單繫結實現正確的做法):

2014040923033384924.png

然後只要屬性有set器,引數就會被填充,以及後面的Tomcat的一些屬性被掛載到class.classLoader下等原因,然後Tomcat的屬性被訪問到並且賦值.然後才是發生上面的利用!

0x02 總結


任何使用commons-beanutils元件或內省(及反射)不安全實現表單引數繫結功能的框架,都會受影響(當然,我這裡只測試了Tomcat,其他web容器感興趣的也可以測試一下利用思路!).

J2EE規範在現今使用極廣了,但如果不是Struts2漏洞,或許很少有人關注Java安全,這點很是奇怪!

Author: Nebula, HelloWorld security team

相關文章