利用开放重定向漏洞劫持GitHub Gist账户

作者: 黑客网 分类: 正规黑客联系方式 发布时间: 2022-05-27 15:17

利用开放重定向漏洞劫持GitHub Gist账户

近期,我针对GitHub做了一些安全测试,特别对其不同的CSRF token进行了绕过测试,在此过程中,我顺带研究了urls生成的各种方法函数,希望从中发现用来创建token的相关方法,最后发现了其中的一个开放重定向漏洞,利用该漏洞可以成功劫持GitHub Gist账户。漏洞收获了$10,000的奖励。

漏洞发现

在我测试的urls生成方法中,有一个名为url_for的方法,它通常被用来生成一些与控制器(controller)相关的链接。虽然从该方法中我没找到任何可绕过漏洞,但却发现了利用用户可控哈希(controllable hash)进行url_for方法调用的线索。一般来说,url_for方法调用需要把添加进额外参数的用户哈希附加到url后,作为一个查询字符串进行查询,但我通过阅读github说明文档发现,在该方法调用实现过程中,存在一些可控的选项参数:

:only_path - 如果为真true,即返回相应的URL,默认为假false;
:protocol - 即希望连接的协议方式,默认为'http';
:host - 指定连接的特定主机,如果:only_path为false,该选项必须明确提供或显式提示,或通过default_url_options给出信息;
:subdomain - 指定连接的特定子域名,使用tld_length从host主机信息中分离出子域名。如果该项为false,则从连接的主机信息中删除所有子域名信息;
:domain - 指定连接的特定域名,使用tld_length从host主机信息中分离域名信息;
:tld_length - 组成顶级域名TLD id的标签数量,当:subdomain 或 :domain提供时有用,默认为ActionDispatch::Http::URL.tld_length,而该项值又默认为1;
:port - 指定可选的连接端口;
:anchor - 附加在路径后的属性anchor名称;
:params - 附加在路径后的请求参数;
:trailing_slash - 如果为true,则在末尾添加'/',如/archive/2009/;
:script_name - 相对于网站根目录的应用程序路径,如果有该选项,则附上应用程序路径。

由于此前我在其它一些应用中见过:protocol、:host选项,以及blacklisted/removed和 :only_path设置为true的实例,但从没见过:script_name选项的使用。貌似:script_name用在path_for方法中居多,且一般被放在路径path开头,如下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)     path end

GitHub中有多个地方用类似以下的代码来创建相应链接:

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

也就是说,如果构造形如?script_name=javascript:alert(1)// 的请求字符串,将会生成如以html文件代码:

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

可以看出,如果点击'Click me',将会触发一个反射型XSS,虽然可能会被CSP策略阻拦,但也算是一个有意思的漏洞。

另外我还发现了一个用可控参数调用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_path: true,它通常只允许现有主机相关的URL,并且只保留查询参数,但使用script_name的技巧却会引发一些有意思结果。script_name选项不需要以斜线开头,且如果用到了redirect_to的话,script_name中的相关信息将会附加到host之后。最终的请求构造如下:

curl -i 'http://local.dev?source=message&script_name=ggg' HTTP/1.1 302 Found X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff X-Download-Options: noopen X-Permitted-Cross-Domain-Policies: none Referrer-Policy: strict-origin-when-cross-origin Location: Content-Type: text/html; charset=utf-8 Cache-Control: no-cache X-Request-Id: 7c8eedfa-f552-4d5a-bbcd-295f4e7fd9c0 X-Runtime: 0.002744 Transfer-Encoding: chunked <html><body>You are being <a href="http://local.devggg/welcome/index" >redirected</a>.</body></html>

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

标签云