Full Report
Django is an open source Python framework used for web applications. It is used by many organizations as the backend web server for a website. Django is an MVC (model view controller) framework. It has a templating engine to allow for simple integration between the different sections of code - Django Templating Language (DTL). The author gives a simple example: showing registered users of a database with some control over the fields being sorted on, such as starting letter or something else. The Python code returns a list of objects for the context of the template. The template runs {% for e in users|dictsort:sort % to iterate through all of the usernames in a filtered way. What's the problem? It's an issue with the resolution process of the sort itself. Under the hood, dictsort uses the built in function sorted with a custom function to decide the order. The sort attempts to exclude all sensitive values in the object (__ by convention). Django has done a wonderful job to prevent access to unauthorized things. What's the problem? The lookup attempts 4 different ways for searching: dictionary lookup, attribute lookup (class), list-index lookup and method call without arguments. The FINAL option allows us to call arbitrary functions without parameters. According to the author, they claim this could be used to delete application files or any bad things. Instead of showing this route, they take it another way: a sorting oracle. The idea is that since we fully control the sorting key, we can learn information about the service. For instance, if we sort based upon the first character of the password, we can learn if a password is ABOVE or BELOW our password. Nice! :) With full control over the data in the database for a given password, this could be used to extract a ton of sensitive information. In Django, passwords are hashed with an unknown secret and a random salt though. How is this possible then? First, a trick: if two values are the same, then it will always be the same order - the unsorted form. With the unsorted form at hand, users can be put into groups. If a column has a particular character ('a') then it will be put first or 'b' be put second. What if two column characters are the same? We will know this since we KNOW the unsorted order of the column! This allows for the discerning of the end of one group and the start of another. The key to this scheme is the unsorted items to understand the ending of a group. This requires a character to be EXACTLY the same in every single group. Another assumption that is made is that every character is represented. Given enough entries (like a password hash) this is likely though. The attack has many requirements but allows for the extraction of sensitive data via a side channel of sorting. To fix this vulnerability, the maintainers restricted the ability of _proper_resolver to remove list and function invocation. Overall, amazing post on a crazy side channel!
Analysis Summary
# Vulnerability: Information Disclosure via `dictsort` Side-Channel in Django
## CVE Details
- **CVE ID:** CVE-2021-45116
- **CVSS Score:** 7.5 (High)
- **CWE:** CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor) / CWE-203 (Observable Discrepancy)
## Affected Systems
- **Products:** Django Web Framework
- **Versions:**
- Django 2.2 before 2.2.26
- Django 3.2 before 3.2.11
- Django 4.0 before 4.0.1
- **Configurations:** Applications using the Django Templating Language (DTL) that pass user-controlled input to the `dictsort` filter.
## Vulnerability Description
The vulnerability resides in the `dictsort` template filter's variable resolution logic. Under the hood, `dictsort` uses a helper function called `_property_resolver` which attempts to resolve the sorting key using four methods:
1. Dictionary lookup
2. Attribute lookup (class attributes)
3. List-index lookup
4. Method call without arguments
The latter two methods allow for unintended behavior. Specifically, the ability to call arbitrary methods without arguments could potentially lead to unauthorized actions (e.g., file deletion if such a method exists on the object). More critically, the list-index lookup allows an attacker to sort data based on specific characters of a string (like a password hash). By controlling the sorting key and observing the resulting order of displayed items, an attacker can create a "sorting oracle" to extract sensitive information character-by-character.
## Exploitation
- **Status:** PoC available (detailed in the research post).
- **Complexity:** High (Requires a side-channel attack and specific data conditions, such as knowing the original unsorted order).
- **Attack Vector:** Network (Remote).
## Impact
- **Confidentiality:** High (Extraction of sensitive data including email addresses and password hashes).
- **Integrity:** None (Low in theory if a sensitive method call is triggered).
- **Availability:** Low (Possible if a triggered method affects application state).
## Remediation
### Patches
The Django maintainers released the following security updates:
- Django 2.2.26
- Django 3.2.11
- Django 4.0.1
The fix involved restricting `_property_resolver` to allow only dictionary and attribute lookups, specifically removing the ability to perform list-index lookups and function invocations.
### Workarounds
- Rigidly validate any user input used as an argument for the `dictsort` filter.
- Avoid passing sensitive objects (like User models with password hashes) directly to the template context if they are to be sorted by user-controlled keys.
## Detection
- **Indicators of Compromise:** Unusual URL parameters where the `sort` key contains unexpected characters (indices like `0`, `1`) or method names (e.g., `delete`).
- **Detection Methods:** Audit Django templates for the use of the `dictsort` filter and trace the source of the argument to ensure it is not directly sourced from `request.GET` or `request.POST` without sanitization.
## References
- Vendor Advisory: hxxps[:]//www[.]djangoproject[.]com/weblog/2022/jan/04/security-releases/
- Patch: hxxps[:]//github[.]com/django/django/commit/761f449e0daf3de06b0132bd4d6dfcdeef578e26
- Research Post: hxxps[:]//www[.]sonarsource[.]com/blog/disclosing-information-with-a-side-channel-in-django/