예제 #1
0
        private SafeProcThreadAttributeList AllocateAttributeList()
        {
            using (var localDisposalEscrow = new DisposalEscrow())
            {
                SECURITY_CAPABILITIES securityCapabilities = new SECURITY_CAPABILITIES();
                this.SetSecurityCapabilities(
                    ref securityCapabilities,
                    this.securityIdentifierHandle,
                    new WELL_KNOWN_SID_TYPE[] { WELL_KNOWN_SID_TYPE.WinCapabilityInternetClientSid });

                var attributeListHandle        = localDisposalEscrow.Add(new SafeProcThreadAttributeList(1));
                var securityCapabilitiesMemory = localDisposalEscrow.Add(new SafeHGlobalBuffer(Marshal.SizeOf(securityCapabilities)));

                Marshal.StructureToPtr(securityCapabilities, securityCapabilitiesMemory.DangerousGetHandle(), fDeleteOld: false);

                if (!Methods.UpdateProcThreadAttribute(
                        attributeListHandle.DangerousGetHandle(),
                        dwFlags: 0,
                        attribute: PROC_THREAD_ATTRIBUTES.PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES,
                        securityCapabilitiesMemory.DangerousGetHandle(),
                        securityCapabilitiesMemory.Size,
                        lpPreviousValue: IntPtr.Zero,
                        lpReturnSize: IntPtr.Zero))
                {
                    throw new SandboxException(
                              $"Failed to update proc thread attribute list (0x{Marshal.GetLastWin32Error():X08})",
                              new Win32Exception());;
                }

                this.disposalEscrow.Subsume(localDisposalEscrow);
                return(attributeListHandle);
            }
        }
예제 #2
0
        private void SetSecurityCapabilities(
            ref SECURITY_CAPABILITIES securityCapabilities,
            SafeSecurityIdentifier appContainerSid,
            WELL_KNOWN_SID_TYPE[] appCapabilities)
        {
            using (var localDisposalEscrow = new DisposalEscrow())
            {
                securityCapabilities.AppContainerSid = appContainerSid.DangerousGetHandle();
                securityCapabilities.Capabilities    = IntPtr.Zero;
                securityCapabilities.CapabilityCount = 0;
                securityCapabilities.Reserved        = 0;

                if (appCapabilities != null && appCapabilities.Length > 0)
                {
                    var attributesMemory = localDisposalEscrow.Add(new SafeHGlobalBuffer(Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES)) * appCapabilities.Length));

                    for (int i = 0; i < appCapabilities.Length; i++)
                    {
                        Int32 sidSize = Constants.SECURITY_MAX_SID_SIZE;

                        var safeMemory = localDisposalEscrow.Add(new SafeHGlobalBuffer(sidSize));

                        if (!Methods.CreateWellKnownSid(appCapabilities[i], IntPtr.Zero, safeMemory, ref sidSize))
                        {
                            throw new SandboxException(
                                      "Unable to create well known sid.",
                                      new Win32Exception());
                        }

                        var attribute = new SID_AND_ATTRIBUTES
                        {
                            Attributes = SID_ATTRIBUTES.SE_GROUP_ENABLED,
                            Sid        = safeMemory.DangerousGetHandle(),
                        };

                        Marshal.StructureToPtr(attribute, IntPtr.Add(attributesMemory.DangerousGetHandle(), i * Marshal.SizeOf(typeof(SID_AND_ATTRIBUTES))), fDeleteOld: false);
                    }

                    securityCapabilities.Capabilities    = attributesMemory.DangerousGetHandle();
                    securityCapabilities.CapabilityCount = appCapabilities.Length;
                }

                this.disposalEscrow.Subsume(localDisposalEscrow);
            }
        }
        private static SID_AND_ATTRIBUTES ConvertSecurityIdentifierToSidAndAttributes(
            SecurityIdentifier securityIdentifier,
            DisposalEscrow disposalEscrow)
        {
            var sidBytes = new byte[securityIdentifier.BinaryLength];

            securityIdentifier.GetBinaryForm(sidBytes, 0 /* offset */);

            var nativeBytes = disposalEscrow.Add(new SafeHGlobalBuffer(sidBytes.Length));

            Marshal.Copy(sidBytes, 0 /* startIndex */, nativeBytes.DangerousGetHandle(), sidBytes.Length);

            return(new SID_AND_ATTRIBUTES
            {
                Sid = nativeBytes.DangerousGetHandle(),
            });
        }
