Pages

Friday, June 15, 2012

Layer3 DOM XSS in Latest jquery - Etsy

Turning Useless Self DOM XSS into a treat!

During my research and study over DOM XSS and developing my own detection technique, I found this interesting case which a DOM XSS undetected by any DOM XSS scanner I came across triggered, so I decided to share and provide a quick overview of how I took this self layer 3 DOM XSS into a Working and exploitable DOM XSS.
it all started with this small code:

1
2
3
4
<script type="text/javascript">
Etsy.loader.require('jquery.ba-hashchange');
Etsy.loader.require('help');
</script>

This is Going to be a long one but this covers:

  • DOM XSS.
  • Logic Flow Exploitation .
  • JQuery Vulnerability (Latest release - Unpatched yet).


Final Conclusions:
1. 1st conclusion(easy) the main javascript layer loads a second dynamic block (covered)

2. the payload goes through the dynamic code block and inside gets through a filter to avoid DOM XSS (appears as Self-Only).
3. the layer 2 code block loads another layer(3) this time it is jquery, and then exectue a function that is vulnerable to DOM XSS(latest version 1.72 and older versions as well).

Self DOM XSS?

using the help section search feature I could trigger a very simple XSS by typing the payload in the search box and hit search X_X,This is pretty lame. 
(once I used a payload directly in the url it got filtered without trigger)
We need the victim to be careless enough to actually type the payload and click the search button. I consider this situation as almost worthless and low risk Self XSS,
so I thought this might be useless and not exploitable.

Strange Behavior (not so fast...):

But, that is when I noticed a strange behavior! Look at the picture below:
 (using a payload of <img src=1 onerror=javascript......>)



Something strange?

If you do not see it you should gather yourself and look more carefully next time you think you covered the security holes and were about to report: not exploitable or low risk vulnerability.



What is so strange?


what you can see in the picture is that the XSS triggered (<img src=1> TAG) and for some reason the payload "disappeared" from the URL Query right?

Oh wait, was it??? No!
It was only filtered! the query is now changed to "http://site.com/help/2643#/afsddddddd/1" where did that "/1" came from? what is going on?
let us run more tests:
Look what happens when I used this URL:
"http://site.com/help/2643#/String     2ndString/1"

The filter turned it into the following:
http://site.com/help/2643#/String/2ndString/1

Now that is definitely worth a deeper look at the code loaded as layer 2:


Filter Details (YES It is vulnerable):

The Filter was checking for location.hash, and replaced any space with a "forward slash" to separate the query string as well as replacing any potential XSS HTML Tag with NULL using a regex.

seems right, but, There was a HUGE Security Hole.

Final Conclusions again:
1. 1st conclusion(easy) the main javascript layer loads a second dynamic block (covered).

2. the payload goes through the dynamic code block and inside gets through a filter to avoid DOM XSS (appears as Self-Only) (covered).
3. the layer 2 code block loads another layer(3) this time it is jquery, and then execute a function that is vulnerable to DOM XSS(latest version 1.72 and older versions as well).

HUGE Security Hole in legitimate XSS Filter:


This is where the magic happened! there is a javascript code block condition, this condition determines if the user is actually using the search feature and should be checked for DOM XSS using the layer 2 code block javascript code or not.
after researching all the relevant code in the javascript filter block, it appeared that exploitation is pretty simple!
legitimate behavior:
http://site.com/help/2643#/PAYLOAD/1

So once the search string starts with Forward Slash "/" the condition match, the system consider this a legitimate safe search, and the filter will replace any payload with NULL then pass filtered payload to a vulnerable jquery function.

Remember! This is a DOM event that executes jquery!

So what had to be done is:
1. Insert a Forward slash into the global URI (Before the hash sign).


2. Then insert the payload into the client-side only URI (Right after the hash sign without a slash)

Logic flow Vulnerability:
This way I managed to Trick the system to pass the Forward Slash Condition, and cause the filter to fail and step over the regex to the next stage of passing my payload directly into the vulnerable jquery function!

Final Payload:
http://site.com/help/2643/#<img src=1 onerror=javascript:alert('EXPLOITABLE!')>




but why is this vulnerable?


Vulnerable DOM XSS CODE - 

JQUERY(line 10):



 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
(function(){
        o=$("#attachment-shim");
        k=$("#file-form");
        var a="iframe"+(new Date).getTime();
        g=$('<iframe id="'+a+'" name="'+a+'" />').hide();//<iframe id="iframe1334892619493" name="iframe1334892619493" />

        k.attr("target",a).after(g);
        o.bind("change",q);
        if(a=window.location.hash.substring(1)) // sets a to "<img src=1 onerror=javascript:alert(1)>"
if($('a[name|="'+a+'"]')[0]) { // GAME OVER we got our Executer "a[name|=\"<img src=1 onerror=javascript:alert(1)>\"]" (later will be parsed with jquery " for(s.innerHTML=m[1]+l+m[2];o--;) "
            a=$('a[name|="'+a+'"]').parents("div.topic");
            a.find("ol span").removeClass("bottom");
            a.find(".view-less").show();
            a.find(".view-all").hide()
        }
        else m();
        f=true;
        topic_box=
        $("#topic");
        topic_box_val=topic_box.val();
        if(topic_box_val!=0||$("#message").val()!=""){
            d=true;
            h=topic_box_val;
            n()
        }
        
    })()
});

inside this "IF" Condition(line 10) there is a call to a vulnerable jquery function:

if($('a[name|="'+a+'"]')[0])


Which executes a function in jquery code that include the following line:
for(s.innerHTML=m[1]+l+m[2];o--;)

after bypassing all of these conditions our payload will be stored in "l" variable like this

l = "<img src=1 onerror=javascript:alert('DOM XSS')>"
and as a result of the for loop, will be injected into the innerHTML of the page triggering the XSS.

After I reported I noticed that, I never knew but it seems like there is an opened ticket in jquery bugs,
Reference: http://bugs.jquery.com/ticket/9521

Final Conclusions again:
1. 1st conclusion(easy) the main javascript layer loads a second dynamic block (covered).
2. the payload goes through the dynamic code block and inside gets through a filter to avoid DOM XSS (appears as Self-Only) (covered).
3. the layer 2 code block loads another layer(3) this time it is jquery, and then execute a function that is vulnerable to DOM XSS(latest version 1.72 and older versions as well) (covered).


It is always fun when web applications are open for a vulnerability report allowing me to research over these very interesting security flows that requires combining all the pieces together.


Thank you etsy!
Etsy Thank you list: http://www.etsy.com/help/article/2463

5 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Can you clarify the title of your article that this is a flaw in the Etsy site's use of jQuery? It is well known that passing untrusted data to jQuery's $() method is unsafe. This research did not uncover a new unknown issue with jQuery itself.

    -Dave

    ReplyDelete
  3. Ben: Thanks again for reaching out to us on this.

    For those interested, we pushed a fix for the vulnerability on May 1st. We think responsibly disclosed security research is awesome and our vulnerability reporting page is available at http://www.etsy.com/help/article/2463

    - Zane, Etsy security team

    ReplyDelete
  4. DOMXss have been a very difficult task to deal with because developers do not think that they may add security issues in the Javascript code.

    I tried a couple of new tools like "Dominator" (I don't remember the url but you can search it on google) and they seems to work. In particular this one. Reading the guide it should get similar results semi-automatically.

    ReplyDelete
  5. Thanks a ton for this – love the info and agree with your perspective. However many others will not, so thanks for speaking up. Nice blog, well done!

    ReplyDelete