Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Django JSONField/HStoreField SQL 注入漏洞(CVE-2019-14234)的复现与分析 #1

Open
Rivaill opened this issue Aug 3, 2019 · 0 comments

Comments

@Rivaill
Copy link
Owner

commented Aug 3, 2019

昨天看到长亭发了篇django的漏洞预警,于是就看了下,完成了分析与复现。

漏洞描述

当使用用户可控的数据作为参数,以 **kwargs 的形式传入 QuerySet.filter() 函数,对 django.contrib.postgres.fields.JSONField 进行键/索引查找,或对 django.contrib.postgres.fields.HStoreField 进行键查找时,将会导致 SQL 注入。

影响范围

  • Django 主开发分支
  • Django 2.2.x < 2.2.4
  • Django 2.1.x < 2.1.11
  • Django 1.11.x < 1.11.23

环境搭建

  • Python 2.7.10
  • Postgresql(docker)
  • 漏洞demo
    漏洞代码片段:
    image
    为了触发漏洞,将http get里面的参数字典以函数参数的形式传过去,**的作用就是可以将字典转为函数参数,例如filter(**{'a':'1','b':'2'})与filter(a=1,b=2)是等价的

漏洞复现

MD5 PoC:
http://127.0.0.1:8000/select?info__test%27+%3d+%27%22a%22%27)%20and%207778%3dCAST((SELECT%20md5(version()))::text%20AS%20NUMERIC)--
image
Version EXP:
http://127.0.0.1:8000/select?info__test%27+%3d+%27%22a%22%27)%20and%207778%3dCAST((SELECT%20version())::text%20AS%20NUMERIC)--
image

原理分析

先看看官方补丁,定位到关键代码处:
image

定位到该函数 as_sql(jsonb.py:L97):

image
通过函数名推测该函数是用来生产相关SQL语句的,后来查了一下,这个函数主要是为filter()函数提供json查询支持。
举个例子:现在数据库里存了一些这样的数据
image
如果我要查info字段里面json数据里name键为rivaill2的数据就可以用如下代码去查

XXXmodel.objects.filter(info__name='rivaill1')

最终上面的as_sql会生成如下一段SQL:
image
这是postgresql进行json查询的语法
image
再回来看看存在漏洞的代码:
image
大概的代码逻辑就是会将key强转一下,如果强转不成功就在两侧加个单引号,但是这里面没有经过任何过滤,追踪整个调用链也没有发现针对这个key的过滤,然后将字段名、操作符、key拼接了一下就return出去了。
所以这里的注入点实际上是在json的key里面,想要这个输入点可控就需要filter函数中的参数key可控,这也是为什么demo中要将http get以**kwargs 的形式传入filter函数的原因。
测试一下:
image
可以看到确实可控了,info是指定的字段,name是info字段的json数据中的key,最终解析到filter函数里面就是filter(info__name='rivaill2')
往key后面加个单引号就能触发注入:
image

修复措施

看一下官方的补丁:
image
其实关键就在最后一行,lookup(也就是key)没有直接与前面的字段名和操作符拼接了,而是放到了第二个返回值里面和params拼接,最后追踪了一下调用链发现第二个返回值里面的数据都会作为参数化查询的value被代入进去,这样就避免了注入。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
1 participant
You can’t perform that action at this time.