Last week I published an article about the changes in the behavior of Group Policy processing after the deployment of security update MS16-072 under KB3163622. It included a script to assist with the remediation of Group Policy permissions: Script to report on and remediate the Group Policy security change in MS16-072.
Of course that’s not where it ends. What about new Group Policies? Do you create a procedure that requires you to add “Domain Computers” with Read permission every time you create a new Group Policy Object (GPO)? No…of course not!
What we need to do now is change the defaultSecurityDescriptor attribute on the Group-Policy-Container schema class object so that new GPOs are created with Domain Computers having Read permissions by default. Microsoft didn’t released an official script or method to do this, so here’s the next best thing.
I will note that Darren Mar-Elia created a blog post to show you how to manually do this. Jeremy Moskowitz also included these steps in his article.
I’m a big fan of scripting, and already had a script to this that I wrote back in 2011 using the Quest ActiveRoles Snapin Module. I wrote it for a delegated administrative model I implemented for a large University. I also recently found a great script from Peter Hinchley: Set Default Permissions for New Group Policy Objects. So I took both scripts and merged the ideas, basing a new script on Peter’s. The outcome was a very simple and repeatable way to modify the defaultSecurityDescriptor on the Group-Policy-Container schema class object without the need to go through the steps that Darren and Jeremy laid out.
Before running the script you (or the account you’re running the script as) must be a member of the Schema Admins group.
Run the script with no parameters (in report only mode) and it will report on the existing Security Descriptor, and what the new one will look like. So it’s basically showing you the projected outcome. The following screen shot shows that DC (the acronym for the well-known SID of Domain Computers) is added with Read permissions.
So how do we know that we’re applying Read permissions? Well I guess you need to understand a little bit about the Security Descriptor Definition Language (SDDL). Without going too deeply into SDDL in this article, the specific permission we are adding here is made up of the following 6 fields:
ACE Type:
- A = Access Allowed
ACE Flags:
- CI = Container Inherit
Permissions:
- LC = List Contents
- RP = Read All Properties
- LO = List Object
- RC = Read Permissions
ObjectType:
- (nothing added here, hence why the field is blank)
Inherited Object Type:
- (nothing added here, hence why the field is blank)
Trustee:
- DC = Domain Computers
Running the script again with the -Action parameter applies the change as demonstrated in the following screen shot.
So now when you create a new Group Policy Object (GPO), Domain Computers will have Read permissions by default!
How easy is that?
Here’s an improved version written by Stu (kewalaka@gmail.com) and posted to his Github repository.
I’ve made this script as generic as possible so that it can be used for other purposes too.
Here is the Modify-GroupPolicyContainer.ps1 (1044 downloads) script:
<# This script will modify the defaultSecurityDescriptor attribute on the Group-Policy-Container schema class object, which ensures that a specific set of permissions are applied by default to new Group Policy Objects (GPOs). It's been setup to add groups with either/or: - Modify Permissions - Read Permissions This script was specifically written to overcome the Group Policy security change Microsoft made in security update MS16-072 (KB3163622) by ensuring that Domain Computers is set with default Read permissions. This is the defaultSecurityDescriptor of the Group-Policy-Container class object: D:P(A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;DA)(A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;EA) (A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;CO)(A;CI;RPWPCCDCLCLORCWOWDSDDTSW;;;SY) (A;CI;RPLCLORC;;;AU)(OA;CI;CR;edacfd8f-ffb3-11d1-b41d-00a0c968f939;;AU)(A;CI;LCRPLORC;;;ED) Where... - (A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;DA) = Domain Admins - (A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;EA) = Enterprise Admins - (A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;CO) = Creator Owner - (A;CI;RPWPCCDCLCLORCWOWDSDDTSW;;;SY) = System - (A;CI;RPLCLORC;;;AU) = Authenticated Users - (OA;CI;CR;edacfd8f-ffb3-11d1-b41d-00a0c968f939;;AU) = Authenticated Users - (A;CI;LCRPLORC;;;ED) = Enterprise Domain Controllers This translates to the following: ACE Type: - A = ACCESS ALLOWED - OA = OBJECT ACCESS ALLOWED: ONLY APPLIES TO A SUBSET OF THE OBJECT(S). ACE Flags: - CI = CONTAINER INHERIT: Child objects that are containers, such as directories, inherit the ACE as an explicit ACE. Permissions: - RC = Read Permissions - SD = Delete - WD = Modify Permissions - WO = Modify Owner - RP = Read All Properties - WP = Write All Properties - CC = Create All Child Objects - DC = Delete All Child Objects - LC = List Contents - SW = All Validated Writes - LO = List Object - DT = Delete Subtree - CR = All Extended Rights Trustee: - DA = Domain Admins - EA = Enterprise Admins - CO = Creator Owner - SY = System - AU = Authenticated Users - ED = Enterprise Domain Controllers So we simply need to append these: - (A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;<Creator Owners Group Sid>) - (A;CI;LCRPLORC;;;<Read Only Group Sid>) Some good references to help you understand this further: - http://www.sdmsoftware.com/general-stuff/group-policy-delegation/ - http://support.microsoft.com/kb/321476 - http://clintboessen.blogspot.com/2011/08/ad-delegation-how-to-set-default.html - https://blogs.technet.microsoft.com/askds/2008/04/18/the-security-descriptor-definition-language-of-love-part-1/ - https://blogs.technet.microsoft.com/askds/2008/05/07/the-security-descriptor-definition-language-of-love-part-2/ This script is based on: - A script I originally wrote on 11th November 2011 using the Quest ActiveRoles Snapin Module for a delegated administrative model for a large University. - A script published by Peter Hinchley 10th Oct 2015: Set Default Permissions for New Group Policy Objects http://hinchley.net/2015/10/10/set-default-permissions-for-new-group-policy-objects/ Syntax examples: - To execute the script in report only mode: Modify-GroupPolicyContainer.ps1 - To execute the script and take action: Modify-GroupPolicyContainer.ps1 -Action Script name: Modify-GroupPolicyContainer.ps1 Release 1.2 Written by Jeremy Saunders (jeremy@jhouseconsulting.com) 11th November 2011 Modified by Jeremy Saunders (jeremy@jhouseconsulting.com) 28th June 2016 #> #------------------------------------------------------------- param( [switch]$Action ) # Set Powershell Compatibility Mode Set-StrictMode -Version 2.0 #------------------------------------------------------------- # Set the Read Permissions Group Name. $ReadOnlyGroup = "Domain Computers" # Set the Modify Permissions Group Name. $ModifyGroup = "" # Set this to True to reset the base security descriptor to default settings. $ResetDefault = $False $defaultSecurityDescriptor = @" D:P(A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;DA)(A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;EA)(A;CI;RPWPCCDCLCLOLORCWOWDSDDTSW;;;CO)(A;CI;RPWPCCDCLCLORCWOWDSDDTSW;;;SY)(A;CI;RPLCLORC;;;AU)(OA;CI;CR;edacfd8f-ffb3-11d1-b41d-00a0c968f939;;AU)(A;CI;LCRPLORC;;;ED) "@ #------------------------------------------------------------- # You must be a member of the Schema Admins group to perform this task. # Get Group Membership of current user try { $groups = (([System.Security.Principal.WindowsIdentity]::GetCurrent()).Groups | %{ $_.Translate([System.Security.Principal.NTAccount]) } | Sort) -join "`r`n" } catch { "Groups could not be retrieved." } # Check if current user is a member of Schema Admins $IsMemberOfSchemaAdmins = $False ForEach ($group in $groups) { If ($group -like "*\Schema Admins*") { $IsMemberOfSchemaAdmins = $True } } If ($IsMemberOfSchemaAdmins) { write-verbose "The current user is a member of the Schema Admins group." -verbose # Import the Active Directory Module Import-Module ActiveDirectory -WarningAction SilentlyContinue # Get Domain information. $domain = Get-ADDomain # Get Schema Master FSMO role holder. $SchemaMaster = (Get-ADForest -Server $domain.Forest).SchemaMaster write-verbose "The Schema Master is: $SchemaMaster" -verbose # Get the Naming Context (NC) for the Schema $schemaNamingContext = (Get-ADRootDSE).schemaNamingContext # Get existing security descriptor for group policy container from schema partition in Active Directory. $descriptor = ($container = Get-ADObject -Server $SchemaMaster "CN=Group-Policy-Container,$schemaNamingContext" -Properties defaultSecurityDescriptor).defaultSecurityDescriptor write-verbose "The existing security descriptor is:" -verbose $descriptor If ($ResetDefault) { $descriptor = $defaultSecurityDescriptor write-verbose "Resetting the security descriptor to default:" -verbose $descriptor } If ($ReadOnlyGroup -ne "") { write-verbose "Adding the read only group to the security descriptor." -verbose switch ($ReadOnlyGroup) { "Domain Computers" { # Use the commonly used acronym of DC for the well-known SID $reader = "DC"; Break } "Authenticated Users" { # Use the commonly used acronym of AU for the well-known SID $reader = "AU"; Break } default { # Get SID of ReadOnlyGroup. $reader = New-Object System.Security.Principal.NTAccount($domain.NetBIOSName, "$ReadOnlyGroup") $reader = $reader.Translate([System.Security.Principal.SecurityIdentifier]).value } } # Set the access control entry for the Read Only group. $descriptor = $descriptor + "(A;CI;LCRPLORC;;;$reader)" } If ($ModifyGroup -ne "") { write-verbose "Adding the modify group to the security descriptor." -verbose # Get SID of ModifyGroup. $modifier = New-Object System.Security.Principal.NTAccount($domain.NetBIOSName, "$ModifyGroup") $modifier = $modifier.Translate([System.Security.Principal.SecurityIdentifier]).value # Set the access control entry for the Modify group. $descriptor = $descriptor + "(A;CI;RPWPCCDCLCLORCWOWDSDDTSW;;;$modifier)" } If ($Action) { # Concatenate the access control entries with the existing security descriptor. $container | Set-ADObject -Replace @{defaultSecurityDescriptor = "$descriptor";} -Server $SchemaMaster write-verbose "The new security descriptor after the change is:" -verbose (Get-ADObject -Server $SchemaMaster "CN=Group-Policy-Container,$schemaNamingContext" -Properties defaultSecurityDescriptor).defaultSecurityDescriptor } Else { write-verbose "The new security descriptor to be applied is:" -verbose $descriptor } } Else { write-warning "The current user is NOT a member of the Schema Admins group." -verbose write-warning "This is a requirement to run this script." -verbose }
Enjoy!