某OA 历史RCE分析

某OA 历史RCE分析 payload /data/sys-common/datajson.js?s_bean=sysFormulaValidate&script=Runtime.getRuntime().exec("whoami"); 漏洞分析 分析payload可以看到漏...

某OA 历史RCE分析

payload

/data/sys-common/datajson.js?s_bean=sysFormulaValidate&script=Runtime.getRuntime().exec("whoami");

漏洞分析

分析payload可以看到漏洞是在/data/sys-common/datajson路由下,在源码中找到该路由的位置为lib目录下的com/xxx/kmss/common/actions/DataController.class

我们分析一下该方法逻辑,首先提取关键代码

String s_bean = request.getParameter("s_bean");
JSONArray array = new JSONArray();
JSONArray jsonArray = null;

try {
    Assert.notNull(s_bean, "参数s_bean不能为空!");
    RequestContext requestInfo = new RequestContext(request, true);
    String[] beanList = s_bean.split(";");
    List result = null;

    for(int i = 0; i < beanList.length; ++i) {
        IXMLDataBean treeBean = (IXMLDataBean)SpringBeanUtil.getBean(beanList[i]);
        result = treeBean.getDataList(requestInfo);

首先是从request请求中获取s_bean的值,通过;分号对s_bean进行分割,循环分割后的数组,通过SpringBeanUtil工具类的getBean来获取IXMLDataBean类型的bean对象,最后调用bean对象的getDataList将请求包数据作为参数传入。

我们跟进看看IXMLDataBean,发现其实是一个接口

public interface IXMLDataBean {
    List getDataList(RequestContext var1) throws Exception;
}

同时看到整个项目其接口实现类其实有四百个之多

根据payload,我们找到s_bean名为sysFormulaValidate,全局搜索找到该类的定义

可以看到其是IXMLDataBean的实现类,主要处理也是在getDataList方法中。
主要是获取请求参数中script的值,并new一个FormulaParser对象,调用parseValueScriptscript进行处理得到结果,最后根据value进行返回。

我们继续进入FormulaParser类中parseValueScript方法的实现

发现其是重载方法,该方法主要是调用this.parseValueScript(script)方法,我们继续跟进

该方法首先是创建一个Interpreter对象,其依赖来自Beanshell库,它可以执行java代码

BeanShell是一种脚本语言,一种完全符合java语法的java脚本语言,并且又拥有自己的一些语法和方法,beanShell是一种松散类型的脚本语言(这点和JS类似)

传入的script参数会进行trim(),返回通过提取$符号来对index进行赋值,在后面index>-1是进入if语句,而我们poc中并没有$符号,所以会跳过这个判断。

往后是对要执行的语句拼接:

final String m_script = importPart.toString() + preparePart.toString() + leftScript + rightScript;

因为没有进入if判断所以preparePartleftScript变量为空,importPart变量是已经经过处理的,rightScript变量就是请求参数中script的值;所以两者拼接最终得到m_script变量。

value = SecurityController.doPrivileged(new PrivilegedAction<Object>() {
    public Object run() {
        try {
            return interpreter.eval(m_script);
        } catch (EvalError var2) {
            FormulaParser.logger.warn("执行公式出错:" + m_script, var2);
            throw new EvalException(var2);
        }
    }
});

最后调用Interpreter对象的eval方法执行m_script,从而导致任意代码执行。

另外SysFormulaValidate类同级目录下还有一个类SysFormulaSimulateByJS,也是实现了IXMLDataBean接口。

与之不同的是,在SysFormulaSimulateByJS是调用FormulaParserByJSparseValueScript方法,而该方法是调用ScriptEngine对象的eval方法(该方法可执行javascript代码)。

最终也导致任意代码执行。

权限绕过

我们看到poc中该接口/data/sys-common/datajson.js最后是加了.js,而去掉.js是无法未授权访问该接口的,所以此时的.js其实是一种绕过方式,而.js结尾常常是静态文件,系统是如何成功识别到访问/data/sys-common/datajson.js/data/sys-common/datajson接口呢

其实这个问题是springmvc导致的,在springmvc版本<5.3之前后缀匹配模式参数useSuffixPatternMatch默认值为true

当开启后缀匹配模式时,路由匹配中/users会被映射到/users.*

具体调用是在PatternRequestCondition#getMatchingPattern中。

例如:controller路由定义为/admin,我们访问/admin.aaa

PatternRequestCondition#getMatchingPattern方法中,首先是判断模式与路径是否相等,如果相等就直接返回模式。
此时模式为/admin,请求路径为/admin.aaa,会进入else逻辑中处理:

首先是判断useSuffixPatternMatch是否为true,在org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping可以看到该值的初始化


前面也提到了在version<5.3的情况下默认为true, 从而进入下一层,由于不满足if判断,进入else语句中

else {
    boolean hasSuffix = pattern.indexOf(46) != -1;
    if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) {
        return pattern + ".*";
    }
}

判断pattern中是否存在.符号(这里46对应的Ascii码是.),如果不存在且pattern+".*"可以匹配到lookupPath时,返回pattern+".*"的字符串

后续因为.*的缘故,成功将/admin.aaa匹配到/admin

总结:当filter等权限认证对静态文件利用后缀进行放行时,就可能导致绕过。
如访问/api/admin 403,可以替换为/api/admin.html进行绕过

我们来看一下该系统的filter:

web.xml中找到了全局filter,通过注释找到sys\authentication\spring.xml配置文件

可以看到其是通过文件后缀来判断是否是静态资源,同时其springmvc为3.x.x版本,导致我们可以访问xx.jsxx.tmpl等静态资源后缀进行权限绕过,访问漏洞路由。

  • 发表于 2023-07-20 09:00:00
  • 阅读 ( 6813 )
  • 分类:漏洞分析

0 条评论

请先 登录 后评论
中铁13层打工人
中铁13层打工人

79 篇文章

站长统计