Sunday, March 27, 2011

Man-in-the-middle attacking Mozilla Firefox updates

Earlier today I was proved wrong when I claimed it was not possible to update from Firefox 3.x to 4.x securely.  I'm not quite sure what happened when I originally tested it -- potentially it was something to do with one of their security updates going out, but in any case, this got me looking at how their updates work.

The latest Firefox 3.x actually pulls update information from an SSLized URL, and it verified the binary package before installing it.  I was pretty happy when I saw that, but I started wondering how the update box below was rendered:


It turns out that the user-readable details about the new version are pulled as plaintext HTTP from Mozilla's servers.  I wondered what would happen if I added a meta refresh to another download in this page.  Below is the edited HTML using Charles Proxy but clearly any man-in-the-middle attacker could do the same thing. 



The result of this is an arbitrary file being pushed to you at the time you are trying to update.  I personally think this could be convincing enough to get people to run malicious code.  An attacker could also edit the HTML directly to make it seem like you should click a button in the untrusted frame instead of the "Get the New Version" button or be even more creative.



I think it is important for software vendors like Mozilla to keep these kinds of attacks in mind when mixing application chrome and untrusted content in the same view.  It is important for us to think of the barely computer literate user who is just trying to safely browse the web.  I'm pretty sure more badness could be done with this, and the fix is relatively simple -- all they needed to do was serve this over SSL and the threat would be greatly reduced.

Monday, March 21, 2011

Apple fixes Installer bug, CVE-2011-0190


Mac OS X v10.6.7 addressed a bug I reported in Installer:
From Apple's advisory
Impact: Visiting a maliciously crafted website may lead to the installation of an agent that contacts an arbitrary server when the user logs in, and mislead the user into thinking that the connection is with Apple
Description: A URL processing issue in Install Helper may lead to the installation of an agent that contacts an arbitrary server when the user logs in. The dialog resulting from a connection failure may lead the user to believe that the connection was attempted with Apple. This issue is addressed by removing Install Helper.

Some additional details:
- A web page could open a url with the x-mini-installer:// scheme, with a host and path of where to attempt to load files.
- It attempts to download a plist pointing it at an update server at every login
- When an update package fails to process properly, the UI displayed instructs a user to run something from their ~/Downloads folder, where a malicious file could have been dropped.
- I did not succeed in getting it to automate the install of arbitrary content.  
- This bug could be used to trigger this format string bug

Friday, March 18, 2011

Quick note on LWP and Perl security - CVE-2011-0633

As a follow-up to this post on how most LWP-based scripts can be man-in-the-middled, I contacted Jesse Vincent, Gisle Aas, and the Perl security team about this LWP issue.  They immediately saw the issue and began fixing it.  I would strongly recommend using LWP 6.00 for anything that needs to handle an HTTPS URL.
CVE-2011-0633 
The libwww−perl (LWP) module Net::HTTPS did not fully validate SSL certificates by default prior to version 6.00. Multiple Perl modules (such as WWW::Mechanize and LWP::UserAgent) do not enable full validation of SSL certificates when using libwww-perl, leaving software that uses them vulnerable to man-in-the-middle attacks. This issue was addressed by changing the default behavior of libwww-perl to enable full validation of SSL certificates.
The LWP 6.00 changelog includes the following about the change:
For https://... default to verified connections with require IO::Socket::SSL and Mozilla::CA modules to be installed. Old behaviour can be requested by setting the PERL_LWP_SSL_VERIFY_HOSTNAME environment variable to 0. The LWP::UserAgent got new ssl_opts method to control this as well.
Thanks to all of their hard work, lots of projects that previously did not validate certificates will begin to do so, once LWP is updated.

Wednesday, March 9, 2011

Safari Errorjacking - CVE-2011-0167

Intro:

Today's Safari update addresses a rather serious issue (CVE-2011-0167) that I found and reported to Apple.  This issue allows Javascript from any website to jump into the local zone and access any files accessible to the user running Safari.  This bug actually exists in WebKit, so other browsers could be affected.  This is why it's good when browsers do what Chrome did, and heavily limit what can be done by local HTML. . Check out the related reading at the end of this post for more on that.

Here's a quick video of the bug in action.  Only HTML and JavaScript are required to accomplish this.






Bug description:

The problem is that when internally generated pages (such as error pages) are loaded, they are loaded from the local "file:" zone, but the window's location can still be set by all scripts that have a reference to the window, such as an attacker's website.

Why remote-to-local matters:

Safari normally blocks loading of local content from remote sites for a ton of reasons.  One reason is that local content in Safari has a fair bit of power*, including the ability to read any file accessible to the current user.  This can be accomplished a number of ways.  The easiest is using XMLHttpRequest()s in order to read files by "file:///" URL.  Once loaded, the content can be sent anywhere.  

*To read about the many other things that local HTML can do, check out the related reading links below.

How remote-to-local restrictions normally work:

You can see remote-to-local restrictions in action by trying to load the following HTML from a web server in Safari:


Click on the link and you will get the following error in the Javascript console:

Not allowed to load local resource: file:///tmp/foo.html

Sometimes remote-to-remote turns into remote-to-local:

I noticed something interesting occurs when trying to load a web page that doesn't exist, cannot be reached, or any other action that results in an internally generated page being displayed.  The page is loaded from a file on disk  and the current window location points at a "file:///" URL.  For example, execute the following javascript from a web page:

var x = window.open("http://www.thisisamadeupdomain4321.com:9876","bogusWin")

