Leaked Token Handles Preventing RDS Session ID Reuse
A recent article on Microsoft’s Ask the Directory Services Team blog piqued my interest. It talks about how leaked access tokens prevent logon sessions to be freed when the user logs off. This wastes system resources that can only be reclaimed by rebooting. A symptom of this happening is that RDS session IDs are not reused.
What are Leaked Token Handles?
When applications need to work with kernel objects they request a handle to the object by way of calling an API function. Having received a handle, an application is supposed to call the CloseHandle API function once it is done with it. The OS keeps track of the handles handed out. As long as there is still an open handle to an object, that object is not destroyed and consequently its resources are not freed. A process object, for example, lingers in memory until the last handle to it has been closed.
When application developers “forget” to close a handle, that handle remains valid even though the application is not using it any more. The handle has leaked.
Leaks are pretty bad as they prevent system resources from being freed and reused. In the case of access tokens they unnecessarily keep obsolete logon sessions and associated RDS session IDs around as zombies.
Logon Sessions and RDS Session IDs
Whenever the local security authority (LSA) authenticates a user, a new logon session is created. Logon types can be any of the following: interactive, remote interactive, service, network, etc.
Logon sessions are associated with RDS session IDs. An easy way to examine this is to run Sysinternals’ logonsessions.exe. Output looks like this:
[9] Logon session 00000000:000fd2f5:
User name: HK\Helge
Auth package: Negotiate
Logon type: RemoteInteractive
Session: 3
Sid: S-1-5-21-2684510436-795239710-1557501712-1104
Logon time: 06.04.2017 01:16:36
Logon server: SRV1
DNS Domain: HK.TEST
UPN: [email protected]
[10] Logon session 00000000:0017c025:
User name: HK\test01
Auth package: Kerberos
Logon type: RemoteInteractive
Session: 4
Sid: S-1-5-21-2684510436-795239710-1557501712-16601
Logon time: 06.04.2017 01:17:38
Logon server: SRV1
DNS Domain: HK.TEST
UPN: [email protected]
The RDS session ID is shown as Session.
Listing Processes with Leaked Token Handles
As the logonsessions.exe output above shows session ID 4 cannot be reused because there is still a logon session referring to it. This can easily be verified by performing additional logons: every new logon gets a new RDS session ID.
Let’s find out what keeps session ID 4 from being reused. We can list processes with open handles to token 17c025 with Sysinternals’ handle.exe:
C:\>handle.exe -a 17c025
System pid: 4 type: Directory D84: \Sessions\0\DosDevices\00000000-0017c025
System pid: 4 type: Token D88: HK\test01:17c025
System pid: 4 type: Token D8C: HK\test01:17c025
System pid: 4 type: Token D90: HK\test01:17c025
System pid: 4 type: Token F08: HK\test01:17c025
lsass.exe pid: 960 type: Token 804: HK\test01:17c025
lsass.exe pid: 960 type: Token 1658: HK\test01:17c025
lsass.exe pid: 960 type: Token 165C: HK\test01:17c025
lsass.exe pid: 960 type: Token 1664: HK\test01:17c025
svchost.exe pid: 1064 type: Token 48C: HK\test01:17c025
svchost.exe pid: 1064 type: Token 4D8: HK\test01:17c025
svchost.exe pid: 1100 type: Token E94: HK\test01:17c025
svchost.exe pid: 1100 type: Token 1AC8: HK\test01:17c025
svchost.exe pid: 1424 type: Token 964: HK\test01:17c025
CtxSvcHost.exe pid: 2148 type: Token 15C: HK\test01:17c025
svchost.exe pid: 3164 type: Token 1E8: HK\test01:17c025
svchost.exe pid: 2960 type: Token 3DC: HK\test01:17c025
Wow, that is a lot! Let’s wait for a little while.
Running the same command again after 10-20 minutes gives us the following:
C:\>handle.exe -a 17c025
System pid: 4 type: Directory D84: \Sessions\0\DosDevices\00000000-0017c025
lsass.exe pid: 960 type: Token 804: HK\test01:17c025
svchost.exe pid: 1064 type: Token 48C: HK\test01:17c025
svchost.exe pid: 1064 type: Token 4D8: HK\test01:17c025
svchost.exe pid: 1100 type: Token E94: HK\test01:17c025
svchost.exe pid: 1100 type: Token 1AC8: HK\test01:17c025
svchost.exe pid: 1424 type: Token 964: HK\test01:17c025
CtxSvcHost.exe pid: 2148 type: Token 15C: HK\test01:17c025
svchost.exe pid: 3164 type: Token 1E8: HK\test01:17c025
Much better, but there are still 8 open token handles referencing session ID 4.
Getting RDS Session IDs to be Reused
I wanted to prove that the open token handles shown above are indeed what keeps an RDS session ID from being reused, so I closed them one by one starting with the first svchost instance:
C:\>handle -c 48C -y -p 1064
48C: Token HK\test01:17c025
Handle closed.
This reduced the list of open handles by one:
C:\>handle -a 17c025
System pid: 4 type: Directory D84: \Sessions\0\DosDevices\00000000-0017c025
lsass.exe pid: 960 type: Token 804: HK\test01:17c025
svchost.exe pid: 1064 type: Token 4D8: HK\test01:17c025
svchost.exe pid: 1100 type: Token E94: HK\test01:17c025
svchost.exe pid: 1100 type: Token 1AC8: HK\test01:17c025
svchost.exe pid: 1424 type: Token 964: HK\test01:17c025
CtxSvcHost.exe pid: 2148 type: Token 15C: HK\test01:17c025
svchost.exe pid: 3164 type: Token 1E8: HK\test01:17c025
To make sure this has no effect on the reuse of RDS session ID 4 I logged on another user. By now they were getting the session ID 6:
C:\temp>qwinsta
SESSIONNAME USERNAME ID STATE TYPE DEVICE
services 0 Disc
console 1 Conn
>rdp-tcp#1 Helge 3 Active
ica-cgp#4 test01 6 Active
So I continued closing handles. After every closed handle I logged on again, only to notice that each new session was getting an incremented session ID.
Finally there was only one open token handle left (ignoring System and LSASS):
C:\>handle -a 17c025
System pid: 4 type: Directory D84: \Sessions\0\DosDevices\00000000-0017c025
lsass.exe pid: 960 type: Token 804: HK\test01:17c025
svchost.exe pid: 3164 type: Token 1E8: HK\test01:17c025
In my testing I found that in some cases closing the last handle held open by svchost.exe would cause the other two handles (in System and lsass.exe) to be closed automatically. When that happened, the RDS session ID would be reused for the next logon.
In other cases the System and lsass.exe handles were not closed automatically, and we cannot do it manually because we get access denied trying to close System’s handle.
Missing Information
Why is it that above procedure of freeing open logon session token handles only works sometimes?
Ryan Ries, the author of the AskDS blog article I linked to above, was kind enough to provide the answer in a comment and even went to the trouble of creating the following screenshot:
As you can see, Sysinternals’ handle.exe apparently does not (always?) list all the open handles. In some cases we may get all, in others only a subset.
Conclusion
When session IDs are not being reused system resources are wasted that can only be reclaimed by rebooting the machine. Identifying the likely cause – token handle leaks – is quite easy with Sysinternal tools as was demonstrated above.
Fixing the issue is an entirely different matter altogether, unfortunately. Token leaks occur because of developer negligence and require code changes in order to go away.
Apparently Microsoft and/or Citrix have some homework to do. I tested machines with Server 2008 R2 and 2012 R2 and different versions of Citrix XenApp. RDS session IDs were not reused in a single case.
1 Comment
Hello Helge,
A BIG THANK YOU for this article! I was looking the whole day why our RDS servers stop handing out vIPs from the pool we configured after several days. Following your article and using Mark Russinovich’s logonsessions and handle utilities I was able to identify the lingering sessions that still had handles opened after the RDS session was either logged off properly, or disconnected abruptly and not closed properly.
While I was getting rid of those svhost.exe handles, I observed one thing: The sessions that only had svchost handles with pid: 1196 were closed after the last svchost process was terminated, whereas the ones that had a single svchost with a pid: 1008 still left the system and lsass.exe processes opened, with no option to terminate them.
I am not sure if this might be a step or not in maybe identifying what else is keeping those sessions still opened or not. Maybe you can look into it when you have few minutes.
All the best and thank you again.