Local Procedure Call Elevates Privileges
Reported January 14, 2000 by
Bindview
VERSIONS AFFECTED
  • Windows NT 4.0 Workstation
  • Windows NT 4.0 Server
  • Windows NT 4.0 Server, Enterprise Edition
  • Windows NT 4.0 Server, Terminal Server Edition

DESCRIPTION

Due to a flaw in the NtImpersonateClientOfPort Windows NT 4 system
call, any local user on a machine is able to impersonate any other
user on the machine, including LocalSystem. We have written a
demonstration exploit which allows any user to spawn a cmd.exe window as LocalSystem.

All Windows NT 4.0 systems up to and including SP6a are affected. Bindview tested their exploit on W2K RC2 and found that it was not vulnerable.

All Windows NT 4.0 machines are subject to compromise by any user who can log in locally and run arbitrary programs. This may lead to
Domain Admin access, if Domain Admin credentials are on the machine.

In the case of Terminal Server, it should also be possible to use the
credentials of other users on the compromised machine to take actions across the network as those other users. This has not been tested, however.

Windows NT includes a mostly undocumented feature called Lpc ports, which are used for making Local Procedure Calls on a machine. One of the system apis used with Lpc ports is NtImpersonateClientOfPort, which allows a server to act in the security context of the client who is calling it. However, the interface to the call lets the server specify which client to impersonate based on process and thread IDs.

The kernel does do some sanity checking of the parameters to verify that the call is legitimate, but it"s possible to fake it. First it verifies that the port you"re trying to impersonate on actually has an outstanding request. This is easy to satify by making a request to it ourselves. Next, it checks that the message ID in the request matches the outstanding message ID in the thread you"re asking to impersonate.

This is also easy to satisfy, because if a thread is _not_ making a request, it"s outstanding message ID will be zero. So, as the server, when the request comes in, we just change the pid and tid to the ones we want, and change the Message ID to 0. Once we"re impersonating we can do whatever we want as that user.

The pseudo-code for our exploit works like this. There are two
threads.

Server thread
Client thread
NtCreatePort
NtReplyWaitReceivePort...
NtConnectPort...

(returns)
NtAcceptConnectPort
NtCompleteConnectPort
NtReplyWaitReceivePort...

(returns)
NtRequestWaitReplyPort...
(returns)
modify the LpcMessage received in the request so that the process and thread ids point to the thread we want, and change the message id to 0.
NtImpersonateClientOfPort