Skip to content

Latest commit

 

History

History
392 lines (310 loc) · 23.9 KB

File metadata and controls

392 lines (310 loc) · 23.9 KB

Here is the combined list of vulnerabilities, formatted as markdown, with duplicates removed and all details preserved:

Combined Vulnerability List

This document outlines the identified vulnerabilities, their descriptions, impacts, ranks, mitigations, preconditions, source code analysis, and security test cases.

1. Command Injection in changelog.py

  • Description:

    • The changelog.py script takes a version tag as a command-line argument (sys.argv[1]).
    • This tag is then directly incorporated into shell commands executed by the script using subprocess.check_output.
    • Specifically, the tag is used in the following commands:
      • git log --pretty=format:'%h %D' {last_release}..
      • git log --format='%an--sep--%B' -n1 {sha}
      • git log --format='%as' -n1 {sha}
    • If an attacker can control the value of sys.argv[1] (the version tag), they can inject arbitrary shell commands that will be executed by the script.
    • In a real-world scenario, while an external attacker cannot directly control the execution of changelog.py, if there's a workflow or automation that uses user-provided data to generate release tags and subsequently runs this script, it could be exploited. For example, if a CI/CD pipeline uses a user-provided version tag from a Git tag or branch name to trigger a release process that includes running changelog.py.
  • Impact:

    • Critical
    • Successful command injection can allow the attacker to execute arbitrary commands on the server with the privileges of the user running the changelog.py script.
    • This can lead to complete compromise of the server, including data theft, modification, and denial of service.
  • Vulnerability Rank: Critical

  • Currently implemented mitigations:

    • None
    • The script directly uses the user-provided version tag in shell commands without any sanitization or validation.
  • Missing mitigations:

    • Input Sanitization: The version tag from sys.argv[1] should be strictly validated and sanitized before being used in shell commands.
    • Command Parameterization: Instead of string formatting, use command parameterization features provided by subprocess to pass arguments safely to shell commands, preventing injection. For example, use list for subprocess.run arguments and avoid shell=True.
  • Preconditions:

    • An attacker needs to find a way to influence the input to changelog.py script. This is typically not directly exposed to external attackers. However, if the release process is automated and triggered by user-controlled inputs (e.g., git tags, branch names, CI/CD pipeline triggers), then exploitation is possible.
  • Source code analysis:

    • File: /code/changelog.py
    import subprocess
    import sys
    
    tag = sys.argv[1]
    
    git_log = subprocess.check_output(
        f"git log --pretty=format:'%h %D' {last_release}..", # Vulnerable line 1
        shell=True,
    ).decode('utf8').split('\n')
    
    # ...
    
    author, description = subprocess.check_output(
        f"git log --format='%an--sep--%B' -n1 {sha}", # Vulnerable line 2
        shell=True,
    ).decode('utf8').split('\n')[0].split('--sep--')
    
    # ...
    
    date = subprocess.check_output(
        f"git log --format='%as' -n1 {sha}", # Vulnerable line 3
        shell=True,
    ).decode('utf8').strip()
    • The code directly embeds the tag variable, which is derived from sys.argv[1], into f-strings that are then executed as shell commands using subprocess.check_output(..., shell=True).
    • There is no input validation or sanitization performed on the tag variable before it's used in the shell commands.
    • This allows for command injection because a malicious tag value could contain shell metacharacters or commands that would be interpreted by the shell when executing the git log commands.
  • Security test case:

    • Pre-requisite: Need to be able to execute changelog.py script. Assume we can simulate the release process locally.
    • Steps:
      1. Prepare a malicious version tag. For example, v1.0.0; touch /tmp/pwned. This tag attempts to execute the touch /tmp/pwned command after the intended version tag part.
      2. Execute the changelog.py script with the malicious tag as a command-line argument: ./changelog.py 'v1.0.0; touch /tmp/pwned'
      3. Check if the command injection was successful. In this case, check if the file /tmp/pwned was created.
      4. If the file /tmp/pwned exists, it confirms that the command injection vulnerability is present.

