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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2021-35042 — Django SQLインジェクションの脆弱性 | Kitploit
ツール/GitHubGitHub/luuanhduc/cve-2021-35042
脆弱性分析エクスプロイトウェブアプリケーション悪用学習と教育データベースセキュリティラボと実践
GitHubluuanhduc/cve-2021-35042

CVE-2021-35042

Django SQLインジェクションの脆弱性

リポジトリを見る
13年前未レビュー

人気

すべて見る →

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

すべてのツールを探索

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

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

CVE-2021-35042: Django SQLインジェクションの脆弱性

I. 概要

Djangoは、Pythonで書かれたオープンソースのWebアプリケーションフレームワークであり、MVC(Model - View - Controller)モデルに従って構築されています。当初は、Lawrence出版社が所有するニュースコンテンツサイトを管理するために構築されたCMS(コンテンツ管理システム)ソフトウェアでした。

Django バージョン 3.1.x -> 3.1.13 および バージョン 3.2.x -> 3.2.5 にはSQLインジェクションの脆弱性が存在します。

この脆弱性の原因は、ユーザーが制御する入力データを QuerySet.order_by() でフィルタリングする機能が、SQLインジェクション攻撃を防ぐのに十分ではなかったためです。この脆弱性が悪用されると、攻撃者は不正な操作を実行でき、機密データの漏洩につながる可能性があります。

CVE - IDCVE-2021-35042
重要度9.8 - CRITICAL
CWE - IDCWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
脆弱性公開日2021年7月1日
影響を受けるソフトウェア3.1.x < 3.1.13, 3.2.x < 3.2.5
認証の必要性不要

II. 概要 x2

0x01. DjangoのModel

Djangoでは、データベース内のテーブルの作成とフィールドの定義は、models.py ファイル内でモデルクラスを宣言することによって行われます。この例では、Wolf という名前のテーブルと、name という名前のフィールドを宣言します。

0x02. DjangoにおけるQuerySetとorder_by()

Djangoに組み込まれているORMフレームワークはデータベースを操作するために使用され、クエリの結果はコレクション、すなわちQuerySetです。

order_by(fields) デフォルトでは、order_by() は、ModelのMeta内のorderingオプションで指定された順序で並べ替えられたQuerySetを返します。各クエリで order_by() メソッドを使用して、order_by条件を上書きできます。

例

wolves = Wolf.objects.order_by('-name', 'id')

上記のクエリの結果は、name フィールドの降順、次に id の昇順で並べ替えられます。name フィールド名の前にあるマイナス記号は、結果を降順で並べ替えることを示します。

次の例では、ユーザーから受け取ったフィールドで結果を並べ替えます。値が渡されない場合は、id フィールドで並べ替えられます。

結果

バージョン3.1および3.2では、Djangoはorder_byクエリにテーブル名をクエリメソッドと組み合わせて渡すことを許可しています。これがこの脆弱性の主な原因でもあります。

テーブル名を渡すと、通常のフィールド名を渡すのと同じ結果が得られます。

cve202135042_wolf はテーブル名です

まずアプリケーションはorder_by()関数を直接呼び出します。order_by()関数を処理するコードは、以下で定義されています: django/db/models/query.py

order_by()関数は2つのことを行います

  1. order_by()によって現在呼び出されているすべてのメソッドを削除し、order_byが別の値を受け取ったときに渡されるデフォルトパラメータを削除します。
  1. パラメータをorder_byに渡します。add_ordering() 関数がこれを実行します。
