Java表达式注入

OGNL表达式注入已经分析过了:https://xz.aliyun.com/t/10482,另外分析下MVEL和SPEL Java表达式注入就算收尾了。

MVEL

MVEL2 exp

1
2
3
4
5
6
7
8
9
10
11
public void exp(){
Map vars = new HashMap();
String expression1 = "Runtime.getRuntime().exec(\"calc\")";
Serializable serializable = MVEL.compileExpression(expression1);
vars.put("1",expression1);
MVEL.executeExpression(serializable,vars);

String expression2 = "new java.lang.ProcessBuilder(new java.lang.String[]{\"calc\"}).start()";
vars.put("2",expression2);
MVEL.eval(expression2,vars);
}

MVEL2执行表达式通过org.mvel2.ast.ASTNode.getReducedValueAccelerated()处理表达式结果

image-20211108155749290

通过optimize()最终会调用org.mvel2.optimizers.impl.refl.ReflectiveAccessorOptimizer().compileGetChain处理,当chain是method时,调用getMethod()

image-20211108160027431

getMethod()最终会调用method.invoke()实现表达式中方法的调用,调用前,mevl没有检查类或方法的防护措施来防止危险方法的执行,因此可以完成命令执行。

image-20211108160145441

Apache Unomi CVE-2020-13942

通过 org.apache.unomi.persistence.elasticsearch.conditions.ConditionContextHelper.getContextualCondition()解析传进来的参数

image-20211108165250653

当以script::开始时,获取后面的内容作为脚本执行。

image-20211108165638897

处理脚本的执行,会被当作mvel脚本执行并调用MVEL.executeExpression()处理

image-20211108165747268

MVEL并没有限制类或方法的执行因此可以导致RCE。

image-20211108170052920

SpEL

1
2
3
4
5
6
7
8
9
// 1.创建解析器:SpEL 使用 ExpressionParser 接口表示解析器,提供 SpelExpressionParser 默认实现;
ExpressionParser parser = new SpelExpressionParser();
// 2.解析表达式:使用 ExpressionParser 的 parseExpression 来解析相应的表达式为 Expression 对象。
Expression expression = parser.parseExpression("T(java.lang.Runtime).getRuntime().exec(\"calc\")");
// 3.构造上下文:准备比如变量定义等等表达式需要的上下文数据。
EvaluationContext context = new StandardEvaluationContext();
// 4.求值:通过 Expression 接口的 getValue 方法根据上下文获得表达式值。
Object value = expression.getValue(context);
System.out.println(value);

org.springframework.expression.spel.standard.SpelExpression.getValue()首先会解析生成三个AST节点,java.lang.Runtime的TypeReference和2个MethodReference分别是getRuntimeexec

image-20211108201722756

通过SpelNodeImpl.getValue()调用CompoundExpression.getValueInternal()处理,首先通过getValueRef获取ref,再调用ref.getValue计算最后的结果。

image-20211108202658394

跟进getValueRef()看下,循环计算除前n-1个node的结果,然后调用nextNode.getValueRef(state)获取最终的ref

image-20211108203811789

这里nextNode就是MethodReference,调用MethodReference.getValueRef()返回MethodReference$MethodValueRef实例

image-20211108204741944

跟进ref.getValue会调用getValueInternal,getValueInternal调用ReflectiveMethodExecutor.execute()通过执行方法

image-20211108205450733

image-20211108205735821

下图,ReflectiveMethodExecutor.execute()通过反射执行方法调用method.invoke

image-20211108205933496

另外EL推荐阅读:https://xz.aliyun.com/t/7692

总结

表达式语言主要是解析表达式为AST语法树计算每个树节点,当用户可以控制输入的表达式时,并且绕过黑名单限制则可达到RCE。

java审计时需要注意以下导入及相关流

1
2
3
org.mvel2.MVEL
ognl
org.springframework.expression.spel.standard.

参考链接:

1
2
3
https://www.freebuf.com/vuls/197008.html
https://blog.51cto.com/u_9597987/3494673
https://jax777.win/2017/03/02/%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B3%A8%E5%85%A5payload/