2. Hardcoded SECRET_KEY in Settings

  • Description: The project’s settings file hardcodes the Django secret key (e.g., SECRET_KEY = '58$1jvc332=lyfk_m^jl6ody$7pbk18nm95==!r$7m5!2dp%l@') within the source code. An attacker who obtains the source code (for example, by browsing a public repository) can extract this key and use it to:

    1. Forge session cookies and CSRF tokens.
    2. Impersonate users by creating or modifying signed data.
    3. Possibly escalate privileges by tampering with cryptographically protected values.
  • Impact:

    • Critical compromise of application security. With the secret key known, an attacker may hijack sessions, forge authentication tokens, and bypass security measures based on token signing.
  • Vulnerability Rank: Critical

  • Currently Implemented Mitigations:

    • There is no mitigation in place—the key is stored as a literal in the settings module.
  • Missing Mitigations:

    • • Pull the secret key out of source code and load it from a secure environment variable or secrets manager.
    • • Enforce secret rotation and avoid committing production secrets to version control.
  • Preconditions:

    • The source code is visible (e.g. via a public repository) or accessible through code leaks.
  • Source Code Analysis:

    • In /code/test_project/settings/base.py, the file plainly defines:
    SECRET_KEY = '58$1jvc332=lyfk_m^jl6ody$7pbk18nm95==!r$7m5!2dp%l@'
    • No fallback or secure import is performed.
  • Security Test Case:

    1. Access the public repository and locate the settings file (e.g. settings/base.py).
    2. Extract the hardcoded secret key from the file.
    3. Using a tool (or custom script), craft session cookies or tamper with CSRF tokens by signing them with the extracted key.
    4. Attempt to use the forged cookies/tokens to authenticate or perform sensitive actions on the deployed application.
    5. Verify that the attacker can bypass standard protections.

3. Insecure ALLOWED_HOSTS Configuration

  • Description: The settings specify ALLOWED_HOSTS = ['*'], which tells Django to accept requests from any host header. An attacker can exploit this by sending requests with a forged Host header to:

    1. Abuse URL generation (e.g. in password reset emails) by injecting attacker-controlled hostnames.
    2. Facilitate host header poisoning or DNS rebinding attacks.
  • Impact:

    • High risk of host header injection that can lead to phishing, cache poisoning, and redirect attacks—potentially tricking users or other systems into interacting with a malicious endpoint.
  • Vulnerability Rank: High

  • Currently Implemented Mitigations:

    • No mitigation is present; the project simply allows any host.
  • Missing Mitigations:

    • • Restrict ALLOWED_HOSTS to a whitelist of known, legitimate domain names (e.g. ['yourdomain.com']).
    • • Validate the Host header against an approved list before proceeding with request processing.
  • Preconditions:

    • The deployed application is accessible over the Internet, and ALLOWED_HOSTS is not overridden by an environment–specific configuration.
  • Source Code Analysis:

    • In /code/test_project/settings/base.py the configuration is directly set as:
    ALLOWED_HOSTS = ['*']
    • This setting leaves the application vulnerable to any Host header manipulation.
  • Security Test Case:

    1. Use a tool such as curl, Postman, or Burp Suite to send an HTTP request to the application endpoint with a custom Host header (for example, Host: evil.com).
    2. Confirm that the server processes the request normally despite the non–approved header.
    3. Create a scenario where the application generates absolute URLs (such as in email templates) and verify that the forged Host header appears.
    4. Document that unrestricted host names allow manipulation.

