mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/binary-exploitation/ios-exploiting/ios-physical-uaf-ios
This commit is contained in:
parent
af52d587ab
commit
8f6497d302
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,3 +11,4 @@ book
|
||||
book/*
|
||||
hacktricks-preprocessor.log
|
||||
hacktricks-preprocessor-error.log
|
||||
searchindex.js
|
||||
|
@ -3,54 +3,68 @@
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
## Atténuations des exploits iOS
|
||||
|
||||
- **Code Signing** in iOS works by requiring every piece of executable code (apps, libraries, extensions, etc.) to be cryptographically signed with a certificate issued by Apple. When code is loaded, iOS verifies the digital signature against Apple’s trusted root. If the signature is invalid, missing, or modified, the OS refuses to run it. This prevents attackers from injecting malicious code into legitimate apps or running unsigned binaries, effectively stopping most exploit chains that rely on executing arbitrary or tampered code.
|
||||
- **CoreTrust** is the iOS subsystem responsible for enforcing code signing at runtime. It directly verifies signatures using Apple’s root certificate without relying on cached trust stores, meaning only binaries signed by Apple (or with valid entitlements) can execute. CoreTrust ensures that even if an attacker tampers with an app after installation, modifies system libraries, or tries to load unsigned code, the system will block execution unless the code is still properly signed. This strict enforcement closes many post-exploitation vectors that older iOS versions allowed through weaker or bypassable signature checks.
|
||||
- **Data Execution Prevention (DEP)** marque des régions mémoire comme non-exécutables à moins qu'elles ne contiennent explicitement du code. Cela empêche les attaquants d'injecter du shellcode dans des régions de données (comme la stack ou le heap) et de l'exécuter, les forçant à recourir à des techniques plus complexes comme ROP (Return-Oriented Programming).
|
||||
- **ASLR (Address Space Layout Randomization)** randomise les adresses mémoire du code, des bibliothèques, de la stack et du heap à chaque démarrage. Cela rend beaucoup plus difficile pour un attaquant de prédire où se trouvent des instructions ou gadgets utiles, cassant de nombreuses chaînes d'exploit qui dépendent de dispositions mémoire fixes.
|
||||
- **KASLR (Kernel ASLR)** applique le même concept de randomisation au kernel iOS. En déplaçant l'adresse de base du kernel à chaque boot, il empêche les attaquants de localiser de manière fiable les fonctions ou structures du kernel, augmentant la difficulté des exploits au niveau kernel qui chercheraient à obtenir un contrôle complet du système.
|
||||
- **Kernel Patch Protection (KPP)** aussi connu sous le nom **AMCC (Apple Mobile File Integrity)** dans iOS, surveille en continu les pages de code du kernel pour s'assurer qu'elles n'ont pas été modifiées. Si toute altération est détectée — comme un exploit tentant de patcher des fonctions du kernel ou d'insérer du code malveillant — l'appareil panique et redémarre immédiatement. Cette protection rend les exploits persistants du kernel beaucoup plus difficiles, car les attaquants ne peuvent pas simplement hooker ou patcher les instructions du kernel sans provoquer un crash système.
|
||||
- **Kernel Text Readonly Region (KTRR)** est une fonctionnalité matérielle introduite sur les appareils iOS. Elle utilise le contrôleur mémoire du CPU pour marquer la section code (text) du kernel comme définitivement en lecture seule après le boot. Une fois verrouillée, même le kernel lui-même ne peut pas modifier cette région mémoire. Cela empêche les attaquants — et même le code privilégié — de patcher les instructions du kernel à l'exécution, fermant une grande classe d'exploits qui reposaient sur la modification directe du code du kernel.
|
||||
- **Pointer Authentication Codes (PAC)** utilisent des signatures cryptographiques intégrées dans des bits inutilisés des pointeurs pour vérifier leur intégrité avant utilisation. Lorsqu'un pointeur (comme une adresse de retour ou un pointeur de fonction) est créé, le CPU le signe avec une clé secrète ; avant la déréférence, le CPU vérifie la signature. Si le pointeur a été altéré, la vérification échoue et l'exécution s'arrête. Cela empêche les attaquants de forger ou réutiliser des pointeurs corrompus dans des exploits de corruption mémoire, rendant des techniques comme ROP ou JOP beaucoup plus difficiles à mettre en œuvre de façon fiable.
|
||||
- **Privilege Access never (PAN)** est une fonctionnalité matérielle qui empêche le kernel (mode privilégié) d'accéder directement à la mémoire user-space à moins d'activer explicitement l'accès. Cela stoppe les attaquants ayant obtenu une exécution de code kernel d'accéder facilement à la mémoire utilisateur pour escalader des privilèges ou voler des données sensibles. En faisant respecter une séparation stricte, PAN réduit l'impact des exploits kernel et bloque de nombreuses techniques d'escalade de privilèges courantes.
|
||||
- **Page Protection Layer (PPL)** est un mécanisme de sécurité iOS qui protège des régions critiques de la mémoire gérées par le kernel, en particulier celles liées au code signing et aux entitlements. Il applique des protections strictes en écriture via la MMU (Memory Management Unit) et des contrôles supplémentaires, garantissant que même du code kernel privilégié ne peut modifier arbitrairement des pages sensibles. Cela empêche les attaquants ayant obtenu une exécution au niveau kernel de manipuler des structures critiques pour la sécurité, rendant la persistance et les contournements de code signing significativement plus difficiles.
|
||||
|
||||
|
||||
## Physical use-after-free
|
||||
|
||||
Ceci est un résumé de l’article disponible sur [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html). De plus, des informations complémentaires sur des exploits utilisant cette technique se trouvent sur [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd)
|
||||
Ceci est un résumé du post disponible sur [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html). De plus, des informations complémentaires sur des exploits utilisant cette technique peuvent être trouvées sur [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd)
|
||||
|
||||
### Gestion de la mémoire dans XNU <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a>
|
||||
### Memory management in XNU <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a>
|
||||
|
||||
L’**espace d’adresses mémoire virtuelle** pour les process utilisateur sur iOS s’étend de **0x0 à 0x8000000000**. Cependant, ces adresses ne correspondent pas directement à la mémoire physique. Le **kernel** utilise des **page tables** pour traduire les adresses virtuelles en adresses **physiques** réelles.
|
||||
L'**espace d'adressage mémoire virtuelle** pour les processus utilisateur sur iOS s'étend de **0x0 à 0x8000000000**. Cependant, ces adresses ne correspondent pas directement à la mémoire physique. À la place, le **noyau** utilise des **tables de pages** pour traduire les adresses virtuelles en adresses **physiques** réelles.
|
||||
|
||||
#### Niveaux des page tables sur iOS
|
||||
#### Niveaux des tables de pages dans iOS
|
||||
|
||||
Les page tables sont organisées hiérarchiquement en trois niveaux :
|
||||
Les tables de pages sont organisées hiérarchiquement en trois niveaux :
|
||||
|
||||
1. **L1 Page Table (Level 1)** :
|
||||
* Chaque entrée représente une large plage de mémoire virtuelle.
|
||||
* Elle couvre **0x1000000000 bytes** (soit **256 GB**) de mémoire virtuelle.
|
||||
* Chaque entrée ici représente une large plage de mémoire virtuelle.
|
||||
* Elle couvre **0x1000000000 bytes** (ou **256 GB**) de mémoire virtuelle.
|
||||
2. **L2 Page Table (Level 2)** :
|
||||
* Une entrée représente une région plus petite de mémoire virtuelle, spécifiquement **0x2000000 bytes** (32 MB).
|
||||
* Une entrée ici représente une région plus petite de mémoire virtuelle, spécifiquement **0x2000000 bytes** (32 MB).
|
||||
* Une entrée L1 peut pointer vers une table L2 si elle ne peut pas mapper toute la région elle-même.
|
||||
3. **L3 Page Table (Level 3)** :
|
||||
* C’est le niveau le plus fin, où chaque entrée mappe une page mémoire de **4 KB**.
|
||||
* C'est le niveau le plus fin, où chaque entrée mappe une seule page mémoire de **4 KB**.
|
||||
* Une entrée L2 peut pointer vers une table L3 si un contrôle plus granulaire est nécessaire.
|
||||
|
||||
#### Mapper le virtuel vers le physique
|
||||
#### Mappage de la mémoire virtuelle vers la mémoire physique
|
||||
|
||||
* **Direct Mapping (Block Mapping)** :
|
||||
* Certaines entrées dans une page table mapent directement une plage d’adresses virtuelles vers une plage contiguë d’adresses physiques (comme un raccourci).
|
||||
* **Pointeur vers une child page table** :
|
||||
* Si un contrôle plus fin est requis, une entrée à un niveau (par ex. L1) peut pointer vers une **child page table** au niveau suivant (par ex. L2).
|
||||
* Certaines entrées d'une table de pages mappent directement une plage d'adresses virtuelles vers une plage contiguë d'adresses physiques (comme un raccourci).
|
||||
* **Pointer to Child Page Table** :
|
||||
* Si un contrôle plus fin est nécessaire, une entrée à un niveau (par ex. L1) peut pointer vers une **table de pages enfant** au niveau suivant (par ex. L2).
|
||||
|
||||
#### Exemple : résolution d’une adresse virtuelle
|
||||
#### Exemple : Mappage d'une adresse virtuelle
|
||||
|
||||
Supposons que vous accédiez à l’adresse virtuelle **0x1000000000** :
|
||||
Supposons que vous essayez d'accéder à l'adresse virtuelle **0x1000000000** :
|
||||
|
||||
1. **L1 Table** :
|
||||
* Le kernel vérifie l’entrée de la table L1 correspondant à cette adresse virtuelle. Si elle contient un **pointeur vers une L2 page table**, il se rend dans cette L2.
|
||||
* Le noyau vérifie l'entrée de la table L1 correspondant à cette adresse virtuelle. Si elle contient un **pointeur vers une table L2**, il se rend dans cette table L2.
|
||||
2. **L2 Table** :
|
||||
* Le kernel vérifie la L2 pour un mapping plus détaillé. Si cette entrée pointe vers une **L3 page table**, il continue vers celle-ci.
|
||||
* Le noyau vérifie la table L2 pour un mappage plus détaillé. Si cette entrée pointe vers une **table L3**, il poursuit dans cette table.
|
||||
3. **L3 Table** :
|
||||
* Le kernel consulte l’entrée finale de L3, qui pointe vers l’**adresse physique** de la page mémoire réelle.
|
||||
* Le noyau consulte l'entrée finale L3, qui pointe vers l'**adresse physique** de la page mémoire réelle.
|
||||
|
||||
#### Exemple de mapping d’adresses
|
||||
#### Exemple de mappage d'adresses
|
||||
|
||||
Si vous écrivez l’adresse physique **0x800004000** dans le premier index de la table L2, alors :
|
||||
Si vous écrivez l'adresse physique **0x800004000** dans le premier index de la table L2, alors :
|
||||
|
||||
* Les adresses virtuelles de **0x1000000000** à **0x1002000000** sont mappées vers les adresses physiques de **0x800004000** à **0x802004000**.
|
||||
* Il s’agit d’un **block mapping** au niveau L2.
|
||||
* Les adresses virtuelles de **0x1000000000** à **0x1002000000** se mappent vers les adresses physiques de **0x800004000** à **0x802004000**.
|
||||
* C'est un **block mapping** au niveau L2.
|
||||
|
||||
Alternativement, si l’entrée L2 pointe vers une table L3 :
|
||||
Alternativement, si l'entrée L2 pointe vers une table L3 :
|
||||
|
||||
* Chaque page de 4 KB dans la plage virtuelle **0x1000000000 -> 0x1002000000** serait mappée par des entrées individuelles dans la table L3.
|
||||
|
||||
@ -58,42 +72,42 @@ Alternativement, si l’entrée L2 pointe vers une table L3 :
|
||||
|
||||
Un **physical use-after-free** (UAF) se produit lorsque :
|
||||
|
||||
1. Un process **alloue** de la mémoire en lecture/écriture.
|
||||
2. Les **page tables** sont mises à jour pour mapper cette mémoire à une adresse physique spécifique accessible par le process.
|
||||
3. Le process **désalloue** (libère) la mémoire.
|
||||
4. Cependant, en raison d’un **bug**, le kernel **oublie de supprimer le mapping** des page tables, alors même qu’il marque la mémoire physique correspondante comme libre.
|
||||
5. Le kernel peut alors **réallouer cette mémoire physique "libérée"** pour d’autres usages, comme des données kernel.
|
||||
6. Comme le mapping n’a pas été supprimé, le process peut toujours **lire et écrire** cette mémoire physique.
|
||||
1. Un processus **alloue** de la mémoire en lecture/écriture.
|
||||
2. Les **tables de pages** sont mises à jour pour mapper cette mémoire à une adresse physique spécifique que le processus peut accéder.
|
||||
3. Le processus **désalloue** (libère) la mémoire.
|
||||
4. Cependant, à cause d'un **bug**, le noyau **oublie de supprimer le mapping** des tables de pages, même s'il marque la mémoire physique correspondante comme libre.
|
||||
5. Le noyau peut alors **réallouer cette mémoire physique "libérée"** à d'autres fins, par exemple pour des **données du kernel**.
|
||||
6. Comme le mapping n'a pas été supprimé, le processus peut toujours **lire et écrire** dans cette mémoire physique.
|
||||
|
||||
Cela signifie que le process peut accéder à des **pages de mémoire kernel**, qui peuvent contenir des données sensibles ou des structures, permettant potentiellement à un attaquant de **manipuler la mémoire kernel**.
|
||||
Cela signifie que le processus peut accéder à des **pages de la mémoire du kernel**, qui peuvent contenir des données ou structures sensibles, permettant potentiellement à un attaquant de **manipuler la mémoire du kernel**.
|
||||
|
||||
### IOSurface Heap Spray
|
||||
|
||||
Étant donné que l’attaquant ne peut pas contrôler quelles pages kernel spécifiques seront allouées à la mémoire libérée, il utilise une technique appelée **heap spray** :
|
||||
Puisque l'attaquant ne peut pas contrôler quelles pages kernel spécifiques seront allouées à la mémoire libérée, il utilise une technique appelée **heap spray** :
|
||||
|
||||
1. L’attaquant **crée un grand nombre d’objets IOSurface** dans la mémoire kernel.
|
||||
2. Chaque objet IOSurface contient une **valeur magique** dans l’un de ses champs, ce qui facilite son identification.
|
||||
3. Ils **scannent les pages libérées** pour voir si l’un de ces objets IOSurface a été placé sur une page libérée.
|
||||
4. Lorsqu’ils trouvent un objet IOSurface sur une page libérée, ils peuvent l’utiliser pour **lire et écrire la mémoire kernel**.
|
||||
1. L'attaquant **crée un grand nombre d'objets IOSurface** dans la mémoire kernel.
|
||||
2. Chaque objet IOSurface contient une **valeur magique** dans l'un de ses champs, ce qui facilite son identification.
|
||||
3. Il **scanne les pages libérées** pour voir si l'un de ces objets IOSurface a atterri sur une page libérée.
|
||||
4. Lorsqu'il trouve un objet IOSurface sur une page libérée, il peut l'utiliser pour **lire et écrire la mémoire du kernel**.
|
||||
|
||||
Plus d’infos à ce sujet sur [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups)
|
||||
Plus d'infos à ce sujet sur [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups)
|
||||
|
||||
> [!TIP]
|
||||
> Notez que les appareils iOS 16+ (A12+) apportent des mitigations matérielles (comme PPL ou SPTM) qui rendent les techniques de physical UAF beaucoup moins viables.
|
||||
> PPL applique des protections MMU strictes sur les pages liées au code signing, aux entitlements et aux données kernel sensibles : même si une page est réutilisée, les écritures depuis userland ou depuis du code kernel compromis vers des pages protégées par PPL sont bloquées.
|
||||
> Secure Page Table Monitor (SPTM) étend PPL en renforçant les mises à jour des page tables elles-mêmes. Il garantit que même du code kernel privilégié ne peut pas remapper silencieusement des pages libérées ou altérer des mappings sans passer par des vérifications sécurisées.
|
||||
> KTRR (Kernel Text Read-Only Region) verrouille la section code du kernel en lecture seule après le boot. Cela empêche toute modification à l’exécution du code kernel, fermant un vecteur d’attaque majeur sur lequel les exploits physical UAF s’appuient souvent.
|
||||
> De plus, les allocations `IOSurface` sont moins prévisibles et plus difficiles à mapper dans des régions accessibles par l’utilisateur, ce qui rend la technique de “scan de valeur magique” beaucoup moins fiable. Et `IOSurface` est désormais protégé par des entitlements et des restrictions de sandbox.
|
||||
> Sachez que les appareils iOS 16+ (A12+) apportent des mitigations matérielles (comme PPL ou SPTM) qui rendent les techniques de physical UAF bien moins viables.
|
||||
> PPL applique des protections MMU strictes sur les pages liées au code signing, aux entitlements et aux données sensibles du kernel, donc même si une page est réutilisée, les écritures depuis l'userland ou du code kernel compromis vers des pages protégées par PPL sont bloquées.
|
||||
> Secure Page Table Monitor (SPTM) étend PPL en renforçant les mises à jour des tables de pages elles-mêmes. Il garantit que même le code kernel privilégié ne peut pas remapper silencieusement des pages libérées ou altérer des mappings sans passer par des contrôles sécurisés.
|
||||
> KTRR (Kernel Text Read-Only Region), qui verrouille la section code du kernel en lecture seule après le boot. Cela empêche toute modification à l'exécution du code du kernel, fermant un vecteur d'attaque majeur sur lequel les exploits de physical UAF s'appuyaient souvent.
|
||||
> De plus, les allocations `IOSurface` sont moins prévisibles et plus difficiles à mapper dans des régions accessibles par l'utilisateur, ce qui rend la technique de scan de "valeur magique" beaucoup moins fiable. Et `IOSurface` est désormais protégée par des entitlements et des restrictions de sandbox.
|
||||
|
||||
### Step-by-Step Heap Spray Process
|
||||
### Processus de Heap Spray étape par étape
|
||||
|
||||
1. **Spray IOSurface Objects** : L’attaquant crée de nombreux objets IOSurface avec un identifiant spécial (« valeur magique »).
|
||||
2. **Scan Freed Pages** : Il vérifie si l’un des objets a été alloué sur une page libérée.
|
||||
3. **Read/Write Kernel Memory** : En manipulant les champs de l’objet IOSurface, il obtient la capacité d’effectuer des **lectures et écritures arbitraires** en mémoire kernel. Cela lui permet de :
|
||||
* Utiliser un champ pour **lire n’importe quelle valeur 32 bits** en mémoire kernel.
|
||||
* Utiliser un autre champ pour **écrire des valeurs 64 bits**, obtenant ainsi un primitive stable de **kernel read/write**.
|
||||
1. **Spray IOSurface Objects** : L'attaquant crée de nombreux objets IOSurface avec un identifiant spécial ("valeur magique").
|
||||
2. **Scan Freed Pages** : Il vérifie si l'un des objets a été alloué sur une page libérée.
|
||||
3. **Read/Write Kernel Memory** : En manipulant des champs de l'objet IOSurface, il obtient la capacité d'effectuer des **lectures et écritures arbitraires** dans la mémoire du kernel. Cela lui permet de :
|
||||
* Utiliser un champ pour **lire n'importe quelle valeur 32-bit** dans la mémoire du kernel.
|
||||
* Utiliser un autre champ pour **écrire des valeurs 64-bit**, obtenant un primitive stable de **kernel read/write**.
|
||||
|
||||
Générez des objets IOSurface avec la valeur magique IOSURFACE\_MAGIC pour les rechercher ensuite :
|
||||
Generate IOSurface objects with the magic value IOSURFACE\_MAGIC to later search for:
|
||||
```c
|
||||
void spray_iosurface(io_connect_t client, int nSurfaces, io_connect_t **clients, int *nClients) {
|
||||
if (*nClients >= 0x4000) return;
|
||||
@ -114,7 +128,7 @@ io_connect_t id = result.surface_id;
|
||||
}
|
||||
}
|
||||
```
|
||||
Rechercher des objets **`IOSurface`** dans une page physique libérée :
|
||||
Rechercher des objets **`IOSurface`** dans une page physique libérée:
|
||||
```c
|
||||
int iosurface_krw(io_connect_t client, uint64_t *puafPages, int nPages, uint64_t *self_task, uint64_t *puafPage) {
|
||||
io_connect_t *surfaceIDs = malloc(sizeof(io_connect_t) * 0x4000);
|
||||
@ -148,25 +162,25 @@ free(surfaceIDs);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
### Obtenir Kernel Read/Write avec IOSurface
|
||||
### Obtention d'un accès lecture/écriture au kernel avec IOSurface
|
||||
|
||||
Après avoir pris le contrôle d'un objet IOSurface dans kernel memory (mappé sur une page physique libérée accessible depuis userspace), nous pouvons l'utiliser pour **arbitrary kernel read and write operations**.
|
||||
Après avoir pris le contrôle d'un objet IOSurface dans la mémoire kernel (mappé sur une page physique libérée accessible depuis userspace), on peut l'utiliser pour des **opérations arbitraires de lecture et d'écriture kernel**.
|
||||
|
||||
**Champs clés dans IOSurface**
|
||||
|
||||
L'objet IOSurface contient deux champs cruciaux :
|
||||
L'objet IOSurface possède deux champs cruciaux :
|
||||
|
||||
1. **Use Count Pointer** : Permet un **32-bit read**.
|
||||
2. **Indexed Timestamp Pointer** : Permet un **64-bit write**.
|
||||
1. **Use Count Pointer** : Permet une **lecture 32-bit**.
|
||||
2. **Indexed Timestamp Pointer** : Permet une **écriture 64-bit**.
|
||||
|
||||
En écrasant ces pointeurs, nous les redirigeons vers des adresses arbitraires dans kernel memory, ce qui permet des capacités de read/write.
|
||||
En écrasant ces pointeurs, on les redirige vers des adresses arbitraires en mémoire kernel, activant des capacités de lecture/écriture.
|
||||
|
||||
#### 32-Bit Kernel Read
|
||||
#### Lecture 32-bit du kernel
|
||||
|
||||
Pour effectuer une lecture :
|
||||
|
||||
1. Écrasez le **use count pointer** pour qu'il pointe vers l'adresse cible moins un 0x14-byte offset.
|
||||
2. Utilisez la méthode `get_use_count` pour lire la valeur à cette adresse.
|
||||
1. Écraser le **use count pointer** pour qu'il pointe vers l'adresse cible moins un offset de 0x14 octets.
|
||||
2. Utiliser la méthode `get_use_count` pour lire la valeur à cette adresse.
|
||||
```c
|
||||
uint32_t get_use_count(io_connect_t client, uint32_t surfaceID) {
|
||||
uint64_t args[1] = {surfaceID};
|
||||
@ -184,11 +198,11 @@ iosurface_set_use_count_pointer(info.object, orig);
|
||||
return value;
|
||||
}
|
||||
```
|
||||
#### Écriture 64 bits du Kernel
|
||||
#### Écriture 64 bits dans le noyau
|
||||
|
||||
Pour effectuer une écriture :
|
||||
|
||||
1. Écraser le **indexed timestamp pointer** par l'adresse cible.
|
||||
1. Écrasez le **indexed timestamp pointer** pour qu'il pointe vers l'adresse cible.
|
||||
2. Utilisez la méthode `set_indexed_timestamp` pour écrire une valeur 64 bits.
|
||||
```c
|
||||
void set_indexed_timestamp(io_connect_t client, uint32_t surfaceID, uint64_t value) {
|
||||
@ -203,13 +217,13 @@ set_indexed_timestamp(info.client, info.surface, value);
|
||||
iosurface_set_indexed_timestamp_pointer(info.object, orig);
|
||||
}
|
||||
```
|
||||
#### Récapitulatif du flux d'exploit
|
||||
#### Exploit Flow Recap
|
||||
|
||||
1. **Trigger Physical Use-After-Free**: Des pages libérées sont disponibles pour réutilisation.
|
||||
2. **Spray IOSurface Objects**: Allouer de nombreux objets IOSurface avec une "magic value" unique dans kernel memory.
|
||||
3. **Identify Accessible IOSurface**: Localiser un IOSurface sur une page libérée que vous contrôlez.
|
||||
4. **Abuse Use-After-Free**: Modifier des pointeurs dans l'objet IOSurface pour permettre des **kernel read/write** arbitraires via les méthodes IOSurface.
|
||||
|
||||
Avec ces primitives, l'exploit fournit des **32-bit reads** contrôlés et des **64-bit writes** vers kernel memory. Des étapes de jailbreak supplémentaires pourraient impliquer des primitives de read/write plus stables, ce qui peut nécessiter de contourner des protections additionnelles (par ex., PPL sur les nouveaux appareils arm64e).
|
||||
Avec ces primitives, l'exploit fournit des **32-bit reads** contrôlés et des **64-bit writes** vers kernel memory. Les étapes de jailbreak supplémentaires pourraient impliquer des primitives de read/write plus stables, qui peuvent nécessiter de contourner des protections additionnelles (par ex., PPL sur les appareils arm64e plus récents).
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -1,69 +1,147 @@
|
||||
# Détection du phishing
|
||||
# Détection du Phishing
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## Introduction
|
||||
|
||||
Pour détecter une tentative de phishing, il est important de **comprendre les techniques de phishing qui sont utilisées de nos jours**. Sur la page principale de ce post, vous pouvez trouver cette information, donc si vous n'êtes pas au courant des techniques utilisées aujourd'hui, je vous recommande d'aller sur la page principale et de lire au moins cette section.
|
||||
Pour détecter une tentative de phishing, il est important de **comprendre les techniques de phishing utilisées aujourd'hui**. Sur la page parente de ce post, vous pouvez trouver cette information ; si vous n'êtes pas au courant des techniques actuellement utilisées, je vous recommande d'aller sur la page parente et de lire au moins cette section.
|
||||
|
||||
Ce post est basé sur l'idée que les **attaquants essaieront d'une manière ou d'une autre de mimer ou d'utiliser le nom de domaine de la victime**. Si votre domaine s'appelle `example.com` et que vous êtes phishé en utilisant un nom de domaine complètement différent pour une raison quelconque comme `youwonthelottery.com`, ces techniques ne vont pas le révéler.
|
||||
Ce post repose sur l'idée que les **attaquants vont essayer d'une manière ou d'une autre de mimer ou d'utiliser le nom de domaine de la victime**. Si votre domaine s'appelle `example.com` et que vous êtes victime d'un phishing en utilisant un nom de domaine complètement différent pour une raison quelconque comme `youwonthelottery.com`, ces techniques ne le découvriront pas.
|
||||
|
||||
## Variations de noms de domaine
|
||||
## Variations de nom de domaine
|
||||
|
||||
Il est assez **facile** de **détecter** ces **tentatives de phishing** qui utiliseront un **nom de domaine similaire** dans l'email.\
|
||||
Il suffit de **générer une liste des noms de phishing les plus probables** qu'un attaquant pourrait utiliser et de **vérifier** s'ils sont **enregistrés** ou simplement vérifier s'il y a une **IP** qui l'utilise.
|
||||
Il est assez **facile** de **détecter** ces tentatives de **phishing** qui utiliseront un **nom de domaine similaire** à l'intérieur de l'email.\
|
||||
Il suffit de **générer une liste des noms de phishing les plus probables** qu'un attaquant pourrait utiliser et de **vérifier** s'ils sont **enregistrés** ou simplement vérifier s'il y a une **IP** qui les utilise.
|
||||
|
||||
### Trouver des domaines suspects
|
||||
|
||||
À cette fin, vous pouvez utiliser l'un des outils suivants. Notez que ces outils effectueront également des requêtes DNS automatiquement pour vérifier si le domaine a une IP qui lui est assignée :
|
||||
Pour cela, vous pouvez utiliser n'importe lequel des outils suivants. Notez que ces outils effectueront également automatiquement des requêtes DNS pour vérifier si le domaine a une IP assignée :
|
||||
|
||||
- [**dnstwist**](https://github.com/elceef/dnstwist)
|
||||
- [**urlcrazy**](https://github.com/urbanadventurer/urlcrazy)
|
||||
|
||||
Astuce : Si vous générez une liste de candidats, alimentez-la également dans vos logs de résolveur DNS pour détecter les recherches **NXDOMAIN depuis l'intérieur de votre organisation** (des utilisateurs essayant d'atteindre une faute de frappe avant que l'attaquant ne l'enregistre réellement). Sinkhole ou pre-block ces domaines si la politique le permet.
|
||||
|
||||
### Bitflipping
|
||||
|
||||
**Vous pouvez trouver une brève explication de cette technique sur la page principale. Ou lire la recherche originale dans** [**https://www.bleepingcomputer.com/news/security/hijacking-traffic-to-microsoft-s-windowscom-with-bitflipping/**](https://www.bleepingcomputer.com/news/security/hijacking-traffic-to-microsoft-s-windowscom-with-bitflipping/)
|
||||
Vous pouvez trouver une courte explication de cette technique sur la page parente. Ou lisez la recherche originale sur [**https://www.bleepingcomputer.com/news/security/hijacking-traffic-to-microsoft-s-windowscom-with-bitflipping/**](https://www.bleepingcomputer.com/news/security/hijacking-traffic-to-microsoft-s-windowscom-with-bitflipping/)
|
||||
|
||||
Par exemple, une modification de 1 bit dans le domaine microsoft.com peut le transformer en _windnws.com._\
|
||||
**Les attaquants peuvent enregistrer autant de domaines de bit-flipping que possible liés à la victime pour rediriger les utilisateurs légitimes vers leur infrastructure**.
|
||||
Par exemple, une modification d'1 bit dans le domaine microsoft.com peut le transformer en _windnws.com._\
|
||||
**Les attaquants peuvent enregistrer autant de domaines bit-flipping que possible liés à la victime pour rediriger les utilisateurs légitimes vers leur infrastructure**.
|
||||
|
||||
**Tous les noms de domaine de bit-flipping possibles devraient également être surveillés.**
|
||||
**Tous les noms de domaine possibles issus du bit-flipping devraient également être surveillés.**
|
||||
|
||||
Si vous devez aussi prendre en compte les lookalikes homoglyphes/IDN (par exemple, mélange de caractères Latin/Cyrillique), vérifiez :
|
||||
|
||||
{{#ref}}
|
||||
homograph-attacks.md
|
||||
{{#endref}}
|
||||
|
||||
### Vérifications de base
|
||||
|
||||
Une fois que vous avez une liste de noms de domaine potentiellement suspects, vous devriez **les vérifier** (principalement les ports HTTP et HTTPS) pour **voir s'ils utilisent un formulaire de connexion similaire** à celui du domaine de la victime.\
|
||||
Vous pourriez également vérifier le port 3333 pour voir s'il est ouvert et exécute une instance de `gophish`.\
|
||||
Il est également intéressant de savoir **quel âge a chaque domaine suspect découvert**, plus il est jeune, plus il est risqué.\
|
||||
Vous pouvez également obtenir des **captures d'écran** de la page web suspecte HTTP et/ou HTTPS pour voir si elle est suspecte et dans ce cas **y accéder pour examiner plus en profondeur**.
|
||||
Une fois que vous avez une liste de noms de domaines potentiellement suspects, vous devriez les **vérifier** (principalement les ports HTTP et HTTPS) pour **voir s'ils utilisent un login form similaire** à celui d'un des domaines de la victime.\
|
||||
Vous pouvez aussi vérifier le port 3333 pour voir s'il est ouvert et exécute une instance de `gophish`.\
|
||||
Il est aussi intéressant de savoir **quel âge a chaque domaine suspect découvert** ; plus il est jeune, plus il est risqué.\
|
||||
Vous pouvez également obtenir des **captures d'écran** de la page HTTP et/ou HTTPS suspecte pour voir si elle est suspecte et, le cas échéant, **y accéder pour l'examiner plus en détail**.
|
||||
|
||||
### Vérifications avancées
|
||||
|
||||
Si vous souhaitez aller un peu plus loin, je vous recommande de **surveiller ces domaines suspects et de rechercher d'autres** de temps en temps (tous les jours ? cela ne prend que quelques secondes/minutes). Vous devriez également **vérifier** les **ports** ouverts des IPs associées et **rechercher des instances de `gophish` ou d'outils similaires** (oui, les attaquants font aussi des erreurs) et **surveiller les pages web HTTP et HTTPS des domaines et sous-domaines suspects** pour voir s'ils ont copié un formulaire de connexion des pages web de la victime.\
|
||||
Pour **automatiser cela**, je vous recommande d'avoir une liste de formulaires de connexion des domaines de la victime, d'explorer les pages web suspectes et de comparer chaque formulaire de connexion trouvé dans les domaines suspects avec chaque formulaire de connexion du domaine de la victime en utilisant quelque chose comme `ssdeep`.\
|
||||
Si vous avez localisé les formulaires de connexion des domaines suspects, vous pouvez essayer d'**envoyer des identifiants non valides** et **vérifier s'il vous redirige vers le domaine de la victime**.
|
||||
Si vous voulez aller un cran plus loin, je vous recommande de **surveiller ces domaines suspects et d'en rechercher d'autres** de temps en temps (tous les jours ? ça ne prend que quelques secondes/minutes). Vous devriez aussi **vérifier** les **ports** ouverts des IPs associées et **rechercher des instances de `gophish` ou d'outils similaires** (oui, les attaquants font aussi des erreurs) et **surveiller les pages web HTTP et HTTPS des domaines et sous-domaines suspects** pour voir s'ils ont copié un login form des pages de la victime.\
|
||||
Pour **automatiser cela**, je recommanderais d'avoir une liste des login forms des domaines de la victime, d'explorer (spider) les pages web suspectes et de comparer chaque login form trouvé dans les domaines suspects avec chaque login form du domaine de la victime en utilisant quelque chose comme `ssdeep`.\
|
||||
Si vous avez localisé les login forms des domaines suspects, vous pouvez essayer d'**envoyer des credentials bidon** et **vérifier si cela vous redirige vers le domaine de la victime**.
|
||||
|
||||
## Noms de domaine utilisant des mots-clés
|
||||
---
|
||||
|
||||
La page principale mentionne également une technique de variation de nom de domaine qui consiste à mettre le **nom de domaine de la victime à l'intérieur d'un domaine plus grand** (par exemple, paypal-financial.com pour paypal.com).
|
||||
### Hunting by favicon and web fingerprints (Shodan/ZoomEye/Censys)
|
||||
|
||||
### Transparence des certificats
|
||||
Many phishing kits reuse favicons from the brand they impersonate. Internet-wide scanners compute a MurmurHash3 of the base64-encoded favicon. You can generate the hash and pivot on it:
|
||||
|
||||
Il n'est pas possible d'adopter l'approche précédente "Brute-Force", mais il est en fait **possible de détecter de telles tentatives de phishing** également grâce à la transparence des certificats. Chaque fois qu'un certificat est émis par une CA, les détails sont rendus publics. Cela signifie qu'en lisant la transparence des certificats ou même en la surveillant, il est **possible de trouver des domaines qui utilisent un mot-clé dans leur nom**. Par exemple, si un attaquant génère un certificat pour [https://paypal-financial.com](https://paypal-financial.com), en voyant le certificat, il est possible de trouver le mot-clé "paypal" et de savoir qu'un email suspect est utilisé.
|
||||
Exemple Python (mmh3):
|
||||
```python
|
||||
import base64, requests, mmh3
|
||||
url = "https://www.paypal.com/favicon.ico" # change to your brand icon
|
||||
b64 = base64.encodebytes(requests.get(url, timeout=10).content)
|
||||
print(mmh3.hash(b64)) # e.g., 309020573
|
||||
```
|
||||
- Interroger Shodan: `http.favicon.hash:309020573`
|
||||
- Avec des outils : regardez des outils communautaires comme favfreak pour générer des hashes et des dorks pour Shodan/ZoomEye/Censys.
|
||||
|
||||
Le post [https://0xpatrik.com/phishing-domains/](https://0xpatrik.com/phishing-domains/) suggère que vous pouvez utiliser Censys pour rechercher des certificats affectant un mot-clé spécifique et filtrer par date (uniquement les certificats "nouveaux") et par l'émetteur CA "Let's Encrypt" :
|
||||
Remarques
|
||||
- Les favicons sont réutilisés ; considérez les correspondances comme des pistes et validez le contenu et les certs avant d'agir.
|
||||
- Combinez avec domain-age et des heuristiques par mot-clé pour une meilleure précision.
|
||||
|
||||
### Recherche de télémétrie d'URL (urlscan.io)
|
||||
|
||||
`urlscan.io` stocke des captures d'écran historiques, le DOM, les requêtes et les métadonnées TLS des URLs soumises. Vous pouvez chasser l'usurpation de marque et les clones :
|
||||
|
||||
Exemples de requêtes (UI ou API):
|
||||
- Trouver des sites ressemblants en excluant vos domaines légitimes : `page.domain:(/.*yourbrand.*/ AND NOT yourbrand.com AND NOT www.yourbrand.com)`
|
||||
- Trouver des sites hotlinkant vos ressources : `domain:yourbrand.com AND NOT page.domain:yourbrand.com`
|
||||
- Restreindre aux résultats récents : ajouter `AND date:>now-7d`
|
||||
|
||||
Exemple d'API:
|
||||
```bash
|
||||
# Search recent scans mentioning your brand
|
||||
curl -s 'https://urlscan.io/api/v1/search/?q=page.domain:(/.*yourbrand.*/%20AND%20NOT%20yourbrand.com)%20AND%20date:>now-7d' \
|
||||
-H 'API-Key: <YOUR_URLSCAN_KEY>' | jq '.results[].page.url'
|
||||
```
|
||||
Dans le JSON, pivoter sur :
|
||||
- `page.tlsIssuer`, `page.tlsValidFrom`, `page.tlsAgeDays` pour repérer les certificats très récents utilisés par des lookalikes
|
||||
- `task.source` valeurs comme `certstream-suspicious` pour associer les découvertes à la surveillance CT
|
||||
|
||||
### Âge du domaine via RDAP (scriptable)
|
||||
|
||||
RDAP renvoie des événements de création lisibles par machine. Utile pour repérer les **domaines nouvellement enregistrés (NRDs)**.
|
||||
```bash
|
||||
# .com/.net RDAP (Verisign)
|
||||
curl -s https://rdap.verisign.com/com/v1/domain/suspicious-example.com | \
|
||||
jq -r '.events[] | select(.eventAction=="registration") | .eventDate'
|
||||
|
||||
# Generic helper using rdap.net redirector
|
||||
curl -s https://www.rdap.net/domain/suspicious-example.com | jq
|
||||
```
|
||||
Enrichissez votre pipeline en étiquetant les domaines selon des tranches d'âge d'enregistrement (par ex. <7 jours, <30 jours) et priorisez le triage en conséquence.
|
||||
|
||||
### Empreintes TLS/JAx pour repérer l'infrastructure AiTM
|
||||
|
||||
Le credential-phishing moderne utilise de plus en plus des reverse proxies Adversary-in-the-Middle (AiTM) (par ex. Evilginx) pour voler des session tokens. Vous pouvez ajouter des détections côté réseau :
|
||||
|
||||
- Enregistrez les empreintes TLS/HTTP (JA3/JA4/JA4S/JA4H) à l'egress. Certaines builds d'Evilginx ont été observées avec des valeurs JA4 client/server stables. Alarmez sur des empreintes known-bad uniquement comme signal faible et confirmez toujours avec le contenu et le renseignement sur le domaine.
|
||||
- Enregistrez de manière proactive les métadonnées des certificats TLS (issuer, nombre de SAN, usage de wildcard, validité) pour les hôtes lookalike découverts via CT ou urlscan et corrélez avec l'âge DNS et la géolocalisation.
|
||||
|
||||
> Note : Traitez les empreintes comme enrichissement, pas comme unique critère de blocage ; les frameworks évoluent et peuvent randomiser ou obfusquer.
|
||||
|
||||
### Domain names using keywords
|
||||
|
||||
La page parente mentionne aussi une technique de variation de nom de domaine consistant à insérer le **nom de domaine de la victime à l'intérieur d'un domaine plus grand** (par ex. paypal-financial.com pour paypal.com).
|
||||
|
||||
#### Certificate Transparency
|
||||
|
||||
Il n'est pas possible d'appliquer l'approche "Brute-Force" précédente mais il est en réalité **possible de déceler de telles tentatives de phishing** aussi grâce à la certificate transparency. Chaque fois qu'un certificat est émis par une CA, les détails sont rendus publics. Cela signifie qu'en lisant la certificate transparency ou même en la surveillant, il est **possible de trouver des domaines qui utilisent un mot-clé à l'intérieur de leur nom**. Par exemple, si un attaquant génère un certificat pour [https://paypal-financial.com](https://paypal-financial.com), en voyant le certificat il est possible de trouver le mot-clé "paypal" et de savoir qu'un domaine suspect est utilisé.
|
||||
|
||||
Le post [https://0xpatrik.com/phishing-domains/](https://0xpatrik.com/phishing-domains/) suggère d'utiliser Censys pour rechercher des certificats contenant un mot-clé précis et filtrer par date (seulement les certificats "new") et par CA issuer "Let's Encrypt":
|
||||
|
||||
.png>)
|
||||
|
||||
Cependant, vous pouvez faire "la même chose" en utilisant le web gratuit [**crt.sh**](https://crt.sh). Vous pouvez **rechercher le mot-clé** et **filtrer** les résultats **par date et CA** si vous le souhaitez.
|
||||
Cependant, vous pouvez faire "la même chose" en utilisant le service web gratuit [**crt.sh**](https://crt.sh). Vous pouvez **rechercher le mot-clé** et **filtrer** les résultats **par date et par CA** si vous le souhaitez.
|
||||
|
||||
.png>)
|
||||
|
||||
En utilisant cette dernière option, vous pouvez même utiliser le champ Matching Identities pour voir si une identité du domaine réel correspond à l'un des domaines suspects (notez qu'un domaine suspect peut être un faux positif).
|
||||
Avec cette dernière option, vous pouvez même utiliser le champ Matching Identities pour voir si une identité du domaine réel correspond à l'un des domaines suspects (notez qu'un domaine suspect peut être un faux positif).
|
||||
|
||||
**Une autre alternative** est le projet fantastique appelé [**CertStream**](https://medium.com/cali-dog-security/introducing-certstream-3fc13bb98067). CertStream fournit un flux en temps réel de certificats nouvellement générés que vous pouvez utiliser pour détecter des mots-clés spécifiés en (quasi) temps réel. En fait, il existe un projet appelé [**phishing_catcher**](https://github.com/x0rz/phishing_catcher) qui fait exactement cela.
|
||||
**Une autre alternative** est le projet fantastique [**CertStream**](https://medium.com/cali-dog-security/introducing-certstream-3fc13bb98067). CertStream fournit un flux en temps réel des certificats nouvellement générés que vous pouvez utiliser pour détecter des mots-clés en (quasi) temps réel. En fait, il existe un projet appelé [**phishing_catcher**](https://github.com/x0rz/phishing_catcher) qui fait exactement cela.
|
||||
|
||||
### **Nouveaux domaines**
|
||||
Conseil pratique : lors du triage des hits CT, priorisez les NRDs, les registrars non fiables/inconnus, les WHOIS en privacy-proxy, et les certs avec des temps `NotBefore` très récents. Maintenez une allowlist de vos domaines/marques possédés pour réduire le bruit.
|
||||
|
||||
**Une dernière alternative** est de rassembler une liste de **domaines nouvellement enregistrés** pour certains TLDs ([Whoxy](https://www.whoxy.com/newly-registered-domains/) fournit ce service) et **vérifier les mots-clés dans ces domaines**. Cependant, les longs domaines utilisent généralement un ou plusieurs sous-domaines, donc le mot-clé n'apparaîtra pas dans le FLD et vous ne pourrez pas trouver le sous-domaine de phishing.
|
||||
#### **New domains**
|
||||
|
||||
**Une dernière alternative** est de rassembler une liste de **domaines nouvellement enregistrés** pour certains TLDs ([Whoxy](https://www.whoxy.com/newly-registered-domains/) fournit ce service) et **vérifier la présence de mots-clés dans ces domaines**. Cependant, les domaines longs utilisent souvent un ou plusieurs sous-domaines ; par conséquent le mot-clé n'apparaîtra pas dans le FLD et vous ne pourrez pas trouver le sous-domaine de phishing.
|
||||
|
||||
Heuristique additionnelle : traitez certains **file-extension TLDs** (par ex. `.zip`, `.mov`) avec une suspicion accrue dans les alertes. Ceux-ci sont souvent confondus avec des noms de fichiers dans les leurres ; combinez le signal TLD avec des brand keywords et l'âge NRD pour une meilleure précision.
|
||||
|
||||
## References
|
||||
|
||||
- urlscan.io – Référence de l'API Search: https://urlscan.io/docs/search/
|
||||
- APNIC Blog – JA4+ network fingerprinting (includes Evilginx example): https://blog.apnic.net/2023/11/22/ja4-network-fingerprinting/
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -24,13 +24,15 @@
|
||||
/* 2 — load a single index (remote → local) */
|
||||
async function loadIndex(remote, local, isCloud=false){
|
||||
let rawLoaded = false;
|
||||
try {
|
||||
const r = await fetch(remote,{mode:'cors'});
|
||||
if (!r.ok) throw new Error('HTTP '+r.status);
|
||||
importScripts(URL.createObjectURL(new Blob([await r.text()],{type:'application/javascript'})));
|
||||
rawLoaded = true;
|
||||
} catch(e){ console.warn('remote',remote,'failed →',e); }
|
||||
if(!rawLoaded){
|
||||
if(remote){
|
||||
try {
|
||||
const r = await fetch(remote,{mode:'cors'});
|
||||
if (!r.ok) throw new Error('HTTP '+r.status);
|
||||
importScripts(URL.createObjectURL(new Blob([await r.text()],{type:'application/javascript'})));
|
||||
rawLoaded = true;
|
||||
} catch(e){ console.warn('remote',remote,'failed →',e); }
|
||||
}
|
||||
if(!rawLoaded && local){
|
||||
try { importScripts(abs(local)); rawLoaded = true; }
|
||||
catch(e){ console.error('local',local,'failed →',e); }
|
||||
}
|
||||
@ -40,13 +42,41 @@
|
||||
return data;
|
||||
}
|
||||
|
||||
async function loadWithFallback(remotes, local, isCloud=false){
|
||||
if(remotes.length){
|
||||
const [primary, ...secondary] = remotes;
|
||||
const primaryData = await loadIndex(primary, null, isCloud);
|
||||
if(primaryData) return primaryData;
|
||||
|
||||
if(local){
|
||||
const localData = await loadIndex(null, local, isCloud);
|
||||
if(localData) return localData;
|
||||
}
|
||||
|
||||
for (const remote of secondary){
|
||||
const data = await loadIndex(remote, null, isCloud);
|
||||
if(data) return data;
|
||||
}
|
||||
}
|
||||
|
||||
return local ? loadIndex(null, local, isCloud) : null;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const MAIN_RAW = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/refs/heads/master/searchindex.js';
|
||||
const CLOUD_RAW = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/refs/heads/master/searchindex.js';
|
||||
const htmlLang = (document.documentElement.lang || 'en').toLowerCase();
|
||||
const lang = htmlLang.split('-')[0];
|
||||
const mainReleaseBase = 'https://github.com/HackTricks-wiki/hacktricks/releases/download';
|
||||
const cloudReleaseBase = 'https://github.com/HackTricks-wiki/hacktricks-cloud/releases/download';
|
||||
|
||||
const mainTags = Array.from(new Set([`searchindex-${lang}`, 'searchindex-en', 'searchindex-master']));
|
||||
const cloudTags = Array.from(new Set([`searchindex-${lang}`, 'searchindex-en', 'searchindex-master']));
|
||||
|
||||
const MAIN_REMOTE_SOURCES = mainTags.map(tag => `${mainReleaseBase}/${tag}/searchindex.js`);
|
||||
const CLOUD_REMOTE_SOURCES = cloudTags.map(tag => `${cloudReleaseBase}/${tag}/searchindex.js`);
|
||||
|
||||
const indices = [];
|
||||
const main = await loadIndex(MAIN_RAW , '/searchindex.js', false); if(main) indices.push(main);
|
||||
const cloud= await loadIndex(CLOUD_RAW, '/searchindex-cloud.js', true ); if(cloud) indices.push(cloud);
|
||||
const main = await loadWithFallback(MAIN_REMOTE_SOURCES , '/searchindex.js', false); if(main) indices.push(main);
|
||||
const cloud= await loadWithFallback(CLOUD_REMOTE_SOURCES, '/searchindex-cloud.js', true ); if(cloud) indices.push(cloud);
|
||||
|
||||
if(!indices.length){ postMessage({ready:false, error:'no-index'}); return; }
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user