tl;dr
In this blog post, I will show you a better way to exploit non-root-relative path overwrite issues in ASP.NET Web Form applications. This is a low risk vulnerability that can be used to inject a resource such as a stylesheet or even a dynamic JavaScript into an affected web page.
Introduction
Many of us already know for a long time about path-info and the ability of several web application technologies such as .Net, php, or java that can accept parameters after the file extension by using a slash (“/”) or a semi-colon (“;”) character. This is a useful feature to create customised restful applications. In case of Java, it can normally accept a session token after a semi-colon character in the URL (when jsessionid is not in the cookies – bad practice).
Web pages which include their URLs in their output without encoding it properly (assuming it is safe) or with improper encoding can become vulnerable to cross-site scripting issues (e.g. http://www.exploit-db.com/exploits/31865/). However, this is not the topic of this blog post as it has nothing to do with the relative paths!
Many of us know about the functional issues that relative paths could cause a web application especially when URL-rewrite rules or restful URLs are in use. For instance, we could see broken pages with messed-up images and stylesheets from time to time by adding or changing parameters in the URL. Â However, I did not have any idea how to exploit this issue in any ways until Gareth Heyes shed light on it and published the following useful blog post:
http://www.thespanner.co.uk/2014/03/21/rpo/
After that I mistakenly thought this attack would only be useful in the old versions of IE until James Kettle published the following useful post in the PortSwigger’s blog:
http://blog.portswigger.net/2015/02/prssi.html
It is highly recommended to read this blog post if you have not read it yet. Especially the “Exploiting Quirks” section caught my eye! Who knows? It may catch yours too ;-)
RPO and ASP.NET Applications
The usage of non-root-relative paths is common among the ASP.NET Web Form applications and I am going to focus on this type of application in this blog post.
In order to add a stylesheet and a JavaScript file with non-root-relative paths, the following code can be added to an ASP.NET page (.aspx or its Master page):
<head runat="server"> <link href="~/style.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="<%=Page.ResolveClientUrl("~/js.js")%>"></script> </head>
You can use the following vulnerable “.aspx” page (VB.NET) in your test environment:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <!-- Author: Soroush Dalili (@irsdl) - https://Soroush.SecProject.com/blog/ --> <head runat="server"> <link href="~/style.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="<%=Page.ResolveClientUrl("~/js.js")%>"></script> </head> <% Dim input: input = Request("input") if input <> "" then session("input") = input %> <body> <form runat="server"> <div class="page" id="page">Just an example. Your input goes here: "<%=Server.HtmlEncode(session("input"))%>"</div> <div class="DotNetVersion">.Net Framework version: "<%=Environment.Version%>"</div> </form> <script> var pageColor = ''; var pageObj = document.getElementById('page'); var textColor; if (pageObj.currentStyle){ textColor = pageObj.currentStyle.color; }else if (window.getComputedStyle){ textColor = document.defaultView.getComputedStyle(pageObj, null).getPropertyValue("color"); } if(textColor=="red" || textColor=="rgb(255, 0, 0)"){ pageColor = 'red'; } </script> </body> </html>
This page uses an old doctype which can simply be exploited in an RPO attack. As explained in the PortSwigger’s blog post, a list of doctypes that can open a website in Quirks mode can be found in the following website: https://hsivonen.fi/doctype/#handling and sometimes it is even possible to force a web page to be opened in Quirks mode by using IE (see PortSwigger’s blog post for the requirements).
This ASPX page has been saved in the following URL (.Net Framework 2): http://sdl.me/demo/RPO/test.aspx?input=test
As this page HTML-encode the “input” parameter, it is not possible to exploit a cross-site scripting issue. Sometimes in real ASP.NET applications, the protection can be because of the Request Validation module.
The above ASP.NET page shows the following HTML code in the browser:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <!-- Author: Soroush Dalili (@irsdl) - https://Soroush.SecProject.com/blog/ --> <head><link href="../../style.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="../../js.js"></script> <title> </title></head> <body> <form name="ctl01" method="post" action="test.aspx?input=test" id="ctl01"> <div> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTQ3MDQ5NDcxMmRkL+8De/6ibnWrmbVRn1YZfKdyQoI=" /> </div> <div> <input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="EBB86DB2" /> </div> <div class="page" id="page">Just an example. Your input goes here: "test"</div> <div class="DotNetVersion">.Net Framework version: "2.0.50727.4253"</div> </form> <script> var pageColor = ''; var pageObj = document.getElementById('page'); var textColor; if (pageObj.currentStyle){ textColor = pageObj.currentStyle.color; }else if (window.getComputedStyle){ textColor = document.defaultView.getComputedStyle(pageObj, null).getPropertyValue("color"); } if(textColor=="red" || textColor=="rgb(255, 0, 0)"){ pageColor = 'red'; } </script> </body> </html>
.NET resolves the “~” operator to the root of the current application (in IIS). Therefore, you may see a “../” pattern when the page is located in a subdirectory.
Note: If a web page is in a directory but does not contain a “../” pattern in a non-root-relative path (does not start with a protocol or a “/” character), the directory name shows the application name that is installed on IIS (this is in fact root of that web application).
.Net Framework 4 resolves the “~” operator a little bit different from .Net Framework 2 and here is when the confusion arises!
In our example page, .Net Framework 4 adds more “../” to the “style.css” path when we have additional folders after the “.aspx” extension. In other words, by opening the “/test.aspx/1111/2222/3333” page in .Net Framework 4 the “style.css” path has 3 additional “../” as it shown below:
<link href="../../../../../style.css" rel="stylesheet" type="text/css" />
Unfortunately I do not have a web server with .Net Framework 4 but we can use our local IIS server to reproduce the issue.
This behaviour of .Net Framework 4 neutralises normal RPO attacks as the page cannot load itself as a stylesheet anymore. However, vulnerable pages in .Net Framework 2 can be exploited with the same exploitation method that was described in Gareth Heyes’ or PortSwigger’s blog posts:
http://sdl.me/demo/RPO/test.aspx/1111/2222/?input=%0C}}}}]]]]]*/ {} *{color:red;}
Note: “%0A”, “%0C”, or “%0D” should be used to escape the double or single quotation characters. In addition, the attack will not work if there is any open “{“ or “[“ characters as well as “/*”. Therefore, the “%0C}}}}]]]]]*/” string was used before the CSS injection vector to ensure that the rest of string can be loaded as a valid style to make the page red. The “import” method could also be used as another vector of CSS injection.
Note: This PoC code will not work if .Net Framework 4 is used as it was explained above.
A Generic Solution for ASP.NET Pages:
.Net Framework 4 does not add additional “../” to the path when the directory names after the “.aspx” extension are empty! As a result, the following easy solution could be used in .Net Framework 2 and 4 at the same time:
http://sdl.me/demo/RPO/test.aspx////////////?input=%0C}}}}]]]]]*/ {} *{color:red;}
Overwrite by Using Another File:
RPO in a web server such as IIS which understands an encoded directory traversal pattern in the path “%2F.. %2F” can also be exploited to point to the other files (that supports path-info) on the same website.
The following shows an example in which “anotherpage.css.aspx” is used to replace the stylesheet:
</pre> http://sdl.me/demo/RPO/anotherpage.css.aspx/%2f..%2f..%2f..%2fdemo/RPO/test.aspx <pre>
This can also cause a cross-site scripting issue if an attacker can use another dynamic file (such as a JSON handler file) to rewrite the path of a non-root relative JavaScript file as it is shown below:
</pre> http://sdl.me/demo/RPO/anotherpage.js.aspx/%2f..%2f..%2f..%2fdemo/RPO/test.aspx <pre>
In IIS, “%2F” (URL-encoded version of “/”) can also be replaced with a “%5C” (URL-encoded version of “\”).
Note: This method is similar to the one I had used before to exploit location.pathname in JavaScript (https://soroush.secproject.com/blog/2013/09/simple-security-tip-window-location-window-location-pathname-can-cause-open-redirect-issue/).
Note: In case of having an application on IIS in the path, this method can only be exploited within this application path (not from root of the website). The following shows an example:
</pre> http://example.com/ApplicationName/page2.aspx/%2f..%2fpage1.aspx <pre>
Note: Using another dynamic page may help an attacker to inject other resources such as even JavaScript files in special cases.
.NET Framework 4 Form’s Action:
This feature of .NET Framework 4 was known to me for a long time but I could never exploit it in any useful way. However, some people may find this inspirational to find some security issues and please don’t forget to update me on that as I am really interested!
Anything after the last forward-slash or backslash (“/” or “\”) characters before the QueryString part of the URL is used as the form’s action without URL-encoding (double and single quotation characters have HTML-encoding). For instance, opening the “example.aspx/1.aspx/2.aspx?test=test” will create the following HTML form in the page:
<form method="post" action="2.aspx" id="ctl01">
I could not use this to exploit RPO as the control characters (such as “%0A”, “%0C”, or “%0D”) will be blocked by default IIS settings and characters like “< > * % & : \ ?” and double escaping are blocked by the default ASP.NET settings (requestPathInvalidCharacters). Exploiting CSS injection by using “import” is more feasible in this case but there is limitation on the usage of forward slash or backslash characters (you will see it when you start exploiting it yourself).
Most of the ASP.NET pages also do not use the “action” attribute of .Net forms, otherwise we could probably exploit this by chaining it to an open redirect or a reflected cross-site scripting issue (to bypass browsers’ anti-XSS protections).
In the end, I should not forget to thank Dean McCarten (@2bitwannabe) who provided me with an example from Visual Studio 2013 which used relative paths in a stylesheet. This .Net sample code and a recent penetration testing project encouraged me to write this blog post.