Better know a data source: Process integrity levels
Impossible to spoof, process integrity levels dictate trust between securable objects, offering defenders great visibility into privilege escalation.
This blog was originally written by me and posted by Red Canary.
In this second installment of our Better know a data source series, we’re showcasing process integrity levels. Integrity levels define the trust between process/thread and another object (files, processes, threads) and help control what that object can or can’t do on a system. A sudden change in a process’s integrity level might be a sign that an adversary has obtained system privileges.
While an adversary might be able to obtain a higher integrity level, they have no control over its metadata, meaning they can’t masquerade, hide, or otherwise fake it. Process integrity level is a kernel attribute from a kernel object, making it a reliable data point for defenders seeking to weed out malicious activity.
What are process integrity levels?
Before diving into integrity levels, it’s important to understand the basics of access tokens. Access tokens serve to identify the security context (user security identifier, security identifier, group memberships, and privileges) of a process or thread. Unless a token is explicitly assigned to a thread, all threads will inherit the token of the primary thread (i.e., the first thread started in a process), which is also known as the primary token. All actions the process takes will fall under the security context of that token.
Every token is tied to a logon session. Any time a user logs in, a new token is generated and then applied to any process that user spawns during its logon lifecycle. When a local administrator logs in, two separate logon sessions are created: one for the unelevated token (medium integrity level) and one with the elevated token (high integrity level). These logon sessions are bound to one another and are referred to as a “split token.”
The image above shows a logon session in which two LogonIDs are specified and tied to the appropriate token. Although this is the same user, there will be another logon session for the elevated token with different LogonID values and a higher integrity level.
Because every token is tied to a logon session, identity logs associated with logon sessions can serve as a link between process events when privilege escalation is performed. You can see an example of this in the Detection opportunities section below.
By default, child processes will inherit a copy of their parent’s token, meaning that all processes running under a logon session will have the same token. Token attributes include:
- groups the user is associated with
- token type
- token ID
- user’s privileges
- integrity level
Integrity levels help define the trust between a process/thread and a securable object (processes, threads, tokens, files, etc. — any object that can have a security descriptor). Because access tokens can be tied to a process or thread, integrity levels can be tied to either of these as well. The only time a thread’s token will be different from the primary token is when a token is explicitly set on a target thread.
Integrity levels are stored as security identifiers (SID) but can be converted to a human readable string that defenders can more easily interpret.
Below are the integrity level SIDs defined by Microsoft:
Why focus on process integrity level?
Integrity levels can be applied to securable objects as a way of evaluating the level of access that a source object has to a target object. Monitoring processes spawning from a lower integrity level into a higher integrity level can tip defenders off when a process is about to perform a task that requires higher privileges or when sensitive privileges have been co-opted.
Each integrity level comes with a set of system privileges. By default, the higher integrity level you are, the more system privileges you have on a host and the more access you have to securable objects. This is one of the primary drivers that compels adversaries to find ways to escalate their integrity level. The following example from James Forshaw’s NtObjectManager PowerShell module shows the default privileges given to a process of medium integrity level and then again of high integrity level.
Medium integrity level:
High integrity level:
These token privileges dictate what you can and can’t do on a machine from a system operations perspective, such as loading drivers, changing the host’s time, impersonating a client, creating a primary token, etc.
As we noted earlier, when an admin logs in, a split token is generated. Unless you explicitly set the process token to a high integrity level, the process will default to the lowest group the user/token is associated with, which for most users will be a medium integrity level. Users must specify when they want to use their administrative privileges, and in most circumstances, User Account Control (UAC) kicks in to either allow that elevation or not. If the elevation request is successful, the elevated token is granted to that process and the appropriate privileges are applied.
What users can obtain a high integrity level token?
By default, users who are a part of the Administrators groups can obtain a high integrity level token. However, a high integrity level token can still be applied to a user outside of the Administrators group if a sensitive privilege is explicitly granted to that user.
Adversaries have been known to gain access to SeImpersonatePrivilege. This privilege allows an attacker to impersonate different clients — such as named pipe clients, RPC clients, and COM clients — to act with the security context of a targeted token.
This privilege is given to users within the Administrators group by default. Admins can grant this privilege to a user outside of that group using the User Rights Assignments (URA) within
Group Policy Object - Computer Configuration\Windows Settings\Security Settings\Local Policies\User Rights Assignment. To do so, the admins would need to add the user to the “impersonate a client after authentication” assignment and start a new logon session with that user.
If the user starts a normal process, they will not have a token that possesses that sensitive privilege. In the screenshot below, I granted
TargetUser, but when I started a normal PowerShell process, I didn’t have that sensitive privilege. The integrity level of
TargetUser is “medium.” The examples below are using whoami /groups.
To apply the sensitive privilege
SeImpersonatePrivilege, the user has to run as an elevated prompt. After doing so, the sensitive privilege and corresponding high integrity level are applied.
To my knowledge, there is no easy way to determine what token privileges should be considered sensitive — other than by looking at how the privileges are used for sensitive system operations. Knowing this, adversaries prefer to set their sights on processes with specific sensitive privileges granted to their token, or, alternatively, by simply targeting accounts in the Administrators group.
How do adversaries leverage access token process integrity levels?
T1134.002: Create Process With Token is a MITRE ATT&CK technique that describes when an adversary targets access tokens of higher integrity levels and creates a process with a copy of the targeted token. This allows attackers to operate under the security context of the targeted user and run a program, access resources, or establish persistence where they otherwise wouldn’t have been able to.
Adversaries will also impersonate another user either by obtaining a copy of the target token or by obtaining the handle to the target token and setting it to a thread within their running process, in turn changing the integrity level for that thread (T1134.001: Token Impersonation/Theft). By default this will only work with a token that has a high integrity level process, because that’s the level required to abuse
In general, it is unusual to see a newly spawned process go from a lower integrity level to a higher one (outside of normal UAC elevation) — it could mean that an adversary has obtained a token of a higher integrity level process or has logged in as another user who is of higher integrity level and is using that logon session token. It could also mean that an administrator is using
Start-Process as a normal everyday activity.
As we explained earlier, it’s impossible for an adversary to directly manipulate integrity level metadata. Therefore, if you’re able to gather integrity level-related telemetry, it can serve as a reliable data source.
What data sources are available to retrieve process integrity level?
There are a couple of data sources that you can use to gain insight into a process’s integrity level.
One way to view the integrity level of a process/thread is to use the
GetTokenInformation Win32 API and pass in the
TokenIntegrityLevel value into the
TokenInformationClass parameter. This will specify that you want to enumerate the
TokenIntegrityLevel attribute within the
TOKEN_INFORMATION_CLASS enum. The
TokenIntegrityLevel attribute is backed by the
TOKEN_MANDATORY_LABEL structure, which has an attribute within it called
Label is backed by the
SID_AND_ATTRIBUTES structure that identifies the mandatory integrity level of the token. The SID attribute can be translated to one of the values seen in the table above.
Here is an example of what this can look like:
PS > Get-IntegrityLevel
Title : Displaying Process/Primary Information
ProcessName : powershell
SessionId : 1
PID : 2800
TokenIntegrityLevel : MEDIUM_MANDATORY_LEVEL
Windows Process Auditing:
Data source: Windows Security Log Event ID 4688
Relevant field(s): Mandatory Label
Security ID: MARVEL\thor
Account Name: thor
Account Domain: MARVEL
Logon ID: 0x2FC7277
Security ID: NULL SID
Account Name: -
Account Domain: -
Logon ID: 0x0
New Process ID: 0x1b54
New Process Name: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Token Elevation Type: %%1938
Mandatory Label: Mandatory Label\Medium Mandatory Level
Creator Process ID: 0x16b0
Creator Process Name: C:\Windows\explorer.exe
Process Command Line:
Data source: Event ID 1: Process creation
Relevant Field[s]: IntegrityLevel
UtcTime: 2021-10-28 01:19:37.643
FileVersion: 10.0.17134.1 (WinBuild.160101.0800)
Description: Windows PowerShell
Product: Microsoft® Windows® Operating System
Company: Microsoft Corporation
Microsoft Defender for Endpoint (MDE):
Data source: DeviceProcessEvents
ProcessVersionInfoCompanyName: Microsoft Corporation
ProcessVersionInfoProductName: Microsoft® Windows® Operating System
ProcessVersionInfoFileDescription: Windows PowerShell
InitiatingProcessAccountSid : S-1-5-21-3637186843-3378876361-2759896766-1104
InitiatingProcessVersionInfoCompanyName: Microsoft Corporation
InitiatingProcessVersionInfoProductName: Microsoft® Windows® Operating System
InitiatingProcessVersionInfoFileDescription: Windows Explorer
InitiatingProcessFolderPath : c:\windows\explorer.exe
How do I generate integrity level metadata for sensor and detection validation?
Within the AtomicTestHarnesses PowerShell module, there is a function called
Invoke-ATHCreateProcessWithToken that generates telemetry to test optics, detection gaps, and technique knowledge. This module will simulate the behavior of targeting a token then creating a process with a duplicated copy of that targeted token. It will terminate all processes created and close any handles that were opened as a result of this activity. It will then print out an accessible view of data that relates to the activity so that it can easily be tested against the optics available to the users/analysts.
PS > Invoke-ATHCreateProcessWithToken
TechniqueID : T1134.002
TestSuccess : True
TestGuid : a8e88199-1036-4ceb-9845-c556bc09ded9
TestCommand : Invoke-ATHCreateProcessWithToken
SourceUser : MARVEL\thor
SourceExecutableFilePath : C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
SourceExecutableFileHash : D3F8FADE829D2B7BD596C4504A6DAE5C034E789B6A3DEFBE013BDA7D14466677
SourceProcessId : 8952
GrantedRights : QueryLimitedInformation
ImpersonatedUser : NT AUTHORITY\SYSTEM
TargetExecutableFilePath : C:\Windows\system32\winlogon.exe
TargetExecutableFileHash : 10098BBE7EFD4B16014493F7D26E593E06910CC36D4BA4A3E59FCF8C15E4F1D7
TargetProcessId : 588
NewProcessExecutablePath : C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
NewProcessCommandline : powershell.exe -nop -Command Write-Host a8e88199-1036-4ceb-9845-c556bc09ded9; Start-Sleep
-Seconds 2; exit
NewProcessExecutableHash : D3F8FADE829D2B7BD596C4504A6DAE5C034E789B6A3DEFBE013BDA7D14466677
NewProcessId : 5828
The following Microsoft Defender for Endpoint (MDE) queries identify when lower integrity level processes spawn a higher integrity level process or a potentially impersonated login. These will only work for instances where a new process is spawned. Currently, we aren’t aware of any scalable optics or analytics that would give insight into times when token impersonation happens on a thread level. See Access Token Manipulation: Token Impersonation/Theft for more.
Detecting parent-child process integrity level disparities
This MDE query looks for high integrity level processes spawning a system integrity level process and/or low integrity level process spawning high or system integrity level process.
| where InitiatingProcessIntegrityLevel != ProcessIntegrityLevel and (InitiatingProcessIntegrityLevel == "Low" or InitiatingProcessIntegrityLevel == "High")
| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessIntegrityLevel, AccountName, FileName, ProcessIntegrityLevel, InitiatingProcessParentId
Detecting login impersonation
As in the previous MDE query, this detection logic looks for integrity level disparities between parent and child processes. However, here we’re also looking for a corresponding command line with the
seclogon (secondary logon) string. The
seclogon string is a good indication of logging on as another user and abusing their security context. This logic will show when a process was spawned with CreateProcessWithLogon.
| where (InitiatingProcessIntegrityLevel != ProcessIntegrityLevel and (InitiatingProcessIntegrityLevel == "Low" or InitiatingProcessIntegrityLevel == "High")) or (InitiatingProcessAccountName != AccountName and ProcessIntegrityLevel == "Medium" and InitiatingProcessCommandLine contains "seclogon")
| project Timestamp, DeviceName, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessIntegrityLevel, AccountName, FileName, ProcessIntegrityLevel
Another way to detect login impersonation
You can also use identity events to look for logins where the
InitiatingProcessCommandLine tag has
seclogon (secondary logon). This telemetry also suggests the use of CreateProcessWithLogon.
| where Protocol == "Negotiate" and InitiatingProcessCommandLine contains "seclogon"
| project InitiatingProcessAccountName, AccountName, Protocol, LogonType, InitiatingProcessId, LogonId
| join (
| project InitiatingProcessId, InitiatingProcessLogonId, AccountName, FileName
on InitiatingProcessId, AccountName
| project-rename TargetProcessId = InitiatingProcessId1
| project-rename TargetUser = AccountName1
| project InitiatingProcessAccountName, Protocol, LogonType, TargetUser, LogonId, TargetProcessId, FileName