前言这是2016年的一个洞 , 利用条件是至少知道一个触发 springboot 默认错误页面的接口及参数名 。
影响版本:1.1.0-1.1.121.2.0-1.2.71.3.0
修复方案:升级版本
环境搭建下载链接:https://github.com/LandGrey/SpringBootVulExploit/tree/master/repository/springboot-spel-rce
用idea打开之后配置一下,如下图:
![SpringBoot框架SpEL表达式注入漏洞复现与原理分析](http://img.zhejianglong.com/231018/1I2101110-0.png)
文章插图
然后启动访问出现如下页面 , 代表搭建成功 。
![SpringBoot框架SpEL表达式注入漏洞复现与原理分析](http://img.zhejianglong.com/231018/1I21030O-1.png)
文章插图
漏洞复现访问:http://localhost:9091/article?id=${9*9} , 可以发现
${9*9}
的SpEL表达式进行了解析 , 随后将该表达式的运行的结果进行了返回,如下图 。![SpringBoot框架SpEL表达式注入漏洞复现与原理分析](http://img.zhejianglong.com/231018/1I2104W3-2.png)
文章插图
现在尝试弹出计算器,访问:http://localhost:9091/article?id=${T(java.lang.Runtime).getRuntime().exec(new String(new byte[]{0x63,0x61,0x6c,0x63}))}
成功弹出,如下图:
![SpringBoot框架SpEL表达式注入漏洞复现与原理分析](https://m.360buyimg.com/jdcms/jfs/t1/42520/14/19359/89858/63515703E22c1aa4c/6fff567a45c8902e.png)
文章插图
调试分析为什么会出现这情况呢,这是因为springboot返回错误页面的时候提供了详细信息,这些信息包括
错误status("status"->500)、时间戳("timestamp"->"Fri Dec.....")、错误信息("error"->"Internal Server Error")、和用户输入的参数("message"->"test"),然后后端渲染视图时,会解析错误模板中的参数名 。然后拿到对应的参数值,通过函数检查参数值中是否存在
${}
, 如果存在则去除,然后传入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>
程序会判断模板中每个${}的位置,然后将参数名一个一个取出来后传入spel引擎,解析参数名对应的值 。这里就是漏洞的触发点,假如我输入${payload},spel取出来payload后进行解析,然后触发漏洞 。触发点如下:![SpringBoot框架SpEL表达式注入漏洞复现与原理分析](http://img.zhejianglong.com/231018/1I21051H-4.png)
文章插图
浏览器访问http://localhost:9091/article?id=${T(java.lang.Runtime).getRuntime().exec(new%20String(new%20byte[]{0x63,0x61,0x6c,0x63}))},现在开始调试,首先会将map的值传入,context的rootObject中 , 之后以
this.template
和this.resolver
为参数调用replacePlaceholders
方法,如下图:![SpringBoot框架SpEL表达式注入漏洞复现与原理分析](http://img.zhejianglong.com/231018/1I21051R-5.png)
文章插图
this.template的内容就是上文的错误模板,跟进
replacePlaceholders
方法 ,进入PropertyPlaceholderHelper文件 。![SpringBoot框架SpEL表达式注入漏洞复现与原理分析](http://img.zhejianglong.com/231018/1I210B21-6.png)
文章插图
继续跟进
parseStringValue
方法【SpringBoot框架SpEL表达式注入漏洞复现与原理分析】
![SpringBoot框架SpEL表达式注入漏洞复现与原理分析](http://img.zhejianglong.com/231018/1I21064E-7.png)
文章插图
![SpringBoot框架SpEL表达式注入漏洞复现与原理分析](http://img.zhejianglong.com/231018/1I2101S7-8.png)
文章插图
分析一下代码,首先StringBuilder将strVal转为字符串,并赋值给result,接着判断result中
${
和}
位置,结果为157、168,然后通过substring截取157和168的中间值,并赋值给placeholder,本次的值为"timestamp" , 然后将placeholder作为第一个参数,再次调用本方法 。结果如下图:![SpringBoot框架SpEL表达式注入漏洞复现与原理分析](https://m.360buyimg.com/jdcms/jfs/t1/145971/16/31565/104077/63515707Eb8a92287/2581261788cb901b.png)
文章插图
strVal的值变为timestamp , 所以在indexOf判断时,由于没出现${,所以变为了-1 , 跳过了while循环,直接执行下边的
return result.toString();
。继续跟进,下一步是调用resolvePlaceholder方法,此函数的作用是查找this.context中对应参数的值并返回,如下图:
![SpringBoot框架SpEL表达式注入漏洞复现与原理分析](https://m.360buyimg.com/jdcms/jfs/t1/15340/37/18388/119051/63515707Edf3c3052/e372ba7f4dcf17f4.png)
文章插图
发现拿到了时间戳"timestamp" -> "Wed Oct 19 00:38:36 CST 2022",然后赋值给propVal,此时不为空,进入下一个if循环 , 再次调用parseStringValue 。
![SpringBoot框架SpEL表达式注入漏洞复现与原理分析](http://img.zhejianglong.com/231018/1I2103504-11.png)
推荐阅读
- 手写自定义springboot-starter,感受框架的魅力和原理
- 一 JPA入门学习集合springboot
- 关于Springboot启动报错 Whitelabel Error Page: This application has no explicit mapping
- 七 SpringBoot - Redis 缓存
- 一篇文章带你了解网页框架——Vue简单入门
- 五 SpringBoot - Java8 新特性
- 四 SpringBoot - 整合Mybatis,逆向工程,JPA
- 一篇文章带你掌握主流办公框架——SpringBoot
- Taurus.MVC 微服务框架 入门开发教程:项目部署:7、微服务节点的监控与告警。
- 三 SpringBoot - Slf4j+logback 日志,异步请求,定时任务