Une vulnérabilité permettant l'exécution de code à distance a été découverte dans le framework Electron
mais elle peut être corrigée manuellement

Electron est un framework permettant de développer des applications multiplateformes de bureau avec des technologies web (Javascript, HTML et CSS). Initialement développé pour l'éditeur Atom de GitHub, Electron est maintenant utilisé pour créer des applications par des entreprises comme Microsoft, Facebook, Slack ou encore Docker. Son infrastructure (backend) est codée en node.js, et l'interface (frontend) est bâtie sur la base des outils chromium, la partie open source de Google Chrome. Electron est un logiciel libre open source développé par Github sous licence MIT. Il a notamment permis de développer les éditeurs de texte libres Atom de Github et Visual Studio Code de Microsoft.

Fin janvier, il a souffert d'une vulnérabilité qui aurait pu permettre à un intrus d'exécuter son propre code arbitraire sur l'ordinateur d'une victime. Une autre faille, numérotée CVE-2018-1000136, a été repérée par Brendan Scarvell, chercheur en sécurité informatique chez Trustwave. Elle affecte les versions d'Electron en dessous de 1.7.13, 1.8.4 ou 2.0.0-beta.3. Les applications Electron sont construites avec HTML, CSS et JavaScript. Une application Electron par défaut inclut l'accès non seulement à ses propres API, mais également l'accès à tous les modules intégrés de Node.js. Et puisque les applications Electron sont essentiellement des applications Web, elles sont susceptibles de subir des attaques de script intersite (souvent appelées XSS).

Nom : electron2.png
Affichages : 2645
Taille : 64,4 Ko

Certaines applications qui n'ont pas besoin d'accéder à Node l'ont désactivée par défaut. Mais ce que Scarvell a découvert est un moyen de le réactiver dans une circonstance particulière. Toutes les applications Electron ont un fichier de configuration dans lequel il y a un attribut appelé nodeIngration. Lorsque ce paramètre est défini sur false, l'accès à l'API et aux modules Node.js est désactivé par défaut. Il existe un autre attribut distinct appelé webviewTag. Cela contrôle le comportement de WebView, ce qui permet à une application Electron d'intégrer une page Web distincte. Si webviewTag est défini sur false, il désactive également nodeIngration s'il n'a pas été défini, implicitement par défaut à false.

Par défaut, Electron utilise également sa propre fonction window.open () qui crée une nouvelle instance de BrowserWindow. La fenêtre enfant héritera de toutes les options de la fenêtre parente (y compris ses webPreferences) par défaut. La fonction personnalisée window.open () vous permet de remplacer certaines des options héritées en passant en argument :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (!usesNativeWindowOpen) {
    // Make the browser window or guest view emit "new-window" event.
    window.open = function (url, frameName, features) {
      if (url != null && url !== '') {
        url = resolveURL(url)
      }
      const guestId = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features))
      if (guestId != null) {
        return getOrCreateProxy(ipcRenderer, guestId)
      } else {
        return null
      }
   }

  if (openerId != null) {
    window.opener = getOrCreateProxy(ipcRenderer, openerId)
  }
Lorsque la fonction window.open personnalisée d'Electron est appelée, elle émet un événement ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN. Le gestionnaire d'événement ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN analyse ensuite les fonctionnalités fournies, les ajoute en tant qu'options à la fenêtre nouvellement créée, puis émet un événement ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN. Pour empêcher les fenêtres enfants de faire des actions non souhaitées comme la réactivation de nodeIntegration lorsque la fenêtre parent est explicitement désactivée, guest-window-manager.js contient une liste codée en dur des options webPreferences et leurs valeurs restrictives :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
// Security options that child windows will always inherit from parent windows
const inheritedWebPreferences = new Map([
 ['contextIsolation', true],
 ['javascript', false],
 ['nativeWindowOpen', true],
 ['nodeIntegration', false],
 ['sandbox', true],
 ['webviewTag', false]
]);
Le gestionnaire d'événements ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN appelle ensuite la fonction mergeBrowserWindowOptions qui garantit que les attributs restreints des préférences Web de la fenêtre parent sont appliqués à la fenêtre enfant :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
const mergeBrowserWindowOptions = function (embedder, options) {
    [...]
    // Inherit certain option values from parent window
    for (const [name, value] of inheritedWebPreferences) {
      if (embedder.getWebPreferences()[name] === value) {
        options.webPreferences[name] = value
      }
    }
    // Sets correct openerId here to give correct options to 'new-window' event handler
    options.webPreferences.openerId = embedder.id
    return options
  }
Et voici là où la vulnérabilité se trouve. La fonction mergeBrowserWindowOptions ne prend pas en compte les valeurs par défaut de ces attributs restreints s'ils n'étaient pas définis. En d'autres termes, si webviewTag : false n'était pas explicitement déclaré dans les webPreferences de votre application (et était donc déduit en définissant explicitement nodeIntegration : false), quand mergeBrowserWindowOptions est allé vérifier le webviewTag, il reviendrait alors indéfini. La valeur retour de la condition if sera false et donc n'applique pas la préférence webviewTag du parent. Cela permet à window.open de transmettre l'option webviewTag en tant que fonctionnalité supplémentaire, en réactivant nodeIntegration et en permettant éventuellement l'exécution de code à distance.

La preuve suivante montre comment une charge utile XSS peut réactiver nodeIntegration pendant l'exécution et permettre l'exécution de commandes système :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
<script>
var x = window.open('data://yoloswag','','webviewTag=yes,show=no');
x.eval(
  "var webview = new WebView;"+
  "webview.setAttribute('webpreferences', 'webSecurity=no, nodeIntegration=yes');"+
  "webview.src = `data:text/html;base64,PHNjcmlwdD5yZXF1aXJlKCdjaGlsZF9wcm9jZXNzJykuZXhlYygnbHMgLWxhJywgZnVuY3Rpb24gKGUscikgeyBhbGVydChyKTt9KTs8L3NjcmlwdD4=`;"+
  "document.body.appendChild(webview)"
);
</script>
Pour corriger manuellement ce problème, il faut :
  • déclarer WebviewTag à false (WebviewTag : false) dans les webPreferences ;
  • activer l'option nativeWindowOption dans les webPreferences ;
  • intercepter les événements de nouvelle fenêtre et remplacer event.newGuest sans utiliser la balise d'options fournie.


Sources : trustwave, Common Vulnerabilities and Exposures

Et vous ?

Qu'en pensez-vous ?
Avez-vous aussi remarqué cette faille dans le framework Electron ?

Voir aussi

Une vulnérabilité critique dans le framework Electron pourrait affecter de nombreuses applications populaires comme Skype, Slack et bien d'autres ?
Faut-il utiliser Electron pour le développement d'applications de bureau ? Quels sont ses avantages et inconvénients ?