漏洞介绍
Django是一个基于Python的开源Web应用框架。Django中QuerySet数据合集的order_by函数存在SQL注入漏洞。如果攻击者可以控制order_by传入的值,那么就可以注入恶意SQL语句造成SQL注入漏洞。
漏洞范围
django 3.1、3.2
漏洞靶场
使用vulhub的靶场:
vulhub-master/django/CVE-2021-35042
使用的环境为Django 3.2.4
启动靶场环境:
1 | docker-compose up -d |
环境启动后,访问http://your-ip:8000即可看到Django默认首页。
漏洞原理
1. Django的models概念
在Django中,想要在数据库中创建表并定义字段是容易的,只需要在models.py文件中声明一个模型类即可。如下图所示,这里定义了一个叫Collection的表,表中有一个叫name的字段。


vuln/models.py#L6-L7
2. QuerySet与order_by
Django内置了一个ORM框架,从数据库查询出来的结果是一个合集,这个合集就是QuerySet。而order_by这个方法的作用,一般是将查询出来的结果按照某字段的值,由小到大或由大到小进行排序。如下图所示,在views.py的视图函数中,先是获取到了用户传入的参数值order(如果没有传入参数默认值为id)。然后到Collection表中进行数据查询,对返回的结果按照id值从小到大进行排序。

vuln/views.py#L7-L10
最后使用values()函数将数据合集转化成一个一个json的数据格式返回。

3. 判断是否使用了order_by
比如返回的结果按照id值排序Collection.objects.order_by('id'),默认是从小到大的顺序。如果想要变成从大到小,只需要把'id'变成'-id'即可。因此可以通过在参数值前面加'-'来判断,如果返回的顺序颠倒了那么就是使用了order_by。如下图所示。

4. 源码分析
当运行到Collection.objects.order_by('id')的时候,主要是进入如下函数来判断order_by 的排序顺序和表达式。
1 | def add_ordering(self, *ordering): |
django/db/models/sql/query.py#L1960-L2004
在add_ordering()函数中,进行如下了五个判断:字段中是否带点、字段是否为问号、字段开头是否为短横杠、判断是否在一个map字典、判断是否有额外的参数信息。如果全部参数无异常会进入self.names_to_path中进行数据获取,并进行相关逻辑处理,这个过程是不会进行SQL注入拼接的。
当用户输入的字段中带了点'id.',就会跳出循环进入到_fetch_all中,这个时候会进行SQL查询:
1 | SELECT "vuln_collection"."id", "vuln_collection"."name" FROM "vuln_collection" ORDER BY ("id".) ASC。 |

可以看到会把点带进查询。也就是说把'id.'进行了拼接。因此可以尝试闭合语句并配合debug回显进行报错注入:
1 | SELECT "vuln_collection"."id", "vuln_collection"."name" FROM "vuln_collection" ORDER BY (vuln_collection.id);select updatexml(1,concat(0x7e,(select @@version)),1);# ASC |

漏洞复现
首先访问http://your-ip:8000/vuln/,并添加参数order=-id以查看按ID降序排序的数据:

要利用SQL注入漏洞,使用以下payload修改order参数,其中vuln_collection是模型(models.py里)名称:
1 | http://your-ip:8000/vuln/?order=vuln_collection.name);select updatexml(1, concat(0x7e,(select @@version)),1)%23 |
SQL错误信息将会显示,通过基于错误的SQL注入泄露数据库信息:

成功显示数据库版本
参考
https://github.com/vulhub/vulhub/blob/master/django/CVE-2021-35042/README.zh-cn.md