root@kitploit:~
def add_ordering(self, *ordering):
        """
        Add items from the 'ordering' sequence to the query's "order by"
        clause. These items are either field names (not column names) --
        possibly with a direction prefix ('-' or '?') -- or OrderBy
        expressions.

        If 'ordering' is empty, clear all ordering from the query.
        """
        errors = []
        for item in ordering:
            if isinstance(item, str):
                if '.' in item:
                    warnings.warn(
                        'Passing column raw column aliases to order_by() is '
                        'deprecated. Wrap %r in a RawSQL expression before '
                        'passing it to order_by().' % item,
                        category=RemovedInDjango40Warning,
                        stacklevel=3,
                    )
                    continue
                if item == '?':
                    continue
                if item.startswith('-'):
                    item = item[1:]
                if item in self.annotations:
                    continue
                if self.extra and item in self.extra:
                    continue
                # names_to_path() validates the lookup. A descriptive
                # FieldError will be raise if it's not.
                self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)
            elif not hasattr(item, 'resolve_expression'):
                errors.append(item)
            if getattr(item, 'contains_aggregate', False):
                raise FieldError(
                    'Using an aggregate in order_by() without also including '
                    'it in annotate() is not allowed: %s' % item
                )
        if errors:
            raise FieldError('Invalid order_by arguments: %s' % errors)
        if ordering:
            self.order_by += ordering
        else:
            self.default_ordering = False
            

add_ordering() に渡されるパラメータは配列です。

例えば、次のようなパラメータが渡された場合: wolves = Wolf.objects.order_by( 'name' , 'id' ) その場合、アプリケーションはデータベース内の次のようなクエリに変換して実行します:

root@kitploit:~
SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY "cve202135042_wolf"."name" ASC, "cve202135042_wolf"."id" ASC

渡されると、add_ordering関数は配列内の各要素をチェックします。それがstringの場合、次の5つのケースがチェックされます:

  1. if '.' in item: SQL文で指定されたテーブル名を持つ列のクエリかどうかをチェックします。もしそうなら、警告を出して continue します。
  2. if item == '?': 要素の値が '?' の場合、出力結果はランダムに並べ替えられ、continue します。
  3. if item.startswith('-'): アイテムが「-」文字で始まる場合、クエリ結果は降順(DESC)で並べ替えられます。
  4. if item in self.annotations: コメント(注釈)が含まれているかどうかをチェックし、含まれている場合は continue します。
  5. if self.extra and item in self.extra: 追加があるかどうかを確認し、ある場合は continue します。

5回のチェックの後、パラメータは self.names_to_path(item.split(LOOKUP_SEP), self.model._meta) 関数に渡され、それが有効な列名かどうかのチェックが続けられます。その後、有効であれば、Query クラスの self.ordering に追加されて処理が続行されます。

III. DjangoにおけるSQLインジェクション脆弱性の分析

0x31. 原因

DjangoのORMは、クエリに入力されるデータを非常に厳密にフィルタリングしますが、今回のソースコードの変更がSQLインジェクションにつながったのは、著者が、列名がUUID(Universal Unique Identifier)列の場合、order_by クエリを実行できないという仮説を立てたためです。

つまり、渡されるデータが xxx-xxx-xxx-xxx(UUIDの形式)の場合、クエリを実行できません。

変更前のコード

root@kitploit:~
# django/db/models/sql/constants.py 
ORDER_PATTERN  =  _lazy_re_compile ( r '\?|[-+]?[.\w]+$' )

# django/db/models/sql/query.py 
def  add_ordering ( self ,  * ordering ): 
        errors  =  [] 
        for  item  in  ordering : 
            if  isinstance ( item ,  str )  and  ORDER_PATTERN . match ( item ): 
                if  '.'  in  item : 
                    warnings . warn ( 
                        'Passing column raw column aliases to order_by() is ' 
                        'deprecated. Wrap %r in a RawSQL expression before '
                        'passing it to order_by().'  %  item , 
                        category = RemovedInDjango40Warning , 
                        stacklevel = 3 , 
                    ) 
            elif  not  hasattr ( item ,  'resolve_expression' ): 
                errors . append ( item ) 
            if  getattr ( item ,  'contains_aggregate' ,  False ): 
                raise  FieldError ( 
                    'Using an aggregate in order_by() without also including ' 
                    'it in annotate() is not allowed: %s ' %  item 
                ) 
        if  errors : 
            raise  FieldError ( 'Invalid order_by arguments: %s '  %  errors ) 
        if  ordering : 
            self . order_by  +=  ordering 
        else : 
            self . default_ordering  =  False

