网站遭遇 XSS 攻击

昨天晚上 11 点半,正在为网站更新内容,刚发布好的文章页面突然跳转到了国外的广告网站,这是怎么回事?赶紧排查解决,直到凌晨 1点半才算完全搞定。

这是一次典型权限问题导致的 XSS(跨站脚本)攻击,非常有必要记录下来。我希望我能写的通俗易懂,让不懂技术的产品经理、运营也能理解过程和原理,这样看的人在「知识广度」上就能有所扩充。

先打个比方。你平时总坐 365 路公交车去公司,今天你像往常一样上了 365 路,但是意外的情况发生了,一种情况是 365 路到了你公司那一站,它居然没停,直接把你带到了其它地方,或者,它根本就没走正常的路线;另一种情况是,公交车上的显示屏,不是正常放新闻广告,而是播起了爱情动作片!

XSS 攻击就像上面这个例子一样,被攻击的网站页面中,嵌入了恶意的代码,这些代码会干各种坏事。

现象描述

打开网站首页或任意一篇文章,加载完成后,浏览器状态栏显示请求一个跨站地址(非本域名的地址),先跳转到了一个空白页,此时浏览器地址栏的 URL 也已变化,紧接着又是一次重定向,最终落地页面是一个国外的广告页面。

截断重定向,在网页将要跳转前,快速按下 ESC 键,让网页停止加载,看网页源代码,在 head 区发现有这样一段代码:

这是一段编码后的 JavaScript 代码,不用解码,凭经验能判断它就是罪魁祸首!

打开浏览器开发者工具查看请求过程(macOS 下 Chrome 的开发者工具快捷键是 ⌘ + Option + i),在 Network 标签下刷新网页,看到过程中会请求几个第三方地址,首先是 hellofromhony.org/counter,接着有 thebiggestfavoritemake.com,还有nnatrevaleur.tk。

排查过程:

网站用的是开源的 WordPress,赶紧去看模板文件。发现模板文件最近都没有修改过,修改时间没有接近现在的。打开 header.php 模板头文件查看,也没有发现恶意代码。

这时候有点慌了,如果是别的文件被注入恶意代码,那就不太容易找了,WordPress 的文件那么多。

无论如何,先确定下是不是模板文件的问题。新安装并启用默认的模板,仍然会跳转——不是模板文件的问题。

这时候在 Google 上搜索那段恶意代码,隐约有人提到是插件引起的。我的网站安装了很少的插件,1 个官方的 Akismet Anti-Spam 和 4 个其它的,把其中 2 个我认为有风险的关掉,果然好了!网页不会再跳转了。

分析与解决:

接下来就好办了,也能有时间看问题是怎么产生的了。

根据插件名称和相关关键词搜索,定位到了是 yuzo-related-post 这个插件引起的。这个插件的用途是生成「相关文章」,大约在一周前,作者把这个插件停用了。

在 Stack Overflow (国外的一个程序员问答网站)上,已经对这个问题有了详细的讨论。地址是:https://stackoverflow.com/questions/55610548/vulnerability-in-closed-plugin-yuzo-related-posts

有位程序员已经搞清楚问题的原因,是这个插件保存配置的文件没有严格判断管理员权限,即使没有登录,也可以向/wp-admin/options-general.php?page=yuzo-related-post发送形如yuzo_related_post_css_and_style=</style><script+language=javascript>alert('hacked');</script> 的 POST 请求,这个数据会被成功地保存在数据库里。

上面那段编码后的恶意脚本就是通过这种方式注入到数据库中的。具体是在wp_options表中,名称为yuzo_related_post_options的行记录中,在option_value列中保存了这段恶意代码。

这个插件使用了is_admin()函数来判断是否是管理页面,但在 WordPress 官方文档中,明确提到了is_admin()不能代替安全认证检查。只要当前 URL 被用于 WordPress 管理页面,它就会返回 true。它不会检查用户是否已登录,也无法检查用户是否有权访问所请求的页面。它并不适用于验证安全请求。如果要判断用户权限,应该用current_user_can()函数。

进一步查询得知,Yuzo Related Posts 插件有超过 60,000 个网站安装,安全研究人员在 2019 年 3 月 30 日发现了一个未修补的漏洞,同一天进行了公开披露,4 月10 日开始被黑客正式利用。插件作者在 16 个小时前发布紧急通知让用户移除插件。

我的运气非常好,第一天就赶上了。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注