查看原文
其他

通过开放重定向接管 GitHub Gist 账户,获奖$1万(GitHub $6.1万奖金系列之三)

vakzz 代码卫士 2022-04-06

 聚焦源代码安全,网罗国内外最新资讯!

编译:奇安信代码卫士团队


在上篇文章中,我们探讨了针对每种形式的 CSRF 令牌的绕过,当时我在研究可用于生成 url 的每种方法,试图找到可用于创建所需令牌的方法。


发现


其中一种方法是 url_for,它通常用于生成指向其它控制器的链接。尽管无法找到可绕过的地方,但我发现在有些地方可以通过一个用户一个可控哈希的方法调用 url_for。调用完成后,哈希中的任意额外参数都会被作为查询字符串附加到该 url,但查看实现和文档后发现,有很多选项是可控的:

  • :only_patch —— 如为 ture,则返回相对 URL,默认为false

  • :protocol —— 要连接的协议,默认为 “http”。

  • :host —— 指定主机应当针对的连接。如果 :only_pathfalse,则该选项必须明确提供或者通过 default_url_options 提供。

  • :subdomain —— 指定该链接的子域名,使用 tld_length 从主机分离子域名。如为 false,则从该链接的host 部分删除所有的子域名。

  • :domain —— 指定该链接的域名,使用 tld_length 从主机分离域名。

  • :tld_length —— 组成 TLD id 的标签数目,只有在提供 :subdomain:domain 时才使用,默认为ActionDispacth::Http::URL.tld_length,值默认为1。

  • :port —— 可选,指定连接的端口。

  • :anchor —— 附加到路径的 anchor 名称。

  • :params —— 附加到路径的查询参数。

  • :trailing_slash —— 如为 true,则添加一个反斜杠,如 “/achive/2009/“ 中所示。

  • :script_name —— 指定相对于域 root 的应用程序路径。如已提供,预置应用程序路径。

此前,我曾在其它应用中看到更为常见的选项如 :protocol、被黑名单或删除的 :host、或被设置为 true :only_path,以阻止它们被使用,但从未见到过 :script_name 参数。它最后被 path_for 方法所使用,而且如果它出现,则总会用于路径开头:

def path_for(options) path = options[:script_name].to_s.chomp("/") path << options[:path] if options.key?(:path)
add_trailing_slash(path) if options[:trailing_slash] add_params(path, options[:params]) if options.key?(:params) add_anchor(path, options[:anchor]) if options.key?(:anchor)
pathend

GitHub 中有一些地方通过类似于下面代码的代码创建链接:

<a class="link" href="<%= url_for(request.query_parameters.merge(only_path: true)) %>"> Click me</a>


这就意味着,如果使用了查询字符串如 ?script_name=javascript:alert(1)// ,那么它最终会生成如下 html:

<a class="link" href="javascript:alert(1)//user/repo/..."> Click me</a>


这样,我们就得到了一个严重性很低的反射型 XSS漏洞,要求一次点击才能被利用。虽然它遭 CSP 拦截,但仍然是一个很有意思的 bug。

之后我碰到另外一个使用具有可控参数的 url_for 的地方,这次是重定向的一部分。该代码位于应用程序控制器中,执行如下代码(方法/参数名称已更改):

before_action :check_source
def check_source source = params["source"] return redirect_to(check_source_redirect_url) if source == "message" end
def check_source_redirect_url query = Addressable::URI.parse(request.env["REQUEST_URI"]).query_values || {} filtered_params = query.except("source", "token").merge(only_path: true) url_for(filtered_params) end


以上使用了 only_patch:true。在正常情况下,它仅允许现有 host 的 url 且保留查询参数,但用于 script_name 会产生一些有意思的结果。Script_name 并不要求以斜杠开头,而当与 redirect_to 使用时,它被直接附加到 host 上:

