Showing posts with label uiwebview. Show all posts
Showing posts with label uiwebview. Show all posts

Saturday, June 21, 2014

Browser Shredders Challenge #1

For some time now I haven't succeeded in triggering password autofill in any iOS browser from a downloaded HTML file (which would allow another easy way to steal passwords). There are no Same Origin Policy constraints for local HTML files, so it seems easy to just open the target website and read the password, but there are some problems:
- password autofill does not work in cross-domain frames in iOS browsers based on UIWebView
- calling window.open() overwrites the calling window, so I cannot control content while having the target website open
-  I didn't find any other way to set location.host or mainDocumentURL.host to target domain while still controlling the content

I guess there may be some solution which I overlooked, so here is the challenge for you:



A sample application with the code above - which you may run on iOS simulator - is available on GitHub. The task is to prepare a local HTML file (/tmp/challenge1.html) in such way that "You completed the challenge" line will get executed.

There is no reward unfortunately, apart from a humble "thank you" during our SyScan360 talk :-)

Exploring and Exploiting iOS Web Browsers - local HTML files

A quick summary of the possible methods for preventing UXSS when loading untrusted local HTML files into iOS UIWebView:

1. Load as plain text
This would probably break the planned functionality of the application, but you can always decide to use loadData method with mimeType text/plain and forget about all the HTML problems.

2. Content-Security-Policy headers
By using NSURLProtocol and connection:didReceiveResponse you can inject CSP headers to block JavaScript execution (also for file: URIs).
The only application implementing similar solution that I know of is currently Onion Browser. Cure53 found a way to bypass CSP in this browser, but this was due to the fact that file and data URIs were explicitly skipped before injecting the headers (it is a bit misleading that you can inject HTTP response headers for documents that are not requested through HTTP, but it really works).

3. HTML5 sandbox
Using iframe sandbox instead of CSP seems potentially less prone to bypasses, but it is a bit less convenient to implement. You first use loadHTMLString with baseURL=nil and a sandboxed iframe in content, then you run stringByEvaluatingJavaScriptFromString to point the iframe to the file you want to load.
This way your JavaScript can operate on the loaded file, but the loaded file cannot execute JavaScript (unless you allow-scripts or allow-top-navigation; any of them gives UXSS again).

4. Less-privileged baseURL
This is the most common solution used by many applications. Most of them use baseURL pointing to some domain related to the application, but an even better solution is to use about:blank. I already wrote about it before. This does not prevent JavaScript from running, but restores Same Origin Policy constraints.

Do you know other solutions? Or maybe you know how to break these above? Comments welcome!

Thursday, June 12, 2014

Exploring and Exploiting iOS Web Browsers - part 1

After our Hack In The Box talk, I thought it would be a good idea to explain some of the slides in more detail. I am currently playing with iOS8 and WKWebViews, which solve a lot of issues, but not all of them. More on this soon - for now, let's stay on with UIWebViews.

UIWebView is the component used to load web content into iOS applications - basically it's Mobile Safari with the UI stripped off, leaving just the viewport part. Some of the differences:

  1. UIWebView can only view a single document at a time (+ frames)
For example, links with target="_blank" will work as target="_top", and window.open() will work   more or less the same as location.assign().

  2. There is also no UI for error events
The user will not be asked for accepting invalid SSL certificate, will not be informed the connection to the given host failed, etc. However, JavaScript-initiated alerts and prompts are shown (and are not controllable through available API).

  3. There is an API to control UIWebView content and behaviour

(By the way, you may want to take a look at this presentation from OWASP Poland meeting.)

Browsers using UIWebViews have to add the user interface - this means at least address bar and error handling. Usually to do this programmers implement UIWebViewDelegate methods:
webViewDidStartLoad:
webViewDidFinishLoad:
webView:didFailLoadWithError:

The last one causes problems usually. The most common issue is lack of address bar update within this method. This may allow an attacker to spoof the address bar by forcing some kind of an error. Apart from events like invalid SSL certificate, NXDOMAIN DNS response or simply response timeout, this method is also called when document.write() function interrupts page load or replaces loaded page.

Lack of address bar update on webView:didFailLoadWithError: was the reason for about a half of the address bar spoofing vulnerabilities we reported to browser authors. Most of the second half was due to updating address bar too early - as soon as the navigation was initiated, before the document actually started loading into UIWebView ("loading loop" from the HITB presentation is an example). By combining both techniques, an attacker can be almost sure the address bar will point to whatever she/he wishes.

See you in the next part soon.

Sunday, February 09, 2014

JavaScript Off for iOS

While testing UIWebView on iOS I found out there is no option to turn off JavaScript for web content. Initially I tried to use HTML5 "sandbox" attribute do achieve this, but in practice Content Security Policy is a much more flexible solution. Injecting CSP headers into NSHTTPURLResponse seems to work even for file: and data: URL schemes.

A sample application testing above solution is available on GitHub, and a similar solution has recently made its way into Onion Browser as an experimental feature. I am looking forward to see how it's going to work in practice.

On a side note, Quick Look is another solution if you need to load a single HTML document with JavaScript turned off.

iOS UIWebView baseURL

UIWebView is one of the most popular components in Cocoa Touch library. It can be used to easily embed web content into iOS applications and - of course - to equally easily introduce Cross-Site Scripting vulnerabilities.

When loading content into webView on iOS, a programmer can choose one of three methods:

  • – loadData:MIMEType:textEncodingName:baseURL:
  • – loadHTMLString:baseURL:
  • – loadRequest:

Did you notice baseURL in the first two? This inconspicuous parameter is quite important when dealing with XSS.

Same Origin Policy works a bit different in iOS than we're used to in desktop browsers, namely file: and applewebdata: URLs have complete cross-origin access to any web resources, in addition to local file access. This means if you save untrusted HTML document locally and then call loadRequest method to load it from its file:/// path into UIWebView, you're simply creating a universal Cross-Site Scripting vulnerability. This document can now read all cookies stored by the application, call JavaScript across domains (including your corporate intranet), and do all that stuff Same Origin Policy usually prevents.

Alternatively to loading untrusted local file with loadRequest, you may first read its content and then use one of the two remaining methods: loadData or loadHTMLString. Unfortunately, by default UIWebView content is loaded with file/applewebdata privileges. This is where the baseURL comes in handy. Simply set the baseURL to "about:blank" URL and Same Origin Policy will prevent cross-origin access (as it probably should do by default, Apple?).

Of course this won't be enough if a webView contains sensitive data and untrusted content at the same time, or if you bridge JavaScript with Objective-C through special URLs catched in webView:shouldStartLoadWithRequest:request. In such cases you should remove all XSS vulnerabilities from your application, one by one.