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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2021-35042 — Уязвимость SQL-инъекции в Django | Kitploit
Инструменты/GitHubGitHub/luuanhduc/cve-2021-35042
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийОбучение и ОбразованиеБезопасность Баз ДанныхЛаборатории и Практика
GitHubluuanhduc/cve-2021-35042

CVE-2021-35042

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

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

Популярное

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

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

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

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

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

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
Критичность9.8 - КРИТИЧЕСКИЙ
CWE - IDCWE-89: Неправильная нейтрализация специальных элементов, используемых в SQL-команде ('SQL-инъекция')
Дата публикации уязвимости1/7/2021
Затронутое ПО3.1.x < 3.1.13, 3.2.x < 3.2.5
Требуется аутентификацияНе требуется

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, он проверяется в следующих пяти случаях:

  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.

После пяти проверок параметр передаётся в функцию 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/LUUANHDUC/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. Эксплуатация

Условие: Чтобы провести эксплуатацию, нам необходимо любым способом узнать имя таблицы :))

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

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

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

Запрос в этом случае будет таким: 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

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