Handling URLs is easy to mess up. Sometimes a slight inaccuracy in the URL validation of an application can lead to minor issues, or vulnerabilities if it's the browser which messes it up. This time, two bugs related to Internet Explorer problematic URL redirection will be presented, with the second half of the post covering an interesting RPO exploitation technique.

GitHub OAuth Code Theft

URL encoding, or Percent-encoding, is usually used in query string or request body. Hostname accepts URL encoding too. However, when Internet Explorer is redirecting to a URL encoded hostname, something strange happens. For example, if you visit the link below using Internet Explorer 11 on Windows 7 / 8.1 versus other browsers, you will notice the destination is completely different:

https://httpbin.org/redirect-to?url=http://%77%77%77%2E%6D%69%63%72%6F%73%6F%66%74%2E%63%6F%6D/test

HTTP/1.1 302 Found location: http://%77%77%77%2E%6D%69%63%72%6F%73%6F%66%74%2E%63%6F%6D/test

Redirected URL for Internet Explorer: http://www.microsoft.com9crosoft.com/test

Redirected URL for other browsers: http://www.microsoft.com/test

This weird behavior was discovered by Sergey Bobrov, which was also covered in a similiar bug Michał Bentkowski found. Basically, Internet Explorer somehow overlays parts of the URL decoded value with URL encoded value, and mixes them together. The final destination then points to something other then the original destination. The bug seems to be fixed in Internet Explorer 11 on Windows 10, but remains unfixed for Internet Explorer 11 on Windows 7 / 8.1.

Anyways, the important bit is that if a redirect accepts URL encoded hostname, there is a chance users will be redirected to an unexpected website.

Interestingly, In GitHub's OAuth authorization page, the redirect_uri parameter accepts URLs with URL encoded hostname. For example, GitHub Gist is supposed to accept only https://gist.github.com/auth/github/callback/ as the callback URL, but https://%2567%2569%2573%2574.github.com/auth/github/callback/ is also accepted. However, if we directly use the above URL as redirect_uri , the value in the Location header will be no difference from the intended one (remains decoded). The reason why is that GitHub normalizes redirect_uri before doing further handling. The true magic happened when it's triple encoded (i.e. https://%252567%252569%252573%252574.github.com/auth/github/callback/ ). By doing that, the validation recursively normalized the URL, and thought it matched the configured callback URL, while single encoded it in the Location header.

PoC:

https://github.com/login/oauth/authorize?client_id=7e0a3cd836d3e544dbd9&redirect_uri=https%3A%2F%2F%2567%2569%2573%2574.github.com%2Fauth%2Fgithub%2Fcallback&response_type=code

HTTP/1.1 302 Found location: https://%67%69%73%74.github.com/auth/github/callback/?code={{$CODE}}

Redirected URL for Internet Explorer: https://gist.github.comthub.com/auth/github/callback?code={{$CODE}}

Redirected URL for other browsers: http://gist.github.com/auth/github/callback?code={{$CODE}}

As you can see, the final destination on IE points to the domain gist.github.comthub.com , which is unregistered. Since Gist is a pre-approved application for all GitHub accounts, any user visiting the PoC link would leak the code to the attacker who owns that domain. Of course, it affected other applications as well.

GitHub fixed the bug by also recursively normalizing the value in the Location header.

References

RPO in Google Fusion Table

June 5, 2018

During URL normalization, patterns indicating directory traversal will be taken care of. These include dot-slash ( ./ ) and dot-dot-slash ( ../ ). For example, foo/./bar , foo/baz/../bar and foo/baz/%2e%2e/bar (encoded dots) will be normalized to foo/bar before a request is even sent off the browser. You can hover over the following two links and notice they point to the same URI in browser's status bar. https://exapmle.com/foo/%2e%2e/bar and https://exapmle.com/bar. Note that however it is not true for foo/..%2fbar (encoded slash) since ..%2fbar can be a filename. https://exapmle.com/foo/..%2Fbar.

Internet Explorer unsurprisingly does weird things again (to be fair though, Edge was also affected). When redirecting to a URL where the path contains URL encoded dot-dot-slash, the request sent to the server will be slightly different than what the address bar shows.

Supposedly and as explained, when browsers see http://example.com/foo/bar.jsp;/%2e%2e/%2e%2e/1337 , a request to http://example.com/1337 will be sent to the server, and the address bar will also show http://example.com/1337 . In Internet Explorer however, the address bar will still show http://example.com/1337 , but a request to http://example.com/foo/bar.jsp;/%2e%2e/%2e%2e/1337 will be sent as is. This discrepancy allows showing a page with arbitrary path, if the server ignores trailing path (e.g. path paramater).

Here comes the fun part. An external script file can be imported in either absolute URL or relative URL. When relative URL is used, the directory of the file will be relative to the path of the loading page. A lot of websites use relative path and there's nothing wrong with it. In fact, Google Fusion Table is one of them.

The only wrong here is Google Fusion Table accepted path parameter. Navigating to https://www.google.com/fusiontables/DataSource and https://www.google.com/fusiontables/DataSource;/foo/bar/%2e%2e/baz showed the exact same content. This combined with the IE bug gave us ability to load Google Fusion Table with our desired path, and hence the path of imported scripts. If there is a way to upload a JavaScript file on the Google domain or an Open Redirect is present, then our evil script can be imported. Luckily, Google AMP is an Open Redirector by design. Nativating to https://www.google.com/amp/example.com/path on non-mobile devices will redirect to https://example.com/path. Now, we can load Google Fusion Table in https://www.google.com/amp/innerht.ml, then https://www.google.com/amp/innerht.ml/js/gvizchart_all_js.js will be imported, with final destiniation being https://innerht.ml/js/gvizchart_all_js.js.

Voila! XSS via RPO it is.

Hi, Thanks again for your report. Very nice bug! We were able to convert this to full working XSS exploit in IE11. The panel has decided to issue reward a of $6000 total ($5000 for XSS vulnerability in www.google.com + $1000 bonus for cool bug and novel approach) and you should receive the normal reward information email soon.

Google has fixed this class of bug by moving many products to dedicated subdomains and removing support for path parameter.

References