More from Insomnia

Check back to read more Insomnia blogs in the coming months.

July 15, 2020

Toby Reynolds Senior Security Consultant

Advanced Open Redirection Vulnerability Discovery

Toby discloses advanced methods for detecting open redirection vulnerabilities.

From an attacker's perspective open redirect vulnerabilities have seen a variety of uses, including within phishing messages, as a means of gaining authentication credentials.

In recent years unvalidated forwards and redirects have been essentially removed from the OWASP Top 10. However, with the rise of APIs and JSON Web Tokens (JWT), it is commonly found that third-parties are utilised as part of the authentication process.

From 2017, a steady rise in the use of JWTs for session management has presented its own problems and issues, including the passing of session variables using URL parameters or location.hash values, in favour of session cookies.

Parsing URLs is harder than you think

Using a third-party solution for your authentication removes a lot of the risks associated with creating and implementing your own solution. No matter how good you are, there is always something that could go wrong. One potential issue with using a third-party is that they usually need to redirect the user back to the service they are authenticating for.

First, lets delve in to all the possible parts of a URL.

The components of a URI, from http://medialize.github.io/URI.js/about-uris.html, used with permission.

More details on URL components can be found in RFC7320.

As attackers, we want to be able to redirect a user to a site under our control while the user still believes they are talking to the service they expected.

If we can modify the domain - great! If we cannot do this, we must find a way to redirect them to us. This is where we need some creative thinking to leverage features of the RFC, and the browser, to our advantage.

When the original URI RFC was written passwords were likely dictionary words or phrases. Today the guidance for what should be accepted as a password is much wider. If we can include any number of resource control attributes within the authority portion of the URL, we may be able to control the resulting destination URL.

First case study

Many third-party authentication providers allow their customers to specify a callback URL, something like https://authed-service.example.com/callback with a token of some kind sent to confirm a successful authentication.

In this scenario, an absolute URL was configured with the third-party service as follows:

https://example.com/callback

As far as a security aware client administrator is concerned, simple is better, and this should not allow for anything else.

After some testing, we realised we could actually send a modified return URL within our application's login page with the third-party. The request that stood out to us contained the following:

https://*example.com/callback 

What happened next was unexpected. The modified callback URL became a redirection request to the following URL:

https:///*example.com/callback
        ^

It looks like the auth provider had potentially tried to handle our modified input, but failed to escape it correctly. When attempting to escape a * they had mistakenly put a / before the character instead. After some tinkering, we could use a callback URL such as https://localhost*example.com/callback and it would be reflected back as https://localhost/*example.com/callback.

At this point, we could easily redirect the user to an unqualified hostname, but if the hostname or IP address contained a . then the open redirection attempt would fail.

To get around this we had to take a trip down memory lane, back to our earlier school years. We remembered some of the tricks we employed back then for bypassing the old “school internet filter” and converted one of our external IP addresses to its decimal representation.

Our final attack string resembled the following:

callback=https://3405803781*example.com/callback

The resulting HTTP redirection response resembled the following:

HTTP/1.1 302 Moved Temporarily
Location: https://3405803781/*example.com/callback

The browser saw this resulting redirect as:

https://203.0.113.5/%2aexample.com/callback

Second case study

DOM controlled redirects are currently one of my favourite avenues for attack - especially relative redirects. The reason for this favouritism is because you are essentially playing with two engines at the same time, JavaScript itself, but also the web browser.

White space means different things to different languages. A function within a given language to determine whether a redirection attempt is relative or not could resemble the following code:

1   // Allows "/" or "/foo" but not "//" or "/\".
2   if (url[0] == '/'){
3       // url is exactly "/"
4       if (url.Length == 1){
5           return true;}
6       // url doesn't start with "//" or "/\"
7       if (url[1] != '/' && url[1] != '\\'){
8           return true;}
9       return false;}

For checking server-side code, the above appears to do the job. However, a potential issue can exist on line 7, if this code would be implemented in a client-side scripting language like JavaScript.

What would happen if you inserted an encoded newline character after the initial /? When line 7 is executed, it would see a newline, which is neither a / or a \ and thus would return true.

However, there are a couple of caveats here we did not explain. A URL-encoded newline character would not be interpreted as a newline within the JavaScript code. Luckily for us, due to the tight XSS filter that was in place, special characters injected within the application were actually converted to HTML entities. In JavaScript, HTML entities are evaluated prior to the JavaScript code being run. So, this allowed us to send a URL encoded newline, which would be reflected within the JavaScript as an HTML entity-encoded newline. Our final attack string would resemble the following:

url=/%0a/www.example.com

This was reflected in the affected JavaScript code as:

url: “/
/www.example.com”;

Furthermore, as the JavaScript ignores white space, it concatenates the string, essentially removing our injected newline. This allowed for an open redirect using an encoded newline.

Third case study

Different services and applications support different character encodings. There are situations where specific encodings are not supported, in these cases, we have seen a variety of outcomes. Either the character is excluded completely from the output, or is merely replaced with another supported character.

Internal redirects within applications have been appearing often recently, each time with an absolute URL, similar to the earlier example. However, in this case, rather than allowing anything before the domain, we had to take advantage of the URI RFC in order to achieve an open redirect. Rather than just the login page, this affected the entire application.

The redirects within the application were handled by a relay.html page which accepted a single URL parameter. In this case, the URL parameter was always set to a full, absolute URL:

https://www.example.com/destination_page 

What we were able to do here was “pretend” to include some HTTP Basic authentication credentials within the request:

https://www.insomniasec.com:443@www.example.com/destination_page  

This would send a request to https://www.example.com/destination_page with an HTTP username of www.insomniasec.com and password of 443.

However, by leveraging the RFC, as part of the password section, we were able to include a non-printable character. In this case, we used the character 0xFF. Because of the encoding supported by the application, this URL encoded hex character was converted to a ?.

The resulting URL redirect resembled the following:

Location: https://www.insomniasec.com:443?@example.com/destination_page

If you remember from the previous diagram, a ? is considered part of the URL query. We have essentially been able to include this within the password field for an HTTP Basic auth request. The browser, however, does not see the same, but considers it as the start of the URL query. The browser will treat the redirection URL as follows:

https://www.insomniasec.com:443/?@example.com/destination_page

By injecting an unsupported character into the application, the output of the character normalisation process allowed us to include parts of the URL query before the domain of the URL, resulting in another instance of arbitrary open redirection to occur.

Conclusion

Despite what others may believe, open redirect and unvalidated forward vulnerabilities do still exist in modern day web applications. Not only does this present an increased phishing risk, but also the potential compromise of the session via disclosures from unprotected headers, URL parameters or location.hash values.

To find out more