4. Potential DEBUG Mode Misconfiguration in Production

  • Description: The DEBUG setting is derived from an environment variable with a fallback that inspects command–line arguments. In environments where the DEBUG environment variable is not set explicitly, the fallback logic may enable DEBUG mode (especially when commands like “runserver” or “pytest” are detected). An external attacker could force errors that trigger detailed debug pages containing sensitive stack traces, configuration data, and other internal details.

  • Impact:

    • High risk of information disclosure. Verbose error output may leak sensitive details like file paths, database settings, and even parts of the source code, aiding an attacker in further exploits.
  • Vulnerability Rank: High

  • Currently Implemented Mitigations:

    • The code attempts to set DEBUG using:
    DEBUG = os.environ.get('DEBUG', False)
    if 'DEBUG' not in os.environ:
        for cmd in ('runserver', 'pytest', 'py.test'):
            if cmd in sys.argv[0] or len(sys.argv) > 1 and cmd in sys.argv[1]:
                DEBUG = True
                continue
    • However, this fallback can inadvertently enable DEBUG mode outside of testing.
  • Missing Mitigations:

    • • Remove fallback logic that enables DEBUG mode based on command–line inspection.
    • • Require an explicit setting (or a default safe value) for DEBUG in production environments.
    • • Consider adding a safeguard that prevents running with DEBUG=True in production deployments.
  • Preconditions:

    • The production environment is deployed without an explicit DEBUG environment variable setting, causing the fallback to enable debug mode.
  • Source Code Analysis:

    • In /code/test_project/settings/base.py, the fallback logic may set DEBUG to True if environment variables are missing and certain commands are detected. This logic is risky because it does not distinguish between a development command and a production deployment.
  • Security Test Case:

    1. Deploy the application in an environment where the DEBUG variable is not defined.
    2. Trigger an error by accessing a non–existent page or causing an exception (e.g., sending an invalid parameter).
    3. Observe that a detailed debug page is rendered, showing the full traceback and sensitive information.
    4. Verify that this page is accessible to unauthenticated external users.
    5. Document the exposure of internal details.

5. Insecure Data Access via Unvalidated Forwarded Parameters

  • Description: Some autocomplete endpoints (for example, the one in the linked_data app) use a “forward” mechanism to filter querysets based on JSON–encoded GET parameters. In the LinkedDataView, the code retrieves a forwarded parameter (e.g. "owner") and applies it directly to the queryset:

    owner = self.forwarded.get('owner', None)
    if owner:
        qs = qs.filter(owner_id=owner)

    Because the value for owner is taken directly from the request without verification, an attacker can supply an arbitrary owner identifier—accessing data that belongs to users they should not see.

  • Impact:

    • High risk of unauthorized data disclosure (an Insecure Direct Object Reference vulnerability). Attackers can manipulate forwarded parameters to retrieve data associated with other users, breaching data confidentiality and privacy.
  • Vulnerability Rank: High

  • Currently Implemented Mitigations:

    • Some parts of the project (for instance, the secure_data endpoint) correctly tie data to the authenticated request.user, but endpoints like the one in linked_data (and similar patterns in rename_forward) lack such checks.
  • Missing Mitigations:

    • • Validate that any forwarded parameter (such as owner ID) matches the identity or permissions of the authenticated user.
    • • Enforce proper authentication for these endpoints and deny filtering based solely on client–provided forwarded values.
  • Preconditions:

    • The endpoint (e.g. /linked_data/) is accessible without sufficient access control, and the forwarded parameters are taken directly from the client request (via JSON–encoded GET parameters).
  • Source Code Analysis:

    • In /code/test_project/linked_data/urls.py, the view’s get_queryset() method processes the forwarded “owner” parameter without verifying that it belongs to request.user:
    class LinkedDataView(autocomplete.Select2QuerySetView):
        def get_queryset(self):
            qs = super(LinkedDataView, self).get_queryset()
            owner = self.forwarded.get('owner', None)
            if owner:
                qs = qs.filter(owner_id=owner)
            return qs
    • This design assumes that the forwarded data is “trusted”—which is not the case in a public instance.
  • Security Test Case:

    1. As an external attacker (or using an unauthorized account), send a GET request to the /linked_data/ endpoint with a manipulated forwarding parameter in the query string. For example:
      GET /linked_data/?forward={"owner": "1"}
      
    2. Observe that the response includes autocomplete suggestions filtered by owner_id = 1 regardless of the attacker’s own user ID.
    3. Try altering the forwarded “owner” value to different numbers and verify that data belonging to other users is returned.
    4. Document that the endpoint returns data not limited to the authenticated user, confirming an insecure data–filtering flaw.