curl -i 'http://local.dev?source=message&script_name=ggg'HTTP/1.1 302 FoundX-Frame-Options: SAMEORIGINX-XSS-Protection: 1; mode=blockX-Content-Type-Options: nosniffX-Download-Options: noopenX-Permitted-Cross-Domain-Policies: noneReferrer-Policy: strict-origin-when-cross-originLocation: http://local.devggg/welcome/indexContent-Type: text/html; charset=utf-8Cache-Control: no-cacheX-Request-Id: 7c8eedfa-f552-4d5a-bbcd-295f4e7fd9c0X-Runtime: 0.002744Transfer-Encoding: chunked
<html><body>You are being <a href="http://local.devggg/welcome/index">redirected</a>.</body></html>


由于该域名的末尾是可控的,如果 .attacker.domain 被用作 script_name,则会被重定向到自己的域。由于我还在查找 CSRF 绕过,因此将该 bug 只以开放重定向的名义提交并继续进行研究。


Exploit


第二天,当我和 corb3nik 谈到开放重定向带来的影响时,他提到 OAuth 令牌通常是不错的目标。再次查看该 bug 后,我发现它实际上非常强大。由于在应用控制器中很早就会点击到它,因此它将影响很多任意路由。

GitHub 内置一些 OAuth 应用,其中一个是为 Gist 准备的。GitHub Gist 像 GitHub 一样也是 Rails app,只是主机名不同,被暴露了不同的路由。登录到 Gist 后,你会发现正常的 OAuth 流,它是整个重定向堆,如:

https://github.com/login/oauth/authorize?client_id=7e0a3cd836d3e544dbd9&redirect_uri=https://gist.github.com/auth/github/callbackhttps://gist.github.com/auth/github/callback?browser_session_id=XXX&code=YYYhttps://gist.github.com/auth/githubhttps://github.com/login/oauth/authorize?client_id=7e0a3cd836d3e544dbd9&redirect_uri=https%3A%2F%2Fgist.github.com%2Fauth%2Fgithub%2Fcallback&response_type=code&state=ZZZhttps://gist.github.com/auth/github/callback?browser_session_id=XXX&code=YYY&state=ZZZhttps://gist.github.com/


为成功登录到 Gist,攻击者只需 browser_session_id code 即可,因为 client_id 是公开的,而攻击者可以生成参数 state,因为它已存在,目的是阻止 CSRF。

最初的重定向指向具有 code 和 browser_seesion_idredirect_url,因此我尝试添加 script_name=.wbowling.info,竟然奏效!我被重定向到附加所需参数的自己的域名。

在一个新的隐私页面中,我访问 https://gist.github.com/auth/github/callback抓取一个有效的 state 参数,然后这次再次使用合法的 browser_session_idcode state 参数进行登录且成功。

由于 GitHub 和 Gist 使用不同的会话令牌,因此并不允许访问 github.com 但被授予 Gist 的完整访问权限。


时间线


  • 2020年7月26日(美国东部时间,下同)00:33:38 – 报告开放重定向漏洞

  • 2020年7月26日12:57:38 —— 更新 Gist 账户接管问题

  • 2020年7月26日23:33:30 —— 报告分类

  • 2020年7月29日(不记得确切时间了)—— 由于该 bug 仅影响 github.com 而不影响 GHE,因此发布热修复方案。

  • 2020年10月15日05:45:45 —— 获得1万美元的奖金





推荐阅读
我从GitHub 企业版找到严重的 RCE 漏洞,意外得$2万奖金 (GitHub $6.1万系列之一)
Kramdown 配置不当引发 GitHub Pages 多个 RCE,得 $2.5万($6.1万系列之二)




原文链接

https://devcraft.io/2020/10/19/github-gist-account-takeover.html


题图:Pixabay License


本文由奇安信代码卫士编译,不代表奇安信观点。转载请注明“转自奇安信代码卫士 https://codesafe.qianxin.com”。


奇安信代码卫士 (codesafe)

国内首个专注于软件开发安全的

产品线。

    觉得不错,就点个 “在看” 或 "” 吧~


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存