【⼲货】Spring远程命令执⾏漏洞(CVE-2022-22965)原理分析和思考
前⾔
上周⽹上爆出Spring框架存在RCE漏洞,野外流传了⼀⼩段时间后,Spring官⽅在3⽉31⽇正式发布了漏洞信息,漏洞编号为CVE-2022-22965。本⽂章对该漏洞进⾏了复现和分析,希望能够帮助到有相关有需要的⼈员进⼀步研究。
1►前置知识
1.1 SpringMVC参数绑定
为了⽅便编程,SpringMVC⽀持将HTTP请求中的的请求参数或者请求体内容,根据Controller⽅法的参数,⾃动完成类型转换和赋值。之后,Controller⽅法就可以直接使⽤这些参数,避免了需要编写⼤量的代码从HttpServletRequest中获取请求数据以及类型转换。下⾯是⼀个简单的⽰例:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@RequestMapping("/addUser")
public @ResponseBody String addUser(User user) {
return "OK";
}
}
public class User {
private String name;
private Department department;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
public class Department {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
当请求为:
/
addUser?name=test&department.name=SEC时,public String addUser(User user)中的user参数内容如下:
可以看到,name⾃动绑定到了user参数的name属性上,department.name⾃动绑定到了user参数的department属性的name属性上。
注意department.name这项的绑定,表明SpringMVC⽀持多层嵌套的参数绑定。实际上department.name的绑定是Spring通过如下的调⽤链实现的:
Department.setName()
假设请求参数名为foo.bar.baz.qux,对应Controller⽅法⼊参为Param,则有以下的调⽤链:
spring到底是干啥的Baz()
Baz.setQux() // 注意这⾥为set
SpringMVC实现参数绑定的主要类和⽅法是WebDataBinder.doBind(MutablePropertyValues)。
1.2 Java Bean PropertyDescriptor
PropertyDescriptor是JDK⾃带的java.beans包下的类,意为属性描述器,⽤于获取符合Java Bean规范的对象属性和get/set⽅法。下⾯是⼀个简单的例⼦:
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
public class PropertyDescriptorDemo {
public static void main(String[] args) throws Exception {
User user = new User();
user.setName("foo");
BeanInfo userBeanInfo = BeanInfo(User.class);
PropertyDescriptor[] descriptors = PropertyDescriptors();
PropertyDescriptor userNameDescriptor = null;
for (PropertyDescriptor descriptor : descriptors) {
if (Name().equals("name")) {
userNameDescriptor = descriptor;
System.out.println("userNameDescriptor: " + userNameDescriptor);
System.out.println("Before modification: ");
System.out.println("user.name: " + ReadMethod().invoke(user)); WriteMethod().invoke(user, "bar");
}
}
System.out.println("After modification: ");
System.out.println("user.name: " + ReadMethod().invoke(user));
}
}
}
userNameDescriptor: java.beans.PropertyDescriptor[name=name; values={expert=false; visualUpdate=false; hidden=false; enumerationValues=[Ljava.lang.Object;@5cb9f472; required=false}; propertyType=class
java.lang.String; readMethod=public java.lang.String cn.Name(); writeMethod=public void
cn.jidun.User.setName(java.lang.String)]
Before modification:
user.name: foo
After modification:
user.name: bar
从上述代码和输出结果可以看到,PropertyDescriptor实际上就是Java Bean的属性和对应get/set⽅法的集合。
1.3 Spring BeanWrapperImpl
在Spring中,BeanWrapper接⼝是对Bean的包装,定义了⼤量可以⾮常⽅便的⽅法对Bean的属性进⾏访问和设置。BeanWrapperImpl类是BeanWrapper接⼝的默认实现,BeanWrapperImpl.wrappedObject属性即为被包装的Bean对象,BeanWrapperImpl对Bean的属性访问和设置最终调⽤的是PropertyDescriptor。
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
public class BeanWrapperDemo {
public static void main(String[] args) throws Exception {
User user = new User();
user.setName("foo");
Department department = new Department();
department.setName("SEC");
user.setDepartment(department);
BeanWrapper userBeanWrapper = new BeanWrapperImpl(user);
userBeanWrapper.setAutoGrowNestedPaths(true);
System.out.println("userBeanWrapper: " + userBeanWrapper);
System.out.println("Before modification: ");
System.out.println("user.name: " + PropertyValue("name"));
System.out.println("user.department.name: " + PropertyValue("department.name")); userBeanWrapper.setPropertyValue("name", "bar");
userBeanWrapper.setPropertyValue("department.name", "IT");
System.out.println("After modification: ");
System.out.println("user.name: " + PropertyValue("name"));
System.out.println("user.department.name: " + PropertyValue("department.name"));
}
}
userBeanWrapper: org.springframework.beans.BeanWrapperImpl: wrapping object [cn.jidun.User@1d371b2d] Before modification:
user.name: foo
user.department.name: SEC
After modification:
user.name: bar
user.department.name: IT
从上述代码和输出结果可以看到,通过BeanWrapperImpl可以很⽅便地访问和设置Bean的属性,⽐直接使⽤PropertyDescriptor要简单很多。
1.4Tomcat AccessLogValve 和 access_log
Tomcat的Valve⽤于处理请求和响应,通过组合了多个Valve的Pipeline,来实现按次序对请求和响应进⾏⼀系列的处理。其中AccessLogValve⽤来记录访问⽇志access_log。Tomcat的l中默认配置了AccessLogValve,所有部署在Tomcat中的Web应⽤均会执⾏该Valve,内容如下:
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
下⾯列出配置中出现的⼏个重要属性:
· directory:access_log⽂件输出⽬录。
· prefix:access_log⽂件名前缀。
· pattern:access_log⽂件内容格式。
· suffix:access_log⽂件名后缀。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论