如何在一个spel表达式中实现多个语句的执行?
Intro
最近在看表达式注入,学到了一些有意思的东西,但是实习公司在信息安全这块管控得比较严格,连自己的笔记都没办法带出来,因此只能把学到的东西都装进脑袋里,趁自己还记得赶紧写下来
回到正题,背景是一个可控的Spel表达式注入点,但是开发进行了一定的防护,其中一个waf就是将所有的输入都转小写了,导致一些函数无法使用,最主要的是由于类名都是大写开头,我们无法通过forName的方法获取到Runtime、ProcessBulider等恶意类,因此引出本文的Spel多语句执行
我们可以写个demo:
| |
假设这里的input就是用户可控的点,使用toLowerCase进行防护
Getter
首先用到的一个知识点是EL表达式在使用诸如x.propertyName访问对象属性的时候其实调用的是它的Getter方法,可以参考:jsp - Is it more safe to use getter instead of field name in EL? - Stack Overflow
而Spel表达式同样有着这个特性,于是我们可以将.getClass()、.getRuntime()替换成.class、.runtime来获取相应的class以及currentRuntime
现在我们的payload可以改成:
| |
现在还剩下forName和Runtime需要解决
forName是一个方法,我们没办法使用刚刚说的Getter绕过,那么如何获得方法呢,熟悉反射的师傅应该马上就能想到getMethods可以获得某个类的所有方法,而它又是个Getter方法,可以使用.methods代替,由于getMethods是Class类的方法,因此我们还得先找到String类的父类,于是我们可以进一步得到:
| |
这里的下标通过调试得到,不知道是和java的版本有关还是和JVM的环境有关,但通常是固定的
至此还剩下Runtime,由于这是个字符串,因此可以通过上面的method找到toUpperCase方法,最后得到payload:
| |
Variables
上面的绕过方法其实只是通过简单地链式操作实现多语句执行,事实上当时的代码还多了一层限制,最后的绕过方法才是重点。
简单地说就是会根据+-*\()这几个符号将我们输入的表达式分组,然后在每一组前面添加一个#,例如输入:
| |
最后会得到:
| |
最开始的想法是不引入任何特殊符号,但事实上这很困难,特别是需要调用方法,而且哪怕真的没有任何符号,输入的字符串作为一整组也会在最前面添加一个#
而#号在spel表达式中具有特殊的含义,因此显然利用这个#号才是切实可行的方法
SpEL expression allows to create and use variables specific to expression using #variable-name syntax. A variable is set using setVariable on EvaluationContext. There are two types of inbuilt variables as well, #this and #root. #this variable always refers to current evaluation object where as #root variable refers to the root object of the evaluation context.
通过查阅spel的文档,刚开始的想法是利用#this或者#root,然而在没有上下文的情况下这俩都是null,不一定能行,最后是查阅了公司的内部文档,发现这个变量其实不用在java代码中定义并赋值:
| |
在spel中使用:#newName=Parashar即可定义并赋值,因此最后多语句执行的payload如下:
| |