/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
对象,调用parseValueScript
将script
进行处理得到结果,最后根据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判断所以preparePart
和leftScript
变量为空,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
是调用FormulaParserByJS
的parseValueScript
方法,而该方法是调用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.js
、xx.tmpl
等静态资源后缀进行权限绕过,访问漏洞路由。
79 篇文章
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!