COMMAND
kernel
SYSTEMS AFFECTED
- Microsoft Windows NT 4.0 Workstation
- Microsoft Windows NT 4.0 Server
- Microsoft Windows NT 4.0 Server, Enterprise Edition
- Microsoft Windows NT 4.0 Server, Terminal Server Edition
PROBLEM
Following is based on BindView Security Advisory. 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. BindView wrote a
demonstration exploit which allows any user to spawn a cmd.exe
window as LocalSystem. Affected systems found were all Windows
NT 4.0 systems up to and including SP6a. BindView tested their
exploit on W2K RC2, and 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
At this point, we're running under the token of the thread we
specified above. For our exploit, we choose to impersonate a
thread of lsass. The reason has to do with the privileges that
lsass has enabled.
When impersonating, it seems that the impersonation token only
gets those privileges that are _enabled_ in the client at the
time of impersonation. Privileges that are disabled in the
client, are not put into the impersonation token, even in a
disabled state. Now, lsass happens to have the CREATE_TOKEN
privilege enabled, so we can impersonate lsass, and use that
privilege to create a new token for ourselves based on the lsass
impersonation token, but with _all_ privileges enabled. We can
then launch another process under that token. So to continue:
// get the information about the current token.
// TOKEN_USER, TOKEN_GROUP, etc. (esp. TOKEN_PRIVILEGES)
NtOpenThreadToken
GetTokenInformation // (several times)
// Add _all_ privileges to our TOKEN_PRIVILEGES struct
// all user space, preparing for NtCreateToken
NtCreateToken // with info from the lsass token, except that
// all privileges are enabled
Finally. We have our token. Now, we can CreateProcessAsUser with
that, except that there are a couple more hoops to jump through.
CreateProcessAsUser requires more privileges than lsass had
enabled, so we don't currently have them, however, our new token
does! So, we can just impersonate the new token. But again,
that's not enough. CreateProcessAsUser checks for the privileges
in the _process_ token, ignoring any impersonation token. So as
a final step, we change the new token to be our primary process
token.
ImpersonateLoggedOnUser
NtSetInformationProcess (... ProcessAccessToken ...)
CreateProcessAsUser
and we're finally there.
SOLUTION
Install the hotfix from Microsoft. Limit local logon privileges,
if possible. Patch availability:
- Microsoft Windows NT 4.0 Workstation, Server and Server,
Enterprise Edition:
Intel: http://www.microsoft.com/downloads/release.asp?ReleaseID=17382
Alpha: http://www.microsoft.com/downloads/release.asp?ReleaseID=17383
- Microsoft Windows NT 4.0 Server, Terminal Server Edition:
To be released shortly.