Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2021-35042 — Базовый анализ CVE-2021-35942. SQL-инъекция в Django. | Kitploit
Инструменты/GitHubGitHub/zer0qs/cve-2021-35042
Анализ уязвимостейЭксплуатацияВеб-безопасностьСтатьи и ИсследованияОбучение и Образование
GitHubzer0qs/cve-2021-35042

CVE-2021-35042

Базовый анализ CVE-2021-35942. SQL-инъекция в Django.

Репозиторий
24 лет назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2021-35042: Уязвимость SQL-инъекции в Django

I. Обзор

Django — это веб-фреймворк с открытым исходным кодом, написанный на Python и построенный по модели MVC (Model - View - Controller). Изначально он был создан для управления новостными веб-сайтами, принадлежащими издательской корпорации Lawrence, как программное обеспечение CMS (Content Management System).

В Django версии 3.1.x -> 3.1.13 и версии 3.2.x -> 3.2.5 существует уязвимость SQL-инъекции.

Причина этой уязвимости заключается в том, что функция фильтрации входных данных, контролируемых пользователем, в QuerySet.order_by() недостаточна для защиты от атак SQL-инъекций. Эта уязвимость может быть использована злоумышленниками для выполнения несанкционированных действий, приводящих к утечке конфиденциальных данных.

CVE - IDCVE-2021-35042
Severity9.8 - CRITICAL
CWE - IDCWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
Vulnerability Publication Date1/7/2021
Affected Software3.1.x < 3.1.13, 3.2.x < 3.2.5
Require AuthenticationNo required

II. Обзор x2

0x01. Модель Django

В Django создание таблиц и определение полей в базе данных выполняется путем объявления класса модели в файле models.py. В этом примере мы объявляем таблицу с именем Wolf и поле с именем name.

0x02. QuerySet и Order_by() в Django

ORM-фреймворк, встроенный в Django, используется для работы с базой данных, и результатом запроса является набор, который называется QuerySet.

order_by(fields) По умолчанию order_by() возвращает QuerySet, отсортированный в порядке, указанном в опции ordering в Meta модели. Мы можем переопределить условие order_by в каждом запросе, используя метод order_by().

Пример

wolves = Wolf.objects.order_by('-name', 'id')

Результат приведенного выше запроса будет отсортирован по убыванию по полю name, затем по возрастанию по id. Знак минуса перед именем поля name указывает на сортировку результатов по убыванию.

Следующий пример сортирует возвращаемые результаты по полю, полученному от пользователя; если значение не передано, сортировка выполняется по полю id.

Результат

В версиях 3.1 и 3.2 Django позволяет комбинировать метод запроса с именем таблицы в запросе order_by. Это также является основной причиной данной уязвимости.

Передача имени таблицы дает нам тот же результат, что и передача обычного имени поля

cve202135042_wolf — это имя таблицы

Сначала приложение напрямую вызывает функцию order_by(); код, обрабатывающий функцию order_by(), определен в: django/db/models/query.py

Функция order_by() выполняет две задачи

  1. Удаляет все текущие методы, вызываемые order_by(), и удаляет параметр по умолчанию, переданный, когда order_by получает другое значение.
  1. Передает параметр в order_by. Функция add_ordering() выполняет это
root@kitploit:~
def add_ordering(self, *ordering):
        """
        Add items from the 'ordering' sequence to the query's "order by"
        clause. These items are either field names (not column names) --
        possibly with a direction prefix ('-' or '?') -- or OrderBy
        expressions.

        If 'ordering' is empty, clear all ordering from the query.
        """
        errors = []
        for item in ordering:
            if isinstance(item, str):
                if '.' in item:
                    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 == '?':
                    continue
                if item.startswith('-'):
                    item = item[1:]
                if item in self.annotations:
                    continue
                if self.extra and item in self.extra:
                    continue
                # names_to_path() validates the lookup. A descriptive
                # FieldError will be raise if it's not.
                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
            

Параметр, передаваемый в add_ordering(), представляет собой массив.

Например, когда параметр передается следующим образом: wolves = Wolf.objects.order_by( 'name' , 'id' ) В этом случае приложение преобразует его в следующий запрос к базе данных:

root@kitploit:~
SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY "cve202135042_wolf"."name" ASC, "cve202135042_wolf"."id" ASC

При передаче функция add_ordering проверяет каждый элемент массива; если это string, проверяются следующие 5 случаев:

  1. if '.' in item: Проверяется, является ли это запросом с именем столбца, и указано ли имя таблицы для этого столбца в SQL-операторе. Если да, выдается предупреждение и выполняется continue.
  2. if item == '?': Если значение элемента — знак '?', результаты выводятся в случайном порядке, выполняется continue.
  3. if item.startswith('-'): Если элемент начинается с символа '-', результаты запроса сортируются по убыванию (DESC).
  4. if item in self.annotations: Проверяется, содержит ли он комментарий; если да, выполняется continue.
  5. if self.extra and item in self.extra: Определяется, есть ли дополнительные параметры; если есть, выполняется continue.

После 5 проверок параметр передается в функцию self.names_to_path(item.split(LOOKUP_SEP), self.model._meta) для дальнейшей проверки, является ли он допустимым именем столбца; затем, если он допустим, он добавляется в self.ordering класса Query для дальнейшей обработки.

III. Анализ уязвимости SQL-инъекции в Django

0x31. Причина

ORM Django очень строго фильтрует данные, вставляемые в запрос, но изменение исходного кода, приведшее к SQL-инъекции, связано с тем, что автор предположил, что если имя столбца является UUID (Universal Unique Identifier), то запрос order_by не может быть выполнен.

То есть, если переданные данные имеют формат xxx-xxx-xxx-xxx (формат UUID), запрос не может быть выполнен.

Код до внесения изменений

root@kitploit:~
# django/db/models/sql/constants.py 
ORDER_PATTERN  =  _lazy_re_compile ( r '\?|[-+]?[.\w]+$' )

# django/db/models/sql/query.py 
def  add_ordering ( self ,  * ordering ): 
        errors  =  [] 
        for  item  in  ordering : 
            if  isinstance ( item ,  str )  and  ORDER_PATTERN . match ( item ): 
                if  '.'  in  item : 
                    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 , 
                    ) 
            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

Из приведенного выше кода видно, что запрос выполняется только в том случае, если параметр соответствует ? или начинается с - и за ним следуют обычные символы или знак ..

Поэтому, когда имя столбца является UUID, оно становится недопустимым значением и не может быть передано в order_by. Изменение кода обработки было принято и изменено следующим образом: https://github.com/charettes/django/commit/513948735b799239f3ef8c89397592445e1a0cd5

Для проверки входных данных использовалась функция self.name_to_path.

Но после проверки, если в элементе содержится ., он считается запросом с именем таблицы, и выполняется команда continue, что приводит к прямому пропуску использования функции self.name_to_path для проверки допустимости данных.

Код обработки точки . в функции get_order_by выглядит следующим образом: django/db/models/sql/compiler.py

root@kitploit:~
if  '.'  in  field : 
    table ,  col  =  col . split ( '.' ,  1 ) 
    order_by . append (( 
            OrderBy ( 
                RawSQL ( ' %s . %s '  %  ( 
                self . quote_name_unless_alias ( table ),  col ),  [ ]), 
                descending = descending 
            ),  False )) 
    continue

Функция self.quote_name_unless_alias обрабатывает имя таблицы, фильтрует допустимые имена таблиц и пропускает фильтрацию имени столбца, поэтому мы можем вставить оператор SQL-инъекции.

0x32. Патч

В текущей версии Django 4.0 запрос по имени таблицы с помощью точки . был удален и больше не поддерживается; патч выпущен для версий 3.1 и 3.2. Затронуты версии 3.2 -> 3.2.4 и 3.1 -> 3.1.12. 3.2.x Fixed CVE-2021-35042 -- Prevented SQL injection in QuerySet.o…

3.1.x Fixed CVE-2021-35042 -- Prevented SQL injection in QuerySet.o…

Исправление очень простое: была возвращена старая проверка данных с помощью ReGex.

0x33. Устранение

Обновите Django до незатронутой версии.

IV. Демонстрация

0x41. Среда

Docker & Docker-compose

0x42. Установка

  1. git clone https://github.com/WynSon/CVE-2021-35042.git
  2. Запустите ./setup.sh для первоначальной настройки
  3. sudo docker-compose up --build
  4. sudo docker exec -it cve-2021-35042_web_1 python manage.py makemigrations cve202135042
  5. sudo docker exec -it cve-2021-35042_web_1 python manage.py migrate
  6. Перейдите на http://localhost:8000/load_example_data для загрузки примеров данных:
  7. Путь, содержащий уязвимый параметр: http://localhost:8000/wolves/ http://localhost:8000/wolves/?order_by=name

Экран после завершения установки

0x43. Эксплуатация

Условие: Для успешной эксплуатации мы обязательно должны знать имя таблицы каким-либо способом :))

При инъекции оператора мы должны знать имя таблицы, чтобы иметь возможность выполнить SQLi-оператор.

При вводе неправильного имени таблицы

При вводе правильного имени таблицы запрос orderby выполняется нормально.

Оператор в этом случае будет следующим: SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY ("cve202135042_wolf"."name") ASC

В этот момент мы можем завершить предыдущий оператор order_by и вставить SQL-оператор для эксплуатации.

SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY ("cve202135042_wolf"."name"); SELECT * from cve202135042_wolf where id =1; --) ASC

V. Ссылки

https://www.djangoproject.com/weblog/2021/jul/01/security-releases/ https://xz.aliyun.com/t/9834 https://www.bugxss.com/vulnerability-report/3095.html https://blankheart.top/2022/04/07/cve-2021-35042/ https://itcn.blog/p/1648921763575859.html https://github.com/YouGina/CVE-2021-35042

Скачать инструмент