Skip to content

Latest commit

 

History

History
322 lines (278 loc) · 21.1 KB

File metadata and controls

322 lines (278 loc) · 21.1 KB

Here is the combined list of vulnerabilities, formatted as markdown, with duplicate vulnerabilities removed and descriptions kept as provided.

Combined Vulnerability List

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

1. Insufficient HTML Sanitization in Email Templates leading to Cross-Site Scripting (XSS) in Sent Emails

  • Description:

    1. An attacker with access to create or modify Email Templates (e.g., if admin account is compromised or through another vulnerability) can inject malicious HTML code within the html_content of an Email Template.
    2. When an email is sent using this template, the html_content is rendered and sanitized using bleach library with a predefined set of allowed tags, attributes, and styles in post_office/sanitizer.py.
    3. If the allowed tags, attributes, and styles are not restrictive enough, an attacker can craft a malicious HTML payload that bypasses the sanitization and injects JavaScript code.
    4. When a recipient opens the email in their email client that renders HTML, the injected JavaScript code will be executed, leading to Cross-Site Scripting (XSS).
  • Impact:

    • High. Successful XSS can lead to various malicious activities:
      • Data theft: Attacker can steal recipient's cookies, session tokens, and potentially sensitive information displayed in the email client if it's web-based.
      • Account takeover: In some scenarios, XSS can be used to perform actions on behalf of the recipient, potentially leading to account takeover if the email client is integrated with other web services.
      • Malware distribution: Attacker could redirect the recipient to malicious websites or trigger downloads of malware.
      • Reputation damage: Sending emails containing malicious content can damage the sender's reputation and lead to blacklisting.
  • Vulnerability Rank: High

  • Currently implemented mitigations:

    • HTML content of email templates is sanitized using the bleach library in post_office/sanitizer.py before being displayed in the Django admin interface.
    • The clean_html function in post_office/sanitizer.py defines allowed tags, attributes, and styles for sanitization. This sanitization is applied when rendering the email body in the Django admin panel.
  • Missing mitigations:

    • Strict Content Security Policy (CSP): Implementing a strict CSP header for the Django admin interface could help mitigate the impact of XSS if an attacker manages to inject malicious code that gets executed within the admin panel itself. However, this is not directly related to email sending vulnerability but general admin security.
    • More restrictive sanitization rules: The current set of allowed tags, attributes, and styles in post_office/sanitizer.py might be too permissive. A security review of these rules is needed to identify and remove potentially dangerous elements that could be exploited for XSS. For example, allowing <a> tags with target attribute might be risky. Allowing style attribute on many tags could also be problematic if not very carefully filtered.
    • HTML Sanitization during email sending: The code review suggests that HTML sanitization is only performed when rendering the email for preview in the admin panel, not during the actual email sending process. Sanitization should be applied right before sending the email to ensure recipients are protected from potentially malicious HTML.
    • Security Test Cases: There are no specific test cases in the provided test_mail.py that verify the effectiveness of HTML sanitization or ensure that XSS is prevented in emails. Security test cases should be added to cover XSS scenarios in email templates.
  • Preconditions:

    • Attacker needs to have the ability to create or modify Email Templates. This could be achieved by compromising an admin account or exploiting another vulnerability that allows template modification.
    • Recipient's email client must be HTML-enabled and vulnerable to XSS. Most modern email clients render HTML emails by default.
  • Source code analysis:

    • post_office/sanitizer.py:

      try:
          import bleach
      except ImportError:
          # if bleach is not installed, render HTML as escaped text to prevent XSS attacks
          heading = gettext_lazy("Install 'bleach' to render HTML properly.")
          clean_html = lambda body: format_html('<p><em>{heading}</em></p>\n<div>{body}</div>', heading=heading, body=body)
      else:
          # ... (allowed tags, attributes, styles are defined here) ...
          try:
              from bleach.css_sanitizer import CSSSanitizer
              # ... (CSSSanitizer is used if available) ...
              clean_html = lambda body: mark_safe(
                  bleach.clean(
                      body,
                      tags=tags,
                      attributes=attributes,
                      strip=True,
                      strip_comments=True,
                      css_sanitizer=css_sanitizer,
                  )
              )
          except ModuleNotFoundError:
              # if bleach version is prior to 5.0.0
              clean_html = lambda body: mark_safe(
                  bleach.clean(
                      body,
                      tags=tags,
                      attributes=attributes,
                      strip=True,
                      strip_comments=True,
                      styles=styles,
                  )
              )

      This code snippet confirms that bleach is used for sanitization with a defined set of allowed tags, attributes and styles. The security of this sanitization depends on how restrictive these rules are.

    • post_office/admin.py:

      class EmailAdmin(admin.ModelAdmin):
          # ...
          def render_html_body(self, instance):
              pattern = re.compile('cid:([0-9a-f]{32})')
              url = reverse('admin:post_office_email_image', kwargs={'pk': instance.id, 'content_id': 32 * '0'})
              url = url.replace(32 * '0', r'\1')
              for message in instance.email_message().message().walk():
                  if isinstance(message, SafeMIMEText) and message.get_content_type() == 'text/html':
                      payload = message.get_payload(decode=True).decode('utf-8')
                      return clean_html(pattern.sub(url, payload))
          # ...

      The render_html_body function in EmailAdmin uses clean_html to sanitize HTML content before displaying it in the admin panel.

    • post_office/mail.py and post_office/models.py: Further review of post_office/mail.py, post_office/models.py and the provided test_mail.py files does not reveal any explicit sanitization of the html_message content before sending emails. The tests in test_mail.py also do not include any test cases related to HTML sanitization or XSS prevention. This reinforces the conclusion that sanitization might be missing in the email sending pipeline.

      Visualization:

      graph LR
          A[Email Template Creation/Modification (Admin Panel)] --> B{Email Template Database};
          B --> C[Email Sending Process (mail.send)];
          C --> D{Email Object Creation};
          D --> E[Email Dispatch (email.dispatch)];
          E --> F{Email Message Preparation (email_message)};
          F --> G{Django Email Backend (EmailMultiAlternatives)};
          G --> H[Recipient Email Client];
          B --> I[Admin Panel - Email Preview (render_html_body in EmailAdmin)];
          I --> J{HTML Sanitization (clean_html in sanitizer.py)};
          J --> K[Admin Display];
          style J fill:#f9f,stroke:#333,stroke-width:2px
          style K fill:#ccf,stroke:#333,stroke-width:2px
          style G fill:#ccf,stroke:#333,stroke-width:2px
          style H fill:#ccf,stroke:#333,stroke-width:2px
      
      Loading

      The visualization confirms that clean_html is used for Admin display (I->J->K), but it's not evident in the email sending path (C->D->E->F->G->H).

  • Security test case:

    1. Log in to the Django admin panel as a superuser.
    2. Navigate to "Post Office" -> "Email Templates".
    3. Create a new Email Template with:
      • Name: xss_test_template
      • Subject: XSS Test
      • Content: This is a plain text email.
      • HTML content: <img src=x onerror=alert('XSS Vulnerability!')>
    4. Save the Email Template.
    5. Go to "Post Office" -> "Emails".
    6. Click "Add Email".
    7. Fill in the form:
    8. Save the Email.
    9. Run the management command python manage.py send_queued_mail.
    10. Check the inbox of [email protected].
    11. Open the received email in an HTML-enabled email client.
    12. Observe if an alert box with "XSS Vulnerability!" is displayed when the email is opened. If the alert box appears, the XSS vulnerability is confirmed.

