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


    1|0前言

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

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

    修复方案:升级版本

    2|0环境搭建

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

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

    image-20221017181515325

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

    image-20221017181803951

    3|0漏洞复现

    访问:http://localhost:9091/article?id=${9*9} ,可以发现${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

    4|0调试分析

    为什么会出现这情况呢,这是因为springboot返回错误页面的时候提供了详细信息,这些信息包括

    错误status("status"->500)、时间戳("timestamp"->"Fri Dec.....")、错误信息("error"->"Internal Server Error")、和用户输入的参数("message"->"test"),然后后端渲染视图时,会解析错误模板中的参数名。然后拿到对应的参数值,通过函数检查参数值中是否存在${},如果存在则去除,然后传入SpEL引擎进行解析。模板内容如下所示:

    <html>
       <body>
           <h1>Whitelabel Error Pageh1>
           <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后进行解析,然后触发漏洞。触发点如下:

    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

    5|0参考文章

    https://www.cnblogs.com/litlife/p/10183137.html

    https://www.cnblogs.com/zpchcbd/p/15536569.html

    https://blog.csdn.net/haduwi/article/details/126326511

    https://blog.csdn.net/weixin_54902210/article/details/124533353


    __EOF__

    本文作者雪痕*
    本文链接https://www.cnblogs.com/lxfweb/p/16811366.html
    关于博主:评论和私信会在第一时间回复。或者直接私信我。
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
    声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
  • 相关阅读:
    【路由交换技术】Cisco Packet Tracer基础入门教程(四)
    【软考软件评测师】基于风险的测试技术
    10月17日,每日信息差
    vue3中安装并使用CSS预处理器Sass的方法介绍
    【linux】性能优化
    leecode#Excel表列名称#多数元素
    CI/CD docker compose 部署 humpback - single mode
    PS/TiO2核壳复合微球/聚苯乙烯/SiO2壳核复合微球/聚苯乙烯蒙脱土二氧化硅空心微球的性能研究
    一文读懂vue3和vue2的API风格对比
    微服务框架 SpringCloud微服务架构 15 RabbitMQ 快速入门 15.2 消息模型介绍
  • 原文地址:https://www.cnblogs.com/lxfweb/p/16811366.html