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 ...
随机推荐
- 如何让你的.NET WebAPI程序支持HTTP3?
下面我将总结构建Http3的经验,以Token Gateway的项目为例,请注意使用Http3之前你需要知道它的限制, Windows Windows 11 版本 22000 或更高版本/Window ...
- [转帖]Prometheus 监控之 Blackbox_exporter黑盒监测 [icmp、tcp、http(get\post)、dns、ssl证书过期时间]
Blackbox_exporter 主动监测主机与服务状态 Prometheus 官方提供的 exporter 之一,可以提供 http.dns.tcp.icmp 的监控数据采集 官方github: ...
- [转帖]s3fs - 使用S3FS存储桶目录允许其他用户使用权限
https://www.coder.work/article/6661505 我在使用S3FS时遇到问题.我正在使用 ubuntu@ip-x-x-x-x:~$ /usr/bin/s3fs --ve ...
- [转帖]记录自己安装内存带宽测试工具——Stream过程
测试环境: CPU:Kunpeng 920 8Core MEM:16G Storage:200G OS:openEuler 20.03 (LTS-SP3) 1 服务器资源监控工具--Stream 1. ...
- 手工创建一个带sticky模块的nginx镜像 并且实现容器化负载均衡的方法
最近想进行容器化运行的nginx反向代理负载均衡服务. 找了一下 dockerhub上面的 nginx 发现里面不包含 sticky模块. 会报错为: nginx: [emerg] unknown d ...
- [译]深入了解现代web浏览器(一)
本文是根据Mariko Kosaka在谷歌开发者网站上的系列文章https://developer.chrome.com/blog/inside-browser-part1/ 翻译而来,共有四篇,该篇 ...
- IdentityServer4 系列文章01---密码授权模式
IdentityServer4实现.Net Core API接口权限认证(快速入门) 什么是IdentityServer4 官方解释:IdentityServer4是基于ASP.NET Core实 ...
- 字节码编程,Javassist篇五《使用Bytecode指令码生成含有自定义注解的类和方法》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 到本章为止已经写了四篇关于字节码编程的内容,涉及了大部分的API方法.整体来说对 J ...
- 字节码编程,Javassist篇四《通过字节码插桩监控方法采集运行时入参出参和异常信息》
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 字节码编程插桩这种技术常与 Javaagent 技术结合用在系统的非入侵监控中,这样 ...
- requests模块的高级应用
requests抓取数据报错 - HttpConnectinPool: - 原因: - 1.短时间内发起了高频的请求导致ip被禁 - 2.http连接池中的连接资源被耗尽 - 解决: - 1.代理 - ...