After the page load fails, the Safari error page is displayed:



Because of how Safari displays its error pages, we now have a window in the local "file:///" zone, but triggered by a load of an "http://" URL.  

Exploiting this behavior:

The vulnerability exists in the fact that this window, with full access to the "file:///" URLs, can be navigated by its parent window, when it should be blocked by a security check.  We can navigate this child window by setting x.window.location, and can set it to any URL including those in "file:///".  All we need in order to exploit it is some malicious HTML to point at using a "file:///" URL.  

There are numerous ways to get a local path to point at remotely originating payload content.  It's a little silly, but here's what I did.  This proved to be reliable enough for a proof of concept that works with the default configuration of Safari on Mac OS X:

1. When you launch the proof of concept by clicking the submit button on thing.html, we start by mounting a disk image with our local payload
2. If you have the default Safari settings, Mac OS X will mount the disk image in /Volumes/poc
3. We then attempt to load http://127.0.0.1:7/ in a new window.  Since port 7 (tcp echo) is on the port block list, an error page in the "file://" zone is loaded
4. We then tell the window that is displaying this error page that it should load a different file, /Volumes/poc/ds.html.  The fact that we can do this is the bug itself
5. Safari loads the Javascript in ds.html in the local zone.  The Javascript opens a SQLite3 file, /var/db/dslocal/indices/Default/index, because it contains the names of local users
6. We use Javascript to rip some usersnames from the SQLite3 database file using a regular expression, and then attempt to access the target file in each of their accounts
7. File contents (if we have the right user) and usernames are communicated back to the parent window using postMessage (direct communications between the windows)

Some questions and answers about this bug:

1. Does the pop-up blocker stop this bug? 
No.

2. What if I changed my preferences so that 'Open "safe" files' is not selected? Am I safe against this?
No.  The slightly more technical explanation for this answer is that my proof of concept is using a disk image just to get malicious content at a known local path.  There are a lot of other ways to accomplish this task.  For example, my original proof of concept used an "ftp://" URL, and triggered Finder.app to automatically mount the site in /Volumes.  I felt like setting up a public FTP server was a bit of a hassle so I switched to this easy and portable version.  There are also less obvious ways to do this, including methods that involve no user-visible clues that a malicious file is now accessible through a "file://" path.

3. Will the Mac OS X file quarantine feature stop this exploit?
No. While we're using a disk image that came from Safari and this file is therefore quarantined, we are not launching our payload.  Instead we are loading it from an active HTML document that has already been loaded into the "file://" zone.

4. Your proof of concept did not work for me. Does that mean I am not vulnerable?
No. In the proof of concept I'm basically grepping a binary SQLite3 database file with Javascript. I am going to assume this isn't a terribly portable way of grabbing usernames.  There are a bunch of other ways we could get the username of our visitor.  For example, my original version of this proof of concept used a rather simple method of gathering usernames by parsing monthly.out.  The monthly.out file is generated once a month and contains active usernames as part of system accounting.  I got annoyed that my proof of concept wouldn't work on a fresh system, so I switched to the new method.  Here's how I was grabbing usernames from the monthly.out file:

                function process_log(monthlog) {
                 var gathered_users=new Array();
                 var line_pos = 0;
                 var in_block = 0;
                 var month_lines = monthlog.split("\n");
                 for (line_pos=0;line_pos<=month_lines.length-1;line_pos++) {
                  if (month_lines[line_pos] == "Doing login accounting:") {
                        in_block=1;
                  } else if (in_block==1 && month_lines[line_pos] == "") {
                        in_block=0;
                  } else if (in_block) {
                        var user_line = month_lines[line_pos].split(/[\t ]/);
                        if (user_line[1].length>0 && user_line[1] != "total") {
                                // user_line[1] is a potential username
                                gathered_users[user_line[1]]=1;
                        }
                  }
                 }
                 var victim = "";
                 for (victim in gathered_users) {
                                do_something_bad(victim);
                 }
                }

[Note: I apologize about how gross that Javascript is, but I figured someone might have a use for it. You can replace the proof of concept i posted to use this one pretty easily.]

5. Will this work on Windows?
Good question.  I suspect that it will, and that using a UNC path you might be able to point at remote files for your payload.  However I have not had a chance to test this.  If you have a test system and a moment to port this to Windows, I'd appreciate it if you would post a comment.  I'd also like to know if you can launch programs from this local window by using document.location or by loading a JAR in the "file://" zone.  That could be interesting.

6. Is Safari the only browser affected?
No. Safari is the only browser that this proof of concept was designed to work with, but the bug itself is in WebKit and affects other WebKit-based browsers.  If you are testing your WebKit-based browser, remember that if you use a different URL scheme for things like these error pages, you may need to modify the test case to attempt to access resources using that scheme instead of "file://".

The proof-of-concept:

You should be able to uudecode this into a zip, that is ready to be dropped in a path
in your web server.


Thanks:

I would like to thank Cedric of the Apple Product Security team for the responsive and responsible handling of this issue.
I would also like to thank the Google Security Team for putting in the effort to harden WebKit against many of these issues and protect their users.
Also, thanks cstone.

Related reading:

Excellent work by the Google/Chrome guys:

This bug:
 Apple's advisory page, see CVE-2011-0167 in the latest Safari update

I found this bug a while ago -- before the following blog post was written, but I think people who find this kind of bug interesting would also be interested in lcamtuf's blog post: