Windows Impersonation: A Flaw in the Ointment

In my journey to master the nuances of user impersonation in Windows I first had an issue about getting impersonation to a remote database to occur at all (see this SO question) but I finally figured that out. My next hurdle is undoing/cancelling/reverting (choose your favorite verb) impersonation.

I have tried a couple different impersonation libraries that seem credible to me:

The results are identical with both libraries. Best practices dictate using the LOGON32_LOGON_NEW_CREDENTIALS logon type (see the Windows API LogonUser function) for a remote DB connection. When I do that here is what my sample code produces:

// SCENARIO A
BEGIN impersonation.
Local user = MyDomain\MyUser
DB reports: MyDomain\ImpersonatedUser
END impersonation.
Local user = MyDomain\MyUser
DB reports: MyDomain\ImpersonatedUser << NOT EXPECTED HERE!!

The only workaround I have found is to use the LOGON32_LOGON_INTERACTIVE logon type and then I get this:

// SCENARIO B
BEGIN impersonation.
Local user = MyDomain\ImpersonatedUser << EXPECTED, BUT NOT WANTED!
DB reports: MyDomain\ImpersonatedUser
END impersonation.
Local user = MyDomain\MyUser
DB reports: MyDomain\MyUser

From the terse description of the WindowsImpersonationContext.Undo method it sure seems like it should have worked in Scenario A.

Is it possible to revert using the LOGON32_LOGON_NEW_CREDENTIALS logon type?

Thanks to input from Harry Johnston (in comments attached to the question) and Phil Harding (in separate communication) I was able to determine that SQL Server connection pooling was the culprit here. Since pooling is determined by uniqueness of the connection string, by slightly varying the connection string (e.g. reversing order of parameters within, or even just adding a space on the end) I then observed the behaviors I expected.

===== TEST WITH SAME CONN STRING: True
BEGIN impersonation
Local user: MyDomain\msorens
DB reports: MyDomain\testuser
END impersonation
Local user: MyDomain\msorens
DB reports: MyDomain\testuser <<<<< still impersonating !!

===== TEST WITH SAME CONN STRING: False
BEGIN impersonation
Local user: MyDomain\msorens
DB reports: MyDomain\testuser
END impersonation
Local user: MyDomain\msorens
DB reports: MyDomain\msorens  <<<<< this is what I wanted to get

Windows Impersonation: A Flaw in the Ointment - c# - csharp, In my journey to master the nuances of user impersonation in Windows I first had an issue about getting impersonation to a remote database to occur at all (see  Windows Impersonation: A Flaw in the Ointment. Ask Question Asked 6 years, 3 months ago. Active 9 months ago. Viewed 4k times 9. 4. In my journey

I dug into the internals of the connection pooling, and it turns out that Windows credentials are not considered a part of the connection pooling key at all. Only SQL logins would be taken into account.

So if there is an available connection that was opened under user A and you are now impersonating user B, it will still use it and SQL will see you as user A. The reverse is also true.

The approach of changing the connection string slightly for the two different users is fine. You might do this if you have a "normal" user and then you need to impersonate for some "elevated" user. Of course, you don't want a different string for every user of your application - otherwise you might as well disable connection pooling completely.

When tweaking your connection string, you might consider appending the impersonated username to either the Application Name or Workstation ID fields. This would have the benefit of setting up a separate pool for each impersonated user.

Windows Impersonation: A Flaw in the Ointment, In my journey to master the nuances of user impersonation in Windows I first had an issue about getting impersonation to a remote database to occur at all (see  Pingback: Windows Impersonation: A Flaw in the Ointment | BlogoSfera. Vardhman says: November 27, 2013 at 09:47 Phil, I am having similar issues as LindsayWang11

I have found that the login type LOGON32_LOGON_NETWORK_CLEARTEXT does not have a problem with connections being re-used across impersonation contexts and works as expected without varying the connection string.

According to this thread, the "cleartext" part of this login type seems to be local to the server. I only keep the token alive for the duration of the database query or set of queries, so the token is very short-lived. Using this login type for long-lived tokens may or may not be a security risk.

NET (C#) Impersonation with Network Credentials, I required a C# class to enable ad-hoc user account impersonation Pingback: Windows Impersonation: A Flaw in the Ointment | BlogoSfera. Exploit code is available for this zero-day flaw from researcher SandboxEscaper, who named it BearLPE when she published it ten days ago, and targets the Task Scheduler component in Windows 10.

Impersonation, The API for using a username and password to gain access to a user account in Windows is LogonUser - which is a Win32 native API. There is not currently a  I was using async methods SQL connections to execute SQL commands within the impersonation block. (flaw in the ointment, etc). I remember trying the padding trick

Feature or flaw? How to hijack a Windows account in less than a , Impersonation is the ability of a thread to execute in a security context that is different from the context of the process that owns the thread. "Impersonation" in the .NET space generally means running code under a specific user account. It is a somewhat separate concept than getting access to that user account via a username and password, although these two ideas pair together frequently.

c#, "Every admin can impersonate any logged in user either locally with physical access or remotely via Remote Desktop," he said. "Unfortunately, I  A similar flaw was found in an older version of the software, 2.1, which is no longer supported. Anyone using it is strongly recommended to install the latest release. SEC Consult published proof

Comments
  • Close and reopen the connection to the database. The database doesn't receive notification when you change impersonation levels. I can only guess that in scenario B the database client is establishing a new connection automatically.
  • Thanks for the comment, @HarryJohnston; I should have stated that I did, in fact, close the SQL connection and start a fresh one.
  • Perhaps the database client is caching the SQL connection, or more likely the underlying network connection (a named pipe?). Your best option is probably to launch a subprocess (in the context of the new token) to do the impersonated database connection for you.
  • Something else must be at work. In that very link you provided, it says "Connections are separated into pools by connection string, and by Windows identity when integrated security is used.". Can you show some code?
  • My interpretation in that statement is that "Windows identity" means what System.Security.Principal.WindowsIdentity.GetCurrent().Name returns (what I report as Local user in my answer above). That is, when using LOGON32_LOGON_NEW_CREDENTIALS it is not changing the windows identity and thus, dips in the same pool. Do you agree?
  • I don't think that's the case, but I will do some testing and find out. It's easy enough to watch the credentials in SQL profiler.
  • I didn't want to believe it, but your assessment appears to be correct. The connection pooling key is not updated for the impersonation. In testing, I found that if I disabled connection pooling with Pooling=false in the connection string, it then worked. So it can't be just pulling the current windows identity. Clearly you wouldn't want to disable pooling in production, so there has to be another way. If you are only impersonating for a particular reason (security elevation, for example), then having a slightly different connection string sounds ok.
  • I know I'm a little late to the party, but I have a question... At the project I'm currently involved in, we are simply using LOGON32_LOGON_NETWORK to make the pooling play nice. Pooling is actually aware of identity (see here and here), but LOGON32_LOGON_NEW_CREDENTIALS doesn't change identity and that's why pooling misbehaves there. Are you aware of any negative consequences of using LOGON32_LOGON_NETWORK when connecting to SQL Server?