4

I have a pretty complicated case where I try to perform a stored XSS attack when uploading a jpg file with malicious filename.

Whitespace is being filtered but it seems single and double quotes along with < > are not filtered. Moreover slash / and backslash \ seems to be prohibited.

User controlled input is a filename; this filename is first passed to a js script that builds JSON data in the following way (user-controlled filename is represented as INJECT_HERE).

<script>
var foo = JSON.parse('{"x":1,"id":"2","myarr":[{"x1":"1"}],"x2":[{"id1":3,"id2":"15","name":"INJECT_HERE","path":"\/uploads\/pics\/1\/INJECT_HERE","b":"1"}]}')
</script>

Then the above variable is used to pass the user supplied input to an HTML element. The filename (through foo.myarr.x2.name) is passed to an href attribute (with vue-js and v-bind) and the result html looks like this:

<div href="https://localhost/INJECT_HERE" class="foobar"></div>

How can I break out of both contexts at the same time to perform a stored XSS? Is it feasible or I try something that can't be done?

  • Since the payload is already in javascript, couldn't you break out using the following payload: '); alert(1); this would trigger at least a reflected XSS, for persistent XSS I doubt this will work because it will break the JSON.parse syntax and not render the HTML element. – Jeroen - IT Nerdbox May 31 '18 at 11:22
  • Where is the data injected into the string passed to JSON.parse? Is it server side or client side? – Anders May 31 '18 at 11:59
  • @Jeroen-ITNerdbox I need to upload a valid jpg file with .jpg extension. Also I need to escape both JSON.parse syntax and the href attribute inside the div element – XII May 31 '18 at 12:08
  • @Anders the JS code posted is located in the response body of the upload request. – XII May 31 '18 at 12:09
  • Check. And how does the value from foo end up in the href attribute? The attribute is set using JS, like link.setAttribute("href", foo.bar)? – Anders May 31 '18 at 12:12
4

You can't do it. Vue uses the DOM to set the href attrib, so it's not hard-coded from the server in a way that could fool the HTML parser. It's simply impossible to set an attrib with the DOM in a way that "breaks out" of the attrib, or starts a new attrib as though you had raw HTML source; all invalid characters will be logically escaped, even if the raw values in the dev tools make it look like the attack should work.

If you did any of the templating on the server (perhaps in an attempt to pre-render) then it's a problem, but as-is, you're safe.

note that data: and javascript: urls could still be an issue, but you've probably already got protection from that setup, given your apparent level of expertise.

EDIT: responding to comment

consider a simple page that uses the DOM to inject content:

<html> 
   <a id=a href="#">xss</a>
   <script>a.href='"><script>alert(123)<\/script>';</script>
</html>

If you look at the developer tools inspect view, you might see something scary like this:

<a id="a" href=""><script>alert(123)</script>">xss</a>

It appears there's a <script> tag inside the <a> tag, and indeed, if you copy that into a blank page and view it, it WILL alert. However, it's not actually a problem because the developer tools are playing a visual trick. The actual contents of the element when serialized are more like this:

<a id="a" href="&quot;&gt;&lt;script&gt;alert(123)&lt;/script&gt;">xss</a>

That's what I meant by the DOM auto-escaping any arbitrary content; as long as you use the DOM to set an attrib, there's no way to "bust out" of the attrib with any content. That doesn't mean that an attrib with side effect, like onmouseover can't do you wrong, just that you can't set onmouseover by adjusting an href.

That data: and javascript: url protocol is well-discussed elsewhere, but be aware it a way to link to arbitrary content documents (data:) and JS code (jacascript:).

For example, both of these alert "666" (mark of the beast) when clicked:

Javascript: <a href=javascript:alert(666)>xss</a>

Data: <a href=data:text/html,%3Cscript%3Ealert%28666%29%3C/script%3E>xss</a>

Both can be used in place of an http(s) url, so make sure to guard against that, especially javascript: because it doesn't need a slash to make code executable.

  • Thank you for your answer; can you elaborate a bit more on the "all invalid characters will be logically escaped, even if the raw values in the dev tools make it look like the attack should work." and on "note that data: and javascript: urls could still be an issue, but you've probably already got protection from that setup, given your apparent level of expertise." – XII Jun 1 '18 at 4:07
0

If the whole JSON.parse line is being generated on the server (rather than, say, calling JSON.parse on a variable containing the result of an XHR), and single- and double-quote marks are allowed (plus ( ) and at least one of + - ;) this is easy. Your content is already being evaluated inside a script block, why are you worrying about what happens after the script finishes? You should already have executed your payload by then!

An example filename, using + for string concatenation: badfile"'+alert('xss')+' This will generate a completely nonsense (and invalid) JSON string, but you don't care; your code executes (twice) when the JS engine tries to concatenate the strings, before JSON.parse is even called!

<script>
var foo = JSON.parse('{"x":1,"id":"2","myarr":[{"x1":"1"}],"x2":[{"id1":3,"id2":"15","name":"badfile"'+alert('xss')+'","path":"\/uploads\/pics\/1\/badfile"'+alert('xss')+'","b":"1"}]}')
</script>
-1

This doesn't seem possible at all. When a file is selected for upload, it just stays there until processed. So, for JavaScript to get the filename, it must first select the input and get the attribute value or textContent, which is returned as string. Thus, you cannot break out of JSON.parse.

And, as you stated in the comment, it's setting the href attribute by appending to the base URL. Thus, you cannot also break out of href attribute. You can only manipulate the value of href attribute but cannot also change base_url.

So, in this particular case, it's not possible to trigger XSS in anyway.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.