# DjangoBlog Vulnerability Report Software: DjangoBlog Developer or vendor: liangliangyy Code repository: https://github.com/liangliangyy/DjangoBlog ## 1. Comment Sanitizer Attribute Whitelist Bypass Leading to Stored XSS Brief description: The bleach attribute whitelist for comment bodies puts no restriction on `span`, `div`, `pre` and `code`, so an attacker can place an element carrying `autofocus` and `onfocus` in a comment and the script runs when a visitor opens the article page, with no interaction. Affected versions: 2.1.0.0 to commit f1d13badaa Preconditions: Comments are enabled on the target article, and the attacker holds an account (self-registration when the site has mail configured, a third-party account when OAuth is configured; if neither is available, an account can be registered by way of CVE-2026-6578). ### Root Cause The `comment_markdown` filter renders Markdown, sanitizes the result with `sanitize_html`, and returns it through `mark_safe`: ```python # blog/templatetags/blog_tags.py:156-158 commit f1d13badaa def comment_markdown(content): content = CommonMarkdown.get_markdown(content) return mark_safe(sanitize_html(content)) ``` `sanitize_html` filters attributes against the bleach `attributes` whitelist, and `ALLOWED_ATTRIBUTES` hands the attribute check for `span`, `div`, `pre` and `code` to `class_filter`: ```python # djangoblog/utils.py:248-268 commit f1d13badaa def class_filter(tag, name, value): """自定义class属性过滤器""" if name == 'class': allowed_classes = [cls for cls in value.split() if cls in ALLOWED_CLASSES] return ' '.join(allowed_classes) if allowed_classes else False return value ALLOWED_ATTRIBUTES = { 'a': ['href', 'title', 'rel'], 'img': ['src', 'alt', 'title', 'width', 'height'], 'table': ['border', 'cellpadding', 'cellspacing'], 'th': ['align', 'valign'], 'td': ['align', 'valign'], 'span': class_filter, 'div': class_filter, 'pre': class_filter, 'code': class_filter } ``` `class_filter` validates values only for `class` and returns every other attribute unchanged, and bleach keeps that return value as the attribute value. Event attributes such as `onfocus` and `onmouseover` therefore survive on the four tags above. The comment template prints the sanitized result directly: ```django {# templates/comments/tags/comment_item_modern.html:68 commit f1d13badaa #} {{ comment_item.body|comment_markdown }} ``` The payload fires on page load through `autofocus` with `onfocus`. bleach discards empty attribute values, so `autofocus` needs a non-empty value and is written as `autofocus="x"`. The browser focuses the element while loading the page, and `onfocus` runs. ### Reproduction Environment Deploy from source as described in the official documentation: ```bash git clone https://github.com/liangliangyy/DjangoBlog.git cd DjangoBlog git checkout f1d13badaa cd deploy/docker-compose docker compose up -d --build docker compose exec djangoblog python manage.py createsuperuser ``` The reproduction machine used here listens on `192.168.48.130:8080`. Log in at `http://192.168.48.130:8080/admin/` and create an article. The attacker logs in through GitHub. Create an OAuth App on GitHub with Homepage URL `http://192.168.48.130:8080` and Authorization callback URL `http://192.168.48.130:8080/oauth/authorize?type=github`. Then add a GitHub entry under OAuth in the admin, with AppKey and AppSecret set to the Client ID and Client Secret of that app. ### Reproduction Visit `http://192.168.48.130:8080/oauth/oauthlogin?type=github` and authorize on GitHub. The browser returns to the site in a logged-in state. Open the article, paste the following into the comment box and submit: ```html