Dylan Reinhardt wrote:
At 01:39 PM 2/27/2003, Jamie Heilman wrote:
Pragmatically this is the same as HTML quoting. (Thats not always the case unfortunately.)
Could you offer an example where &dtml-some_var; returns something different from <dtml-var some_var html_quote>?
I'm not saying they do, afaik, they don't. I'm saying sometimes, depending on how much you want to play guardian, escaping < > & and " or ' may not be "enough" to prevent hanky-panky.
I read your post on VHM exploits a couple weeks ago. Is this the scope of the problem?
Not entirely, but it is related.
Is the problem solved by using a proxy cache to drop any requests that contain the magic VHM-related strings? Or does it go deeper?
It goes deeper. Actually using a VHM is a good prevention technique. Andrew Athan made an observation last week that woke me up to a whole new problem.
Also, how does using &dtml-URL1; do anything to guard against this?
It will ensure special characters injected via the URI are quoted ostensibly preventing cross site scripting attacks.
Won't URL1 resolve to what follows VirtualHostBase in either syntax?
Only if you use a VHM. Many don't.
I've got a HOWTO that includes information on virtual hosting... I'll be sure to add this information and any other advice or insight you're able to offer.
OK, here's a breakdown of the problems, both immediate, and theoretical. First the immediate. There exist, in both the 2.6.1 and the 2.7 CVS code, and afaik, all previous code, several spots where anonymous clients may inject markup which will appear in a document with no escaping. I'm not going to go into a huge explanation about why this is bad, its called cross site scripting, its been a problem with web content from day 1, anyone who doesn't know what this is by now can use their favorite search engine to find out. Cache poisoning lets us inject markup in one transaction and have that markup persist across several transactions with completely unrelated clients. I warned earlier about the dangers of not protecting against anonymous requests for resources that contained VHM's "magic" keywords in them. (/VirtualHostBase/, /VirtualHostRoot/, and /_vh_ -- though let me just note that there's an extremely good argument for blocking all requests that contain any path element that beings with _ because so many products are prone to using that as a special pre-traversal hook; for example disabling access rules.) Well it turns out another avenue of attack is available via the Host: header of HTTP 1.1 requests. People who don't use a VHM are especially vulnerable. People who attempt to use a single rewrite-rule to lazily do vhosting for multiple domains may also be at risk (this was discussed a little on zope-dev a few weeks ago). Andrew Athan noted that a bug in an IE beta was screwing up his site where absolute_url() was being used. This is specifically because of an incorrect Host: header, and thats what tipped me off to the problem. So I sent a request to a (bare) ZServer instance I had running with a host header of www.<b>old-flava'-&-"kids".com. The most immediate thing I noticed is that the injected base tag was: <base href="http://www.<b>old-flava'-&-"kids".com/path" /> No escaping[1]. This could be disastrous for sites fronted by a cache! I thought. But then upon experimenting further it became obvious that for RAM Cache users it wouldn't be a poisoning[2] problem though, because the base tag is added dynamically on every request, and never makes it into the ram cache. However, external caches *may* be vulnerable, if they don't use the Host: header as part of their caching key (in theory anyway). I haven't tested that, I am interested in the results if anyone does. I don't use squid or other proxy caches, I don't know what all they use to key content, or if its configurable. The bad news is that its not just the base tag. For example, Image.py is vulnerable[3]. There are probably more. Protecting against this is possible by using a VHM defensively. The Host: header is overridden by a VHM instance's VirtualHostBase hook. Provided you hardcode the URI used by your proxy config, you should be safe. For example, with apache rewrite rules: RewriteEngine on RewriteCond %{REQUEST_URI} /_ [OR] RewriteCond %{REQUEST_URI} /VirtualHostBase/ [OR] # possibly other things zope shouldn't expose but does[4] RewriteCond %{REQUEST_URI} /VirtualHostRoot/ RewriteRule .* - [F,L] RewriteRule ^/foo(.*) http://127.0.0.1:8080/VirtualHostBase/http/example.com:80/example/VirtualHos... [P,L] Note how example.com is hardcoded and the example doesn't rely on any tricks with HTTP_HOST. That is important. (see Oliver Bleutgen and my own comments on zope-dev 2 weeks ago for why) This effectively lets Apache handle HTTP 1.1's name based virtual hosting, and takes the burden off Zope, as well as protecting you against Host header treachery. I bet the same technique is applicable to squid and other proxy servers. The above protection technique is important for other reasons, which bear discussion. Cross site scripting is bad, but its only one avenue of attack. As it currently stands you could poison a cache by doing something like: telnet victim.example.com 80 GET / HTTP/1.1 Host: attacker.example.org Once victim.example.com's cache is poisoned, assuming the attacker controls attacker.example.org, they could easily change the images a browser would likely see when visiting victim.example.com. What was once rendered as: <img src="http://victim.example.com/company_logo.jpg" ... /> is now rendered as <img src="http://attacker.example.org/company_logo.jpg" ... /> Instant remote defacement. I'll let you use your imagination from here on the other directions this can lead. Point is, you don't have to use cross site scripting to be evil, so even once we ferret out and crush all the spots in the zope code that allow xss, there's still this crap to worry about. Solving this issue is sorta ugly. Apache's UseCanonicalName defaults to "on" for a very good reason. For now fronting zope with a trusted proxy is the best workaround I know of. Thus ends today's session of my tips on securing zope. (oh, btw, this is bug 813 in the collector, though as of this writting, its still not public) -- Jamie Heilman http://audible.transient.net/~jamie/ "...thats the metaphorical equivalent of flopping your wedding tackle into a lion's mouth and flicking his lovespuds with a wet towel, pure insanity..." -Rimmer [1] a patch: --- lib/python/ZPublisher/HTTPResponse.py Sat Feb 1 00:25:42 2003 +++ taste/the/fun/HTTPResponse.py Tue Feb 25 20:06:10 2003 @@ -453,7 +453,8 @@ ibase = base_re_search(body) if ibase is None: self.body = ('%s\n<base href="%s" />\n%s' % - (body[:index], self.base, body[index:])) + (body[:index], self.quoteHTML(self.base), + body[index:])) self.setHeader('content-length', len(self.body)) def appendCookie(self, name, value): [2] It should be noted, while cache poisoning with cross site scripting is an ugly combination, removing the cache poisoning from the equation isn't really a complete solution. It does increase the difficulty exploit too a degree, but being able to inject markup into a page is always a problem, regardless if the markup is persistent or not. [3] No patch. I refuse to touch Image.py, as it stands its a total train wreck. Sorry. [4] Does anyone know how to determine every valid object within the entire local, acquisition, or otherwise context? This would help expose things like misc_, p_, HelpSys, Redirect and friends so that the admin could have some control over what they published. As it stands there entirely too much magic "invisible" shit just floating in the acquisition path waiting for abuse, and every product you install just makes it worse. A tool to dump entire namespaces is something I desperately want because auditing the entire code base just isn't practical.