Before we can really dive into modifying GPOs, we need to try and understand some of the intricacies of how they’re updated normally in GPMC and AD. Because believe me, it aint as simple as it appears.
When you first create and name a GPO and then look at it the Details tab, you’ll see it has these User
and Computer
version fields, with # (AD), # (SYSVOL)
.
For every GPO, there is also a corresponding path on disk (which I believe is called the Group Policy Template) at C:\Windows\SYSVOL\domain\Policies\<guid>
. For this GPO, it would be C:\Windows\SYSVOL\domain\Policies\{F3003ADC-17E3-4FBE-A11E-6A41779ADD6E}
. From a domain member, you can also access SYSVOL over its network share e.g. \\testlab.local\SYSVOL\testlab.local\Policies\{F3003ADC-17E3-4FBE-A11E-6A41779ADD6E}
.
Inside the GPT, you have a Machine
and User
directory and a GPT.INI
file.
PS > ls "\\testlab.local\SYSVOL\testlab.local\Policies\{F3003ADC-17E3-4FBE-A11E-6A41779ADD6E}"
Directory: \\testlab.local\SYSVOL\testlab.local\Policies\{F3003ADC-17E3-4FBE-A11E-6A41779ADD6E}
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 13/01/2019 11:13 Machine
d----- 13/01/2019 11:13 User
-a---- 13/01/2019 11:13 59 GPT.INI
No points for guessing that Computer
policies are dropped into Machine
and User
policies into User
.
GPT.INI
is a really simple file that contains:
PS > cat "\\testlab.local\SYSVOL\testlab.local\Policies\{F3003ADC-17E3-4FBE-A11E-6A41779ADD6E}\GPT.INI"
[General]
Version=0
displayName=New Group Policy Object
Note that the
displayName
parameter never changes¯\_(ツ)_/¯
.
If we make some changes to the GPO and refresh GPMC, we can see that the AD
and SYSVOL
numbers for Computer
have been pushed up.
They seem to increase for every individual change that happens, but since GPMC does all sorts in the background that you can’t see unless you’re monitoring with something like Process Monitor, seemingly small changes can result in a large increase.
The AD
and SYSVOL
values are stored in different places, but are pretty important to understand.
SYSVOL
is stored in that GPT.INI
file. If we read it again, we’ll see that the version has changed.
PS > cat "\\testlab.local\SYSVOL\testlab.local\Policies\{F3003ADC-17E3-4FBE-A11E-6A41779ADD6E}\GPT.INI"
[General]
Version=12
displayName=New Group Policy Object
It gets a bit more complicated if you have a GPO that applies both Computer
and User
policies, since the number format of GPT.INI
changes.
PS > cat "\\testlab.local\SYSVOL\testlab.local\Policies\{F3003ADC-17E3-4FBE-A11E-6A41779ADD6E}\GPT.INI"
[General]
Version=65548
displayName=New Group Policy Object
version = [user verion][computer version]
where each value is 16 bits
.
To “translate”, we first convert the decimal
to hex
. With a calculator in scientific mode, this comes out to be 1000C
. But to make up for the fact these are 16 bit, it’s actually 0001000C
, because a calculator won’t display the leading zeros.
So 0001
is 1
and 000C
is 12
.
The AD
number is stored as an attribute on the Group Policy object in AD. You can query it with PowerView like:
PS > Get-DomainGPO -Identity "Test GPO" -Properties VersionNumber
versionnumber
-------------
65548
And it uses exactly the same format.
When you modify a GPO in GPMC it will update the corresponding files in SYSVOL, update the value in GPT.INI
and then update its versionnumber
attribute in AD.
To modify the GPO without GPMC, you have to go into SYSVOL and modify the files manually. E.g. if we wanted to deploy some new local admins using Restricted Groups, we’d have to modify C:\Windows\SYSVOL\domain\Policies\{F3003ADC-17E3-4FBE-A11E-6A41779ADD6E}\Machine\Microsoft\Windows NT\SecEdit\GptTmpl.inf
.
The limitation of updating the file without incrementing the AD
or SYSVOL
version numbers is that:
gpupdate /force
)To enable all clients to pull the changes as part of their regular group policy update schedule, you must increment both AD
and SYSVOL
version numbers manually.
GPT.INI
is straight forward as it’s just a text file. The versionnumber
attribute can be updated with PowerView:
PS > Get-DomainGPO -Identity "Test GPO" | Set-DomainObject -Set @{'versionnumber'='1337'}
PS > Get-DomainGPO -Identity "Test GPO" -Properties VersionNumber
versionnumber
-------------
1337
You must always keep the two values identical, otherwise you will cause AD / SYSVOL Version Mismatch
errors.