java代码审计-表达式注入
0x01漏洞挖掘
spel
spel表达式有三种用法:
-
注解
- @value("#{表达式}")
- public String arg;
这种一般是写死在代码中的,不是关注的重点。
-
xml
- <bean id="Bean1" class="com.test.xxx">
- <property name="arg" value="#{表达式}">
- bean>
这种情况通常也是写死在代码中的,但是也有已知的利用场景,就是利用反序列化让程序加载我们实现构造好的恶意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例外,该包下的类可以不指定包名。得到类实例后会访问类静态方法与字段。
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')}
jexl
关于jexl,比较有代表性的就是前段时间Nexus的rce,Nexus Repository Manager 3 RCE 分析 -【CVE-2019-7238】文章在这,不再赘述。
其它
还有其它种类的表达式,如EL,不会轻易造成安全问题,暂时略过;OGNL表达式会在struts2相关的漏洞中详细说明。
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参考链接
Nexus Repository Manager 3 RCE 分析 -【CVE-2019-7238】
点击关注,共同学习!安全狗的自我修养