2. Arbitrary File Read via Attachment Parameter

  • Description: When an email is “sent” by the library, the developer may pass an attachments parameter to the mail.send() function. In the helper function create_attachments() (located in post_office/utils.py), any attachment value that is of type string is assumed to be a file name or file path and is immediately used to open a file (using Python’s built‑in open() call) without any sanitization or validation. This means that if an attacker can control the value passed as an attachment (or if a public endpoint later exposes this functionality), they can supply an arbitrary absolute or relative file path (for example, /etc/passwd on Unix systems). When the file is opened and read, its contents become attached to the outgoing email. If the attacker can also control the recipient address (or if a misconfiguration overrides recipients), they can cause the sensitive file’s content to be delivered to an attacker‑controlled mailbox.

    Step‑by-step trigger process:

    1. An external attacker sends a crafted request to a publicly exposed endpoint that (directly or indirectly) calls mail.send().
    2. In the request, the attacker supplies—for example—in the JSON body or form parameter an attachments dictionary where one key is a filename (say "sensitive.txt") and its value is a string containing an arbitrary file path (e.g. /etc/passwd).
    3. Inside the create_attachments() function, the code detects that the attachment value is a str and calls open(content, 'rb') on it.
    4. The file is read and attached (via Django’s File API) for inclusion in the email message.
    5. The email is dispatched (either immediately or through the queued process) and delivered to the attacker‑controlled recipient.
  • Impact: An attacker may read any local file that the application process has permission to read. This could result in:

    • Disclosure of sensitive operating system files (e.g. /etc/passwd or configuration files containing secrets).
    • Exposure of internal credentials or private data stored on the disk.
    • Full compromise of sensitive system information leading to further attacks.
  • Vulnerability Rank: High

  • Currently Implemented Mitigations:

    • The code expects attachments to normally be passed as file‑like objects (or in other well‑structured formats) and does not perform explicit checks when a string is provided.
    • There is no built‑in sanitization or restriction on file paths.
  • Missing Mitigations:

    • Validate and sanitize any file input provided as a string.
    • Enforce a whitelist or restrict file paths to a safe directory so that arbitrary files on the system cannot be read.
    • Require that attachments be passed as already‑opened file‑like objects (or wrap file input in an abstraction that validates its source) rather than allowing raw file paths.
  • Preconditions:

    • The application exposes an endpoint or mechanism that passes external (potentially untrusted) input to mail.send() without additional sanitization.
    • The attacker can control both the attachments parameter (to insert a file path) and the recipient address (so that the email is delivered to them).
    • The server process has read access to sensitive files (for example, system files such as /etc/passwd).
  • Source Code Analysis: In the file post_office/utils.py, observe the following excerpt in the create_attachments() function:

      def create_attachments(attachment_files):
          attachments = []
          for filename, filedata in attachment_files.items():
              # If the value is a dict, extract file-like object and mimetype.
              # Otherwise, assume that filedata is either already a file-like object or a filename.
              if isinstance(filedata, dict):
                  content = filedata.get('file', None)
                  mimetype = filedata.get('mimetype', None)
                  headers = filedata.get('headers', None)
              else:
                  content = filedata
                  mimetype = None
                  headers = None
    
              opened_file = None
    
              if isinstance(content, str):
                  # `content` is a filename - try to open the file
                  opened_file = open(content, 'rb')
                  content = File(opened_file)
              ...
              attachment.file.save(filename, content=content, save=True)
              attachments.append(attachment)
              if opened_file is not None:
                  opened_file.close()
          return attachments

    Notice that if a string is provided (i.e. if isinstance(content, str) is true), the code immediately uses open(content, 'rb') without any check to ensure that the path is safe or part of an allowed directory. This unsanitized use of file paths leads directly to the described arbitrary file read.

  • Security Test Case:

    1. Setup: Deploy a test instance of the application with an endpoint that ultimately calls mail.send() (or send_many()) without further sanitization. Ensure that the system has a file (e.g., /etc/passwd on a Unix system) that the process can read.
    2. Request Crafting:
      • Prepare an HTTP request (for example, a POST request) that supplies the following JSON or form parameters:
        • "recipients": ["[email protected]"] (or any attacker‑controlled email address)
        • "sender": "[email protected]"
        • Other required parameters such as "subject": "Test"
        • "attachments": { "sensitive.txt": "/etc/passwd" }
    3. Execution: Send the request to the application’s endpoint.
    4. Observation:
      • Monitor the outgoing email (using a test mailbox or intercepting SMTP traffic) delivered to the attacker's email address.
      • Verify that an attachment named “sensitive.txt” is included and that its content matches that of the system file (e.g., /etc/passwd).
    5. Expected Result: The attached file in the delivered email contains the contents from /etc/passwd, confirming that arbitrary file read is possible.
    6. Cleanup: Ensure that such testing is performed in an isolated environment only.

