<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    Django order_by SQL注入漏洞分析(CVE-2021-35042)

    一顆小胡椒2022-07-26 14:58:44

    漏洞介紹

    官方給出的解釋如下:

    [Django]:https://www.djangoproject.com/weblog/2021/jul/01/security-releases/

    CVE-2021-35042: Potential SQL injection via unsanitized QuerySet.order_by() input

    Unsanitized user input passed to QuerySet.order_by() could bypass intended column reference validation in path marked for deprecation resulting in a potential SQL injection even if a deprecation warning is emitted.

    As a mitigation the strict column reference validation was restored for the duration of the deprecation period. This regression appeared in 3.1 as a side effect of fixing # 31426.

    The issue is not present in the main branch as the deprecated path has been removed.

    該漏洞是由于QuerySet.order_by()查詢時 ,對用戶傳入的參數過濾不嚴格,可以使攻擊者在不需要授權的情況下,構造惡意的參數執行SQL注入攻擊。

    漏洞評級:高危

    影響版本:Django 3.2、Django 3.1

    安全版本:Django >= 3.2.5、Django >= 3.1.13

    漏洞分析

    2.1 order_by()

    order_by是QuerySet下的一種查詢方法,作用是將查詢的結果根據某個字段進行排序,在字段前面加一個符號,結果會倒序輸出。但是如果對列名的查詢過濾不嚴格就會導致SQL注入。

    2.2 漏洞原理

    Django是MTV架構,視圖views.py的代碼。

    # views.pydef vul(request):# 獲取orderquery = request.GET.get('order', default='id')q = Collection.objects.order_by(query)return HttpResponse(q.values())
    

    模型models.py代碼。

    # models.pyclass Collection(models.Model):name = models.CharField(max_length=128)
    

    首先獲取用戶傳入的order,沒有傳入參數默認為id,獲取到參數之后 Collection.objects.order_by處理,跟一下order_by。

    def order_by(self, *field_names):"""Return a new QuerySet instance with the ordering changed."""assert not self.query.is_sliced, \"Cannot reorder a query once a slice has been taken."obj = self._chain()obj.query.clear_ordering(force_empty=False)obj.query.add_ordering(*field_names)return obj
    

    參數傳入order_by后賦值給obj,經過clear_ordering處理。

    def clear_ordering(self, force_empty):"""Remove any ordering settings. If 'force_empty' is True, there will beno ordering in the resulting query (not even the model's default)."""self.order_by = ()self.extra_order_by = ()if force_empty:self.default_ordering = False
    

    clear_ordering的作用是清除所有通過order\_by函數調用的方法。然后再經過add_ordering處理。

    def add_ordering(self, *ordering):"""Add items from the 'ordering' sequence to the query's "order by"clause. These items are either field names (not column names) --possibly with a direction prefix ('-' or '?') -- or OrderByexpressions.
    If 'ordering' is empty, clear all ordering from the query."""errors = []for item in ordering:if isinstance(item, str):if '.' in item:warnings.warn('Passing column raw column aliases to order_by() is ''deprecated. Wrap %r in a RawSQL expression before ''passing it to order_by().' % item,category=RemovedInDjango40Warning,stacklevel=3,)continueif item == '?':continueif item.startswith('-'):item = item[1:]if item in self.annotations:continueif self.extra and item in self.extra:continue# names_to_path() validates the lookup. A descriptive# FieldError will be raise if it's not.self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)elif not hasattr(item, 'resolve_expression'):errors.append(item)if getattr(item, 'contains_aggregate', False):raise FieldError('Using an aggregate in order_by() without also including ''it in annotate() is not allowed: %s' % item)if errors:raise FieldError('Invalid order_by arguments: %s' % errors)if ordering:self.order_by += orderingelse:self.default_ordering = False
    

    傳入的參數到達add_ordering之后進入for循環,但是如果參數中含有.則會直接跳出循環,因為只有一個參數,而且也是單次循環所以最終不會進入names_to_path方法,而是執行self.order_by += ordering(ordering就是傳入的參數),所以add_ordering的作用就是增加self.order_by參數。

    當傳入的參數是默認的id時,SQL語句為。

    SELECT "vuln_collection"."id", "vuln_collection"."name" FROM "vuln_collection" ORDER BY (id) ASC。
    

    當參數為id.時:

    SELECT "vuln_collection"."id", "vuln_collection"."name" FROM "vuln_collection" ORDER BY (id.) ASC。
    

    當參數為

    vuln_collection.id);select updatexml(1,concat(0x7e,(select @@version)),1);#時。

    SELECT "vuln_collection"."id", "vuln_collection"."name" FROM "vuln_collection" ORDER BY (vuln_collection.id);select updatexml(1,concat(0x7e,(select @@version)),1);# ASC
    

    注入原理就是使用)進行閉合再利用;進行堆疊注入。

    2.3 漏洞成因

    在add_ordering中如果正常進入for循環沒有跳出循環,會經過五次if判斷

    1.if '.' in item:,判斷參數中是否帶有.,有則會跳出循環。

    2.if item == '?':,判斷參數是否是?,如果是則跳出循環。

    3.if item.startswith('-'):,判斷參數是否以-開頭,如果是則去除開頭的-。

    4.if item in self.annotations:,判斷參數是否含有注釋標識符,是則跳出循環。

    5.if self.extra and item in self.extra:,判斷參數是否有額外的參數信息,是則跳出循環。

    經過五次if判斷之后表示參數無異常,然后進入names_to_path獲取數據,利用model模型判斷當前的參數是否是有效的列名,如果不是有效的列名則會報錯。

    def names_to_path(self, names, opts, allow_many=True, fail_on_missing=False):"""Walk the list of names and turns them into PathInfo tuples. A singlename in 'names' can generate multiple PathInfos (m2m, for example).
    'names' is the path of names to travel, 'opts' is the model Options westart the name resolving from, 'allow_many' is as for setup_joins().If fail_on_missing is set to True, then a name that can't be resolvedwill generate a FieldError.
    Return a list of PathInfo tuples. In addition return the final field(the last used join field) and target (which is a field guaranteed tocontain the same value as the final field). Finally, return those namesthat weren't found (which are likely transforms and the final lookup)."""path, names_with_path = [], []for pos, name in enumerate(names):cur_names_with_path = (name, [])if name == 'pk':name = opts.pk.name
    field = Nonefiltered_relation = Nonetry:field = opts.get_field(name)except FieldDoesNotExist:if name in self.annotation_select:field = self.annotation_select[name].output_fieldelif name in self._filtered_relations and pos == 0:filtered_relation = self._filtered_relations[name]if LOOKUP_SEP in filtered_relation.relation_name:parts = filtered_relation.relation_name.split(LOOKUP_SEP)filtered_relation_path, field, _, _ = self.names_to_path(parts, opts, allow_many, fail_on_missing,)path.extend(filtered_relation_path[:-1])else:field = opts.get_field(filtered_relation.relation_name)if field is not None:# Fields that contain one-to-many relations with a generic# model (like a GenericForeignKey) cannot generate reverse# relations and therefore cannot be used for reverse querying.if field.is_relation and not field.related_model:raise FieldError("Field %r does not generate an automatic reverse ""relation and therefore cannot be used for reverse ""querying. If it is a GenericForeignKey, consider ""adding a GenericRelation." % name)try:model = field.model._meta.concrete_modelexcept AttributeError:# QuerySet.annotate() may introduce fields that aren't# attached to a model.model = Noneelse:# We didn't find the current field, so move position back# one step.pos -= 1if pos == -1 or fail_on_missing:available = sorted([*get_field_names_from_opts(opts),*self.annotation_select,*self._filtered_relations,])raise FieldError("Cannot resolve keyword '%s' into field. ""Choices are: %s" % (name, ", ".join(available)))break# Check if we need any joins for concrete inheritance cases (the# field lives in parent, but we are currently in one of its# children)if model is not opts.model:path_to_parent = opts.get_path_to_parent(model)if path_to_parent:path.extend(path_to_parent)cur_names_with_path[1].extend(path_to_parent)opts = path_to_parent[-1].to_optsif hasattr(field, 'get_path_info'):pathinfos = field.get_path_info(filtered_relation)if not allow_many:for inner_pos, p in enumerate(pathinfos):if p.m2m:cur_names_with_path[1].extend(pathinfos[0:inner_pos + 1])names_with_path.append(cur_names_with_path)raise MultiJoin(pos + 1, names_with_path)last = pathinfos[-1]path.extend(pathinfos)final_field = last.join_fieldopts = last.to_optstargets = last.target_fieldscur_names_with_path[1].extend(pathinfos)names_with_path.append(cur_names_with_path)else:# Local non-relational field.nfinal_field = fieldtargets = (field,)if fail_on_missing and pos + 1 != len(names):raise FieldError("Cannot resolve keyword %r into field. Join on '%s'"" not permitted." % (names[pos + 1], name))breakreturn path, final_field, targets, names[pos + 1:]
    

    現在看來漏洞出現的原因就很好理解了,如果我們傳入的參數中含有.那么就無法進入names_to_path完成對列名的驗證,攻擊者就可以利用這個缺陷構造含有.的參數利用order by進行SQL注入。

    漏洞復現

    這里使用的環境是vulhub靶場。

    https://vulhub.org/# /environments/django/CVE-2021-35042/

    傳入參數:id

    傳入參數:-id

    傳入參數:id.

    出現了報錯信息。

    傳入參數:

    vuln_collection.id);select updatexml(1,concat(0x7e,(select @@version)),1);#

    成功查詢出數據庫版本信息,后續按正常操作進行注入即可。

    漏洞修復

    官方給出的修改方案:

    https://github.com/django/django/commit/0bd57a879a0d54920bb9038a732645fb917040e9

    django/db/models/sql/constants.py

    django/db/models/sql/query.py

    在django/db/models/sql/query.py中的add_ordering對.的判斷添加了正則,匹配規則更加嚴格。

    參考鏈接

    https://xz.aliyun.com/t/9834# toc-0

    https://www.bugxss.com/vulnerability-report/3095.html

    https://www.cnblogs.com/R3col/p/16094132.html

    https://github.com/django/django/commit/0bd57a879a0d54920bb9038a732645fb917040e9

    https://code.djangoproject.com/ticket/31426

    sql注入django
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    (翻譯版)Numpy反序列化命令執行淺析代碼審計Python安全編碼和代碼審計Python代碼審計連載之一:CSRF?p=738Python代碼審計連載之三:Server Side Request?p=744Python代碼審計連載之四:Command Execution?p=747Dangerous Python Functions, Part 1Dangerous Python Functions, Part 2Dangerous Python Functions, Part 3記一下PythonWeb代碼審計應該注意的地方廖新喜大佬的python代碼審計工具來自openstack安全團隊的python代碼靜態審計工具來自openstack安全團隊的python代碼靜態審計工具2代碼審計工具pytxfkxfk的python自動化代碼審計?
    0x01、漏洞狀態漏洞細節漏洞POC漏洞EXP在野利用否未知未知未知0x02、漏洞描述DjangoDjango基金會的一套基于Python語言的開源Web應用框架。2022年4月11日,Django發布安全公告,修復了兩個存在于Django中的高危漏洞。
    漏洞評級:高危影響版本:Django 3.2、Django 3.1安全版本:Django >= 3.2.5、Django >= 3.1.13漏洞分析2.1 order_by()order_by是QuerySet下的一種查詢方法,作用是將查詢的結果根據某個字段進行排序,在字段前面加一個符號,結果會倒序輸出。
    Django 數據庫函數 Trunc 和 Extract 主要用于進行日期操作,如果將未過濾的數據傳遞給 kind 或 lookup_name 時,將會產生 SQL 注入漏洞 CVE-2022-34265。
    如果流量都沒有經過WAF,WAF當然無法攔截攻擊請求。當前多數云WAF架構,例如百度云加速、阿里云盾等,通過更改DNS解析,把流量引入WAF集群,流量經過檢測后轉發請求到源站。如圖,dict.com接入接入WAF后,dict.com的DNS解析結果指向WAF集群,用戶的請求將發送給WAF集群,WAF集群經過檢測認為非攻擊請求再轉發給源站。
    BypassD盾之SQL注入繞過總結
    今天在這篇文章中,我將分享一篇關于使用授權Header(Authorization Headers token)的 SQL 注入的文章。
    另類字符集編碼繞過繞過原理HTTP協議兼容性:HTTP Charset的多樣性Content-Type頭中使用charset定義字符集的應用場景不只有在responses中,request中同樣可以使用。
    為解決實驗室,編輯會話cookie中的序列化對象以利用此漏洞并獲得管理權限。然后,刪除 Carlos 的帳戶。您可以使用以下憑據登錄自己的帳戶:wiener:peter解決方案此實驗與權限提升有關,我們使用bp抓包,重點關注cookie1.登錄,查看我的賬戶頁面,bp發現cookie內容是序列化的。在Repeater中替換cookie,已經有了admin權限。
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类