7.1 KiB
iOS WebViews
WebViews types
WebViews are in-app browser components for displaying interactive web content. They can be used to embed web content directly into an app's user interface. iOS WebViews support JavaScript execution by default, so script injection and Cross-Site Scripting attacks can affect them.
-
****UIWebView: UIWebView is deprecated starting on iOS 12 and should not be used. It shouldn't be used. JavaScript cannot be disabled.
-
****WKWebView: This is the appropriate choice for extending app functionality, controlling displayed content.
- JavaScript is enabled by default but thanks to the
javaScriptEnabled
property ofWKWebView
, it can be completely disabled, preventing all script injection flaws. - The
JavaScriptCanOpenWindowsAutomatically
can be used to prevent JavaScript from opening new windows, such as pop-ups. - The
hasOnlySecureContent
property can be used to verify resources loaded by the WebView are retrieved through encrypted connections. WKWebView
implements out-of-process rendering, so memory corruption bugs won't affect the main app process.
- JavaScript is enabled by default but thanks to the
-
****SFSafariViewController: It ****should be used to provide a generalized web viewing experience. These WebViews can be easily spotted as they have a characteristic layout which includes the following elements:
- A read-only address field with a security indicator.
- An Action
"**Share**"
button. - A Done button, back and forward navigation buttons, and a "Safari" button to open the page directly in Safari.
- JavaScript cannot be disabled in
SFSafariViewController
and this is one of the reasons why the usage ofWKWebView
is recommended when the goal is extending the app's user interface. SFSafariViewController
also shares cookies and other website data with Safari.- The user's activity and interaction with a
SFSafariViewController
are not visible to the app, which cannot access AutoFill data, browsing history, or website data. - According to the App Store Review Guidelines,
SFSafariViewController
s may not be hidden or obscured by other views or layers.
Discovering WebViews Configuration
Static Analysis
UIWebView
$ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$"
489 0x0002fee9 0x10002fee9 9 10 (5.__TEXT.__cstring) ascii UIWebView
896 0x0003c813 0x0003c813 24 25 () ascii @_OBJC_CLASS_$_UIWebView
1754 0x00059599 0x00059599 23 24 () ascii _OBJC_CLASS_$_UIWebView
WKWebView
$ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$"
490 0x0002fef3 0x10002fef3 9 10 (5.__TEXT.__cstring) ascii WKWebView
625 0x00031670 0x100031670 17 18 (5.__TEXT.__cstring) ascii unwindToWKWebView
904 0x0003c960 0x0003c960 24 25 () ascii @_OBJC_CLASS_$_WKWebView
1757 0x000595e4 0x000595e4 23 24 () ascii _OBJC_CLASS_$_WKWebView
Alternatively you can also search for known methods of these WebView classes. For example, search for the method used to initialize a WKWebView [`init(frame:configuration:)`](https://developer.apple.com/documentation/webkit/wkwebview/1414998-init)
:
$ rabin2 -zzq ./WheresMyBrowser | egrep "WKWebView.*frame"
0x5c3ac 77 76 __T0So9WKWebViewCABSC6CGRectV5frame_So0aB13ConfigurationC13configurationtcfC
0x5d97a 79 78 __T0So9WKWebViewCABSC6CGRectV5frame_So0aB13ConfigurationC13configurationtcfcTO
0x6b5d5 77 76 __T0So9WKWebViewCABSC6CGRectV5frame_So0aB13ConfigurationC13configurationtcfC
0x6c3fa 79 78 __T0So9WKWebViewCABSC6CGRectV5frame_So0aB13ConfigurationC13configurationtcfcTO
Testing JavaScript Configuration
For WKWebView
s, as a best practice, JavaScript should be disabled unless it is explicitly required. To verify that JavaScript was properly disabled search the project for usages of WKPreferences
and ensure that the javaScriptEnabled
property is set to false
:
let webPreferences = WKPreferences()
webPreferences.javaScriptEnabled = false
If only having the compiled binary you can search for this in it:
$ rabin2 -zz ./WheresMyBrowser | grep -i "javascriptenabled"
391 0x0002f2c7 0x10002f2c7 17 18 (4.__TEXT.__objc_methname) ascii javaScriptEnabled
392 0x0002f2d9 0x10002f2d9 21 22 (4.__TEXT.__objc_methname) ascii setJavaScriptEnabled
Testing OnlySecureContent
In contrast to UIWebView
s, when using WKWebView
s it is possible to detect mixed content HTTP content loaded from a HTTPS page
. By using the method hasOnlySecureContent
it can be verified whether all resources on the page have been loaded through securely encrypted connections.
In the compiled binary:
$ rabin2 -zz ./WheresMyBrowser | grep -i "hasonlysecurecontent"
You can also search in the source code or strings the string "http://". However, this doesn't necessary means that there is a mixed content issue. Learn more about mixed content in the MDN Web Docs.
Dynamic Analysis
It's possible to inspect the heap via ObjC.choose()
to find instances of the different types of WebViews and also search for the properties javaScriptEnabled
and hasonlysecurecontent
:
{% code title="webviews_inspector.js" %}
ObjC.choose(ObjC.classes['UIWebView'], {
onMatch: function (ui) {
console.log('onMatch: ', ui);
console.log('URL: ', ui.request().toString());
},
onComplete: function () {
console.log('done for UIWebView!');
}
});
ObjC.choose(ObjC.classes['WKWebView'], {
onMatch: function (wk) {
console.log('onMatch: ', wk);
console.log('URL: ', wk.URL().toString());
},
onComplete: function () {
console.log('done for WKWebView!');
}
});
ObjC.choose(ObjC.classes['SFSafariViewController'], {
onMatch: function (sf) {
console.log('onMatch: ', sf);
},
onComplete: function () {
console.log('done for SFSafariViewController!');
}
});
ObjC.choose(ObjC.classes['WKWebView'], {
onMatch: function (wk) {
console.log('onMatch: ', wk);
console.log('javaScriptEnabled:', wk.configuration().preferences().javaScriptEnabled());
}
});
ObjC.choose(ObjC.classes['WKWebView'], {
onMatch: function (wk) {
console.log('onMatch: ', wk);
console.log('hasOnlySecureContent: ', wk.hasOnlySecureContent().toString());
}
});
{% endcode %}
Load it with:
frida -U com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js
onMatch: <WKWebView: 0x1508b1200; frame = (0 0; 320 393); layer = <CALayer: 0x1c4238f20>>
hasOnlySecureContent: false