Preventing XSS when using Nevow's vhost functionality
Say I implement web server that serves content for a number of different users, each on their own sub domain. My nevow site has a root resource that is sensitive to the host / port information in the request (virtual hosting). Since this happens is in a reverse-proxy situation, the root resource has a child, vhost, that allows the application to gain knowledge of which host the user accessed. One of the users, "evil", has a goal of stealing cookie content from people who visit goody's web shop. The attacker makes the victim go to this URL: http://goody.com/vhost/http/evil.net/pageWithAttackJS.html which gets rewritten (by the reverse proxy) to: http://internalserver:1234/vhost/http/goody.com/vhost/http/ evil.net/pageWithAttackJS.html The web server looks up the vhost child of the root resource which consumes the next two components. It sets the host to goody.com, as it should be and passes control back to the root resource. The remaining URI at this point is: http://goody.com/vhost/http/evil.net/pageWithAttackJS.html This once more invokes the vhost functionality, the following results and is accessed: http://evil.net/pageWithAttackJS.html The malicious javascript code is sent to the client which interprets it. The problem is that the client still believes it accessed a resource on goody.com, and therefore allows the JS to access cookies set by goody.com. The same design flaw can be used for phising attacks etc (faking the "sender" of some information). Can anyone suggest a good approach for preventing this kind of violation? I think limiting vhost from working unless the current request's host is (internalserver, 1234) would be safest. Other ideas? / Thanks, David Remahl
On Oct 19, 2005, at 2:48 PM, David Remahl wrote:
The attacker makes the victim go to this URL: http://goody.com/vhost/http/evil.net/pageWithAttackJS.html which gets rewritten (by the reverse proxy) to: http://internalserver:1234/vhost/http/goody.com/vhost/http/ evil.net/pageWithAttackJS.html
The web server looks up the vhost child of the root resource which consumes the next two components. It sets the host to goody.com, as it should be and passes control back to the root resource. The remaining URI at this point is: http://goody.com/vhost/http/evil.net/pageWithAttackJS.html
This once more invokes the vhost functionality, the following results and is accessed: http://evil.net/pageWithAttackJS.html
Your example is a little confusing. VHostMonsterResource does expose applications using nevow.url to link hijacking. So for your example to work /pageWithAttackJS.html would have to be on the target server, and generated links on that page would get rewritten as http:// evil.net/. Potentially these generated links could include a <script> tag. So now that everyone is on the same page, i've attached a proof of concept tac that better demonstrates the problem. If you start the tac and go to it with http://localhost:8080/vhost/http/foo.bar/vhost/ http/google.com/ you should see the google logo loaded. if you do not include the final vhost call nothing should load. So the problem is two fold, VHostMonsterResource is to trusting of the Host: header. The solution to that is to use something like web2.vhost.VHostURIRewrite which sets the host:port for the request mangling at configuration time. This might not be ideal in all enviroments if you have multiple hosts forwarding to the same application. Another problem is that URL generation is to trusting of the context. Being able to easily generate links relative-to-root (rendering without the scheme and netloc portions) would also prevent this type of link hijacking. (Though it would be potentially vulnerable to attacks from a sister application.) But relative-to-root URL generation (as sane default) would negate the need for vhost.VHostMonsterResource in most cases. However as for fixing VHostMonsterResource, I'd rather just see it die the slow painful death it deserves. But I think if you're going to limit VHostMonsterResource to one proxy url, you might as well switch to a configuration time rewriter. Another solution is to simply tag the request as rewritten so it doesn't get mangled a second time, raising the appropriate error code) -David 
David Reid wrote:
On Oct 19, 2005, at 2:48 PM, David Remahl wrote:
The attacker makes the victim go to this URL: http://goody.com/vhost/http/evil.net/pageWithAttackJS.html which gets rewritten (by the reverse proxy) to:
http://internalserver:1234/vhost/http/goody.com/vhost/http/evil.net/pageWith...
The web server looks up the vhost child of the root resource which consumes the next two components. It sets the host to goody.com, as it should be and passes control back to the root resource. The remaining URI at this point is: http://goody.com/vhost/http/evil.net/pageWithAttackJS.html
This once more invokes the vhost functionality, the following results and is accessed: http://evil.net/pageWithAttackJS.html
Your example is a little confusing. VHostMonsterResource does expose applications using nevow.url to link hijacking. So for your example to work /pageWithAttackJS.html would have to be on the target server, and generated links on that page would get rewritten as http://evil.net/. Potentially these generated links could include a <script> tag.
So pageWithAttackJS.js would more likely be liveglue.js?
So now that everyone is on the same page, i've attached a proof of concept tac that better demonstrates the problem. If you start the tac and go to it with http://localhost:8080/vhost/http/foo.bar/vhost/http/google.com/ you should see the google logo loaded. if you do not include the final vhost call nothing should load.
I don't quite understand why this works. If the first two segments of the vhost request are the protocol and the host why isn't it the case that these segments are consumed, the URL is re-written to: http://foo.bar/vhost/http/google.com ...and it fails because foo.bar doesn't exist? Why are the segments still consumed after this point, or...?
So the problem is two fold, VHostMonsterResource is to trusting of the Host: header. The solution to that is to use something like web2.vhost.VHostURIRewrite which sets the host:port for the request mangling at configuration time. This might not be ideal in all enviroments if you have multiple hosts forwarding to the same application.
Another problem is that URL generation is to trusting of the context. Being able to easily generate links relative-to-root (rendering without the scheme and netloc portions) would also prevent this type of link hijacking. (Though it would be potentially vulnerable to attacks from a sister application.) But relative-to-root URL generation (as sane default) would negate the need for vhost.VHostMonsterResource in most cases.
However as for fixing VHostMonsterResource, I'd rather just see it die the slow painful death it deserves. But I think if you're going to limit VHostMonsterResource to one proxy url, you might as well switch to a configuration time rewriter. Another solution is to simply tag the request as rewritten so it doesn't get mangled a second time, raising the appropriate error code)
-David
On Oct 19, 2005, at 4:29 PM, Jason Mobarak wrote:
So pageWithAttackJS.js would more likely be liveglue.js?
Perhaps, but I don't think liveglue.js is a hardcoded relative link.
I don't quite understand why this works. If the first two segments of the vhost request are the protocol and the host why isn't it the case that these segments are consumed, the URL is re-written to:
http://foo.bar/vhost/http/google.com
...and it fails because foo.bar doesn't exist? Why are the segments still consumed after this point, or...?
Because the point of VHostMonsterResource is so that the application doesn't need to know the url it's actually being accessed from in a ProxyPass situation. In a real world example you'd trick the user to go to http://foo.bar/vhost/http/google.com/ and the user would, the apache server at foo.bar would foward the request to http://localhost: 8080/vhost/http/foo.bar/vhost/http/google.com/ so the above multiple calls happen and the final request consists of a host being set to google.com. There just isn't any implementation for it to fail because foo.bar doesn't exist. The jist is, VHostMonsterResources flaws are numerous and inherent. -David
On 20 okt 2005, at 01.07, David Reid wrote:
On Oct 19, 2005, at 2:48 PM, David Remahl wrote:
The attacker makes the victim go to this URL: http://goody.com/vhost/http/evil.net/pageWithAttackJS.html which gets rewritten (by the reverse proxy) to: http://internalserver:1234/vhost/http/goody.com/vhost/http/ evil.net/pageWithAttackJS.html
The web server looks up the vhost child of the root resource which consumes the next two components. It sets the host to goody.com, as it should be and passes control back to the root resource. The remaining URI at this point is: http://goody.com/vhost/http/evil.net/pageWithAttackJS.html
This once more invokes the vhost functionality, the following results and is accessed: http://evil.net/pageWithAttackJS.html
Your example is a little confusing. VHostMonsterResource does expose applications using nevow.url to link hijacking. So for your example to work /pageWithAttackJS.html would have to be on the target server, and generated links on that page would get rewritten as http://evil.net/. Potentially these generated links could include a <script> tag.
So now that everyone is on the same page, i've attached a proof of concept tac that better demonstrates the problem. If you start the tac and go to it with http://localhost:8080/vhost/http/foo.bar/ vhost/http/google.com/ you should see the google logo loaded. if you do not include the final vhost call nothing should load.
So the problem is two fold, VHostMonsterResource is to trusting of the Host: header. The solution to that is to use something like web2.vhost.VHostURIRewrite which sets the host:port for the request mangling at configuration time. This might not be ideal in all enviroments if you have multiple hosts forwarding to the same application.
Another problem is that URL generation is to trusting of the context. Being able to easily generate links relative-to-root (rendering without the scheme and netloc portions) would also prevent this type of link hijacking. (Though it would be potentially vulnerable to attacks from a sister application.) But relative-to-root URL generation (as sane default) would negate the need for vhost.VHostMonsterResource in most cases.
However as for fixing VHostMonsterResource, I'd rather just see it die the slow painful death it deserves. But I think if you're going to limit VHostMonsterResource to one proxy url, you might as well switch to a configuration time rewriter. Another solution is to simply tag the request as rewritten so it doesn't get mangled a second time, raising the appropriate error code)
-David
<xss-nevow.tac>
Thanks for your reply! I'm not sure your PoC encapsulates precisely the problem I described, but it is a valid variant which doesn't require as many preconditions (but is not possible to exploit in some browsers, such as Safari, that won't trust JS from another host even if directly referenced from goody). I think I should clarify that in my example, goody.com and evil.net are two sites that _are_ indeed served by the same nevow application. They each have their own little space on the same server, but they don't administer the application serving their content. I guess that's what you refer to as "sister applications". If anyone is still unclear on my idea of the attack, I'll construct another PoC, since the two exploits are fundamentally different. I'm not familiar with web2.vhost.VHostURIRewrite, but will check it out..."Configuration time", you say. I assume that means at the start of the handling of a request, not when the application is set up and configured? If so, that sounds like a good solution in many cases. Only allowing a single "invocation" of the monster resource is insufficient in certain situations, namely when http://internalserver: 1234/ is accessible from the outside and not _exclusively_ by the reverse proxy. This might also be a problem with the VHostURIRewrite approach? / Thanks, David
On Oct 19, 2005, at 4:39 PM, David Remahl wrote:
I'm not familiar with web2.vhost.VHostURIRewrite, but will check it out..."Configuration time", you say. I assume that means at the start of the handling of a request, not when the application is set up and configured? If so, that sounds like a good solution in many cases.
In fact configuration time does mean in the .tac. You create a vhost.VHostURIRewrite resource like so: root = vhost.VHostURIRewrite(uri='http://host:port/path', resource=realrootresource) server.Site(root) There is also a AutoVHostURIRewrite which makes use of x-app-scheme, x-forwarded-host, x-app-location, and x-forwarded-for headers to determine the real hostname port path etc and also the client ip address. I'm fairly sure this isn't vulnerable to a similar attack as VHostMonsterResource but I haven't done an extensive audit of that particular code.
Only allowing a single "invocation" of the monster resource is insufficient in certain situations, namely when http:// internalserver:1234/ is accessible from the outside and not _exclusively_ by the reverse proxy. This might also be a problem with the VHostURIRewrite approach?
It would be a problem with AutoVHostURIRewrite but not a problem with VHostURIRewrite, because VHostURIRewrite would mangle the request unconditionally and if the client clicked an absolute link would end up at the proper location anyway. This unconditional mangling might have other implications if internalserver:1234 is accessible from outside but in my opinion that's just more of a reason to not have that kind of configuration. -David
On Oct 19, 2005, at 4:39 PM, David Remahl wrote:
I'm not sure your PoC encapsulates precisely the problem I described, but it is a valid variant which doesn't require as many preconditions (but is not possible to exploit in some browsers, such as Safari, that won't trust JS from another host even if directly referenced from goody). I think I should clarify that in my example, goody.com and evil.net are two sites that _are_ indeed served by the same nevow application. They each have their own little space on the same server, but they don't administer the application serving their content. I guess that's what you refer to as "sister applications".
I just wrote this very long email about your suggested exploit, and then realized that the example configuration I imagined wouldn't cause the type of behavior you describe. I can imagine a very complicated configuration that invovling 3 vhost.VHostMonsterResources and 1 NameVirtualHost might allow someone to suddenly jump between two domains in the NameVirtualHost (at the urging of an attacker) but I haven't tried to put together a PoC. If this is in fact the configuration you're suggesting I only have this to say I can not think of a single use case for vhost.NameVirtualHost vhost.VHostMonsterResource being used today in a configuration such as what I believe you to be describing. But if there is a use-case I'm honestly not sure there is a way to make it work without properly. -David
Attached is a proof of concept which points out the highly specific configuration (erroneous configuration) that would cause such an attack to work. As you'll see http://localhost:8080/vhost/http/goody.com/vhost/http/ evil.net/ actually serves the EvilPage instance.
participants (3)
-
David Reid
-
David Remahl
-
Jason Mobarak