Why does anonymous PIPE access fail on Windows Vista, 2008, Windows 7 or Windows 2008 R2

Hi there,

In this blog post, I would like to talk about a named pipe access issue on Windows 2008 that I had to deal with recently. One of our customers was having problems in accessing named pipes anonymously on Windows 2008 and therefore we were involved in to address the issue. Even the required configuration for anonymous pipe access was in place, the pipe client was getting ACCESS DENIED when trying to access the pipe.

The problem was easy to reproduce on any Windows Vista or later system. Just run a named pipe server application which creates a pipe, then try to connect to the pipe anonymously from a remote system. You can see more details below on how to reproduce this behavior:

a) Compile the sample pipe server&client application given at the following MSDN link:

https://msdn.microsoft.com/en-us/library/aa365588(VS.85).aspx

Multithreaded Pipe Server
https://msdn.microsoft.com/en-us/library/aa365592(VS.85).aspx Named Pipe Client

b) Add the named pipe created by Pipe server to the Null session pipe lists (configuring "Nullsessionpipes" registry key under LanmanServer)

c) Do not enable “Network access: Let Everyone permissions apply to anonymous users” from local GPO or domain GPO

d) Make sure that the Pipe ACL included Anonymous user with Full Control permission. (You can do that by using a 3rd party application like pipeacl.exe)

e) Start a command line within the Local System account security context by running a command similar to below:

at 12:40 /interactive cmd.exe

f) Run the pipe client application from the command line and try to connect to the pipe. You'll get an ACCESS_DENIED in response from the server.

Note that as soon as you re-enable “Network access: Let Everyone permissions apply to anonymous users”, pipe client starts successfully opening the pipe and reading from/writing to pipe.


UNDERSTANDING THE ROOT CAUSE:
==============================

Well now after explaining the problem, now let's take a look at the root cause of this problem:

Note: Some of the outputs below are WinDBG (debugger) outputs.

 

1) From Vista onwards, in order to access an object, you need to pass two security checks:

 

a) Integrity check => For Vista onwards

b) Classical access check (checking object’s security descriptor against the desired access) => For all Windows versions

Note: Integrity check couldn’t be turned off even if you disable UAC (and we wouldn’t want to do that either)

2) In test pipe application, we see the following differences when “Network access: Let Everyone permissions apply to anonymous users” is enabled and disabled:

a) “Network access: Let Everyone permissions apply to anonymous users” ENABLED situation

=> Token of the thread:

- The user is anonymous (as expected)

- The token has also the SID S-1-16-8192 (which represents Medium integrity level). So the thread will be accessing the pipe object while its integrity level is Medium

kd> !token -n

_ETHREAD 892ef810, _TOKEN 9a983b00

TS Session ID: 0

User: S-1-5-7 (Well Known Group: NT AUTHORITY\ANONYMOUS LOGON)

Groups:

 00 S-1-0-0 (Well Known Group: localhost\NULL SID)

    Attributes -

 01 S-1-1-0 (Well Known Group: localhost\Everyone)

    Attributes - Mandatory Default Enabled

 02 S-1-5-2 (Well Known Group: NT AUTHORITY\NETWORK)

    Attributes - Mandatory Default Enabled

 03 S-1-5-15 (Well Known Group: NT AUTHORITY\This Organization)

    Attributes - Mandatory Default Enabled

 04 S-1-5-64-10 (Well Known Group: NT AUTHORITY\NTLM Authentication)

    Attributes - Mandatory Default Enabled

 05 S-1-16-8192 Unrecognized SID

    Attributes - GroupIntegrity GroupIntegrityEnabled

Primary Group: S-1-0-0 (Well Known Group: localhost\NULL SID)

Privs:

 23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default

Authentication ID: (0,15a3fe2)

Impersonation Level: Impersonation

TokenType: Impersonation

Source: NtLmSsp TokenFlags: 0x2000

Token ID: 15a3fe5 ParentToken ID: 0

Modified ID: (0, 15a3fe8)

RestrictedSidCount: 0 RestrictedSids: 00000000

OriginatingLogonSession: 0

=> Security descriptor of the pipe (DACL & SACL of the pipe object)

- Anonymous Logon has full access to the pipe object

- Integrity level’s of objects are stored in the SACL of the security descriptor of the object. If the integrity level is not explicitly assigned, the object’s integrity level is Medium.

kd> !sd 0x81f69848 1

->Revision: 0x1

->Sbz1 : 0x0

->Control : 0x8004

            SE_DACL_PRESENT

            SE_SELF_RELATIVE

->Owner : S-1-5-32-544 (Alias: BUILTIN\Administrators)

->Group : S-1-5-21-1181840707-4124064209-3703316816-513 (no name mapped)

->Dacl :

->Dacl : ->AclRevision: 0x2

->Dacl : ->Sbz1 : 0x0

