java代码审计-表达式注入
描述
Spring表达式语言(Spring Expression Language,SpEL)是一种功能强大的表达式语言,用于在运行时查询和操作对象图,语法上类似于Unified EL,但提供了更多的特性,特别是方法调用和基本字符串模板函数。
Spring为解析SpEL提供了两套不同的接口,分别是“SimpleEvaluationContext”及“StandardEvaluationContext。SimpleEvaluationContext”,这两种接口仅支持SpEL语法的子集,抛弃了Java类型引用、构造函数及beam引用相对较为安全。而StandardEvaluationContext则包含了SpEL的所有功能,并且在不指定 EvaluationContext的情况下,将默认采用StandardEvaluationContext。 产生SpEL表达式注入漏洞的另一个主要原因是,很大一部分开发人员未对用户输入进行处理就直接通过解析引擎对SpEL继续解析。一旦用户能够控制解析的SpEL语句,便可通过反射的方式构造代码执行的SpEL语句,从而达到RCE的目的。SpEL漏洞的危害有:任意代码执行、获取SHELL、对服务器进行破坏等
SpEL表达式的用法:
- 注解
这种一般是写死在代码中的,不是关注的重点。
- xml
这种情况通常也是写死在代码中的,但是也有已知的利用场景,就是利用反序列化让程序加载我们实现构造好的恶意xml文件,如jackson的CVE-2017-17485、weblogic的CVE-2019-2725等。
- 在代码中处理外部传入的表达式
这部分是关注的重点。
@RequestMapping("/spel")
public String spel(@RequestParam(name = "spel") String spel) {
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression(spel);
Object object = expression.getValue();
return object.toString();
}
漏洞可以利用的前置条件有三个:
- 传入的表达式为过滤
- 表达式解析之后调用了getValue/setValue方法
- 使用StandardEvaluationContext(默认)作为上下文对象
spel表达式功能非常强大,在漏洞利用方面主要使用这几个功能:
- 使用T(Type)表示Type类的实例,Type为全限定名称,如T(com.test.Bean1)。但是java.lang例外,该包下的类可以不指定包名。得到类实例后会访问类静态方法与字段。
- 直接通过java语法实例化对象、调用方法
new ProcessBuilder("whoami").start()
//可以利用反射来绕过一些过滤
#{''.getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('ex'+'ec',''.getClass()).invoke(''.getClass().forName('java.la'+'ng.Ru'+'ntime').getMethod('getRu'+'ntime').invoke(null),'calc')}
审计技巧:
- 全局查找关键字
org.springframework.expression
、parseExpression
、getValue
、getValueType
、value="#{*}
0x02漏洞防御
- 最简单的方式,使用SimpleEvaluationContext作为上下文对象。
@RequestMapping("/spel")
public String spel(@RequestParam(name = "spel") String spel) {
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression(spel);
//SimpleEvaluationContext减少了一部分功能,并在权限控制上进一步细化
//可以配置让spel表达式只能访问指定对象
Category category = new Category();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().withRootObject(category).build();
Object object = expression.getValue();
return object.toString();
}
- 如果SimpleEvaluationContext不能满足需求,就需要对输入进行严格的过滤。
0x03参考链接
山石JavaWeb靶场的spel相关页面,具体代码见com.hillstone.controller.spel