6. Unvalidated data creation in Select2ListView and Select2ProvidedValueListView

  • Description:

    1. An attacker can send a POST request to the Select2ListViewAutocomplete or Select2ProvidedValueListViewAutocomplete views with a text parameter.
    2. These views, designed for handling list-based autocompletes, implement a create method that directly returns the provided text without any validation or sanitization.
    3. If the autocomplete widget using these views is configured with create_field, it enables the "Create 'text'" option in the dropdown.
    4. When a user (potentially an admin or other authorized user in certain admin configurations) selects this "Create" option and submits the form, the create method in the view is called via a POST request, adding the unvalidated text directly into the choices.
    5. In the provided Select2ListViewAutocomplete and Select2ProvidedValueListViewAutocomplete views, the create method returns the unsanitized user input directly. This input is then displayed as a selectable option in the autocomplete widget for subsequent requests.
    6. If an attacker provides malicious JavaScript code as the text parameter, this code will be rendered in the HTML when the autocomplete suggestions are displayed, leading to Cross-Site Scripting (XSS).
  • Impact:

    • Cross-Site Scripting (XSS) vulnerability.
    • An attacker can inject malicious JavaScript code into the application.
    • When an authorized user views the page containing the vulnerable autocomplete widget, the injected script will execute in their browser.
    • This can lead to session hijacking, account takeover, data theft, or other malicious actions performed in the context of the victim user's session.
  • Vulnerability Rank: High

  • Currently Implemented Mitigations:

    • None. The create methods in Select2ListView and Select2ProvidedValueListView in dal/autocomplete.py and dal_select2/views.py, and the example views in /code/test_project/select2_list/views.py do not perform any validation or sanitization of the input text.
  • Missing Mitigations:

    • Input validation and sanitization in the create methods of Select2ListView and Select2ProvidedValueListView in both dal/autocomplete.py and dal_select2/views.py.
    • HTML escaping of the text rendered in the autocomplete suggestions, especially when displaying the "Create 'text'" option and the created options themselves.
  • Preconditions:

    • The Select2ListViewAutocomplete or Select2ProvidedValueListViewAutocomplete views (or similar subclasses using Select2ListView or Select2ProvidedValueListView from either dal/autocomplete.py or dal_select2/views.py) are used with ListSelect2 widget.
    • The ListSelect2 widget is configured with create_field option enabled (implicitly or explicitly by setting data-tags="1" or similar).
    • An attacker can send POST requests to the autocomplete view URL.
    • An authorized user (e.g., admin) views a page containing the vulnerable autocomplete widget and triggers the autocomplete functionality by typing in the field and potentially selecting the "Create" option after an attacker has injected malicious code.
  • Source code analysis:

    1. dal/autocomplete.py and dal_select2/views.py - Select2ListView.create and Select2ProvidedValueListView.create:
    # dal/autocomplete.py
    class Select2ListView(ViewMixin, View):
        # ...
        def post(self, request):
            # ...
            if self.create_field and 'text' in request.POST:
                text = request.POST['text']
                if self.has_create_permission(request):
                    new_item = self.create(text) # Vulnerable line
                    if new_item is None:
                        return HttpResponseBadRequest('Create not allowed')
                    # ...
                    return HttpResponse(json.dumps({
                        'id': self.get_created_item_value(new_item),
                        'text': self.get_created_item_text(new_item),
                    }), content_type='application/json')
            return HttpResponseBadRequest('Create not allowed')
    
        def create(self, text):
            """Create and return new item from text, or None."""
            raise ImproperlyConfigured(
                "'create' method must be implemented to use create_field"
            )
    
    class Select2ProvidedValueListView(Select2ListView):
        # ...
        def create(self, text):
            """Create and return new item from text, or None."""
            raise ImproperlyConfigured(
                "'create' method must be implemented to use create_field"
            )
    
    # dal_select2/views.py
    class Select2ListView(ViewMixin, View):
        # ...
        def post(self, request, *args, **kwargs):
            """Add an option to the autocomplete list.
    
            ...
            """
            if not hasattr(self, 'create'):
                raise ImproperlyConfigured('Missing "create()"')
    
            text = request.POST.get('text', None)
    
            if text is None:
                return http.HttpResponseBadRequest()
    
            text = self.create(text) # Vulnerable line
    
            if text is None:
                return http.HttpResponseBadRequest()
    
            return http.JsonResponse({
                'id': text,
                'text': text,
            })
    
        def create(self, text):
            """Create and return new item from text, or None."""
            raise ImproperlyConfigured(
                "'create' method must be implemented to use create_field"
            )
    • The base classes Select2ListView and Select2ProvidedValueListView in dal/autocomplete.py and dal_select2/views.py expect the create method to be overridden in subclasses to provide actual creation logic.
    • If subclasses (like example views or user-defined views) implement create like this (example from previous analysis, but applicable to both locations):
    class CustomSelect2ListViewAutocomplete(Select2ListView): # or Select2ListView from dal_select2/views.py
        def create(self, text):
            return text # Unsafe: Returns unsanitized input directly
        # ...
    • They directly return the user-provided text without any sanitization. This is the root cause of the XSS vulnerability in both locations.
    1. dal_select2/widgets.py - ListSelect2 rendering:
    • The ListSelect2 widget, used in conjunction with these views, renders the options received from the view. If the text attribute in the JSON response from the create method contains malicious JavaScript, it will be inserted into the HTML without proper escaping, leading to XSS when the browser renders the suggestions.

    Visualization:

    sequenceDiagram
        participant Attacker
        participant Browser
        participant Application
        participant Backend
    
        Attacker->Application: POST /test-autocomplete/ with text="<script>alert('XSS')</script>"
        Application->Backend: Call Select2ListView.create("<script>alert('XSS')</script>")
        Backend->Backend: Select2ListView.create returns "<script>alert('XSS')</script>" (unsanitized)
        Backend->Application: Response 200 OK with JSON: {'id': '<script>alert("XSS")</script>', 'text': '<script>alert("XSS")</script>'}
        Application->Browser: Response with HTML containing ListSelect2 widget
        Browser->Browser: User interacts with the ListSelect2 widget, triggering autocomplete
        Browser->Application: GET /test-autocomplete/?q=...
        Application->Backend: Select2ListView.get_list() returns list including "<script>alert('XSS')</script>"
        Backend->Application: Response 200 OK with JSON including {'id': '<script>alert("XSS")</script>', 'text': '<script>alert("XSS")</script>'}
        Application->Browser: Response with HTML for autocomplete suggestions, including unsanitized text
        Browser->Browser: Browser renders HTML, executes injected JavaScript alert('XSS')
    
    Loading
  • Security Test Case:

    1. Pre-requisites:

      • Ensure the test project is set up and running.
      • Access the URL for the select2_list test app, for example, /dal_single/ or /admin/select2_list/tmodel/add/ if the widget is used in admin.
      • Identify the URL used for the ListSelect2 widget, which is likely /select2_list/test-autocomplete/ based on /code/test_project/select2_list/urls.py and /code/test_project/select2_list/forms.py.
    2. Steps:

      • Open a browser and navigate to the page containing the ListSelect2 widget (e.g., /dal_single/).
      • Open the browser's developer tools (usually by pressing F12).
      • In the developer tools, go to the "Network" tab to monitor network requests.
      • In the ListSelect2 input field, type some text to trigger the autocomplete suggestions.
      • Observe the network requests; you should see a GET request to /select2_list/test-autocomplete/?q=....
      • Now, in the ListSelect2 input field, type the following payload: <script>alert("XSS-ListSelect2")</script>.
      • Look for the "Create '...' " option in the dropdown list. It should display "Create '<script>alert("XSS-ListSelect2")</script>'".
      • Select this "Create" option. This will send a POST request in the background.
      • Type some other characters in the input field again to trigger the autocomplete suggestions again.
      • Expected Outcome: An alert box with "XSS-ListSelect2" should pop up in the browser, demonstrating successful XSS. If the alert doesn't appear immediately, inspect the HTML source of the autocomplete suggestions in the developer tools (Elements tab) to confirm that the <script> tag is injected into the HTML.

This test case confirms that the create functionality in Select2ListView and Select2ProvidedValueListView (in both dal/autocomplete.py and dal_select2/views.py) is vulnerable to Cross-Site Scripting due to the lack of input validation and sanitization.