Catching token theft with process-access telemetry, integrity jump and all
Token stealing elevates from a high-integrity process to SYSTEM by duplicating another process token, and it does it quietly. Two Sysmon events see it if you point them at the right processes and read the access mask instead of the image name.
Access token manipulation is the technique I keep having to explain to people who assume every privilege escalation leaves a loud trail. It does not. Token theft, the most common flavor of T1134.001 and the one baked into Cobalt Strike’s steal_token and Meterpreter’s incognito, moves an operator from a high-integrity process up to SYSTEM without dropping a file, without a service install, and without ever touching LSASS. If your detection strategy for privilege escalation is “watch LSASS,” this walks right past it. So let us look at what it actually does, and then at the two Sysmon events that can see it if you point them correctly.
What it does at the API level
The classic chain is short and it is all documented Windows API. The operator’s process, already running at high integrity, enables SeDebugPrivilege. It calls OpenProcess against a process already running as SYSTEM, some svchost.exe, winlogon.exe, services.exe, anything with a SYSTEM token sitting there. It opens that process’s token with OpenProcessToken, duplicates it with DuplicateTokenEx, and then spawns a new process under the stolen token with CreateProcessWithTokenW. The result is a child process running as SYSTEM whose parent was a mere high-integrity user process. That mismatch is the whole detection, if you know to look for it.
The important nuance, the one that trips up rules copied from LSASS-dumping content, is how little access the operator actually needs on the victim process. They are not reading its memory. They only need enough to grab a token handle, which is a far smaller access request than a memory dump, and it can target almost any SYSTEM process rather than one specific high-value one. Narrow access, wide choice of target. That is what makes it quiet.
The two events that see it
Sysmon gives you two angles on this and you want both, because each covers the other’s blind spot. I run these on Sysmon 15 against Windows 11 24H2, feeding a KQL-backed pipeline, and the specifics of the build matter because the legitimate SYSTEM-process population shifts version to version.
The first angle is event ID 10, ProcessAccess. This fires when one process opens a handle to another and records the source, the target, and the granted access mask. Token theft has to open a handle to a SYSTEM process to steal from it, so EID 10 is where the theft itself is visible. The mistake most people make is scoping EID 10 only to lsass.exe, which is right for credential dumping and useless here, because token theft happily targets winlogon.exe or a random SYSTEM svchost.exe instead. To catch this you scope EID 10 to SYSTEM-owned targets broadly and filter on the source, not the target.
The second angle is event ID 1, process creation, and specifically the integrity level and parentage of the new process. CreateProcessWithTokenW produces a child running at System integrity whose ParentImage is not one of the legitimate SYSTEM service parents. A SYSTEM-integrity cmd.exe or powershell.exe whose parent is a high-integrity user process is the integrity jump made visible, and it is a strong signal precisely because Windows itself almost never creates SYSTEM processes that way. The two events corroborate each other: EID 10 shows the token being grabbed, EID 1 shows it being used. Miss one and the other often still catches it.
GrantedAccess is the tell, not the image name
Here is where the rule earns its keep. In EID 10 the GrantedAccess field tells you what the source process actually asked for, and reading that mask is far more durable than matching on any tool name. Token theft needs, at minimum, the rights that let it call OpenProcessToken, which means PROCESS_QUERY_INFORMATION (0x0400) and usually PROCESS_QUERY_LIMITED_INFORMATION (0x1000) folded together into a request like 0x1400 or 0x1410. That is a much smaller mask than the 0x1010, 0x1438, or 0x143a you associate with memory reads, and it is easy to dismiss as benign query traffic if you have not thought about it. The access-mask meanings are all in the process security and access rights reference, and it is worth having open while you build this.
So the durable rule is: a non-SYSTEM source process opening a SYSTEM-owned target with a mask sufficient to reach the token, correlated where possible with a subsequent SYSTEM-integrity child from that same source. That keys on behavior an operator cannot rename their way out of, which is the same reasoning behind reading the handle request rather than the binary when you detect ProcDump or Task Manager reaching into a protected process. The tool changes. The handle does not.
The false positives, and there are a few
I will not pretend this rule ships quiet. It does not, and knowing where it fires legitimately before you turn it on is the difference between a rule the SOC keeps and one they mute by Friday. Your EDR opens handles to SYSTEM processes constantly, so does Defender, so do a handful of management and inventory agents, and so does anything built on Process Explorer or the debugging tooling. Every one of those will trip an unscoped EID 10 rule. You tune them out by source process, once, deliberately, and you document why each exclusion exists so the next engineer does not quietly widen it into a hole an attacker can hide in. On the EID 1 side, some legitimate installers and a few scheduled-task chains do produce SYSTEM children from odd parents, so that signal wants the source-process context too before it pages anyone. Tuned, mine sat at a handful of alerts a quarter. Rarely.
What 2026 changed
The part that has shifted since this technique was first written up is the target set, not the technique. Credential Guard and protected-process-light hardening have made LSASS a harder and louder target, which pushed more operators toward token theft precisely because it never needed LSASS in the first place. Meanwhile the telemetry got better: EID 10 correlation with process creation is now something modern EDR does for you to a degree, and MDE will often surface the parent-child token mismatch as its own signal. That does not retire the Sysmon rule, because plenty of environments still run Sysmon as their primary process telemetry and want the detection under their own control. Map it back to your Sysmon baseline, scope EID 10 wider than LSASS, read the access mask, and correlate the integrity jump. That combination has caught this for me when nothing pointed at LSASS ever would have.
detection-engineeringsysmonprivilege-escalationtoken-manipulation