Skip to content

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表达式的用法:

  1. 注解
@value("#{表达式}")
public String arg;

这种一般是写死在代码中的,不是关注的重点。

  1. xml
<bean id="Bean1" class="com.test.xxx">
 <property name="arg" value="#{表达式}">
</bean>

这种情况通常也是写死在代码中的,但是也有已知的利用场景,就是利用反序列化让程序加载我们实现构造好的恶意xml文件,如jackson的CVE-2017-17485、weblogic的CVE-2019-2725等。

  1. 在代码中处理外部传入的表达式

这部分是关注的重点。

@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();
}

漏洞可以利用的前置条件有三个:

  1. 传入的表达式为过滤
  2. 表达式解析之后调用了getValue/setValue方法
  3. 使用StandardEvaluationContext(默认)作为上下文对象

spel表达式功能非常强大,在漏洞利用方面主要使用这几个功能:

  • 使用T(Type)表示Type类的实例,Type为全限定名称,如T(com.test.Bean1)。但是java.lang例外,该包下的类可以不指定包名。得到类实例后会访问类静态方法与字段。
T(java.lang.Runtime).getRuntime().exec("whoami")
  • 直接通过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.expressionparseExpressiongetValuegetValueTypevalue="#{*}

0x02漏洞防御

  1. 最简单的方式,使用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();
}
  1. 如果SimpleEvaluationContext不能满足需求,就需要对输入进行严格的过滤。

0x03参考链接

山石JavaWeb靶场的spel相关页面,具体代码见com.hillstone.controller.spel

Comments