1H 2026 Examination of Cyber Hostility and Operations

Cynet Security Foundations

PPLDetention: Giving Detention to PPL Processes from a Normal Context

Last updated on October 29, 2026

By: Max Malyutin, Ido Veltzman, Adam Bar Zeev

When Process Protection Doesn’t Ensure Availability

Windows Protected Process Light (PPL) helps defend sensitive processes, including those used by security tools, against tampering. It restricts how less-trusted processes can interact with them. But those protections raise an important question: can a process remain protected while being prevented from doing its job?

Our investigation began with an observation: System Informer could suspend PPL-protected processes from a non-PPL context. Because System Informer uses a kernel driver, that capability initially seemed unsurprising. But was the driver what made this possible, or was there more to how Windows enforces process protection?

Following that question into the Windows kernel led us to two attack techniques that allow a non-PPL process to suspend PPL-protected processes or severely degrade their performance. For security processes that collect and analyze events, either outcome can undermine visibility. Suspension stops them from executing, while performance degradation can impair their ability to keep up, potentially causing events to be mishandled or dropped.

In this article, we examine the access checks that make these techniques possible and explore the gap between protecting a process from tampering and ensuring it can continue operating.

PPL 101

PPL can have different meanings across operating systems, but in the context of Windows, it stands for “Protected Process Light.” Although there is limited official documentation on this topic, Microsoft first introduced the concept of “Protected Processes” in Windows Vista. It later enhanced it in Windows 8.1 by adding a new protective mechanism called “Protection Levels.”

Before this, the protection boundary – i.e., which resources a process could access—was primarily defined by three factors:

  • User privileges: The context in which the process runs (e.g., SYSTEM, service account, administrator, standard user, or anonymous user).
  • Group privileges: The privileges associated with the groups to which the user belongs.
  • Access Control Lists (ACLs): Rules applied to objects that restrict access to specific users or groups.

Protection levels introduced an additional layer that is independent of user identity or access rules, focusing instead on the identity of the process itself. This mechanism was designed to ensure that critical processes cannot be tampered by malicious processes running in the same environment. Process protection is determined by two main components: the protection type and the signer.

There are three protection types:

  • None: The process is not protected.
  • Protected Process Light (PPL): The process runs with limited protection, allowing restricted access from processes with lower protection levels.
  • Protected Process (PP): The process runs with full protection, disallowing access from processes with lower protection levels.

In addition to the protection type, the signer plays a crucial role in determining whether a process can access another, even when both share the same protection type. The available signers are:

  • None: No signature.
  • App: Reserved for applications downloaded from the Microsoft Store.
  • Authenticode: Standard Authenticode signature.
  • CodeGen: Code generation signature.
  • LSA: Reserved for LSASS.
  • Antimalware: Reserved for endpoint protection, EDR, and antivirus solutions.
  • Windows: Reserved for Windows components.
  • WinTCB: Reserved for core Windows components.
  • WinSystem: Reserved for core Windows components.

Each signer is allowed to access processes that have been signed at lower levels. For example, an antimalware-signed application can access processes signed with LSA, Authenticode, App, or None – but not the other way around. A WinTCB-signed process can access all types of processes, while a process with no signature has the least privileges.

In summary, protection levels were introduced to provide an additional security layer for processes running under the same user context. PPL was designed to offer this protection while still allowing limited interaction from processes with lower protection levels or signers. As will be demonstrated later, this design choice is at the root of several security issues associated with PPL.

Understanding Process Protection

To understand how Microsoft protects protected processes, we must examine two sources: Microsoft’s official documentation and the Windows kernel (ntoskrnl).

In Microsoft’s documentation on process access rights, a section titled “Protected Processes” provides valuable insight into which operations are permitted and restricted. However, some of this information is outdated, as will be demonstrated later in this chapter. Notably, the following access rights are not listed as restricted:

  • PROCESS_TERMINATE
  • PROCESS_SUSPEND_RESUME

This raises an important question: Does this imply that a regular process can terminate a PPL-protected process? To answer this, we must examine the PsOpenProcess function – the kernel routine ultimately invoked when a process attempts to open a handle to another process.

PsOpenProcess

This section provides a high-level overview of the PsOpenProcess execution flow as of Windows 25H2. The flow can be divided into three phases: validation, lookup and security checks, and handle creation.

The process begins with argument validation. The kernel verifies that the inputs provided by the caller – such as desired access, client ID, and object attributes – are valid. If this stage fails, execution terminates immediately.

Next, the kernel attempts to locate the target process using either a process ID or a thread ID through the internal process table (PspCidTable). If the process cannot be found, the operation exits with a failure.

Once the process is located, the kernel creates an access state, which encapsulates the security context of the request. This includes the subject’s security token, requested access rights, and any associated privileges.

The system then performs a security check by validating the caller’s token against the target process’s security descriptor. This step determines whether the requested access should be granted. If the access check fails, the function returns an error.

If all checks pass, execution proceeds to handle creation, initiated via a call to ObOpenObjectByPointer. At this stage, the kernel transitions from object lookup to creating a usable handle for the caller. While the internal mechanics of handle creation are beyond the scope of this article, the process involves several key routines:

  • ObpCreateHandle: Allocates and initializes a new handle table entry.
  • ObpIncrementHandleCountEx: Updates reference counts and internal bookkeeping.
  • PspProcessOpen: Applies process-specific logic, such as auditing and policy enforcement.
  • RtlTestProtectedAccess: Enforces additional restrictions for protected processes.

After the handle is successfully created, the operation is logged via Event Tracing for Windows (ETW), enabling visibility for diagnostics, auditing, and security monitoring.

Although the handle creation and bookkeeping stages are out of scope, one function is of particular interest: PspProcessOpen. This routine is responsible for evaluating security and access rights when a user-mode or kernel-mode component attempts to open a handle to a process, with a specific focus on enforcing Protected Process policies.

PspProcessOpen performs four primary checks:

  • Operation ring check: Verifies whether the request originates from user mode or kernel mode.
  • Relationship check: Determines whether the requester is the target process itself or its parent.
  • Protected Process checks: Enforces protection boundaries using RtlProtectedAccess2 and RtlTestProtectedAccess.
  • Debug compatibility checks: Evaluates compatibility with Time Travel Debugging (TTD) via ci!CiCheckProcessDebugAccessPolicy.

At a high level, these checks appear comprehensive, covering multiple potential attack vectors. However, they overlook a critical detail within the PPL enforcement logic. Notably, two distinct mechanisms are used for Protected Process checks.

RtlTestProtectedAccess

There are two “levels” of checks:

  • Protection type check: Whether the access adheres to the previous definition of Protected Process (a process can access other processes at the same or lower level).
  • Access mask check: For PPLs, there isn’t only a check for the type but also for the signer: different signers have different access permissions. The following table describes what permissions a lower-level signer can access from a higher-level signer:
Signer Allowed Process Access Allowed Thread Access
None All All
App PROCESS_SET_QUOTA
PROCESS_TERMINATE
PROCESS_SUSPEND_RESUME
PROCESS_QUERY_LIMITED_INFORMATION
PROCESS_SET_LIMITED_INFORMATION
SYNCHRONIZE
THREAD_SUSPEND_RESUME
THREAD_QUERY_LIMITED_INFORMATION
THREAD_SET_LIMITED_INFORMATION
SYNCHRONIZE
Authenticode PROCESS_TERMINATE
PROCESS_SUSPEND_RESUME
PROCESS_QUERY_LIMITED_INFORMATION
PROCESS_SET_LIMITED_INFORMATION
SYNCHRONIZE
THREAD_SUSPEND_RESUME
THREAD_QUERY_LIMITED_INFORMATION
THREAD_SET_LIMITED_INFORMATION
SYNCHRONIZE
CodeGen PROCESS_TERMINATE
PROCESS_SUSPEND_RESUME
PROCESS_QUERY_LIMITED_INFORMATION
PROCESS_SET_LIMITED_INFORMATION
SYNCHRONIZE
THREAD_SUSPEND_RESUME
THREAD_QUERY_LIMITED_INFORMATION
THREAD_SET_LIMITED_INFORMATION
SYNCHRONIZE
LSA PROCESS_SUSPEND_RESUME
PROCESS_QUERY_LIMITED_INFORMATION
PROCESS_SET_LIMITED_INFORMATION
SYNCHRONIZE
THREAD_QUERY_LIMITED_INFORMATION
THREAD_SET_LIMITED_INFORMATION
SYNCHRONIZE
Antimalware PROCESS_SUSPEND_RESUME
PROCESS_QUERY_LIMITED_INFORMATION
PROCESS_SET_LIMITED_INFORMATION
SYNCHRONIZE
THREAD_QUERY_LIMITED_INFORMATION
THREAD_SET_LIMITED_INFORMATION
SYNCHRONIZE
Windows PROCESS_TERMINATE
PROCESS_SUSPEND_RESUME
PROCESS_QUERY_LIMITED_INFORMATION
PROCESS_SET_LIMITED_INFORMATION
SYNCHRONIZE
THREAD_SUSPEND_RESUME
THREAD_QUERY_LIMITED_INFORMATION
THREAD_SET_LIMITED_INFORMATION
SYNCHRONIZE
WinTCB PROCESS_QUERY_LIMITED_INFORMATION
PROCESS_SET_LIMITED_INFORMATION
SYNCHRONIZE
THREAD_QUERY_LIMITED_INFORMATION
THREAD_SET_LIMITED_INFORMATION
SYNCHRONIZE
WinSystem PROCESS_SUSPEND_RESUME
PROCESS_QUERY_LIMITED_INFORMATION
PROCESS_SET_LIMITED_INFORMATION
SYNCHRONIZE
THREAD_QUERY_LIMITED_INFORMATION
SYNCHRONIZE

At first glance, this logic appears highly restrictive, with two notable exceptions:

  • PROCESS_TERMINATE
  • PROCESS_SUSPEND_RESUME

However, process termination is mitigated by the earlier relationship check, which strips termination rights if the requesting process is not the parent of the target process. As a result, the only remaining permission of interest is the ability to suspend or resume a process with a higher signer level.

That said, due to the preceding protection checks, this permission cannot be effectively leveraged within the context of RtlTestProtectedAccess.

Suspending Any Process

This is where the flaw resides. The checks described earlier are not executed in the order presented. In practice, the first check determines whether execution should proceed to the remaining validations (operation ring, RtlTestProtectedAccess, and debug compatibility checks). This initial check relies on RtlProtectedAccess2 (a duplicate of RtlProtectedAccess) and simply validates the requested access against the previously referenced table. If this check passes, the subsequent checks are never executed.

In other words, an unprotected process can suspend any process, regardless of its protection level, as long as both operate under the same user context. Having established that it is possible to suspend processes across protection boundaries, we can now explore whether this capability can be further extended using PROCESS_SET_LIMITED_INFORMATION and THREAD_SET_LIMITED_INFORMATION.

Performance Degregation of Processes

Using the PROCESS_SET_LIMITED_INFORMATION permission, it is possible to cause processes and threads to function poorly. It isn’t enough for a full starvation, but it causes performance issues, which can lead to mishandling or even dropping events – a useful primitive against processes that need to aggregate many events.

Bonus Section: Threads

The access checks for threads are largely similar to those for processes, with only a few differences. The issue in which the kernel first checks RtlProtectedAccess2 and only then performs the remaining validations exists there as well. However, the primitive available for threads (THREAD_SET_LIMITED_INFORMATION) is not as useful as PROCESS_SET_LIMITED_INFORMATION and does not have the same level of impact.

Implications for PPL-Protected Security Processes

This investigation examined a subset of the access checks Windows performs when one process attempts to access another, demonstrating how a non-PPL process can suspend PPL-protected processes or severely degrade their performance.

These findings highlight a distinction between process protection and operational availability. For security tools, restricting certain forms of tampering does not guarantee that protected processes can continue collecting and analyzing events effectively. Evaluating these protections therefore requires considering both unauthorized access and interference with continued execution.

For questions or feedback regarding this research, contact press@cynet.com.

Note: We reported these findings to the Microsoft Security Response Center (MSRC), which determined that the issue did not meet its criteria for servicing because Protected Process Light (PPL) is not considered a serviceable security boundary under the Microsoft Security Servicing Criteria for Windows.

See how modern teams cut complexity and stop threats

Search results for: