Stealing From Password Managers with XSS

Introduction

If you aren’t familiar with password managers, take a look at the wikipedia article. In short, they let you securely manage all your passwords. Typically they will randomly generate secure passwords for you that you don’t need to remember, and then store them in an encrypted data store. You end up only needing to remember the password for the encrypted data store.

There are different types of password managers; some of them are available as desktop applications or browser extensions; some store the encrypted data locally on disk, some store the data in “the cloud”.

I use both dashlane and 1password for managing my password. The other day, after reinstalling dashlane, I noticed it had installed a browser extension without my permission. I decided to play with it for a bit before ultimately nuking it from my computer.

Browser Autofill

The core functionality of a password manager is to securely retrieve a complicated password and let you enter it on a website. Password managers can’t force you to type your password in manually because that’s slow and annoying, so instead they you can either copy and paste the password or let the browser extension automatically fill it in for you. I’ll be taking a look at the browser autofill option.

image

But wait - how did Dashlane know I was on the Grubhub login page? Surely it can tell that I’m on the login page before stuffing my username and password into the input boxes, right?

Nah. It turns out that Dashlane is happy to insert my credentials into any input box asking for a username and password - whether or not it’s on the login page. All it can do is confirm that the domain matches. If a page on grubhub.com is asking for credentials, all Dashlane knows is that it has grubhub credentials and that it can fill out the form. It can’t verify that grubhub is knowingly asking for credentials or that the credentials will be used to log you in. And if you think about it, that makes sense. There’s no way for a browser or browser extension to determine what page you’re on. There are too many factors to rely on the URL or the page’s contents (different languages, ajax/iframe based logins, etc) so for the sake of convenience it can only do assume that every page is a login page.

This raises an interesting point: how can we take advantage of how friendly these browser extensions are?

XSS like it’s 1999

We’ll need to revisit XSS: the age old trick we can use to serve our malicious content on a vulnerable website. Traditionally, XSS is used to steal data like authentication cookies, CSRF nonces, or perform actions on behalf of the user. In our case, the exploitation will start the same way as with any other XSS, but then we do something a little different. For example, to steal a user’s password for google.com:

Seems pretty straightforward. This attack is useful for a number of reasons. With a traditional payload, cookies can be a little tedious to work with. Sessions may be tied to IP addresses, can be terminated at any time, and may raise suspicion if reused - all this assuming there’s no HttpOnly on the cookie to begin with. Having plaintext passwords is much more flexible than having a cookie. You can start a session whenever you’re ready, or in the case of a red team exercise, use the password to pivot laterally.

For example let’s say you have an XSS in some wiki software commonly used by companies. Using the technique outlined above with the XSS, you can begin stealing passwords from employees who use password managers’ autofill feature. If the company uses something like LDAP, Kerberos, or Active Directory, you’ve just turned an XSS into the most useful tool for pivoting. You can now access all sorts of other systems for free.

So let’s take a look at what a payload for this might look like. I’ve set up a bogus login page on my site.

<h1>The Real Login</h1>
<form id="" method="POST">
Username: <input type="text" name="username" /><br />
Password: <input type="password" name="password" /><br />
<input type="submit" value="gö" />
</form>

I “logged in” and saved my credentials to Dashlane once prompted. Since this is my website and I can do whatever I want, I created another page that simulates an XSS payload. In the real world this would ideally be a stored XSS.

<script>
    function x() {
        document.getElementById('creds').innerHTML = document.getElementById('username').value+':'+document.getElementById('password').value;
    }

    function timer() {
        setTimeout(x, 1000);
    }
</script>
<body onload="timer()">
    <h1 id="creds"></h1>
    <form id="" method="POST" style="visibility: hidden; position: absolute; top: -1000; left: 1000;">
        Username: <input type="text" name="username" id="username" /><br />
        Password: <input type="password" name="password" id="password" /><br />
        <input type="submit" value="gö" />
    </form>
</body>

In short, this creates a “hidden” form way off the screen so the victim doesn’t notice the login form on an unusual page. Once the page loads, we start a timer that after a second (enough time for a password manager to kick in) displays the user’s username and password in large text. In a real attack, you probably wouldn’t show the victim their password and just send it to yourself silently. This payload could also be made more reliable by checking multiple times more frequently and preventing the browser from navigating away.

image

Conclusion

Basically, password autofill sucks. The only way to defend against password theft is to not use it at all. The technique outlined here isn’t specific to any one password manager - it’ll affect anything that automatically fills in passwords for you, whether it’s your actual browser or a browser extension. Stealing a plaintext password is a simple way to make any existing XSS payload much more effective, and in some cases can make pivoting within internal networks easier.