->Dacl : ->AclSize : 0x5c

->Dacl : ->AceCount : 0x4

->Dacl : ->Sbz2 : 0x0

->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE

->Dacl : ->Ace[0]: ->AceFlags: 0x0

->Dacl : ->Ace[0]: ->AceSize: 0x18

->Dacl : ->Ace[0]: ->Mask : 0x001f01ff

->Dacl : ->Ace[0]: ->SID: S-1-5-32-544 (Alias: BUILTIN\Administrators)

->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE

->Dacl : ->Ace[1]: ->AceFlags: 0x0

->Dacl : ->Ace[1]: ->AceSize: 0x14

->Dacl : ->Ace[1]: ->Mask : 0x001f01ff

->Dacl : ->Ace[1]: ->SID: S-1-5-7 (Well Known Group: NT AUTHORITY\ANONYMOUS LOGON)

->Dacl : ->Ace[2]: ->AceType: ACCESS_ALLOWED_ACE_TYPE

->Dacl : ->Ace[2]: ->AceFlags: 0x0

->Dacl : ->Ace[2]: ->AceSize: 0x14

->Dacl : ->Ace[2]: ->Mask : 0x00120089

->Dacl : ->Ace[2]: ->SID: S-1-1-0 (Well Known Group: localhost\Everyone)

->Dacl : ->Ace[3]: ->AceType: ACCESS_ALLOWED_ACE_TYPE

->Dacl : ->Ace[3]: ->AceFlags: 0x0

->Dacl : ->Ace[3]: ->AceSize: 0x14

->Dacl : ->Ace[3]: ->Mask : 0x001f01ff

->Dacl : ->Ace[3]: ->SID: S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM)

->Sacl : is NULL

So in this scenario, a thread with integrity level of Medium is accessing an object with an integrity level of Medium. Hence it’s ok from Integrity check perspective to access the object. Once the integrity check is passed, DACL evaluation is made next (the classical access check that is done in all Windows versions). Since Anonymous user has access on the DACL of the pipe, it passes that stage as well and access to the pipe object is granted.

b) “Network access: Let Everyone permissions apply to anonymous users” DISABLED situation

=> Token of the thread:

- The user is anonymous (as expected)

- The token has also the SID S-1-16-0 (which represents Untrusted integrity level). So the thread will be accessing the pipe object while its integrity level is Untrusted

kd> !token -n

_ETHREAD 892dab58, _TOKEN 9a81b7f8

TS Session ID: 0

User: S-1-5-7 (Well Known Group: NT AUTHORITY\ANONYMOUS LOGON)

Groups:

 00 S-1-0-0 (Well Known Group: localhost\NULL SID)

    Attributes -

 01 S-1-5-2 (Well Known Group: NT AUTHORITY\NETWORK)

    Attributes - Mandatory Default Enabled

 02 S-1-5-15 (Well Known Group: NT AUTHORITY\This Organization)

    Attributes - Mandatory Default Enabled

 03 S-1-5-64-10 (Well Known Group: NT AUTHORITY\NTLM Authentication)

    Attributes - Mandatory Default Enabled

 04 S-1-16-0 Unrecognized SID

    Attributes - GroupIntegrity GroupIntegrityEnabled

Primary Group: S-1-0-0 (Well Known Group: localhost\NULL SID)

Privs:

Authentication ID: (0,15a3909)

Impersonation Level: Impersonation

TokenType: Impersonation

Source: NtLmSsp TokenFlags: 0x0

Token ID: 15a390c ParentToken ID: 0

Modified ID: (0, 15a390f)

RestrictedSidCount: 0 RestrictedSids: 00000000

OriginatingLogonSession: 0

=> Security descriptor of the pipe (DACL & SACL of the pipe object)

- Anonymous Logon has full access to the pipe object

- Integrity level’s of objects are stored in the SACL of the security descriptor objects. If the integrity level is not explicitly assigned, the object’s integrity level is Medium.

kd> !sd 0x81f69848 1

->Revision: 0x1

->Sbz1 : 0x0

->Control : 0x8004

            SE_DACL_PRESENT

            SE_SELF_RELATIVE

->Owner : S-1-5-32-544 (Alias: BUILTIN\Administrators)

->Group : S-1-5-21-1181840707-4124064209-3703316816-513 (no name mapped)

->Dacl :

->Dacl : ->AclRevision: 0x2

->Dacl : ->Sbz1 : 0x0

->Dacl : ->AclSize : 0x5c

->Dacl : ->AceCount : 0x4

->Dacl : ->Sbz2 : 0x0

->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE

->Dacl : ->Ace[0]: ->AceFlags: 0x0

->Dacl : ->Ace[0]: ->AceSize: 0x18

->Dacl : ->Ace[0]: ->Mask : 0x001f01ff

