Here is the combined list of vulnerabilities, formatted as markdown with main paragraphs and subparagraphs for each vulnerability, after removing duplicates:
-
Vulnerability Name: Potential DjangoQL Injection in
completion_demo
view-
Description: The
completion_demo
view in/code/test_project/core/views.py
is vulnerable to DjangoQL injection. The view uses user-provided input from theq
GET parameter to construct and execute DjangoQL queries against theUser
model. TheUserQLSchema
used in this view, while intending to limit searchable fields, by default includes all fields of theUser
andGroup
models except 'password'. This allows an attacker to craft malicious DjangoQL queries to access sensitive user attributes and related data that are not intended for public exposure. -
Impact: Information Disclosure. A remote attacker can potentially extract sensitive information about users, such as
is_staff
,is_superuser
,last_login
,date_joined
, email addresses, and group memberships, by crafting specific DjangoQL queries. This information can be used for further attacks or unauthorized access. -
Vulnerability Rank: High
-
Currently implemented mitigations:
- DjangoQL Schema validation: The project uses
DjangoQLSchema
to define searchable models and fields. However, the default schema incompletion_demo
is not restrictive enough. - Error handling: The code includes error handling for
DjangoQLError
exceptions, which prevents the application from crashing due to invalid queries, but it does not prevent information disclosure.
- DjangoQL Schema validation: The project uses
-
Missing mitigations:
- Restrictive DjangoQL Schema: The
UserQLSchema
in/code/test_project/core/views.py
should be modified to explicitly include only non-sensitive fields of theUser
andGroup
models that are intended for public demonstration. For example, limit fields tousername
,first_name
,last_name
, andgroups.name
. Sensitive fields likeis_staff
,is_superuser
,last_login
,date_joined
, andemail
should be explicitly excluded or not included in the schema. - Input sanitization: While DjangoQL is designed to handle query parsing safely, consider adding an additional layer of input sanitization to filter or validate the
q
parameter before passing it toapply_search
. This can act as a defense-in-depth measure. - Access control: If the
completion_demo
view is intended only for internal demonstration purposes, consider removing it from public access or implementing authentication and authorization to restrict access to trusted users.
- Restrictive DjangoQL Schema: The
-
Preconditions:
- The application must be deployed and publicly accessible, with the
completion_demo
view exposed through a URL. - No Web Application Firewall (WAF) or other security controls are in place to filter or block malicious DjangoQL queries.
- The application must be deployed and publicly accessible, with the
-
Source code analysis:
- Vulnerable Code Location:
/code/test_project/core/views.py:22-37
- Vulnerability Step-by-step:
- The
completion_demo
view is accessed via a GET request. - The view retrieves the value of the
q
parameter from the request:q = request.GET.get('q', '')
. - A Django queryset for
User
model is initialized:query = User.objects.all().order_by('username')
. - If the
q
parameter is not empty, theapply_search
function is called to filter the queryset based on the DjangoQL query provided inq
:query = apply_search(query, q, schema=UserQLSchema)
. - The
UserQLSchema
is defined in the same file (/code/test_project/core/views.py:14-19
) and includesUser
andGroup
models without explicitly excluding any fields. - The
apply_search
function in/code/djangoql/queryset.py
parses the DjangoQL query usingDjangoQLParser
and builds a Django ORM query based on the schema. - Because
UserQLSchema
does not restrict fields, an attacker can query against any field of theUser
andGroup
models, including sensitive ones. - The filtered queryset is then passed to the template for rendering, potentially displaying sensitive information based on the attacker's query.
- The
# /code/test_project/core/views.py @require_GET def completion_demo(request): q = request.GET.get('q', '') # [Step 1] User input is taken from 'q' parameter error = '' query = User.objects.all().order_by('username') # [Step 2] Initialize User queryset if q: try: query = apply_search(query, q, schema=UserQLSchema) # [Step 3] Apply DjangoQL search with user input and UserQLSchema except DjangoQLError as e: query = query.none() error = str(e) # ... rest of the view ...
# /code/test_project/core/views.py class UserQLSchema(DjangoQLSchema): # [Step 4] UserQLSchema definition (not restrictive enough) include = (User, Group) suggest_options = { Group: ['name'], }
- Vulnerable Code Location:
-
Security test case:
- Deploy the
test_project
application to a publicly accessible server. - Access the
completion_demo
view by navigating to the root URL/
. - Construct a malicious URL to test for information disclosure by querying for users with
is_staff = True
. For example, append the following query string to the URL:?q=is_staff=True
. The full URL would look like:http://<your-deployed-app-url>/?q=is_staff=True
. - Examine the search results displayed on the page. If the results only show users who are staff members (i.e.,
is_staff
is True), this indicates that the query was successfully executed and sensitive information based onis_staff
is accessible. - Further test by querying other sensitive fields like
is_superuser=True
,last_login > "2024-01-01"
, or even accessing related fields likegroups.name = "Administrators"
to confirm the extent of information disclosure. - Observe the response. If you can filter users based on these sensitive attributes, the application is vulnerable to DjangoQL injection and information disclosure.
- Deploy the
-
-
Vulnerability Name: Hardcoded Django SECRET_KEY Exposure
-
Description: The settings file (/code/test_project/test_project/settings.py) defines the Django
SECRET_KEY
as a literal static string:SECRET_KEY = 'o56l!!8q!rwqy13optbmnycu97^tvh@bsk1t!-^$&7o@t4nsbg'
An external attacker who reviews the publicly available source code (or who gains access to a production instance deployed with these defaults) can extract the secret key. With this knowledge, the attacker can forge or tamper with session cookies, CSRF tokens, and other signed data. This could then be exploited—for example, by creating forged session cookies to impersonate legitimate users (including administrative users) or tampering with security-critical data.
-
Impact:
- Session hijacking and impersonation of users (including admin accounts).
- Tampering with Django’s signed cookies may bypass authentication/authorization controls.
- Undermines cryptographic integrity of all features that rely on the secret key.
-
Vulnerability Rank: Critical
-
Currently implemented mitigations:
- None. The key is hardcoded directly in the settings file.
-
Missing mitigations:
- Externalize the secret key by loading it from an environment variable or a secure configuration management system.
- Ensure that production deployments override the default key with a securely generated, random value.
-
Preconditions:
- The application is deployed without overriding the hardcoded default and an attacker can view the repository or deduce the key from a misconfigured production environment.
-
Source code analysis:
- In
/code/test_project/test_project/settings.py
, the secret key is assigned directly:SECRET_KEY = 'o56l!!8q!rwqy13optbmnycu97^tvh@bsk1t!-^$&7o@t4nsbg'
- There is no logic to override or replace this value at runtime.
- In
-
Security test case:
- Retrieve the source code or configuration (for example, via an unintended repository exposure).
- Confirm that the
SECRET_KEY
is the hardcoded string. - Using Django’s signing utility (or a crafted session cookie based on Django’s algorithm), attempt to forge a session cookie or CSRF token.
- Submit the forged token/cookie to a secure endpoint and verify whether the application accepts it (indicating a successful exploitation of the key).
-
-
Vulnerability Name: Public Exposure of User Data via Completion Demo Endpoint
-
Description: The view at
/code/test_project/core/views.py
—namedcompletion_demo
—is registered as the root URL (^$
) in the URL patterns. This view unconditionally retrieves a full list of users via:query = User.objects.all().order_by('username')
and passes it directly to the template as part of the context. Because the view only requires a GET request (enforced by
@require_GET
) and does not restrict access via authentication or authorization, any external attacker can simply access the homepage to receive a complete list of user records. -
Impact:
- Exposure of potentially sensitive user information (usernames and group associations).
- Enables account enumeration, facilitating targeted phishing attacks, brute-force login attempts, or other methods to compromise user accounts.
-
Vulnerability Rank: High
-
Currently implemented mitigations:
- None. There is no authentication or authorization check before returning user data.
-
Missing mitigations:
- Restrict access to endpoints that return user information (for example, by requiring authentication or moving demo functionality behind a protected area).
- Limit the fields returned to only non-sensitive information or employ pagination and filtering with strict access controls.
-
Preconditions:
- The application is deployed with the public demo/search endpoint accessible at the root URL without login.
-
Source code analysis:
- In
/code/test_project/core/views.py
, thecompletion_demo
view starts by doing:query = User.objects.all().order_by('username')
- No checks (such as
login_required
) or sanitization measures are implemented before passing the complete queryset to the renderer. - The URL configuration in
/code/test_project/test_project/urls.py
maps the root URL (r'^$'
) tocompletion_demo
, making it publicly accessible.
- In
-
Security test case:
- As an unauthenticated external attacker, navigate to the application’s root URL (e.g., http://example.com/).
- Observe that the rendered page contains a list of users (including usernames and group affiliations).
- Verify that no authentication is required to access this data.
-
-
Vulnerability Name: Excessive Error Information Disclosure via Malformed Search Queries
-
Description: The same
completion_demo
view accepts an optional GET parameterq
intended for search queries. When an attacker supplies an invalid or malformed query string, the view calls:try: query = apply_search(query, q, schema=UserQLSchema) except DjangoQLError as e: query = query.none() error = str(e)
The resulting error message (obtained via
str(e)
) is then passed to the template and rendered to the client. Since the underlying query language (via DjangoQL) raises errors that include internal schema details (such as valid field names and operator expectations), an attacker can trigger these errors to obtain detailed information about the internal data model. -
Impact:
- Attackers gain insight into backend model structure, field names, and valid query parameters.
- Facilitates further exploitation by revealing internal logic that can inform injection, enumeration, or more targeted attacks.
-
Vulnerability Rank: High
-
Currently implemented mitigations:
- The view catches the exception and does not crash; however, it simply passes the raw error string along to the user.
-
Missing mitigations:
- Sanitize error messages shown to the client so that they do not reveal internal schema details.
- Log the detailed error internally while returning a generic error message to the user.
-
Preconditions:
- The application is publicly accessible at the completion demo endpoint.
- No input validation or generic error handling is applied to hide sensitive exception details.
-
Source code analysis:
- In
/code/test_project/core/views.py
, whenq
is provided, the block:assigns the exact error string (which in cases of an unknown field, for example, will include a list of valid field names) to the variabletry: query = apply_search(query, q, schema=UserQLSchema) except DjangoQLError as e: query = query.none() error = str(e)
error
. - In the schema resolution (in
/code/djangoql/schema.py
within theresolve_name
method), if an unknown field is referenced, the error message lists the possible valid field names.
- In
-
Security test case:
- As an external attacker, access the completion demo endpoint by sending a GET request with a clearly invalid query, for example:
http://example.com/?q=nonexistentField=foo
- Note that the response displays an error message including details like:
Unknown field: nonexistentField. Possible choices are: author, content_type, genre, id, is_published, name, object_id, price, rating, similar_books, written, …
- Confirm that the error message contains internal schema details that should not have been exposed.
- Verify that modifying the search query (e.g. omitting or changing field names) continues to reveal similar internal information.
- As an external attacker, access the completion demo endpoint by sending a GET request with a clearly invalid query, for example:
-
-
Vulnerability Name: SQL Injection via crafted string comparison in DjangoQL query
-
Description: An attacker can inject raw SQL into the database query through a crafted DjangoQL query string, specifically when using string comparison operators like
startswith
,endswith
,~
(contains), and their negated forms (not startswith
,not endswith
,!~
). This is due to insufficient sanitization of string literals within the DjangoQL parser before they are used in raw SQL LIKE clauses.Step-by-step trigger:
- Identify a Django application using DjangoQL and exposing a DjangoQL search interface to external users (e.g., via Django Admin or a custom view like
completion_demo
). - Construct a malicious DjangoQL query that includes a string comparison operator (
startswith
,endswith
,~
,!~
,not startswith
,not endswith
,!~
). - Inject raw SQL within the string literal part of the query. For example, using
startswith "') OR 1=1 --"
within a user-controlled input field that is processed by DjangoQL. - Submit the crafted query to the application.
- The DjangoQL parser will generate an AST.
- The
build_filter
function will construct a Django ORM query using this AST, embedding the malicious SQL string directly into a raw SQLLIKE
clause. - The database will execute the query with the injected SQL, potentially leading to data extraction, modification, or other malicious actions.
- Identify a Django application using DjangoQL and exposing a DjangoQL search interface to external users (e.g., via Django Admin or a custom view like
-
Impact: Critical. Successful SQL injection can lead to:
- Data Breach: Unauthorized access to sensitive data stored in the database.
- Data Manipulation: Modification or deletion of data, leading to data integrity issues.
- Account Takeover: In some cases, it might be possible to escalate privileges or gain access to other user accounts.
- System Compromise: In severe cases, depending on database permissions and application setup, it could potentially lead to broader system compromise.
-
Vulnerability Rank: Critical
-
Currently implemented mitigations: None. The provided code does not implement any sanitization or escaping of string literals specifically to prevent SQL injection in
LIKE
clauses. -
Missing mitigations:
- Input Sanitization/Escaping: String literals used in
LIKE
clauses within DjangoQL queries should be properly sanitized or escaped before being incorporated into raw SQL queries. This could involve escaping special characters that have meaning in SQLLIKE
patterns (e.g.,%
,_
,\
). - Parameterized Queries (for LIKE): Ideally, even for
LIKE
clauses, parameterized queries should be used to separate SQL code from user-provided data. However, directly parameterizing the pattern inLIKE
might not be straightforward across all database backends. Escaping is a more practical immediate mitigation forLIKE
. - Content Security Policy (CSP): While not a direct mitigation for SQL injection, a strong CSP can help limit the impact of successful attacks by restricting the actions an attacker can take even if they manage to inject malicious code.
- Input Sanitization/Escaping: String literals used in
-
Preconditions:
- A Django application is using the DjangoQL library.
- The application exposes a DjangoQL search interface to external users, either through Django Admin or custom views.
- The application uses string comparison operators (
startswith
,endswith
,~
,!~
,not startswith
,not endswith
,!~
) in its DjangoQL queries.
-
Source code analysis:
djangoql/lexer.py
: The lexer correctly identifies string literals, including escaped characters, but does not perform any sanitization for SQLLIKE
context.
@TOKEN(r'\"(' + re_escaped_char + '|' + re_escaped_unicode + '|' + re_string_char + r')*\"') def t_STRING_VALUE(self, t): t.value = t.value[1:-1] # cut leading and trailing quotes "" return t
The string literal value is extracted but no SQL escaping is applied.
djangoql/parser.py
: The parser constructs the AST, including string literals, without modification.
def p_string(self, p): """ string : STRING_VALUE """ p[0] = Const(value=unescape(p[1]))
The
unescape
function handles Python string escapes but not SQLLIKE
escapes.-
djangoql/schema.py
: The schema validation focuses on data types and field existence but does not validate the content of string literals for SQL injection risks. -
djangoql/queryset.py
: Thebuild_filter
function constructs Django ORMQ
objects. For string comparisons (~
,!~
,startswith
,endswith
, and their negated forms), it usesicontains
,istartswith
,iendswith
lookups, which translate toLIKE
in SQL. The string literal is directly passed into the ORM lookup without escaping forLIKE
context.
def get_operator(self, operator): """ Get a comparison suffix to be used in Django ORM & inversion flag for it ... """ op = { '=': '', '>': '__gt', '>=': '__gte', '<': '__lt', '<=': '__lte', '~': '__icontains', # <--- icontains, istartswith, iendswith use LIKE 'in': '__in', 'startswith': '__istartswith', # <--- icontains, istartswith, iendswith use LIKE 'endswith': '__iendswith', # <--- icontains, istartswith, iendswith use LIKE }.get(operator) if op is not None: return op, False op = { '!=': '', '!~': '__icontains', # <--- icontains, istartswith, iendswith use LIKE 'not in': '__in', 'not startswith': '__istartswith', # <--- icontains, istartswith, iendswith use LIKE 'not endswith': '__iendswith', # <--- icontains, istartswith, iendswith use LIKE }[operator] return op, True def get_lookup(self, path, operator, value): """ Performs a lookup for this field with given path, operator and value. ... """ search = '__'.join(path + [self.get_lookup_name()]) op, invert = self.get_operator(operator) q = models.Q(**{'%s%s' % (search, op): self.get_lookup_value(value)}) # <--- value is used directly in Q object return ~q if invert else q
The value from the parsed string literal is directly placed into the
Q
object, which will be used by Django ORM to construct the SQL query. Django ORM generally parameterizes values, but forLIKE
patterns, the pattern itself (including%
,_
) is treated as part of the SQL command and not fully parameterized in all database backends, making it vulnerable if not escaped. -
Security test case:
Pre-requisites:
- Set up the
test_project
from the provided files. - Create a superuser and log in to the Django admin panel.
- Ensure there are
Book
objects in the database.
Steps:
- Access the Django Admin changelist view for
Book
objects (/admin/core/book/
). - Locate the DjangoQL search field.
- Enter the following malicious DjangoQL query into the search field:
or
name startswith "') OR 1=1 --"
orname ~ "') OR 1=1 --"
name endswith "') OR 1=1 --"
- Submit the search query.
- Observe the results. If the vulnerability is present, the query
1=1
will always be true, and the--
comment will comment out the rest of the original query condition. This could result in allBook
objects being returned, regardless of the actual intended search criteria. This behavior would indicate successful SQL injection. - (Advanced Test - Requires Database Logging): Examine the raw SQL queries executed by Django in the database logs. You should see the injected SQL being directly embedded within the
LIKE
clause, confirming the SQL injection vulnerability. For example, the logged query might look something like:SELECT ... FROM core_book WHERE core_book.name LIKE '%) OR 1=1 --%';
- Set up the
-
This test case demonstrates a basic SQL injection. More sophisticated injection payloads can be crafted to extract data or perform other database operations.