[javaweb]strut2-001漏洞分析

M發表於2022-01-16

Strut2-001

漏洞描述

框架解析JSP頁面標籤時會對使用者輸入的Value值獲取,在獲取對應的Value值中遞迴解析%{、}造成了二次解析,最終觸發表示式注入漏洞,執行任意程式碼

影響版本

2.0.1 ~ 2.0.8

漏洞分析

環境搭建了好久。。。最後看到一篇https://xz.aliyun.com/t/2672#toc-2,才成功,不知道錯誤在哪兒。最後發現是和tomcat的版本也有關係

建議:環境搭建能搭好即可,不要太深究為什麼。不要偏離主線--分析漏洞

Strut2中的資料流向:請求-》filterStack-》action-》view-》Client

  • 首先直接定位到ParametersInterceptor中的doFilter方法
    圖片

  • ParametersInterceptor先是建立了一個值棧,並通過setParameters進行儲值操作(後門要用,直接從值棧中查)
    圖片

  • 經過一系列的攔截器處理後,資料會成功進入實際業務 Action 。程式會根據 Action 處理的結果,選擇對應的 JSP 檢視進行展示,並對檢視中的 Struts2 標籤進行處理。如下圖,在本例中 Action 處理使用者登入失敗時會返回 error
    圖片

    然後開始對index,jsp中的struts2標籤進行處理

    也就是所謂的標籤渲染,把其中的標籤通過檔案解析替換為既定目標語法

    /**
    	TextParseUtil#translateVariables()分析
    */
    public static Object translateVariables(char open, String expression, ValueStack stack, Class asType, ParsedValueEvaluator evaluator) {
            // deal with the "pure" expressions first!
            //expression = expression.trim();
            Object result = expression;
    
            while (true) {
                int start = expression.indexOf(open + "{");
                int length = expression.length();
                int x = start + 2;
                int end;
                char c;
                int count = 1;
                while (start != -1 && x < length && count != 0) {
                    c = expression.charAt(x++);
                    if (c == '{') {
                        count++;
                    } else if (c == '}') {
                        count--;
                    }
                }
                end = x - 1;
    
                if ((start != -1) && (end != -1) && (count == 0)) {
                    String var = expression.substring(start + 2, end);
    
                    Object o = stack.findValue(var, asType);
                    if (evaluator != null) {
                    	o = evaluator.evaluate(o);
                    }
                    
    
                    String left = expression.substring(0, start);
                    String right = expression.substring(end + 1);
                    if (o != null) {
                        if (TextUtils.stringSet(left)) {
                            result = left + o;
                        } else {
                            result = o;
                        }
    
                        if (TextUtils.stringSet(right)) {
                            result = result + right;
                        }
    
                        expression = left + o + right;
                    } else {
                        // the variable doesn't exist, so don't display anything
                        result = left + right;
                        expression = left + right;
                    }
                } else {
                    break;
                }
            }
    
            return XWorkConverter.getInstance().convertValue(stack.getContext(), result, asType);
        }
    

    上面的函式大概的流程就是先找到%{param},然後提取中間的內容,把%{}左右兩邊的字串先截斷出來分為left,right,對param進行findValue,找到了就把left+value(param)+right進行重新拼接,如果沒有就直接將左右兩邊進行拼接。這是一個正常的標籤處理邏輯(學過編譯原理的應該很熟悉)。

    漏洞點:如果傳進去的引數值為%{1+1},到了29L: findValue(var, asType),那麼此時就會再次進行遞迴解析(感覺和ssti類似),從而觸發Expression Inject

漏洞利用

payload:

%{(new java.lang.ProcessBuilder(new java.lang.String[]{"calc.exe"})).start()}

將calc.exe替換成其他即可進行任意命令執行

圖片

漏洞修復

漏洞形成的原因無非就是遞迴解析字串造成的。所以只要不讓它遞迴解析即可。

圖片

最後官方給出了上面的修復,也就是限制預設的遞迴次數,而不是禁止遞迴。猜想官方應該是為了避免部分情況下還是需要進行遞迴解析留下了餘地吧。

相關文章