예제 #4
0
        /// <summary>
        /// Creates a new desktop with minimal rights.
        /// </summary>
        /// <param name="tracer">A tracer instance.</param>
        /// <returns>The desktop instance.</returns>
        public static Desktop Create(ITracer tracer, string mandatoryLevelSacl)
        {
            using (var disposalEscrow = new DisposalEscrow())
                using (Desktop currentDesktop = Desktop.GetCurrent())
                {
                    try
                    {
                        string name = "sbox" + DateTime.UtcNow.Ticks;
                        tracer.Trace(nameof(Desktop), "Creating desktop '{0}'", name);

                        SECURITY_ATTRIBUTES securityAttributes = null;
                        if (mandatoryLevelSacl != null)
                        {
                            tracer.Trace(nameof(Desktop), "Generating security attributes for '{0}'", mandatoryLevelSacl);

                            // If a security descriptor (in SDDL form) was provided convert it to binary form and then marshal
                            // it into native memory to that we can pass it in to the native method.
                            var rawSecurityDescriptor = new RawSecurityDescriptor(mandatoryLevelSacl);

                            var binaryForm = new byte[rawSecurityDescriptor.BinaryLength];
                            rawSecurityDescriptor.GetBinaryForm(binaryForm, 0 /* offset */);

                            var nativeBinaryForm = disposalEscrow.Add(new SafeHGlobalBuffer(binaryForm.Length));
                            Marshal.Copy(binaryForm, 0 /* startIndex */, nativeBinaryForm.DangerousGetHandle(), binaryForm.Length);

                            securityAttributes = new SECURITY_ATTRIBUTES {
                                lpSecurityDescriptor = nativeBinaryForm.DangerousGetHandle()
                            };
                        }

                        // Since we're creating the desktop we'll ask for all rights so that we have the ability to modify security as
                        // required. Since the created desktop will be referenced by name when creating the process this will not impact
                        // the security of the desktop.
                        IntPtr unsafeDesktopHandle = Methods.CreateDesktop(
                            name,
                            device: null,
                            deviceMode: null,
                            flags: 0,
                            accessMask: DESKTOP_RIGHTS.GENERIC_ALL,
                            attributes: securityAttributes);
                        if (unsafeDesktopHandle == IntPtr.Zero)
                        {
                            throw
                                new SandboxException(
                                    $"Unable to create new desktop",
                                    new Win32Exception());
                        }

                        tracer.Trace(nameof(Desktop), "Desktop successfully created");

                        return(new Desktop(unsafeDesktopHandle, ownsHandle: true)
                        {
                            Name = name,
                        });
                    }
                    finally
                    {
                        // Since CreateDesktop automatically switches to the new desktop we need to make sure that we switch back
                        // to the original one.
                        currentDesktop.MakeCurrent();
                    }
                }
        }
        private SafeTokenHandle GetRestrictedToken()
        {
            if (this.restrictedTokenHandle == null)
            {
                using (var localDisposalEscrow = new DisposalEscrow())
                {
                    // The first step in creating a restricted token is to enumerate the existing one and decide which of
                    // the SIDs we want to deny, and which of the SIDs we want to restrict. For our purposes we want:
                    //
                    // DENY all except:
                    //      CurrentUser
                    //      Everyone
                    //      Users
                    //      Interactive
                    //      Logon
                    //
                    // RESTRICT only:
                    //      CurrentUser
                    //      Everyone
                    //      Users
                    //      Logon
                    //      Restricted

                    var sidsToDeny     = new List <SID_AND_ATTRIBUTES>();
                    var sidsToRestrict = new List <SID_AND_ATTRIBUTES>();

                    foreach (IdentityReference identityReference in this.identityProvider.CurrentUser.Groups)
                    {
                        var securityIdentifier = (SecurityIdentifier)identityReference.Translate(typeof(SecurityIdentifier));

                        if (securityIdentifier.Equals(this.identityProvider.EveryoneSid) ||
                            securityIdentifier.Equals(this.identityProvider.UsersSid))
                        {
                            // Add the group to the restricted list if it's one of the special groups we want to allow.
                            sidsToRestrict.Add(
                                RestrictedProcessProtection.ConvertSecurityIdentifierToSidAndAttributes(securityIdentifier, localDisposalEscrow));
                        }
                        else if (!securityIdentifier.Equals(this.identityProvider.InteractiveSid))
                        {
                            // Otherwise add the group to the deny list, but special case the Interactive SID which
                            // shouldn't be denied and also not restricted.
                            sidsToDeny.Add(
                                RestrictedProcessProtection.ConvertSecurityIdentifierToSidAndAttributes(securityIdentifier, localDisposalEscrow));
                        }
                    }

                    // There are a set of non-group SIDs that we want to always restrict and never deny, so add them to
                    // the appropriate lists.
                    sidsToRestrict.Add(
                        RestrictedProcessProtection.ConvertSecurityIdentifierToSidAndAttributes(
                            this.identityProvider.CurrentUserSid,
                            localDisposalEscrow));
                    sidsToRestrict.Add(
                        RestrictedProcessProtection.ConvertSecurityIdentifierToSidAndAttributes(
                            this.identityProvider.LogonSid,
                            localDisposalEscrow));
                    sidsToRestrict.Add(
                        RestrictedProcessProtection.ConvertSecurityIdentifierToSidAndAttributes(
                            this.identityProvider.RestrictedSid,
                            localDisposalEscrow));

                    this.tracer.Trace(nameof(RestrictedProcessProtection), "Creating restricted token");

                    // Now that we have all the SIDs in the correct buckets we can call the native method to generate our
                    // new token.
                    SafeTokenHandle newTokenHandle;
                    if (!Methods.CreateRestrictedToken(
                            localDisposalEscrow.Add(new SafeTokenHandle(this.identityProvider.CurrentUser.Token, ownsHandle: false)),
                            RESTRICTED_TOKEN_FLAGS.DISABLE_MAX_PRIVILEGE,
                            (uint)sidsToDeny.Count,
                            sidsToDeny.ToArray(),
                            0 /* deletePrivilegeCount */,
                            null /* privilegesToDelete */,
                            (uint)sidsToRestrict.Count,
                            sidsToRestrict.ToArray(),
                            out newTokenHandle))
                    {
                        throw
                            new SandboxException(
                                "Unable to create restricted token",
                                new Win32Exception());
                    }

                    // We'll add the token here in case something breaks, but we'll remove it before we return the token at
                    // the end of this method.
                    localDisposalEscrow.Add(newTokenHandle);

                    this.tracer.Trace(nameof(RestrictedProcessProtection), "Adding mandatory low SID");

                    // Create the low integrity SID.
                    SafeSecurityIdentifier restrictedSecurityIdentifierNative;
                    if (!Methods.AllocateAndInitializeSid(
                            ref Constants.SECURITY_MANDATORY_LABEL_AUTHORITY,
                            1 /* nSubAuthorityCount */,
                            (int)SECURITY_MANDATOR_RID.LOW,
                            0 /* dwSubAuthority1 */,
                            0 /* dwSubAuthority2 */,
                            0 /* dwSubAuthority3 */,
                            0 /* dwSubAuthority4 */,
                            0 /* dwSubAuthority5 */,
                            0 /* dwSubAuthority6 */,
                            0 /* dwSubAuthority7 */,
                            out restrictedSecurityIdentifierNative))
                    {
                        throw
                            new SandboxException(
                                "Unable to allocate and initialize low integrity SID",
                                new Win32Exception());
                    }

                    // Set the integrity level in the access token to low using the low integrity SID we just created.
                    TOKEN_MANDATORY_LABEL managedTokenMandatoryLabel;
                    managedTokenMandatoryLabel.Label.Attributes = SID_ATTRIBUTES.SE_GROUP_INTEGRITY;
                    managedTokenMandatoryLabel.Label.Sid        = restrictedSecurityIdentifierNative.DangerousGetHandle();

                    var nativeTokenMandatoryLabel = localDisposalEscrow.Add(new SafeHGlobalBuffer(Marshal.SizeOf(managedTokenMandatoryLabel)));
                    Marshal.StructureToPtr(managedTokenMandatoryLabel, nativeTokenMandatoryLabel.DangerousGetHandle(), false);

                    if (!Methods.SetTokenInformation(
                            newTokenHandle,
                            TOKEN_INFORMATION_CLASS.TokenIntegrityLevel,
                            nativeTokenMandatoryLabel.DangerousGetHandle(),
                            nativeTokenMandatoryLabel.Size +
                            Methods.GetLengthSid(restrictedSecurityIdentifierNative.DangerousGetHandle())))
                    {
                        throw
                            new SandboxException(
                                "Unable to set token integrity level",
                                new Win32Exception());
                    }

                    this.tracer.Trace(nameof(RestrictedProcessProtection), "Granting login SID access to token");

                    // Now modify the access token to set the DACL so that the Logon SID has appropriate access to any
                    // processes started using the token. Without this things mostly work, but the process will be
                    // restricted in ways that .NET doesn't like (E.g. it's not possible to get the token associated with
                    // the process)
                    var defaultDaclDescriptor = new RawSecurityDescriptor(
                        string.Format(
                            RestrictedProcessProtection.DefaultDaclTemplate,
                            this.identityProvider.LogonSid.Value));

                    var managedDefaultDacl = new byte[defaultDaclDescriptor.DiscretionaryAcl.BinaryLength];
                    defaultDaclDescriptor.DiscretionaryAcl.GetBinaryForm(managedDefaultDacl, 0 /* offset */);

                    var nativeDefaultDacl = localDisposalEscrow.Add(new SafeHGlobalBuffer(managedDefaultDacl.Length));
                    Marshal.Copy(
                        managedDefaultDacl,
                        0 /* startIndex */,
                        nativeDefaultDacl.DangerousGetHandle(),
                        managedDefaultDacl.Length);

                    TOKEN_DEFAULT_DACL managedTokenDefaultDacl;
                    managedTokenDefaultDacl.DefaultDacl = nativeDefaultDacl.DangerousGetHandle();

                    var nativeTokenDefaultDacl = localDisposalEscrow.Add(new SafeHGlobalBuffer(Marshal.SizeOf(managedTokenDefaultDacl)));
                    Marshal.StructureToPtr(managedTokenDefaultDacl, nativeTokenDefaultDacl.DangerousGetHandle(), false /* fDeleteOld */);

                    if (!Methods.SetTokenInformation(
                            newTokenHandle,
                            TOKEN_INFORMATION_CLASS.TokenDefaultDacl,
                            nativeTokenDefaultDacl.DangerousGetHandle(),
                            nativeTokenDefaultDacl.Size))
                    {
                        throw
                            new SandboxException(
                                "Unable to set the DACL to give the Logon SID access to the process",
                                new Win32Exception());
                    }

                    this.disposalEscrow.Transfer(localDisposalEscrow, newTokenHandle);
                    this.restrictedTokenHandle = newTokenHandle;
                }
            }

            return(this.restrictedTokenHandle);
        }