最近在看CNVD時(shí)無意間看到兩條關(guān)于Django的最新漏洞通告,隨即打開看了一下,大概意思是說Django在2.2.28?版本之前的2.2版本、3.2.13版本之前的3.2版本、4.0.4版本之前的4.0版,本使用QuerySet.annotate() aggregate() extra()數(shù)據(jù)聚合函數(shù)時(shí)會導(dǎo)致SQL注入問題。由于筆者平時(shí)開發(fā)一些平臺多半也是使用Django,所以便嘗試進(jìn)行分析了一下,如有描述不當(dāng)之處,還望提出建議修改斧正。
Django Django >=2.2,<2.2.28
Django Django >=3.2,<3.2.13
Django Django >=4.0,<4.0.4
?
Python: 3.7.9
Django: 3.2.11
?
models.py
views.py
urls.py
settings.py
或者使用筆者構(gòu)建好的環(huán)境:
https://github.com/DeEpinGh0st/CVE-2022-28346
?
初始化項(xiàng)目
1.python manage.py makemigrations
2.python manage.py migrate
3.訪問http://x.x.x.x:8000/ 初始化數(shù)據(jù)
觸發(fā)
訪問
http://x.x.x.x:8000/demo?field=demo.name" FROM "demo_user" union SELECT "1",sqlite_version(),"3" --
?
老規(guī)矩,分析前可以先看看官方在修復(fù)的commit中有沒有給出測試用例
在https://github.com/django/django/commit/2044dac5c6968441be6f534c4139bcf48c5c7e48中看到官方在測試組件中給出了基本的測試用例。
我們可以根據(jù)官方給出的代碼編寫相應(yīng)的代碼進(jìn)行調(diào)試和分析。
在views.py的annotate打入斷點(diǎn)后,來到db.models.query.py:__init__,進(jìn)行QuerySet的初始化。
在初始化QuerySet后,會來到db.models.query.py:annotate開始執(zhí)行聚合流程,在annotate中首先會調(diào)用_annotate并傳入kwargs。
annotate在完成對kwargs.values()合法性校驗(yàn)等一系列操作后,將kwargs更新到annotations中,隨后遍歷annotations中的元素調(diào)用add_annotation進(jìn)行數(shù)據(jù)聚合。
跟進(jìn)add_annotation(print是筆者為了分析自己加入的)。
add_annotation繼續(xù)調(diào)用resolve_expression解析表達(dá)式,在此處并沒有對傳入的聚合參數(shù)進(jìn)行相應(yīng)的檢查。在經(jīng)過一系列調(diào)用后,最終會來到db.models.sql.query.py:resolve_ref
resolve_ref會獲取annotations中的元素,并將其轉(zhuǎn)換后帶入到查詢的條件中,最后其結(jié)果通過transform_function聚合到一個Col對象中,可以看到聚合之后的結(jié)果。
返回到db.models.query.py:_annotate可以看到具體聚合后數(shù)據(jù)值,以及執(zhí)行的sql語句。
最后將結(jié)果返回到QuerySet中進(jìn)行展示。
?
在漏洞公開后,Django官方隨即對項(xiàng)目進(jìn)行了修復(fù)。
在add_annotation中加入了check_alias對聚合參數(shù)進(jìn)行檢查。
?
https://github.com/django/django/commit/2044dac5c6968441be6f534c4139bcf48c5c7e48