Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2021-35042 — A basic analysis about CVE-2021-35942. SQL injection in Django. | Kitploit
Tools/GitHubGitHub/zer0qs/cve-2021-35042
Vulnerability AnalysisExploitationWeb SecurityPapers & ResearchLearning & Education
GitHubzer0qs/cve-2021-35042

CVE-2021-35042

A basic analysis about CVE-2021-35942. SQL injection in Django.

View Repository
2114 years agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2021-35042: Django SQL injection vulnerability

I. Overview

Django is an open-source Web Application Framework written in Python, built on the MVC (Model - View - Controller) model. It was originally built to manage news content websites owned by the Lawrence publishing corporation, as a CMS (Content Management System) software.

Django versions 3.1.x -> 3.1.13 and versions 3.2.x -> 3.2.5 contain an SQL injection vulnerability.

The cause of this vulnerability is that the input data filtering functionality for user-controlled data in QuerySet.order_by() is not sufficient to prevent SQL injection attacks. This vulnerability can be exploited to allow attackers to perform unauthorized actions leading to the leakage of sensitive data.

CVE - IDCVE-2021-35042
Severity9.8 - CRITICAL
CWE - IDCWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
Vulnerability Publication Date1/7/2021
Affected Software3.1.x < 3.1.13, 3.2.x < 3.2.5
Require AuthenticationNo required

II. Overview x2

0x01. Django's Model

In Django, creating tables and defining fields in the database is done by declaring a model class in the models.py file. In this example, we declare a table named Wolf and a field named name.

0x02. QuerySet and Order_by() in Django

The ORM framework built into Django is used to interact with the database, and the result of a query is a collection, which is a QuerySet.

order_by(fields) By default, order_by() returns a QuerySet sorted in the order specified in the ordering option in the Model's Meta. We can override the order_by condition in each query by using the order_by() method.

Example

wolves = Wolf.objects.order_by('-name', 'id')

The result of the above query will be sorted in descending order by the name field, then in ascending order by id. The minus sign before the name field name indicates that the results are sorted in descending order.

The following example sorts the returned results by the field received from the user; if no value is passed, it sorts by the id field.

Result

In versions 3.1 and 3.2, Django allows combining query methods with table names in the order_by query. This is also the main cause of this vulnerability.

Passing a table name gives us the same result as passing a field name normally

cve202135042_wolf is the table name

First, the application directly calls the order_by() function; the code handling the order_by() function is defined at: django/db/models/query.py

The order_by() function does 2 things

  1. Clears all current methods being called by order_by() and removes the default parameter passed in when order_by receives a different value.
  1. Passes the parameter to order_by. The add_ordering() function performs this task.
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 OrderBy
        expressions.

        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,
                    )
                    continue
                if item == '?':
                    continue
                if item.startswith('-'):
                    item = item[1:]
                if item in self.annotations:
                    continue
                if 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 += ordering
        else:
            self.default_ordering = False
            

The parameter passed to add_ordering() is an array.

For example, when the parameter is passed as follows: wolves = Wolf.objects.order_by( 'name' , 'id' ) At that point, the application will convert it into the following database query:

SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY "cve202135042_wolf"."name" ASC, "cve202135042_wolf"."id" ASC

When passed in, the add_ordering function checks each element in the array; if it is a string, it is checked against the following 5 cases:

  1. if '.' in item: It checks whether it is a query with a column name and whether that column has a table name specified in the SQL statement. If so, it issues a warning and continue.
  2. if item == '?': If the element's value is the '?' character, the output results will be sorted randomly, continue.
  3. if item.startswith('-'): If the item starts with the '-' character, the query results will be sorted DESC (descending).
  4. if item in self.annotations: It checks whether it contains a comment; if so, continue.
  5. if self.extra and item in self.extra: Determines whether there are additional extras and if so, continue.

After the 5 checks, the parameter is then passed to the self.names_to_path(item.split(LOOKUP_SEP), self.model._meta) function to continue checking whether it is a valid column name; then, if valid, they are added to self.ordering of the Query class for further processing.

III. Analysis of the SQL injection vulnerability in Django

0x31. Cause

Download Tool