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.