Skip to content

Latest commit

 

History

History
191 lines (164 loc) · 14.8 KB

File metadata and controls

191 lines (164 loc) · 14.8 KB

Here is the combined list of vulnerabilities in markdown format:

Combined Vulnerability List

  • Vulnerability Name: Command Injection in Precompiler Commands

  • Description:

    1. An attacker, assuming they can influence Django settings (e.g., through a separate vulnerability or misconfiguration), modifies the COMPRESS_PRECOMPILERS setting in settings.py. This setting defines external precompiler commands for various file types.
    2. The attacker injects a malicious command into the command template string of one of the precompilers. For example, they might configure a precompiler to execute a command like touch /tmp/pwned; {infile} {outfile} for a specific mimetype. This command template is sourced from the configuration settings and is formatted using Python’s string formatting.
    3. The attacker then triggers a scenario where django-compressor processes a file (either a legitimate file or a crafted one) that is associated with the mimetype they configured with the malicious command. This can be achieved by including a file of the configured mimetype within a {% compress %} block in a Django template. For example, rendering a Django template that includes a <style type="text/x-exploit"> block if 'text/x-exploit' is configured as the vulnerable mimetype.
    4. When django-compressor processes this file, the CompilerFilter class in /code/compressor/filters/base.py is invoked. The input method of this class formats the command string using self.command.format(**options), where options can include placeholders like {infile} and {outfile}, and executes it using subprocess.Popen with shell=True.
    5. While file path parameters ({infile} and {outfile}) are shell-quoted on non-Windows platforms, other placeholders or the command template itself are not sanitized. If an attacker controls these unsanitized configuration values, they can inject arbitrary shell commands.
    6. The injected command is executed on the server due to shell=True, leading to arbitrary code execution with the privileges of the Django application process.
  • Impact:

    • Remote Code Execution (RCE): Successful exploitation allows the attacker to execute arbitrary commands on the server hosting the Django application. This can lead to a complete compromise of the server, including unauthorized data access, data manipulation, service disruption, and the potential to pivot to internal networks. The attacker can gain full control over the server environment.
  • Vulnerability Rank: critical

  • Currently Implemented Mitigations:

    • Partial Mitigation: File path parameters (i.e., {infile} and {outfile}) are passed through shlex.quote on non-Windows platforms before being substituted into the command template. This offers some protection against command injection via filenames.
    • Reliance on Secure Configuration: The system relies on the assumption that COMPRESS_PRECOMPILERS and other related settings are defined and managed only by trusted developers using secure, hard-coded configuration values. It assumes that these settings are not influenced by untrusted user input.
  • Missing Mitigations:

    • Input Sanitization: Implement comprehensive input sanitization for all parts of the command strings and any options used in CompilerFilter, not just file paths. This should include validating or sanitizing all placeholders in the command template and dynamically supplied arguments.
    • Avoid shell=True: Modify CompilerFilter to avoid using shell=True in subprocess.Popen. Instead, execute commands directly as a list of arguments. This would require refactoring how precompiler commands are defined and used to ensure commands are executed safely without shell interpretation.
    • Command Whitelisting or Validation: Implement a whitelist or validation mechanism to ensure that the final resolved command matches an approved “safe” pattern. This could involve parsing the command and verifying its structure before execution.
    • Defense-in-Depth: Implement defense-in-depth measures such as executing precompiler commands in a restricted privilege sandbox, chroot, or similar container to limit the impact of successful command injection.
    • Documentation and Warnings: Enhance documentation to clearly warn developers about the critical risks associated with allowing untrusted input to influence COMPRESS_PRECOMPILERS and other precompiler-related settings. Emphasize secure configuration management and access control to Django settings.
  • Preconditions:

    • Attacker Influence on Settings: The attacker must be able to influence Django project settings, specifically the COMPRESS_PRECOMPILERS setting. This could be achieved through a separate vulnerability, a misconfiguration that exposes settings, or if file names or configuration values are allowed to be influenced by untrusted input (e.g., via file upload or static asset naming).
    • Precompilers Enabled: The Django application must be configured to use precompilers defined in COMPRESS_PRECOMPILERS.
    • Trigger Compression: An attacker needs to trigger the compression process for content that will utilize the precompiler with the injected command. This typically involves rendering a Django template that includes a {% compress %} block containing a file type associated with the malicious precompiler configuration.
  • Source Code Analysis:

    1. Configuration: File: /code/compressor/conf.py

      class CompressorConf(AppConf):
          # ...
          PRECOMPILERS = (
              # ('text/coffeescript', 'coffee --compile --stdio'),
              # ('text/less', 'lessc {infile} {outfile}'),
              # ...
          )
          # ...
      • COMPRESS_PRECOMPILERS setting defines tuples of mimetype and command templates. These templates are intended for developer configuration but can be vulnerable if modifiable by attackers.
    2. Command Execution: File: /code/compressor/filters/base.py

      class CompilerFilter(FilterBase):
          command = None
          options = ()
          # ...
          def input(self, **kwargs):
              # ...
              try:
                  command = self.command.format(**options)
                  proc = subprocess.Popen(
                      command,
                      shell=True, # <--- Vulnerable: shell=True
                      cwd=self.cwd,
                      stdout=self.stdout,
                      stdin=self.stdin,
                      stderr=self.stderr,
                  )
                  # ...
              except (IOError, OSError) as e:
                  raise FilterError(
                      "Unable to apply %s (%r): %s"
                      % (self.__class__.__name__, self.command, e)
                  )
              # ...
      • The CompilerFilter.input method formats the self.command string using self.command.format(**options).
      • It then executes the formatted command using subprocess.Popen(command, shell=True, ...).
      • Vulnerability: The use of shell=True in subprocess.Popen is the core vulnerability. It allows shell metacharacters and command chaining to be interpreted by the shell, if the command string or options are attacker-controlled or not properly sanitized.
      • Visualization of Command Construction:
        1. Template: "%(binary)s %(args)s {infile} {outfile}" (Example template from COMPRESS_PRECOMPILERS)
        2. Parameter Preparation:
          • {infile} and {outfile} are sanitized using shell quoting (on non-Windows).
          • Other parameters (e.g., {binary}, {args}, or custom placeholders) are taken directly from configuration without sanitization.
        3. Formatting: Unsanitized placeholders are substituted into the command template. If an attacker controls these values, the final command could become: "trusted_binary --safe_arg " + malicious_payload.
        4. Execution: The constructed command is executed with shell=True, leading to the execution of injected shell commands.
  • Security Test Case:

    1. Setup:

      • Configure a Django project with django-compressor.
      • Modify settings.py to include a malicious precompiler in COMPRESS_PRECOMPILERS:
        COMPRESS_PRECOMPILERS = [
            ('text/x-pwned', 'touch /tmp/django_compressor_pwned_{cachekey}; {infile} {outfile}')
        ]
      • Ensure COMPRESS_ENABLED = True and 'compressor' is in INSTALLED_APPS.
      • Create a Django template that triggers the precompiler:
        {% load compress %}
        {% compress css %}
        <style type="text/x-pwned">body { color: red; }</style>
        {% endcompress %}
      • Create a Django view to render this template and map it to a URL.
    2. Execution:

      • Access the URL in a web browser or using curl that renders the template. This action triggers the compress template tag and the processing of the <style type="text/x-pwned"> block.
    3. Verification:

      • Check for the existence of the file /tmp/django_compressor_pwned_<cachekey> on the server. Replace <cachekey> with an expected cache key value or check for any file starting with /tmp/django_compressor_pwned_.
      • Successful creation of this file confirms that the touch command injected via COMPRESS_PRECOMPILERS was executed, demonstrating command injection.

  • Vulnerability Name: Path Traversal Vulnerability in CSS URL Rewriting

  • Description:

    1. The CssAbsoluteFilter and CssRelativeFilter classes in /code/compressor/filters/css_default.py rewrite URLs within CSS files to be absolute or relative.
    2. The guess_filename method within CssAbsoluteFilter constructs a file path by joining settings.COMPRESS_ROOT with a local_path derived from URLs in the CSS.
    3. This local_path is not properly sanitized against path traversal attacks. An attacker can craft a CSS file with URLs containing directory traversal sequences (e.g., ../).
    4. When CssAbsoluteFilter or CssRelativeFilter processes this CSS, guess_filename is called for each URL. Due to insufficient sanitization, traversal sequences in the URL are preserved in local_path.
    5. os.path.join(self.root, local_path.lstrip("/")) constructs a file path potentially outside of COMPRESS_ROOT.
    6. The vulnerability manifests during file existence checks (os.path.exists) and hash generation (get_hashed_mtime, get_hashed_content) within the filters, potentially leading to information disclosure if error messages reveal file system structure or file existence outside COMPRESS_ROOT.
  • Impact:

    • Path Traversal: Attackers can construct paths that reach outside of the intended COMPRESS_ROOT directory.
    • Information Disclosure (Potential): While direct file reading is not immediately evident, the vulnerability can lead to information disclosure if error messages or logs reveal information about the file system structure or the existence of files outside of COMPRESS_ROOT. In combination with other vulnerabilities or misconfigurations, this could be chained to more severe exploits.
  • Vulnerability Rank: High

  • Currently Implemented Mitigations:

    • None. The guess_filename method in CssAbsoluteFilter and related methods do not sanitize or validate the local_path to prevent directory traversal.
  • Missing Mitigations:

    • Input Sanitization: Implement robust input sanitization for URLs within CssAbsoluteFilter and CssRelativeFilter, specifically within the guess_filename method and potentially in the _converter method.
      • Validate local_path to ensure it does not contain directory traversal sequences like ../.
      • Use secure path manipulation functions that prevent traversal or explicitly resolve and check if the resulting path stays within COMPRESS_ROOT.
  • Preconditions:

    • COMPRESS_ENABLED is True.
    • CssAbsoluteFilter or CssRelativeFilter is enabled in COMPRESS_FILTERS['css'] (default configuration).
    • The application uses {% compress css %} to process CSS content that may contain crafted URLs.
  • Source Code Analysis:

    File: /code/compressor/filters/css_default.py
    
        def guess_filename(self, url):
            local_path = url
            if self.has_scheme:
                # ... (URL cleaning) ...
            # Re-build the local full path by adding root
            filename = os.path.join(self.root, local_path.lstrip("/")) # Vulnerable line
            return os.path.exists(filename) and filename
    • guess_filename processes URLs from CSS content.
    • os.path.join(self.root, local_path.lstrip("/")) joins COMPRESS_ROOT (self.root) with local_path.
    • local_path.lstrip("/") only removes leading slashes, not preventing directory traversal sequences within local_path.
    • Crafted URLs with ../ sequences in CSS will result in filename pointing to locations outside COMPRESS_ROOT.
    • os.path.exists(filename) and hash generation functions will then operate on these potentially out-of-bounds paths.
  • Security Test Case:

    1. Preconditions:

      • Django project with django-compressor enabled.
      • Default CSS filters active.
      • Publicly accessible view rendering a template with {% compress css %}.
      • A sensitive file sensitive.txt outside COMPRESS_ROOT but accessible to the Django application.
    2. Test Steps:

      • Create malicious.css in static files:
        .malicious-class {
            background-image: url('../../../../sensitive.txt'); /* Traversal to project root */
        }
      • Create/modify a Django template:
        {% load static compress %}
            {% compress css %}
                <link rel="stylesheet" href="{% static 'malicious.css' %}" type="text/css">
            {% endcompress %}
      • Access the view rendering the template.
      • Examine the rendered HTML source code and server logs.
    3. Expected Result (Vulnerable):

      • Template renders without critical errors.
      • Server logs may show attempts to access paths outside COMPRESS_ROOT.
      • While direct content disclosure might not be immediately visible, the unsafe path construction and potential for information leakage via logs or error conditions confirm the path traversal vulnerability. If hashing is involved, attempts to hash sensitive.txt would confirm path traversal.

Note: This test demonstrates path traversal in path construction. Direct file content disclosure might require further exploitation or configuration adjustments. The key is to confirm that directory traversal sequences are not handled, leading to path construction outside of COMPRESS_ROOT.