Overview
Today I’ll illustrate how it’s possible to extract sensitive data via Clickjacking by taking advantage of some liberal framing behaviors in Firefox coupled with a X-Frame-Options:Allow header that forms an implicit trust relationship between two sites.
This Clickjacking POC takes advantage of several site and browser behaviors including:
- Etsy.com set an X-Frames-Options: Allow header when accessed directly from a search engine query result
- Microsoft Bing search engine allows framing
- Firefox allows view-source within an iframe
- Firefox allows drag/drop between iframes within the same document regardless of origin
Details
While doing some Clickjacking research, I noticed the Etsy website (https://www.etsy.com), which normally set an X-Frames-Options header of SameOrigin, instead returned a header value of Allow when accessed via a search engine query result. I figured this might be a clever way to bypass its otherwise strong Clickjacking protections.
While Google does not allow framing of its main search page, Bing does (not a big surprise). As a test, I loaded a Bing search query for “Etsy” within an iframe and by clicking on the search result, was able to frame Etsy.com.
Although this was a good start, there were actually a couple of obstacles to overcome.
- First, I could only navigate one-page-deep via the iframe – additional framed access was denied when clicking any link on the framed Etsy.com page.
- Second, I couldn’t frame any HTTPS pages, which are normally the target of a viable Clickjacking attack.
However, I knew from previous research that there was one piece of sensitive data passed in every Etsy response – the CSRF token. Accessing the token would only require access to the source of any single Etsy.com page of an authenticated user. Thankfully, this is possible in Firefox via a URL prefaced with “view-source”.
So, in order to get to the source of Etsy.com, I first tried to access it directly from the view-source of the Bing search query for “Etsy” (view-source:http://www.bing.com/search?q=etsy). Unfortunately, the source of Etsy.com would not load directly from the Bing results page. Overcoming this problem required multiple steps:
1) Load the framed Bing search results for Etsy
2) Click on the Etsy.com result and allow it to load in the frame
3) Load the view-source Bing search results for Etsy
4) Click on the Etsy.com result and the source now loads in the frame
Now that I could get the source of Etsy.com loaded within the frame, I had access to the tag containing the CSRF nonce of an authenticated user.
But how to extract it? For obvious security reasons, modern browsers prevent direct access from the parent document to data contained within an embedded iframe. However, Firefox
does allow access between two different iframes, regardless of the source domains! All I needed to do now was add a second iframe with a source that I controlled.
With all of this in mind, I put together the following POC in which an Etsy user is lured to a page promising a gift card prize (possibly delivered through a web link, phishing email, etc.):
First, the user is directed to the “malicious” page and reminded to login via a semi-transparent overlay. This serves a few purposes:
- Increase probability of success (by ensuring authentication and subsequent CSRF token issuance),
- Entice the user (by displaying the contest details under the overlay) and
- Convince the user that the page is secure (trusted Etsy login page is loaded in a separate window, no login credentials are collected on this site).
When the page loads, it also loads a hidden iframe with the Bing search results for “Etsy”
By clicking the “Okay, I’m logged in” button, the user is actually clicking through to the first Bing result for the Etsy home page.
To ensure the user clicks on the right location I placed the iframe within a div (sized just big enough to display the link) and positioned the iframe within the div so only the desired link is visible. The latter is accomplished by adjusting the top/left values of the iframe (i.e. top: -130px; left: 0px;).
Once the user clicks the hidden Bing search result link, the Etsy.com page is given a chance to load (to allow subsequent access to the page source), after which the hidden div and iframe are automatically repositioned for the next mouse click and a new Bing search is performed — this time for the source of Etsy.com.
The user is then presented with this gift card prize scenario.
By clicking on “Enter Now”, they are actually clicking through to the “view-source” entry for Etsy.com, which will now successfully load in the iframe.
Next, the iframe (now containing the source of Etsy.com) is automatically repositioned under a field where the user is asked to “scratch off” their hidden code.
This is merely designed to trick the user into highlighting their CSRF token contained within the HTML source, which has been positioned under the visible div.
After the user “scratches” off their prize code, some timed JavaScript writes a fake gift card code to the div which the user is then asked to drag to the “Decoder” box. Of course, thanks to another click-through event, they will actually be dragging the underlying highlighted CSRF nonce.
The “decoder box” is actually a second iframe loaded from another external source under my control, ready to catch the user-dragged CSRF nonce. Again, this is possible because Firefox tolerates drag/drop between two iframes regardless of origin. Upon drop, this second iframe automatically parses the csrf_nonce value.
It then automatically submits a hidden form that changes the user’s privacy settings on their behalf.
Here’s a look at the authenticated user’s privacy settings before and after the attack.
This POC was successfully tested up to Firefox 20.0.1.
I notified the Etsy Security Team of the issue and they have since implemented a fix. As a bonus, this was a qualifying bug under its bug bounty program!