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 ...
随机推荐
- [转帖]Nacos使用2.0.1版本启动出现9848端口错误的解决方式(亲测有效)
目录 一.背景 二.报错如下 三.报错原因 四.解决方式 一.背景 nacos服务端和客户端都是 2.x版本. centos7使用原始安装nacos单机版,没有使用docker安装naocs集群. 二 ...
- [转帖]Jmeter中如何读取MYSQL数据作为请求参数
在项目测试过程中,我经常需要将数据库中的数据作为参数传递到请求中.Jmeter中MYSQL数据库连接操作过程如下: 1.下载/n导入mysql的jdbc驱动包 下载mysql驱动包地址: http:/ ...
- [转帖]linux块I/O总体概括
直接先上重点,linux中IO栈的完全图如下: 系统中能够随机访问固定大小数据片的硬件设备称作块设备.固定大小的数据片称为块.常见的块设备就是硬盘了.不能随机访问的就是字符设备了,管理块设备比字符设备 ...
- 【转帖】【奇技淫巧】Linux | 统计网络-netstat
theme: condensed-night-purple 小知识,大挑战!本文正在参与"程序员必备小知识"创作活动. 在构建生产服务器时,我们有的时候需要统计网络接口状况,比如T ...
- [转帖]超线程SMT究竟可以快多少?(AMD Ryzen版 )
https://www.modb.pro/db/139224 昨天我们用Intel I9的10核,每个核2个threads的机器跑了内核的编译: 超线程SMT究竟可以快多少? 今天,我换一台机器,采用 ...
- [转帖]如何理解 iowait
Linux中,%iowait 过高可能是个问题,严重的时候,它能使服务停止, 但问题是,多高才算高? 什么时候应该担心呢? 本文将讨论 iowait 的含义.相关的统计数据.原理以及 iowait的瓶 ...
- [转帖]Linux学习14-ab报错apr_pollset_poll: The timeout specified has expired (70007)
https://www.cnblogs.com/yoyoketang/p/10255100.html 前言 使用ab压力测试时候出现报错apr_pollset_poll: The timeout sp ...
- 【团队效率提升】Python-PyWebIO介绍
作者:京东零售 关键 Q&A快速了解PyWebIO Q:首先,什么是PyWebIO? A:PyWebIO提供了一系列命令式的交互函数,能够让咱们用只用Python就可以编写 Web 应用, 不 ...
- How to Die ( Since 10.10 )
以后再也不要相信 sqrt 的精度!对 long long 级别的数取 sqrt 会炸精度! 对于区间差分 \([l,r]\) 的问题,一定要注意是否会出现 \(l>r\) 的情况!(\(|A| ...
- 使用JSON.stringify()去实现深拷贝,要小心哦,可能有巨坑
对象中有时间类型的时候(时间类型会被变成字符串类型数据) const obj = { date: new Date() } console.log(typeof obj.date === 'objec ...