上記のコードから、パラメータが ? に一致するか、- で始まり、その後に通常の文字または . が続く場合にのみ、クエリが実行されることがわかります。

したがって、列名がUUIDの場合、それは無効な値となり、order_by に渡すことはできません。 この処理部分のコード変更は承認され、次のように変更されました: https://github.com/charettes/django/commit/513948735b799239f3ef8c89397592445e1a0cd5

入力データの検証に self.name_to_path 関数を使用するようになりました。

しかし、itemに . が含まれているかチェックした後、それをテーブル名を使用したクエリと見なし、continue コマンドが実行され、データの有効性を検証するための self.name_to_path 関数の使用が直接スキップされてしまいます。

get_order_by 関数内の . を処理するコードは次のとおりです django/db/models/sql/compiler.py

root@kitploit:~
if  '.'  in  field : 
    table ,  col  =  col . split ( '.' ,  1 ) 
    order_by . append (( 
            OrderBy ( 
                RawSQL ( ' %s . %s '  %  ( 
                self . quote_name_unless_alias ( table ),  col ),  [ ]), 
                descending = descending 
            ),  False )) 
    continue

self.quote_name_unless_alias 関数はテーブル名を処理し、有効なテーブル名をフィルタリングして、列名のフィルタリングをスキップするため、SQLインジェクション文を挿入できます。

0x32. 修正パッチ

現在のDjango 4.0では、ドット . によるテーブル名でのクエリは削除され、サポートされなくなりました。修正パッチはバージョン3.1と3.2向けにリリースされました。バージョン 3.2 -> 3.2.4 および 3.1 -> 3.1.12 が影響を受けます。 3.2.x Fixed CVE-2021-35042 -- Prevented SQL injection in QuerySet.o…

3.1.x Fixed CVE-2021-35042 -- Prevented SQL injection in QuerySet.o…

修正は非常に簡単で、以前のReGexによるデータチェックが復元されました

0x33. 対策

Djangoを影響を受けないバージョンに更新してください。

IV. デモ

0x41. 環境

Docker & Docker-compose

0x42. セットアップ

  1. git clone https://github.com/LUUANHDUC/CVE-2021-35042.git
  2. 初期セットアップのために ./setup.sh を実行
  3. sudo docker-compose up --build
  4. sudo docker exec -it cve-2021-35042_web_1 python manage.py makemigrations cve202135042
  5. sudo docker exec -it cve-2021-35042_web_1 python manage.py migrate
  6. サンプルデータをロードするために http://localhost:8000/load_example_data にアクセス:
  7. 脆弱なパラメータを含むパス: http://localhost:8000/wolves/ http://localhost:8000/wolves/?order_by=name

インストール完了後の画面

0x43. 悪用

条件: 悪用するには、何らかの方法でテーブル名を知っている必要があります :))

SQL文をインジェクションする際、SQLi文を実行するにはテーブル名を知っている必要があります。

テーブル名を間違って入力した場合

テーブル名が正しい場合、orderbyクエリは正常に実行されます。

この時点でのSQL文は次のようになります SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY ("cve202135042_wolf"."name") ASC

この時点で、前のorder_by文を終了し、SQL文を挿入して悪用することができます。

SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY ("cve202135042_wolf"."name"); SELECT * from cve202135042_wolf where id =1; --) ASC

V. 参考

https://www.djangoproject.com/weblog/2021/jul/01/security-releases/ https://xz.aliyun.com/t/9834 https://www.bugxss.com/vulnerability-report/3095.html https://blankheart.top/2022/04/07/cve-2021-35042/ https://itcn.blog/p/1648921763575859.html https://github.com/YouGina/CVE-2021-35042

ツールをダウンロード