3. Stored Cross-Site Scripting (XSS) in Email HTML Body Preview in Admin Panel

  • Vulnerability Name: Stored Cross-Site Scripting (XSS) in Email HTML Body Preview

  • Description:

    1. An attacker with access to the Django admin panel (with appropriate permissions to view emails) can inject malicious JavaScript code into the HTML content of an Email Template.
    2. This malicious template can then be used to send emails, or simply previewed within the Django admin panel.
    3. When an administrator views the email in the admin panel, the injected JavaScript code will be executed in their browser.
    4. This is due to insufficient sanitization of the rendered HTML email body when displayed in the admin panel preview.
  • Impact:

    • Account Takeover: An attacker can potentially gain control of an administrator's account by injecting JavaScript that steals their session cookies or credentials.
    • Data Breach: The attacker could potentially access sensitive data accessible to the administrator within the admin panel.
    • Privilege Escalation: If the compromised administrator account has higher privileges, the attacker can further escalate their access within the application.
  • Vulnerability Rank: High

  • Currently Implemented Mitigations:

    • HTML sanitization is implemented in post_office/sanitizer.py using the bleach library.
    • The EmailAdmin.render_html_body function uses clean_html from post_office/sanitizer.py to sanitize the HTML body before displaying it in the admin panel.
  • Missing Mitigations:

    • The current sanitization in EmailAdmin.render_html_body is insufficient to prevent XSS. While bleach is used, the configuration might be too permissive or there might be bypasses in the bleach version used. It's also possible that the sanitization is applied at the wrong stage or with incorrect context.
  • Preconditions:

    • Attacker needs to have access to the Django admin panel and permissions to:
      • Create or modify Email Templates.
      • View Email objects.
  • Source Code Analysis:

    1. File: post_office/admin.py - EmailAdmin.render_html_body function:
    def render_html_body(self, instance):
        pattern = re.compile('cid:([0-9a-f]{32})')
        url = reverse('admin:post_office_email_image', kwargs={'pk': instance.id, 'content_id': 32 * '0'})
        url = url.replace(32 * '0', r'\1')
        for message in instance.email_message().message().walk():
            if isinstance(message, SafeMIMEText) and message.get_content_type() == 'text/html':
                payload = message.get_payload(decode=True).decode('utf-8')
                return clean_html(pattern.sub(url, payload))
      - This function is responsible for rendering the HTML body of an email in the Django admin panel.
      - It retrieves the HTML payload from the `EmailMessage` object.
      - It uses `clean_html` function from `post_office/sanitizer.py` to sanitize the HTML.
      - It replaces `cid:` URLs with admin URLs to serve inline images.
    
    1. File: post_office/sanitizer.py - clean_html function:
    try:
        from bleach.css_sanitizer import CSSSanitizer
    
        css_sanitizer = CSSSanitizer(
            allowed_css_properties=styles,
        )
        clean_html = lambda body: mark_safe(
            bleach.clean(
                body,
                tags=tags,
                attributes=attributes,
                strip=True,
                strip_comments=True,
                css_sanitizer=css_sanitizer,
            )
        )
    except ModuleNotFoundError:
        # if bleach version is prior to 5.0.0
        clean_html = lambda body: mark_safe(
            bleach.clean(
                body,
                tags=tags,
                attributes=attributes,
                strip=True,
                strip_comments=True,
                styles=styles,
            )
        )
      - This function uses the `bleach.clean` function to sanitize HTML content.
      - It defines allowed tags, attributes, and styles.
      - It marks the sanitized HTML as safe using `mark_safe`.
    

    Vulnerability Analysis:

    • While bleach is used for sanitization, the allowed tags and attributes in post_office/sanitizer.py might be too permissive. Attackers might be able to craft HTML payloads that bypass the sanitization rules and inject malicious JavaScript.
    • The vulnerability lies in the potential for bypasses in the bleach sanitization configuration, allowing malicious HTML content to be rendered and executed in the administrator's browser when previewing an email in the admin panel.
  • Security Test Case:

    1. Log in to the Django admin panel as an administrator.
    2. Navigate to "Email templates" and create a new Email Template.
    3. In the "HTML content" field, paste the following malicious payload:
    <img src="x" onerror="alert('XSS Vulnerability')" />
    1. Save the Email Template.
    2. Navigate to "Emails" and create a new Email.
    3. Select the Email Template created in step 2.
    4. Click "Save and continue editing" to view the Email change form.
    5. Observe the "HTML Body" section. If a JavaScript alert box appears with the message "XSS Vulnerability", the vulnerability is present.

    Expected Result:

    • A JavaScript alert box should appear when viewing the email in the admin panel, demonstrating successful XSS.

    Note: This test case assumes the attacker has admin access. In a real-world scenario, an attacker might try to exploit other vulnerabilities (like account takeover or privilege escalation) to gain admin access and then exploit this XSS vulnerability.