出品|MS08067實驗室(www.ms08067.com)
本文作者:守拙(Ms08067實驗室追洞小組成員)
一、漏洞名稱:
通過StringAgg(分隔符)的潛在SQL注入漏洞
二、漏洞編號:
CVE-2020-7471
三、漏洞描述:
Django 1.11.28之前的1.11.x、2.2.10之前的2.2.x和3.0.3之前的3.0.x版本允許SQL注入,如果不受信任的資料用作StringAgg分隔符(例如,在存在多行資料下載的Django應用程式中,使用用戶指定的列分隔符進行下載的場景),通過向contrib.postgres.aggregates.StringAgg實體傳遞一個精心構造的分隔符,可能會破壞轉義并注入惡意SQL,
四、影響版本:
Django 1.11.x < 1.11.28
Django 2.2.x < 2.2.10
Django 3.0.x < 3.0.3
五、漏洞分析
聚合函式StringAgg的delimiter引數未經任何轉義就嵌入到sql陳述句中,導致sql注入
六、實驗環境及準備:
1.資料庫:postgresql,版本無所謂,本文中使用kali虛擬機中自帶的資料庫,允許外部連接
- 修改如下檔案,監聽所有埠
/etc/postgresql/12/main/postgresql.conf

- 修改如下檔案,允許外部連接
/etc/postgresql/12/main/pg_hba.conf

- 重啟服務后,連接資料庫并創建測驗資料庫
登陸:psql -U postgres -h [kali主機的IP]
創建測驗資料庫:CREATE DATABASE test,后面poc中會用到
其他postgresql語法可以參考菜鳥教程
2.POC: https://github.com/Saferman/CVE-2020-7471,運行環境django3.0.2
- 安裝django3.0.2
pip installdjango==3.0.2 - 使用pycharm除錯POC代碼,參考POC中的readme檔案

- 初始化資料庫后可以用pgadmin連接看下,test資料庫中應該有如下表

- Vul_app_info表中應該如下欄位和資料

七、復現步驟:
POC測驗腳本中有兩個函式query()和query_with_evil(),前者用于模糊測驗,后者用于注入點證明
1.模糊測驗
- 通過運行query()函式的例外捕獲可以知道有兩個特殊字符讓程式產生了報錯(%和’)
- 將程式中例外捕獲注釋掉,payload使用%和’單獨測驗


- 通過報錯可以看出分號沒有轉義導致sql陳述句報錯,并直接在報錯資訊回傳了拼接后的sql陳述句,將斷點打在執行sql陳述句并產生報錯的代碼塊去看完整的sql


- 得到程式運行的實際sql陳述句
SELECT "vul_app_info"."gender", STRING_AGG("vul_app_info"."name", \'\'\') AS "mydefinedname" FROM "vul_app_info" GROUP BY "vul_app_info"."gender" LIMIT 21
2.注入點證明
- 通過注入sql陳述句使查詢結果區別與程式原本的查詢結果來證明注入點的可用
- 程式原本執行的sql陳述句,最后是limit 21
SELECT "vul_app_info"."gender", STRING_AGG("vul_app_info"."name", \'- \') AS "mydefinedname" FROM "vul_app_info" GROUP BY "vul_app_info"."gender" LIMIT 21
- 注入后的 sql 陳述句,最后是 limit 1,只回傳一行資料
SELECT "vul_app_info"."gender", STRING_AGG("vul_app_info"."name", \'- \') AS "mydefinedname" FROM "vul_app_info" GROUP BY "vul_app_info"."gender" LIMIT 1 OFSET 1 --
- 運行結果驗證

八、其他思考:
1. 漏洞利用場景
- Django 應用回傳聚合資料的場景
- 用于聚合的字符用戶可控
- 資料庫得是 postgresql 資料庫
- 好像不容易存在這種場景...
2. 漏洞挖掘思路
- 針對可能存在問題的函式,構建測驗環境
- 針對該函式進行模糊測驗,看是否有 sql 陳述句報錯資訊
- 如果模糊測驗成功讓 sql 陳述句報錯,進行注入點利用驗證
3. 漏洞修復
- 在django的git倉庫的提交記錄中可以看到django官方的修復方案
- https://github.com/django/django/commit/eb31d845323618d688ad429479c6dda973056136

- 新版本中將delimiter 引數用Value函式處理了一下,再傳遞到sql中

- 升級django版本(3.1.6)再debug一下

- 分割符的地方用了%s,沒有直接拼接進去,根據Value函式的注釋說法是將引數放到sql的引數串列中,最終以下面的方式執行,則不存在sql注入風險
sql="SELECT * FROM user_contacts WHERE username = %s"
user='zhugedali'
cursor.execute(sql,[user])
4.同型別函式
- 在postgresql資料庫中和StringAgg函式一樣可以傳遞分隔符引數的函式還有
array_to_string(array_agg(name),'-')

- 但是django中沒有找到這個函式的API(沒有提供或者是我太菜了沒找到..)
?
?
?
轉載請聯系作者并注明出處!
Ms08067安全實驗室專注于網路安全知識的普及和培訓,團隊已出版《Web安全攻防:滲透測驗實戰指南》,《內網安全攻防:滲透測驗實戰指南》,《Python安全攻防:滲透測驗實戰指南》,《Java代碼安全審計(入門篇)》等書籍,
團隊公眾號定期分享關于CTF靶場、內網滲透、APT方面技術干貨,從零開始、以實戰落地為主,致力于做一個實用的干貨分享型公眾號,
官方網站:https://www.ms08067.com/
掃描下方二維碼加入實驗室VIP社區
加入后邀請加入內部VIP群,內部微信群永久有效!
?
?
?
?
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/270664.html
標籤:其他
上一篇:追洞小組 | fastjson1.2.24復現+分析
下一篇:Github無法訪問解決辦法
