Back to Blog
medium SEVERITY8 min read

Sandboxie Kernel Hook Flaw: When Sandboxes Let Keystrokes Escape

A medium-severity vulnerability in Sandboxie's kernel-level input hook allowed malicious sandboxed processes to inject keystrokes and mouse clicks into privileged windows outside the sandbox, including UAC dialogs. The fix adds proper window handle validation to ensure INPUT structures only target windows within the same sandbox boundary. This is a critical reminder that sandbox isolation must be enforced at every layer, especially at the kernel level where input events are processed.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published May 16, 2026•Reviewed June 3, 2026

Answer Summary

Sandboxie's kernel-level input hook contained a window handle validation flaw (CWE-284: Improper Access Control) that allowed sandboxed processes to inject keystrokes into privileged windows outside the sandbox. The fix implements strict window handle validation in the INPUT hook handler to ensure all injected input events only target windows within the same sandbox boundary, restoring the sandbox isolation guarantee.

Vulnerability at a Glance

cweCWE-284 (Improper Access Control)
fixAdded window handle validation to verify target windows belong to the same sandbox context before allowing INPUT injection
riskSandboxed malware could inject keystrokes/mouse clicks into UAC dialogs and other privileged windows, potentially escalating privileges or performing unauthorized actions
languageC (kernel-mode driver)
root causeMissing validation of window handles in INPUT event processing allowed injection into windows outside sandbox boundaries
vulnerabilityKernel-level input hook window handle validation bypass

Introduction

Sandboxes are one of the most powerful tools in a security engineer's toolkit. The fundamental promise of a sandbox is simple: code running inside it cannot affect the world outside it. Whether you're isolating a suspicious email attachment, a browser tab, or an untrusted application, the sandbox is supposed to be the wall between "safe" and "compromised."

But what happens when that wall has a hole in it — and that hole is at the kernel level?

A recently patched vulnerability in Sandboxie (gui_xp.c, line 1370) exposed exactly this kind of flaw. A malicious process running inside the sandbox could craft Windows INPUT structures targeting windows outside the sandbox — including elevated UAC dialogs — and inject keystrokes or mouse clicks as if a legitimate user had performed them. The sandbox was running, the isolation appeared intact, but an attacker could silently click "Yes" on a UAC prompt or type commands into a privileged terminal.

If you work on sandboxing technology, security tooling, or any application that relies on process isolation, this vulnerability is a masterclass in why kernel-level validation cannot be skipped.


The Vulnerability Explained

Background: What Is NtUserSendInput?

Windows provides a system call called NtUserSendInput (the kernel-mode backing for the user-mode SendInput API). It allows a process to programmatically inject keyboard and mouse events into the input stream. Developers use it legitimately for automation, accessibility tools, and testing frameworks.

Sandboxie intercepts this system call via a kernel hook — Gui_NtUserSendInput — to monitor and control input events originating from sandboxed processes. The idea is sound: if a sandboxed process tries to send input, Sandboxie should be the gatekeeper.

The Flaw: Missing Window Handle Validation

Here's where it breaks down. The Gui_NtUserSendInput hook intercepts the call and processes the array of INPUT structures passed by the sandboxed process. However, it failed to validate whether the target window handles (HWNDs) in those structures belonged to processes within the same sandbox.

In Windows, INPUT structures of type INPUT_HARDWARE or synthesized mouse events can reference specific target windows. Without checking who owns those windows, the hook was essentially a rubber stamp — it saw the input coming from a sandboxed process, but never asked: "Where is this input going?"

// Simplified vulnerable pattern (conceptual illustration)
UINT Gui_NtUserSendInput(UINT nInputs, LPINPUT pInputs, int cbSize) {
    // Intercepts the call from sandboxed process...
    // Processes INPUT structures...
    // BUG: Never checks if target HWNDs belong to the sandbox!
    return __sys_NtUserSendInput(nInputs, pInputs, cbSize);
}

How Could It Be Exploited?

An attacker who has already achieved code execution inside a Sandboxie sandbox (e.g., through a malicious document or download) could:

  1. Enumerate top-level windows using EnumWindows to find privileged targets — UAC consent dialogs, administrative command prompts, or password managers.
  2. Craft INPUT structures with those external window handles as targets.
  3. Call NtUserSendInput with those crafted structures.
  4. Watch Sandboxie's hook pass them through without raising an alarm.

The result? The attacker can click "Yes" on a UAC elevation prompt, type commands into an admin shell, or interact with any privileged UI element — all while the user believes the sandboxed process is safely contained.

Real-World Attack Scenario

Imagine a corporate environment where Sandboxie is used to open email attachments safely. An employee opens a malicious PDF inside the sandbox. The PDF exploits a reader vulnerability to achieve code execution. Normally, the sandbox would contain the damage.

But with this vulnerability, the malicious code could:

  • Wait for a UAC dialog to appear (perhaps triggered by a legitimate admin task running concurrently).
  • Inject a mouse click on the "Yes" button of that dialog.
  • Silently escalate privileges on the host machine.
  • The sandbox has now become a launchpad rather than a cage.

This is a sandbox escape via UI interaction — a subtle but devastatingly effective attack vector.

CVSS and Classification

Property Value
Vulnerability ID V-008
CWE CWE-20: Improper Input Validation
CAPEC CAPEC-196: Session Credential Falsification through Forging
Affected Component Sandboxie/core/drv/gui_xp.c:1370
Attack Vector Local (requires code execution inside sandbox)
Impact Privilege Escalation, Sandbox Escape

The Fix

What Changed?

The fix adds window handle ownership validation to the Gui_NtUserSendInput kernel hook. Before forwarding any INPUT structure to the system, the hook now checks whether the target window handle belongs to a process that is part of the same sandbox instance.

