3

SPOILER ALERT: This question contains an answer to one of the problems from the Google's XSS Challenge! Please stop reading further if you're not interested in knowing the answer right now.


I'm able to get pass the level 4 of the challenge, however, I still don't know how exactly the exploit is working. The following is the code from Google's XSS challenge - Level 4:

<!doctype html>
<html>
  <head>
    <!-- Internal game scripts/styles, mostly boring stuff -->
    <script src="/static/game-frame.js"></script>
    <link rel="stylesheet" href="/static/game-frame-styles.css" />

    <script>
      function startTimer(seconds) {
        seconds = parseInt(seconds) || 3;
        setTimeout(function() { 
          window.confirm("Time is up!");
          window.history.back();
        }, seconds * 1000);
      }
    </script>
  </head>
  <body id="level4">
    <img src="/static/logos/level4.png" />
    <br>
    <img src="/static/loading.gif" onload="startTimer('{{ timer }}');" />
    <br>
    <div id="message">Your timer will execute in {{ timer }} seconds.</div>
  </body>
</html>

Basically, they are using Django framework (which uses a bunch of security measure against XSS). The variable timer carries the input from the user. The goal of this activity is to alert a message by sending a payload which can bypass Django's XSS security.

I'm able to alert a message using one of the following payloads:

');alert('xss

OR

3') || alert('1

I'm able to clear the level using the above payloads but I'm still not sure where exactly the alert() method is being called? In the onload handler OR within the startTimer() method?

I'm confused because if I check the source HTML of the page after submitting the payload, Django is encoding the payload:

<html>
  <head>
    <!-- Internal game scripts/styles, mostly boring stuff -->
    <script src="/static/game-frame.js"></script>
    <link rel="stylesheet" href="/static/game-frame-styles.css" />

    <script>
      function startTimer(seconds) {
        seconds = parseInt(seconds) || 3;
        setTimeout(function() { 
          window.confirm("Time is up!");
          window.history.back();
        }, seconds * 1000);
      }
    </script>
  </head>
  <body id="level4">
    <img src="/static/logos/level4.png" />
    <br>
    <img src="/static/loading.gif" onload="startTimer('&#39;);alert(&#39;xss');" />
    <br>
    <div id="message">Your timer will execute in &#39;);alert(&#39;xss seconds.</div>
  </body>
</html>

4 Answers 4

1

What happens is that the HTML output is:

<img src="/static/loading.gif" onload="startTimer('{{ timer }}');" />

{{ timer }} is a variable which gets "translated" to whatever the ?timer GET parameter is. So suppose we have a legit parameter: ?timer=3. It gets "translated" to:

<img src="/static/loading.gif" onload="startTimer('3');" />

But what if you put some code in timer? 3');alert('xss');//

<img src="/static/loading.gif" onload="startTimer('3');alert('xss');//')" />

As you can see, the logo loads, and once it loads, the onload callback is executed. There's the vulnerability.

P.S. it's not in startTimer itself, because it parseInts the variable, and it removes everything but numbers, essentially 3'); ... becoming in 3.

6
  • But the HTML source code shows that it's onload="startTimer('&#39;);alert(&#39;xss');" which means &#39;);alert(&#39;xss is passed to startTimer as a string. Commented Jun 9, 2014 at 16:16
  • 1
    @RahilArora No, it’s not. The actual attribute value of onload would be startTimer('');alert('xss'); as the &#39; are replaced by the character ' they are referring to.
    – Gumbo
    Commented Jun 9, 2014 at 16:32
  • @Gumbo: So, why do we encode them if they are being replaced by the browser with the actual chars? Commented Jun 9, 2014 at 16:46
  • @RahilArora Django encodes them because you could also have used attr='{{ timer }}'.
    – Gumbo
    Commented Jun 9, 2014 at 16:48
  • 1
    @RahilArora No. Django only escapes certain characters that are special in HTML as they are used to delimit certain parts that represent a context in which the containing data is treated differently. Like quoted HTML attribute values. However, Django doesn’t know whether these parts are interpreted as something other than HTML, like the attribute value of onclick is interpreted as JavaScript.
    – Gumbo
    Commented Jun 9, 2014 at 17:23
1

The problem is that strings inside HTML attribute values interpret &#39; as equivalent to '.

So while your smart escaping turns the input 3') || alert('1 into 3&#39;) || alert(&#39;1, so the img element in the page becomes:

<img src="./test_files/loading.gif" onload="startTimer('3&#39;) || alert(&#39;1');">

your browser treats this as equivalent to:

<img src="./test_files/loading.gif" onload="startTimer('3') || alert('1');">

and the XSS works.

The reason it does this is so when you want to say have <img src="image" alt="O&#39;Malley"> the alt text reads as O'Malley.

7
  • Attribute tags?
    – Gumbo
    Commented Jun 9, 2014 at 16:33
  • So, does that mean the XSS prevention using output encoding will not work if a user input is directly used as a part of an HTML attribute? Commented Jun 9, 2014 at 16:37
  • @RahilArora - XSS prevention will prevent you from escaping out of the attribute tag. E.g., if you had <img src="{{ somevar }}"> you wouldn't be able to escape to get to <img src="#" onload="alert('XSS')">. The problem in this case is inserting untrusted input into the middle of a JS function in an HTML attribute.
    – dr jimbob
    Commented Jun 9, 2014 at 16:43
  • Ok. But we encode something so that the browser cannot execute that particular code, right? Then why is still executing the part which is encoding? Commented Jun 9, 2014 at 17:15
  • 2
    @RahilArora The output is escaped to be in HTML context, not in JavaScript context. To use that value in javascript, the quotes should be replaced with \', not HTML entities.
    – David
    Commented Jun 9, 2014 at 17:25
1

I see why the XSS worked! One of the section in the OWASP XSS cheat sheet says:

HTML entity encoding is okay for untrusted data that you put in the body of the HTML document, such as inside a tag. It even sort of works for untrusted data that goes into attributes, particularly if you're religious about using quotes around your attributes. But HTML entity encoding doesn't work if you're putting untrusted data inside a tag anywhere, or an event handler attribute like onmouseover, or inside CSS, or in a URL. So even if you use an HTML entity encoding method everywhere, you are still most likely vulnerable to XSS. You MUST use the escape syntax for the part of the HTML document you're putting untrusted data into. That's what the rules below are all about.

In this case, the user input is being fed into an event handler, which will treat it as a JS instead of HTML. And, the input is being escaped in HTML context (not in JS context). Therefore, JS will treat startTimer('3&#39;) || alert(&#39;1'); as startTimer('') || alert('1'); and will simply run this script.

PS: JS escaping might have prevented the attack.

0

Your alert is executing right in the onload because alert cant be a parameter value. Like this it gets executed instantly.

1
  • But it can reach the method as a param string and become a part of the statement in the method. Like: seconds = parseInt('3') || alert('1') || 3 Commented Jun 9, 2014 at 15:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .