Some Interesting URI Parsing Quirks and Open Redirects

Parsing the “relativeness” of a URI seems to be a pretty browser specific thing, and doing some quick tests there are several quirks that might be useful/dangerous. The Tangled Web (which is an awesome book) aludes to some of these.

Some URI Quirks

Let’s look at some tests with the URIs grabbed from the location header. The browsers I’m testing right now are IE9, Chrome 17 something, and Firefox 11.

All browsers are happy with this, and go to google.com

header(“Location: //google.com”);

Both Firefox and chrome truncate extra slashes
So

header(“Location: http:///////////////////////google.com“);

is completely happy.

IE is interesting, as it will be equally happy with and /

header(“Location: https:\\google.com\“); <– this works

The spacing doesn’t seem to matter, so all browsers are happy with:

header(“Location:                        http://google.com“);

as well as

header(“Location:http://google.com&#8221;);

My favorite is this. In chrome and Firefox

header(“Location: http:google.com”);

will redirect to a relative URI, but for whatever reason

header(“Location: https:google.com“);

will redirect to google.com. wtf?

Same Domain Redirect

These parsing quirks can be useful for several attacks, and the first thing that came to mind for me was open redirects.  It’s a pretty common scenario to want to allow sites  redirecting based on the parameter as long as it’s in the same domain. It can be expensive to whitelist every URI (which would be ideal), so although that’s a great solution, I also think allowing redirects to your own domain is sometimes better than nothing …despite there being some risks associated with it, like giving an attacker a way to bypass the IE8 XSS filter http://packetstorm.wowhacker.com/papers/general/msie-xssbypass.pdf.

So, below are some (broken) examples of websites trying to accomplish this, allowing a redirect but only to their own site.

Broken Example 1 – startswith /

One naive way to try to perform arbitrary on-site local redirects would be something like the following, which takes the redir query parameter and make sure it starts with a slash:

$redir = $_GET['redir'];
#if redir starts with /
if (strpos($redir, "/", 0) === 0)
header("Location: " . $redir);

Obviously, this can be bypassed in all browsers with //google.com

Broken Example 2 – No Semicolons, Can’t start with /, and in fact, don’t start with // either

This PHP tries to prevent off-site redirects with the following snippet

$redir = $_GET['redir'];
#make sure redir doesn't have slashes, and doesn't have semicolons
if ((strpos($redir, "/", 0) != 0) and (strpos($redir, "/", 1) != 1) and (strpos($redir, ":") === false))
{
header("Location: " . $redir);
}

Because you can prepend spaces, one way to bypass this is to send the following:

redir=%20%20//google.com

Broken Example 3 – No Slashes at all

Ok, what if there are no slashes are allowed at all? You can’t very well have http://blah.com without a slash, after all, so this intuitively might make sense. The code for this might look something like:

$redir = $_GET['redir'];
#if no / in the string
if (strpos($redir, "/") === false)
  header("Location: " . $redir);

However, using the quirks above, this can be bypassed by using redir=https:google.com in FF and chrome, and it can be bypassed in IE with redir=\google.com

Broken Example 4 – Built in Libraries:

Surely there are libraries that solve this problem. Well, maybe there are, but there are certainly libraries people use to try to solve this problem, but they don’t do it as people expect (e.g. a library might call a URI relative when a browser treats it as absolute). Making a library that works well is a fundamentally tough problem because all these browsers have quirks and the library has to match all browsers. So is a library supposed to call https:google.com a relative or a full uri? (it’s relative in IE but full in chrome and FF)

Here’s one C# example where someone might try to figure out if a URI is relative URI using the IsAbsoluteUri property in .net system.Uri.

        static void Main(string[] args)
        {

            String[] uriArray = new String[] {
                "//google.com/test.html",                 //relative
                "\\google.com\test.html",              //relative
                "/////////google.com/test.html",          //relative
                "https:google.com",                       //relative
                "http://google.com",                      //absolute
                "http:///////////////////google.com",     //absolute
                "           http://google.com"            //absolute
            };

            foreach (String uriString in uriArray)
            {
                try
                {
                    Uri uri = new Uri(uriString, UriKind.Relative); //works
                    if (!uri.IsAbsoluteUri)
                    {
                        Console.WriteLine("is a relative URI: {0}", uriString);
                    }
                }
                catch (UriFormatException e)
                {
                    Console.WriteLine("not a relative URI: {0}", uriString);
                }
            }
        }

Broken Example 5 – startswith Whitelisted Domain

This is a classic example. Even though it doesn’t have much do do with parsing quirks, it can be subtle and illustrates an important point.  So say an application does the following to make sure the redirect is on the correct domain.

String redir = Request["redir"];
if (redir.StartsWith("http://goodsite.com"))
{
	Response.Redirect(redir);
}

Can an attacker still exploit this? The answer is yes, by setting redir=http://goodsite.com.badsite.com/

What’s the Right Way to do on-domain Redirects?

So open redirects are in the owasp top ten, and they have some guidance here: https://www.owasp.org/index.php/Top_10_2010-A10-Unvalidated_Redirects_and_Forwards. However, to summarize, it basically says to whitelist and don’t redirect. I do agree, but again, what if you want to do on-domain redirects? The best I can think of is to do something like this:

startswith(http://goodsite.com/ || https://goodsite.com/)

Note the trailing slash, which prevents broken example #5.  I don’t think it’s possible to redirect off-site with this type of code… but if someone knows otherwise, I’d definitely be interested in how to do it :)

2 Responses to Some Interesting URI Parsing Quirks and Open Redirects

  1. Rich says:

    I should have also called out the response splitting attacks on the PHP, but that’s not really the point of the post. I spend most of my time in .net, and that’s just taken care of with response.redirect.

  2. A much more comprehensive list has been put up by kotowcz here: http://kotowicz.net/absolute/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s