XSS Attack Using Script, Style, and Image Tags

Learn how to infect a page using an XSS attack with script, style, or image tags. See how to replace page content with your own, even without JavaScript.

Daniel Gustaw

Daniel Gustaw

4 min read

XSS Attack Using Script, Style, and Image Tags

This article describes examples of XSS attacks. While using <script> tags is probably the most known vector, there are other possibilities. You can change the content of a website using an <img> tag or pure CSS.

This is educational material. Remember that hacking without authorization is illegal! :)

Website Code

To present the attack, we create a simple website based on PHP. While separating logic and view in code is generally best practice, for simplicity and to minimize lines of code we place all website code in index.php. To create a vulnerable website, it must save user input to a database and display it on screen without filtering.

For simplicity and clarity, we abandon a full database and use a JSON file instead. The first file of our project is db.json:

db.json

["First comment","Second one"]

To save comments sent via PHP script:

index.php

<?php
$comments = json_decode(file_get_contents('db.json'));

if($_SERVER["REQUEST_METHOD"] === "POST") {
    $comments[] = $_POST["comment"];
    file_put_contents('db.json', json_encode($comments));
}

Regardless of the request method, the script proceeds to display the form and the list of comments:

index.php

echo '<form action="" method="post">
    <input type="text" name="comment">
    <input type="submit" value="send">
</form>
<ul>';

foreach ($comments as $comment) {
    echo "<li>".$comment."</li>";
}
echo '</ul>';

You can start it by command:

php -S localhost:8000

The created website looks like the following:

It is fully functional, allowing users to add a comment, save it in JSON, and display the comment list. If users only intended to submit plain text, this would be the end of our story. But we must assume that at least one user will attempt to exploit the site. :)

How to Hack It?

This flow of data—saving on the server and displaying on the client—makes XSS attacks possible if input is not properly sanitized. XSS stands for Cross-Site Scripting, enabling attackers to inject client-side scripts into web pages viewed by other users.

Injected executable code is interpreted by the browser, not the server, so we cannot compromise the server directly, but we can manipulate client-side behavior. Potential benefits for attackers include:

The payload script can be stored on the server or included in a malicious link. In our case, we want to save the payload script into db.json by submitting it as a comment. Our goal is to change the content of the website to “Hacked by Daniel”. In each attack method presented below, the website will end up looking like this:

Script Tag

The simplest way is to append a <script> tag that dynamically changes the page content after loading. Try adding this comment:

<script>document.querySelector('html').innerHTML="Hacked By Daniel"</script>

This code selects the html root element—meaning the whole page—and replaces its content using innerHTML.

Style Tag

Another method works even if <script> tags are stripped and JavaScript is disabled in the browser:

<style>html::before {content: "Hacked By Daniel";} body {display: none;}</style>

We define two styling rules. The first tells the browser to append the text Hacked By Daniel before the document body. The second hides the body completely.

Image Tag

Blocking <script> and <style> tags in comments is not enough, because scripts can also be executed via event handlers in other HTML tags:

<img src=undefined onerror='document.querySelector("html").innerHTML="Hacked By Daniel"'>

This element references an invalid image URL. When loading fails, the browser immediately executes the JavaScript code inside the onerror attribute.

How to Defend?

To defend against this attack, we must sanitize user input and strip or escape HTML tags. We can update index.php as follows:

-      $comments[] = $_POST["comment"];
+      $comments[] = htmlspecialchars($_POST["comment"]);

After applying this fix, text entered into the form will be rendered literally on the page instead of being parsed as HTML tags:

Summary

We have shown simple examples of XSS attacks using different tags. Modern frameworks like Symfony or Laravel have built-in security mechanisms against XSS, but when writing raw PHP, always remember to use functions like htmlspecialchars.

Other articles

You can find interesting also.