The validation logic works at the kernel level by:

  1. Resolving the HWND to its owning process using kernel object inspection.
  2. Checking whether that process is tagged as belonging to the current sandbox using Sandboxie's internal process tracking structures.
  3. Blocking or redirecting any INPUT structure whose target window belongs to a process outside the sandbox boundary.
// Conceptual illustration of the fix pattern
UINT Gui_NtUserSendInput(UINT nInputs, LPINPUT pInputs, int cbSize) {
    PROCESS *proc = PsGetCurrentProcess();

    for (UINT i = 0; i < nInputs; i++) {
        if (pInputs[i].type == INPUT_MOUSE && pInputs[i].mi.hwnd != NULL) {
            // NEW: Validate that target window belongs to same sandbox
            if (!Gui_IsWindowInSandbox(pInputs[i].mi.hwnd, proc)) {
                // Block cross-sandbox input injection
                SetLastError(ERROR_ACCESS_DENIED);
                return 0;
            }
        }
        // Similar checks for other INPUT types...
    }

    return __sys_NtUserSendInput(nInputs, pInputs, cbSize);
}

Why This Fix Works

The core security improvement is the enforcement of sandbox boundary integrity at the input layer. Previously, Sandboxie enforced isolation for file system access, registry access, and process creation — but the input subsystem had a gap. The fix closes that gap by treating window handles as cross-boundary references that require explicit authorization.

This follows the principle of complete mediation: every access to every resource must be checked, every time. Kernel hooks that intercept system calls must validate not just the source of a request but also the destination.

Defense in Depth

The fix also demonstrates good defense-in-depth thinking. Even if an attacker finds a way to spoof process membership checks, the validation happens at the kernel level where user-mode tampering is significantly harder. The hook runs in kernel context, making it far more trustworthy than any user-mode equivalent.


Conclusion

The Gui_NtUserSendInput vulnerability in Sandboxie is a subtle but important lesson in sandbox security. It wasn't a buffer overflow or a use-after-free — it was a missing validation check in a kernel hook that had every other piece right except one: confirming that the destination of injected input was within the sandbox boundary.

The fix is elegant precisely because it's targeted: add the missing ownership check, enforce the boundary that was always intended to exist, and the attack vector disappears.

For developers building security tools, sandboxes, or any kernel-level components, the takeaways are clear:

  1. Kernel hooks must validate both source and destination of every operation.
  2. Window handles are security-sensitive — treat them like file or process handles.
  3. Threat modeling must cover all resource types, including UI and input subsystems.
  4. Complete mediation is non-negotiable — one unguarded path is all an attacker needs.

Sandboxing is hard. Doing it correctly at the kernel level is harder still. But vulnerabilities like this one, once found and fixed, make the entire ecosystem more robust. The security community's ability to find, disclose, and patch these issues is exactly how we build trustworthy systems over time.

Stay curious, review your kernel hooks, and remember: a sandbox is only as strong as its weakest boundary check.


This post is part of our ongoing series on kernel-level security vulnerabilities and secure systems programming. Found a vulnerability in your codebase? Responsible disclosure saves lives (and systems).

Prevention and further reading

Related Articles

medium

path-to-regexp 0.1.12 DoS: Catastrophic Backtracking on Malformed URL

path-to-regexp version 0.1.12 contains a regular expression engine vulnerability where maliciously crafted URL parameters trigger exponential backtracking, causing CPU exhaustion and service unavailability. Upgrading to version 0.1.13 eliminates the vulnerable pattern from the parsing logic.

critical

CVE-2026-59873: node-tar 7.5.11 DoS via Crafted Gzip Bomb

node-tar versions 7.5.11 through 7.5.18 are vulnerable to a denial-of-service attack through maliciously crafted gzip archives that decompress to disproportionately large sizes. An attacker can exploit this to exhaust memory and CPU resources by submitting a small, highly compressed archive that expands beyond configured limits during extraction.

high

MapManager.get() Race Condition Duplicates API Requests

The MapManager's `get(mapUid, cache)` method used a check-then-act pattern that permitted multiple concurrent requests to pass the cache miss check simultaneously, triggering redundant API calls and risking cache corruption. The fix introduces a `_pending` promise map to deduplicate in-flight fetches for identical map UIDs.

critical

Lampa Desktop Auto-Update Heuristic Bypass: Execution of Unverified

Lampa Desktop's auto-update mechanism downloaded JavaScript and CSS from `raw.githubusercontent.com` using only heuristic validation—file size thresholds and string pattern matching—that attackers could trivially satisfy. The fix introduces cryptographic integrity verification by cross-referencing Git blob hashes from the GitHub Contents API, ensuring downloaded code matches the repository's authoritative state before execution.

high

adm-zip 0.6.0 Preserves SUID Bits From ZIPs: CVE-2026-102282

The `adm-zip` dependency resolved to 0.6.0 in this project's dependency tree, a version affected by CVE-2026-102282: during extraction it applies the Unix permission bits stored in each ZIP entry's external file attributes verbatim, including the setuid (`04000`), setgid (`02000`), and sticky bits. An attacker who controls an archive passed to `extractAllTo()` or `extractEntryTo()` can therefore have the extractor create a setuid binary owned by whatever user the extraction process runs as. The

high

requestInput() Type Confusion: NaN and Object Bypass in JavaScript

The `requestInput()` utility function lacked validation on its `type` parameter and failed to handle `NaN` results from float conversions, creating a type confusion weakness. An attacker could supply malformed inputs that propagate unhandled `NaN` values or unexpected object types through the type system. The fix adds explicit guards against `NaN` type parameters and rejects non-primitive type values.