CVE-2022-34265 Django Extract & Trunc SQL注入漏洞
這段時間 Java 卷的太狠,換一下口味。
漏洞信息

Django 數據庫函數 `Trunc` 和 `Extract` 主要用于進行日期操作,定義如下:


`Extract` 用于提取日期,比如我們可以提取日期字段中的年、月、日等信息, `Trunc` 則用于截取,比如 `2000-01-01 11:11:11` ,可以根據需求獲取到日期 `2000-01-01` 。如果將未過濾的數據傳遞給 `kind` 或 `lookup_name` 時,將會出現 SQL 注入漏洞。影響版本:
- Django 3.2.x prior to 3.2.14
- Django 4.0.x prior to 4.0.6
補丁對比
在 Django 新版本 `operations.py` 的 `BaseDatabaseOperations` 類中新增了 `extract_trunc_lookup_pattern` 正則表達式:

然后在 `datetime.py` 的 `Extract` 和 `Trunc` 類中 `as_sql` 函數分別加入了對 `self.lookup_name` 和 `self.kind` 參數的正則表達式檢查:

環境搭建
為了深入分析漏洞原理,建議采用源碼方式完成環境搭建:

參考 Django 官方提供的 `database-functions` 以及補丁中的測試例子,構造如下數據庫實體類:

對應 MySQL 數據庫定義:

在 `views.py` 中添加測試 API 接口定義:

在 `urls.py` 中配置路由規則:

漏洞分析
以 `Extract` 為例,構造請求 `/extract_test/?lookup_name=year` ,順利進入 API 函數入口:

在 Django 框架自帶的 ORM 模型中,當進行 SQL 查詢操作時,將調用 `\django\db\models\query.py` 的 `QuerySet` 類中對應方法進行處理。比如 `Demo` 中的查詢將調用 `QuerySet#exists` 函數:

經過多次傳遞后,進入 `SQLCompiler#compile` :

最終到達此次存在漏洞的 `Extract` 類中函數 `as_sql` :

因為 `demo` 中選擇的字段 `start_datetime` 屬于 `DateTimeField` 類型,所以將進入第 `58` 行的 `datetime_extract_sql` 函數:

跟進 `date_extract_sql` :

`date_extract_sql` 函數將根據輸入的 `lookup_type` 取值生成不同的 SQL 語句,比如 `demo` 中設置為 `year` ,最終返回如下 :


對來自 GET 請求的 `lookup_name` 參數只是完成大寫轉換,但沒有進行任何檢查,直接拼接到 SQL 查詢語句中,存在注入漏洞:


報錯回顯測試:

修復方式
前面補丁對比已經提到,新版本中對傳入參數新增了正則表達式檢查規則。