5

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?

XII
  • 524
  • 1
  • 6
  • 14
  • 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 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
  • @Anders In the same response body I see something like :href="base_url + foo.bar" – XII May 31 '18 at 12:24
  • @Anders Moreover there are also v-for and v-bind attributes all over the place (I ain't really familiar with web technologies but it looks like vue-js) – XII May 31 '18 at 12:40
  • I think there is to little information here to say what is possible and what is not. But my first try woudl be naming the file `` and see if that works. – Anders May 31 '18 at 13:16
  • @Anders This was the first thing I tried but / is prohibited but also in this way you don't break out of the href content. Also what more information there is to provide ? – XII May 31 '18 at 13:24
  • You don't need to break out of the href context. The point is to break out of the quoted javascript string context inside your first script tag. Not sure you need to close the script tag, as long as you can somehow prevent syntax errors. – Anders May 31 '18 at 13:26
  • To exploit this, I think you need to study the code to understand exactly what is done with your data where - the full journey from filename to href attribute. When yyou have that nailed down, you can hopefully get help with the XSS here. – Anders May 31 '18 at 13:28
  • Oh wait, sorry... You probably can't pull off the – Anders May 31 '18 at 13:33

3 Answers3

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.

dandavis
  • 2,658
  • 10
  • 16
  • 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 01 '18 at 04: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>
CBHacking
  • 40,303
  • 3
  • 74
  • 98
-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.

1lastBr3ath
  • 909
  • 6
  • 13