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:          Helge@hk.test
 
[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:          test01@hk.test

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.

, , , , ,

No comments yet.

Leave a Reply