->Dacl : ->Ace[0]: ->SID: S-1-5-32-544 (Alias: BUILTIN\Administrators)

->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE

->Dacl : ->Ace[1]: ->AceFlags: 0x0

->Dacl : ->Ace[1]: ->AceSize: 0x14

->Dacl : ->Ace[1]: ->Mask : 0x001f01ff

->Dacl : ->Ace[1]: ->SID: S-1-5-7 (Well Known Group: NT AUTHORITY\ANONYMOUS LOGON)

->Dacl : ->Ace[2]: ->AceType: ACCESS_ALLOWED_ACE_TYPE

->Dacl : ->Ace[2]: ->AceFlags: 0x0

->Dacl : ->Ace[2]: ->AceSize: 0x14

->Dacl : ->Ace[2]: ->Mask : 0x00120089

->Dacl : ->Ace[2]: ->SID: S-1-1-0 (Well Known Group: localhost\Everyone)

->Dacl : ->Ace[3]: ->AceType: ACCESS_ALLOWED_ACE_TYPE

->Dacl : ->Ace[3]: ->AceFlags: 0x0

->Dacl : ->Ace[3]: ->AceSize: 0x14

->Dacl : ->Ace[3]: ->Mask : 0x001f01ff

->Dacl : ->Ace[3]: ->SID: S-1-5-18 (Well Known Group: NT AUTHORITY\SYSTEM)

->Sacl : is NULL

So in this scenario, a thread with integrity level of Untrusted is accessing an object with an integrity level of Medium. Hence it’s NOT OK from Integrity check perspective to access the object and integrity check mechanism denies access to the object. Classical DACL evaluation is even not done here.

 

In summary ,  

anonymous pipe access fails because of integrity check. When “Network access: Let Everyone permissions apply to anonymous users” is enabled, the EVERYONE SID is also added to the thread token and hence the token’s integrity level is raised (to medium in this scenario). So integrity check succeeds when this policy is enabled.

 

 

HOW TO FIX IT:

============

1) The most meaningful solution here is to set the Pipe object’s integrity level to Untrusted. If we could achieve this, we should be able to pass the integrity check because the integrity level of both the token and the object that the token was trying to open (with Read/Write permissions) would be the same (untrusted)

2) Changing the pipe object’s integrity level could be achieved in two different ways:

a) Setting the integrity level while creating the PIPE from the server application (via CreateFile() API)

b) Setting the integrity level after the PIPE is created (via SetSecurityInfo() API) (Either from the server application or from another application)

3) While searching for possible programmatic solutions, we have come across a very good source code example on how to set the integrity level of the pipe to Untrusted while creating the pipe. It’s also a good example of how to create pipe applications that will be using Anonymous pipes on Vista onwards systems:

=> Blog link: (in German)

https://blog.m-ri.de/index.php/2009/12/08/windows-integrity-control-schreibzugriff-auf-eine-named-pipe-eines-services-ueber-anonymen-zugriff-auf-vista-windows-2008-server-und-windows-7/

Note: It's a 3rd party link so please connect to it at your own risk.

=> Just a few notes from the source code to further explain how it could be done:

a) While the pipe is created, a security attributes structure is passed:

        hPipe = CreateNamedPipe(

                    server,

                    PIPE_ACCESS_DUPLEX,

                    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,

                    PIPE_UNLIMITED_INSTANCES,

                    sizeof(DWORD),

                    0,

                    NMPWAIT_USE_DEFAULT_WAIT,

                    &sa );

b) Especially integrity level related part of that the security attribute structure is built as follows:

...

      // We need this only if we have Windows Vista, Windows 7 or Windows 2008 Server

      OSVERSIONINFO osvi;

      osvi.dwOSVersionInfoSize = sizeof(osvi);

      if (!GetVersionEx(&osvi))

      {

          DisplayError( L"GetVersionInfoEx" );

        return FALSE;

    }

      // If Vista, Server 2008, or Windows7!

      if (osvi.dwMajorVersion>=6)

      {

            // Now the trick with the SACL:

            // We set SECURITY_MANDATORY_UNTRUSTED_RID to SYSTEM_MANDATORY_POLICY_NO_WRITE_UP

            // Anonymous access is untrusted, and this process runs equal or above medium

            // integrity level. Setting "S:(ML;;NW;;;LW)" is not sufficient.

            _tcscat(szBuff,_T("S:(ML;;NW;;;S-1-16-0)"));

      }

     

...

The highlighted part will cause the integrity level to be set to Untrusted on the pipe object while the pipe is created via CreateNamedPipe()

.

You can find more information on Integrity check at the following link:

https://msdn.microsoft.com/en-us/library/bb625963.aspx Windows Integrity Mechanism Design

https://msdn.microsoft.com/en-us/library/aa379588(VS.85).aspx SetSecurityInfo Function

https://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx CreateFile Function

Thanks,

Murat