# DjangoBlog 漏洞报告 软件名称:DjangoBlog 开发者或厂商:liangliangyy 代码仓库:https://github.com/liangliangyy/DjangoBlog ## 1. 评论净化器属性白名单可绕过导致存储型 XSS 简要描述:评论正文的 bleach 属性白名单对 `span`、`div`、`pre`、`code` 四个标签不做限制,攻击者可以在评论里写入带 `autofocus` 与 `onfocus` 的元素,访客打开文章页时,无需交互即执行脚本。 影响版本:2.1.0.0 ~ commit f1d13badaa 前置条件:目标文章开启评论;攻击者有账号(若站点配置了邮件服务,则可以自助注册;若配置了 OAuth,则可以用第三方账号登录;若均未配置,可以尝试用 CVE-2026-6578 注册账号)。 ### 漏洞成因 `comment_markdown` 过滤器先渲染 Markdown,再用 `sanitize_html` 清洗,最后 `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` 用 bleach 的 `attributes` 白名单过滤属性,而 `ALLOWED_ATTRIBUTES` 把 `span`、`div`、`pre`、`code` 的属性检查交给 `class_filter`。`class_filter` 只对 `class` 做取值检查,其余属性直接返回原值: ```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 } ``` bleach 把回调的返回值当作属性值保留,于是 `span`、`div`、`pre`、`code` 上的 `onfocus`、`onmouseover` 等事件属性都会通过。评论模板直接输出清洗结果: ```django {# templates/comments/tags/comment_item_modern.html:68 commit f1d13badaa #} {{ comment_item.body|comment_markdown }} ``` 要让页面加载即触发,事件属性使用 `autofocus` 与 `onfocus`。bleach 会把空属性值视为假值丢弃,因此 `autofocus` 需要带一个非空值,写成 `autofocus="x"`。浏览器加载页面时会自动聚焦该元素,`onfocus` 随即执行。 ### 复现环境搭建 按官方文档从源码构建部署: ```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 ``` 本文所用复现机器的监听端口是 `192.168.48.130:8080`。登录 `http://192.168.48.130:8080/admin/`,新建一篇文章。 本文采用 GitHub 登录以演示攻击。在 GitHub 新建一个 OAuth App,Homepage URL 填 `http://192.168.48.130:8080`,Authorization callback URL 填 `http://192.168.48.130:8080/oauth/authorize?type=github`。然后在后台的 OAuth 配置里新增一条 GitHub 记录,AppKey 与 AppSecret 填入该应用的 Client ID 与 Client Secret。 ### 复现过程 浏览器访问 `http://192.168.48.130:8080/oauth/oauthlogin?type=github`,在 GitHub 上授权后回到站点,即为登录状态。 打开文章,在评论框粘贴下面的内容并提交: ```html