Failing when using Pinvoke calling UpdateProcThreadAttribute for AppContainer security capabilities

0

I am trying to launch an appcontainer using C# and pinvoke and getting hung up setting the security capabilities into the attribute list via UpdateProcThreadAttribute().

The GetLastError() returns

87 - invalid parameter

Similar c++ code from this article (github link at the bottom https://scorpiosoftware.net/2019/01/15/fun-with-appcontainers/) works well.

Code: (pinvoke structs/enums/externs below)

        public bool testAppContainer()
        {
            const uint SE_GROUP_ENABLED = 0x00000004;
            const int ProcThreadAttributeSecurityCapabilities = 0x00020009;
            STARTUPINFOEX startupInfoEx = new STARTUPINFOEX();
            PROCESSINFO pInfo = new PROCESSINFO();
            SECURITY_CAPABILITIES sc = new SECURITY_CAPABILITIES();
            IntPtr pSidInternetClientServer = IntPtr.Zero;

            startupInfoEx.StartupInfo.cb = Marshal.SizeOf(startupInfoEx);

            string appContainerName = "Test.AppContainer";

            try
            {

                //get the size, then the sid
                IntPtr clientServerSid = IntPtr.Zero;  uint cbSid = 0;
                SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientServerSid, clientServerSid, pSidInternetClientServer, ref cbSid);
                pSidInternetClientServer = Marshal.AllocCoTaskMem(Convert.ToInt32(cbSid));
                if (!CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientServerSid, clientServerSid, pSidInternetClientServer, ref cbSid))
                {
                    throw new ApplicationException($"Unable to create well known Client Server capability sid!");
                }

                //create an array of one capability
                SID_AND_ATTRIBUTES[] sidAndAttributes = new SID_AND_ATTRIBUTES[1];
                sidAndAttributes[0].Sid = pSidInternetClientServer;
                sidAndAttributes[0].Attributes = SE_GROUP_ENABLED;
                sc.Capabilities = sidAndAttributes;
                sc.CapabilityCount = 1;

                IntPtr appConSid = IntPtr.Zero;

                int hResult = AppContainerNative.DeriveAppContainerSidFromAppContainerName(appContainerName, out appConSid);
                if (hResult < 0)
                {
                    throw new ApplicationException($"Application container {appContainerName} was not found on the machine (err code {hResult})");
                }

                //security capabilities for existing app container
                sc.AppContainerSid = appConSid;

                const int reserved = 0; var size = IntPtr.Zero; int attributeCount = 1;

                bool alreadyInit = ProcessNative.InitializeProcThreadAttributeList(IntPtr.Zero, attributeCount, reserved, ref size);
                if (alreadyInit || size == IntPtr.Zero)
                {
                    throw new Exception(string.Format("Couldn't get the size of the attribute list for {0} attributes", attributeCount));
                }

                startupInfoEx.lpAttributeList = Marshal.AllocHGlobal(size);
                if (startupInfoEx.lpAttributeList == IntPtr.Zero)
                {
                    throw new Exception("Couldn't reserve space for a new attribute list");
                }

                bool initAttributeList = ProcessNative.InitializeProcThreadAttributeList(startupInfoEx.lpAttributeList, attributeCount, reserved, ref size);
                if (!initAttributeList )
                {
                    throw new Exception("Couldn't create new attribute list");
                }
            //also tried this (pass in unmanagedAddr instead of ref sc)
            //    IntPtr unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(sc));
             //   Marshal.StructureToPtr(sc, unmanagedAddr, true);

                bool success = UpdateProcThreadAttribute(startupInfoEx.lpAttributeList, reserved, (IntPtr)ProcThreadAttributeSecurityCapabilities,
                    ref sc, (IntPtr)Marshal.SizeOf(sc), IntPtr.Zero, IntPtr.Zero);

             //   Marshal.FreeHGlobal(unmanagedAddr);
             //   unmanagedAddr = IntPtr.Zero;

                if (!success)
                {
                    throw new Exception($"Error adding security capabilities to process launch. Error Code: {Marshal.GetLastWin32Error()}");
                }

                var pSec = new SECURITY_ATTRIBUTES(); pSec.nLength = Marshal.SizeOf(pSec);
                var tSec = new SECURITY_ATTRIBUTES(); tSec.nLength = Marshal.SizeOf(tSec);

                success = CreateProcess(null, @"c:\windows\notepad.exe", ref pSec, ref tSec, false, 
                    (uint)ProcessNative.CreateProcessFlags.EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref startupInfoEx, out pInfo);

                if (success)
                {
                    System.Diagnostics.Debug.WriteLine($"Created new app container process {pInfo.dwProcessId}!");
                    return true;
                }
            }
            finally
            {
                // Free the attribute list
                if (startupInfoEx.lpAttributeList != IntPtr.Zero)
                {
                    ProcessNative.DeleteProcThreadAttributeList(startupInfoEx.lpAttributeList);
                    Marshal.FreeHGlobal(startupInfoEx.lpAttributeList);
                }

                // Close process and thread handles
                if (pInfo.hProcess != IntPtr.Zero)
                {
                    ProcessNative.CloseHandle(pInfo.hProcess);
                }
                if (pInfo.hThread != IntPtr.Zero)
                {
                    ProcessNative.CloseHandle(pInfo.hThread);
                }
                Marshal.FreeHGlobal(startupInfoEx.lpAttributeList);

                if (pSidInternetClientServer != IntPtr.Zero)
                    Marshal.FreeCoTaskMem(pSidInternetClientServer);
            }
            return true;
        }



        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_CAPABILITIES
        {
            public IntPtr AppContainerSid;
            [MarshalAs(UnmanagedType.ByValArray)]
            public SID_AND_ATTRIBUTES[] Capabilities;
            public uint CapabilityCount;
            public uint Reserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SID_AND_ATTRIBUTES
        {
            public IntPtr Sid;
            public UInt32 Attributes;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct PROCESSINFO
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public Int32 dwProcessId;
            public Int32 dwThreadId;
        }
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct STARTUPINFO
        {
            public Int32 cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public Int32 dwX;
            public Int32 dwY;
            public Int32 dwXSize;
            public Int32 dwYSize;
            public Int32 dwXCountChars;
            public Int32 dwYCountChars;
            public Int32 dwFillAttribute;
            public Int32 dwFlags;
            public Int16 wShowWindow;
            public Int16 cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public int nLength;
            public IntPtr lpSecurityDescriptor;
            public int bInheritHandle;
        }
        public enum WELL_KNOWN_SID_TYPE
        {
            WinNullSid,
            WinWorldSid,
            WinLocalSid,
            WinCreatorOwnerSid,
            WinCreatorGroupSid,
            WinCreatorOwnerServerSid,
            WinCreatorGroupServerSid,
            WinNtAuthoritySid,
            WinDialupSid,
            WinNetworkSid,
            WinBatchSid,
            WinInteractiveSid,
            WinServiceSid,
            WinAnonymousSid,
            WinProxySid,
            WinEnterpriseControllersSid,
            WinSelfSid,
            WinAuthenticatedUserSid,
            WinRestrictedCodeSid,
            WinTerminalServerSid,
            WinRemoteLogonIdSid,
            WinLogonIdsSid,
            WinLocalSystemSid,
            WinLocalServiceSid,
            WinNetworkServiceSid,
            WinBuiltinDomainSid,
            WinBuiltinAdministratorsSid,
            WinBuiltinUsersSid,
            WinBuiltinGuestsSid,
            WinBuiltinPowerUsersSid,
            WinBuiltinAccountOperatorsSid,
            WinBuiltinSystemOperatorsSid,
            WinBuiltinPrintOperatorsSid,
            WinBuiltinBackupOperatorsSid,
            WinBuiltinReplicatorSid,
            WinBuiltinPreWindows2000CompatibleAccessSid,
            WinBuiltinRemoteDesktopUsersSid,
            WinBuiltinNetworkConfigurationOperatorsSid,
            WinAccountAdministratorSid,
            WinAccountGuestSid,
            WinAccountKrbtgtSid,
            WinAccountDomainAdminsSid,
            WinAccountDomainUsersSid,
            WinAccountDomainGuestsSid,
            WinAccountComputersSid,
            WinAccountControllersSid,
            WinAccountCertAdminsSid,
            WinAccountSchemaAdminsSid,
            WinAccountEnterpriseAdminsSid,
            WinAccountPolicyAdminsSid,
            WinAccountRasAndIasServersSid,
            WinNTLMAuthenticationSid,
            WinDigestAuthenticationSid,
            WinSChannelAuthenticationSid,
            WinThisOrganizationSid,
            WinOtherOrganizationSid,
            WinBuiltinIncomingForestTrustBuildersSid,
            WinBuiltinPerfMonitoringUsersSid,
            WinBuiltinPerfLoggingUsersSid,
            WinBuiltinAuthorizationAccessSid,
            WinBuiltinTerminalServerLicenseServersSid,
            WinBuiltinDCOMUsersSid,
            WinBuiltinIUsersSid,
            WinIUserSid,
            WinBuiltinCryptoOperatorsSid,
            WinUntrustedLabelSid,
            WinLowLabelSid,
            WinMediumLabelSid,
            WinHighLabelSid,
            WinSystemLabelSid,
            WinWriteRestrictedCodeSid,
            WinCreatorOwnerRightsSid,
            WinCacheablePrincipalsGroupSid,
            WinNonCacheablePrincipalsGroupSid,
            WinEnterpriseReadonlyControllersSid,
            WinAccountReadonlyControllersSid,
            WinBuiltinEventLogReadersGroup,
            WinNewEnterpriseReadonlyControllersSid,
            WinBuiltinCertSvcDComAccessGroup,
            WinMediumPlusLabelSid,
            WinLocalLogonSid,
            WinConsoleLogonSid,
            WinThisOrganizationCertificateSid,
            WinApplicationPackageAuthoritySid,
            WinBuiltinAnyPackageSid,
            WinCapabilityInternetClientSid,
            WinCapabilityInternetClientServerSid,
            WinCapabilityPrivateNetworkClientServerSid,
            WinCapabilityPicturesLibrarySid,
            WinCapabilityVideosLibrarySid,
            WinCapabilityMusicLibrarySid,
            WinCapabilityDocumentsLibrarySid,
            WinCapabilitySharedUserCertificatesSid,
            WinCapabilityEnterpriseAuthenticationSid,
            WinCapabilityRemovableStorageSid,
            WinBuiltinRDSRemoteAccessServersSid,
            WinBuiltinRDSEndpointServersSid,
            WinBuiltinRDSManagementServersSid,
            WinUserModeDriversSid,
            WinBuiltinHyperVAdminsSid,
            WinAccountCloneableControllersSid,
            WinBuiltinAccessControlAssistanceOperatorsSid,
            WinBuiltinRemoteManagementUsersSid,
            WinAuthenticationAuthorityAssertedSid,
            WinAuthenticationServiceAssertedSid,
            WinLocalAccountSid,
            WinLocalAccountAndAdministratorSid,
            WinAccountProtectedUsersSid,
            WinCapabilityAppointmentsSid,
            WinCapabilityContactsSid,
            WinAccountDefaultSystemManagedSid,
            WinBuiltinDefaultSystemManagedGroupSid,
            WinBuiltinStorageReplicaAdminsSid,
            WinAccountKeyAdminsSid,
            WinAccountEnterpriseKeyAdminsSid,
            WinAuthenticationKeyTrustSid,
            WinAuthenticationKeyPropertyMFASid,
            WinAuthenticationKeyPropertyAttestationSid,
            WinAuthenticationFreshKeyAuthSid,
            WinBuiltinDeviceOwnersSid
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct STARTUPINFOEX
        {
            public STARTUPINFO StartupInfo;
            public IntPtr lpAttributeList;
        }
        [DllImport("advapi32.dll", SetLastError = true)]
        internal static extern bool CreateWellKnownSid(WELL_KNOWN_SID_TYPE WellKnownSidType, IntPtr DomainSid, IntPtr pSid, ref uint cbSid);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, 
            ref SECURITY_ATTRIBUTES lpThreadAttributes, 
            bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment,  
            string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, out PROCESSINFO lpProcessInformation);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool InitializeProcThreadAttributeList(IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool UpdateProcThreadAttribute(IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, [MarshalAs(UnmanagedType.Struct), In] ref SECURITY_CAPABILITIES caps, IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern void DeleteProcThreadAttributeList(IntPtr lpAttributeList);



c#
pinvoke
asked on Stack Overflow Aug 28, 2020 by Mandi Bishop • edited Aug 28, 2020 by MickyD

2 Answers

1

Wow, first off, this is some great work and I'm curious how you are leveraging it (CICD? Security?). As a challenge I played around with the code and noticed that your declaration for the SECURITY_CAPABILITIES structure wasn't matching with the Microsoft documentation. They list the Capabilities member as a pointer to the SID_AND_ATTRIBUTES structure, not a structure itself. So, I changed that to IntPtr making it...

 [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_CAPABILITIES
    {
        public IntPtr AppContainerSid;
        public IntPtr Capabilities;
        public uint CapabilityCount;
        public uint Reserved;
    }

I'm not sure how to marshal an array of structs to an IntPtr so for now my example below is the single capability you listed.

           //create one capability
            SID_AND_ATTRIBUTES sidAndAttributes = new SID_AND_ATTRIBUTES();
            sidAndAttributes.Sid = pSidInternetClientServer;
            sidAndAttributes.Attributes = SE_GROUP_ENABLED;
            IntPtr sidAndAttributesPtr = Marshal.AllocHGlobal(Marshal.SizeOf(sidAndAttributes));
            Marshal.StructureToPtr(sidAndAttributes, sidAndAttributesPtr, false);
            sc.Capabilities = sidAndAttributesPtr;
            sc.CapabilityCount = 1;

This allowed notepad.exe to launch with AppContainer integrity and containing the capability flag you specified.

answered on Stack Overflow Aug 31, 2020 by Paul G
1

Thank you for the reply! I did get this to work. Your suggestion did help. This does actually launch a notepad.exe in an app container with two capabilities. Here is the code for the marshal for all that want to try this. My objective is to containerize some programs that we are using to protect the servers as much as possible.

    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_CAPABILITIES
    {
        public IntPtr AppContainerSid;

        public IntPtr Capabilities;
        public int CapabilityCount;
        public int Reserved;
    }

     private void AddDefaultCapabilitiesSid(ref SECURITY_CAPABILITIES sc)
    {
        //get the size, then the sid
        IntPtr mem = IntPtr.Zero;
        IntPtr clientServerSid = IntPtr.Zero;
        IntPtr privateNetworkSid = IntPtr.Zero;
        IntPtr pSid = IntPtr.Zero;
        uint cbSid = 0;

        //get the size, then the sid
        SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientServerSid, pSid, clientServerSid, ref cbSid);
        clientServerSid = Marshal.AllocCoTaskMem(Convert.ToInt32(cbSid));
        if (!SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientServerSid, pSid, clientServerSid, ref cbSid))
        {
            throw new ApplicationException($"Unable to create well known Client Server capability sid!");
        }

    
        //get the size, then the sid
        SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityPrivateNetworkClientServerSid, pSid, privateNetworkSid, ref cbSid);
        privateNetworkSid = Marshal.AllocCoTaskMem(Convert.ToInt32(cbSid));
        if (!SecurityNative.CreateWellKnownSid(SecurityNative.WELL_KNOWN_SID_TYPE.WinCapabilityPrivateNetworkClientServerSid, pSid, privateNetworkSid, ref cbSid))
        {
            throw new ApplicationException($"Unable to create well known Client Server capability sid!");
        }

        SecurityNative.SID_AND_ATTRIBUTES[] sidAndAttributes = new SecurityNative.SID_AND_ATTRIBUTES[2];
        sidAndAttributes[0].Sid = clientServerSid;
        sidAndAttributes[0].Attributes = SE_GROUP_ENABLED;
        sidAndAttributes[1].Sid = privateNetworkSid;
        sidAndAttributes[1].Attributes = SE_GROUP_ENABLED;


        int arraySize = sidAndAttributes.Length;
        int elemSize = Marshal.SizeOf(typeof(SecurityNative.SID_AND_ATTRIBUTES));
        IntPtr result = Marshal.AllocHGlobal(elemSize * arraySize);
        mem = new IntPtr(result.ToInt64());
        for (int i = 0; i < arraySize; i++)
        {
            IntPtr ptr = new IntPtr(result.ToInt64() + elemSize * i);
            Marshal.StructureToPtr(sidAndAttributes[i], ptr, false);

            MemToFree.Add(ptr); //free this mem later
        }
        sc.Capabilities = mem;
        sc.CapabilityCount = arraySize;
    }
answered on Stack Overflow Sep 1, 2020 by Mandi Bishop

User contributions licensed under CC BY-SA 3.0