BPL Logo Banner
CareersContact

Welcome to the final installment of The Attacker’s Playbook. So far, we’ve covered exploiting the human element (Part 1), finding unlocked doors (Part 2), and using forgotten keys to the kingdom (Part 3) to get inside a network.

But initial access is just the beginning. The real damage is done when we move through the internal network, abusing trust and misconfigurations to turn a minor foothold into a major compromise.

Navigating the Active Directory Maze

Active Directory (AD) is the heart of most corporate networks, but its permissions can become a tangled “rat’s nest” of complexity. An automated vulnerability scanner can’t understand the intricate web of trust relationships that a human attacker can exploit.

The Flaw: A standard user account may seem harmless, but it could have a hidden, indirect path to administrative power. It’s not uncommon to find that the default “Domain Users” group has overly broad permissions, such as the ability to log into nearly every computer on the network, including servers. 

Specifically, we look for the “GenericAll” permission. This allows one group to fully control another object. Even if the target account is disabled, an attacker with GenericAll rights can enable it and abuse those permissions.

The Attack: As we acquire user accounts, we use Bloodhound to map complex relationships and find unexpected privilege escalation paths.

This exact flaw was enumerated on a recent pentest where the “Domain Users” group had GenericAll access to a disabled service account. Seemingly for no reason, this permission existed on an account no one was monitoring.

From there, we enabled the account, reset the password, and masqueraded as that service account on the network. We discovered this service account had interesting permissions, including the ability to modify the password of a Domain Administrator.

After confirming this action was allowed within the Rules of Engagement (ROE), the team abused these permissions to take over the entire AD forest and ultimately the network.

The Result:  By manually navigating this maze, we can turn a low-privilege account into a full Domain Administrator, demonstrating a critical risk that no vulnerability scanner would discover.

Mitigation Strategies: Active Directory is a common target because it’s often a “rat’s nest” of complex permissions.

Thinking Like an Attacker

The most damaging breaches rarely rely on a single vulnerability. Instead, they emerge from creative chaining of minor, overlooked issues, a forgotten file, an exposed port, a default password—into a pathway through the maze of internal trust.

Key Takeaways:

Defending against these threats requires adopting the attacker’s perspective. This is where red team expertise provides the greatest value, simulating real-world thinking, not just automated scans.

Your security journey doesn’t end with this playbook. Contact us to learn how our expert-led penetration testing helps organizations secure their networks from the inside out.


That wraps up The Attacker’s Playbook. But your security journey shouldn’t stop here. Check out our deeper dives on Active Directory in  “Abusing Active Directory: Down the Rabbit Hole We Go,” and watch this short clip from our recent webinar on Active Directory permission risks  — or get in touch to test your defenses before attackers do.

In previous installments of The Attacker’s Playbook, we explored how human error (Part 1) creates the initial opportunity and how attackers exploit forgotten physical and digital entry points (Part 2).

But truth be told, we don’t always have to work that hard. Often, the “keys to the kingdom” are left right out in the open, in the form of default passwords, reused credentials, and sensitive data stored in the wrong places.

Ghost Keys in the Machine: The Danger of Default & Weak Passwords

The simplest access control failures are often the most effective. We operate under the principle that default credentials are the same as having no authentication at all.

The Flaw: We find these “ghost keys” everywhere, from routers and medical software to printers. One of the most glaring examples involved finding cameras in medical rooms with live feeds of patients, accessible to anyone who knew the default password.

However, the risk extends beyond defaults. Attackers also hunt for the obscure Active Directory (AD) attribute known as “PassNotReq” (Password Not Required). If this is set to true, the user can circumvent password policies and set any password they want—including a blank one.

We use specialized tools like AD-RECON, an Active Directory reconnaissance tool developed by BreakPoint Labs, to quickly triage Bloodhound data and identify accounts with this attribute, as they are likely configured with weak or nonexistent passwords.

The Attack: An attacker will always start by checking for default credentials and password reuse. They look for exposed configuration files or scripts, such as a PowerShell script that sets the same local admin password across the network, which is essentially a reusable key to the entire kingdom.

In one engagement, we found a Domain Admin account from 2003 with a completely blank password. It’s important to note that this blank password wasn’t a default setting; It was a human-configured error that had remained undetected since its inception in 2003. Why did it persist for so long? Because vulnerability scanners generally don’t check AD password complexity or perform brute-force attacks, and pentesters often assume “blank” won’t work, so they don’t even try it.

The Result: A single default password can allow an attacker to gain a critical foothold, view sensitive patient video, or even achieve full domain takeover.

Mitigation Strategies: Basic security hygiene is non-negotiable.

The Data Graveyard: When Sensitive Information Goes Walkabout

So, why discuss passwords and data in the same post? Because they are the two halves of the “Keys to the Kingdom.” Passwords are the key, and the data is the treasure inside the chest. Once an attacker uses those weak credentials to get in, they immediately start hunting for the data you forgot about.

The Flaw: It’s extremely common to find Protected Health Information (PHI) and PII on systems where it has no business being. This data sprawl is typically the result of human error and a lack of training or convenience, like a doctor bypassing the secure network to use the Guest WiFi for faster internet access. We often find ourselves asking, “Why is there an exposed network share containing not just PHI, but old ransomware artifacts?”

The Attack: An attacker with internal access will hunt through these misplaced files. In one instance, we discovered dumps of email communications between thousands of students and their school’s healthcare center, filled with vast amounts of PHI and left on an easily accessible share.

The Result: This lack of data governance creates a massive, hidden risk. A single compromised workstation can lead to a catastrophic data breach if that station has access to years of forgotten, sensitive files.

Mitigation Strategies: Data governance is a critical, often-overlooked security control.

Protecting Your Critical Data

Leaving keys and sensitive documents out in the open is an invitation to attackers. Whether it’s a text file, an unlocked port, or a default password, poor security hygiene is an attacker’s best friend.

Key Takeaways:

Beyond compliance, protecting patient data is about resilience. Our security assessments simulate how attackers exploit these weaknesses, helping you find and fix them before they’re used against you.


So what happens once we’ve got the keys? That’s when the real damage begins. In the final chapter, we dive deep into the maze of internal trust—exploiting complex Active Directory paths that scanners can’t see. Part 4 is where persistence and privilege collide.


Finding the paths to escalate privileges in Active Directory (AD) can be challenging when dealing with a complex web of permissions, as shown in the picture above. This blog explains a method for discovering less obvious escalation paths by focusing on accounts with broad access that are not well protected (AdminCount False). 

Often, during offensive engagements, you’ll find yourself with some initial access to the network, and you want to expand your access. On most networks, this can be achieved most efficiently by escalating privileges within Active Directory (AD). There are numerous ways to identify escalation paths, which generally involve identifying AD objects that are not properly protected and can perform a variety of interesting actions. 

Understanding AdminCount 

One interesting way to identify this is by analyzing the AD object attribute “AdminCount”. Microsoft leverages this attribute to apply additional protections to the object. I won’t be going over this in detail here, but you can learn more here: https://learn.microsoft.com/en-us/windows/win32/adschema/a-admincount 

This value is set to either “True” or “False” with an AD object. AD objects that can do a lot of actions within the Domain are assigned a value of “True” (Example: Domain Admins, Enterprise Admins, etc.). The way I see networks is that you can divide them into two sections based on inbound permissions: objects with AdminCount: False, which allow more things to reach them, and objects with AdminCount: True, which allow fewer things to reach them. 

It’s actually somewhat predictable when looking at an object’s inbound permissions. If AdminCount False, usually the same set of objects can reach it, and if it’s True, the same objects can reach it. Typically, you will not have an object with AdminCount: False that can reach an object that has AdminCount: True. If you do this, you are likely to identify new escalation paths. 

When performing engagements, I often find a handful of accounts that numerous AD objects can reach due to AdminCount: False, which have excessive permissions that expose the entire domain to takeover. Generally, it’s just these few accounts that open the door for any attacker who can gain access from a lower-level object (for example, a Tier 1 help desk) and then escalate to these accounts, ultimately taking over the entire domain. 

Common Vulnerabilities/Examples

CVE-2017-8613: A good example of this common issue is with the Azure Sync account (MSOL_XXX). These accounts are frequently vulnerable to this permission abuse path because, for a considerable period, Microsoft failed to apply the AdminCount: True setting to these high-access accounts (they can dcsync by design).  

https://cve.mitre.org/cgi-bin/cvename.cgi?name=cve-2017-8613

https://learn.microsoft.com/en-us/security-updates/securityadvisories/2017/4033453

This same vulnerability is introduced to AD environments due to poor permission configuration. I observe this pattern in every engagement, and I wanted to share my analysis techniques to help others identify these escalation paths. 

Analysis Technique 

So, what I started doing on engagements is auditing all user accounts with AdminCount: False and their outbound rights. The idea is to identify accounts with lower inbound protections that can achieve a lot. 

In Bloodhound, you can see this attribute in the “Node Info” tab:

You can list out all users with their AdminCount value via a simple query: 

MATCH (u:User) return u.samaccountname, u.admincount 

Note: I find it helpful to return the admincount attribute for most of the queries. This is provided as a simple example. It’s just good context to have when analyzing various attack paths. 

Now, to automate this further, we need to determine their outbound rights to find who is admincount: false and can do more in the domain, as this will find interesting escalation paths similar to CVE-2017-8613: 

Code Snippet Explanation 

def get_admincountFalseUsers_trans(): 

    with GraphDatabase.driver(URI, auth=AUTH) as driver: 

        result = do_query(driver, "MATCH (m:User {admincount: false}) return m.name, m.objectid, m.admincount") 

        admincount_outbound_file=open("admincount_false_outbound_trans_rights.txt", "w") 

        for record in result: 

          if record["m.name"]: 

              username = record["m.name"] 

              objectid = record["m.objectid"] 

              admincount= record["m.admincount"] 

              result1 = do_query(driver, "MATCH (n) WHERE NOT n.objectid='"+objectid+"' MATCH p=shortestPath((u:User {objectid: '"+objectid+"'})-[r1:MemberOf|AddSelf|WriteSPN|AddKeyCredentialLink|AddMember|AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n)) RETURN count(p)") 

              for record1 in result1: 

                  if record1["count(p)"]: 

                      trans_rights=str(record1["count(p)"]) 

                      admincount_outbound_file.write("[-] User: "+username+" Transitive Outbound Rights: "+trans_rights+" Admincount: "+str(admincount)+"\n") 

        admincount_outbound_file.close() 

 

    with open("admincount_false_outbound_trans_rights.txt", "r") as fp: 

        entries = str(len(fp.readlines())) 

    print("[+] Generating a List of Users with AdminCount=False Transitive Outbound Rights: admincount_false_user_outbound_trans_rights.txt ("+entries+") lines")

Interpreting Results  

The code snippet is pulled from AD-RECON; it will retrieve a list of users with AdminCount: False and then gather the numerical value associated with their transitive outbound rights. Then I will sort the output with bash because I failed to produce the output sorted in Python; maybe I will change that, but for now, just do the following: 

``` 

└─$ cat admincount_false_outbound_trans_rights.txt| sort -k 7,7 -nr | less -S 

``` 

This will sort the output based on the 7th element in the line, which is the numerical count for the transitive outbound rights in reverse numerical order (so highest rights on top): 

 

``` 

[-] User: MSOL_F367GGD8CDD8@ORGRIMMAR.ORG Transitive Outbound Rights: 43848 Admincount: False 

[-] User: acct1@ORGRIMMAR.ORG Transitive Outbound Rights: 43847 Admincount: False 

[-] User: passreset@ORGRIMMAR.ORG Transitive Outbound Rights: 43847 Admincount: False 

[-] User: domainjoin@ORGRIMMAR.ORG Transitive Outbound Rights: 43847 Admincount: False 

[-] User: carol@ORGRIMMAR.ORG Transitive Outbound Rights: 43847 Admincount: False 

... 

 

``` 

The result is output that quickly identifies accounts with lower inbound protections, which can lead to various consequences. Now, all these aren’t always exactly the problem themselves, so you must look at the actual transitive outbound rights for these objects to see how they’re getting their permissions. When you do this, you’ll see there are a handful of accounts ending up in this graph that are allowing these lower-level Admincount: False accounts to do a lot of things. 

Example: Since the MSOL account can dcsync and essentially take over the domain, and its Admincount is false, the “passreset” account can change its password and thus has an escalation path to take over the domain. Had the MSOL account been adequately protected with AdminCount set to True, this escalation path might not have existed. On most networks, you will find that the MSOL account is a problem, but it is not the only account that extends permissions up to higher-level AD objects.  

Another example we often see is that the account used to add computers to the domain may not be properly configured and may not have permissions over the Domain Controllers (such as WriteAccountRestrictions and Owns), which creates a potential escalation path. See, getting to the domain controllers might be hard, but because the AdminCount: False for the “domainjoin” account, you just need to get any account that can reset a password (example, a Tier 1 help desk), and then take over the domainjoin account, and then the Domain Controllers.

Key Takeaways 

When diving deep into AD, as in this case, you can discover numerous additional escalation paths to enhance security, not just the one initially required to achieve the desired access. Please note that on a Red Team, this might not be the primary goal, but for a penetration test (PenTest), it’s beneficial to include as many escalation paths as you can identify. 

Author Image

About the Author

Andrew McNicol is the Chief Technology Officer and a co-founder of BreakPoint Labs, where he oversees the company’s technical strategy and manages the Cybersecurity Assessments line of business. He is a recognized expert in adversarial penetration testing, with more than 15 years of experience leading numerous technical teams on red team operations, vulnerability assessments, and penetration testing engagements to achieve each client’s specific objectives. Andrew holds numerous professional certifications and is a frequent speaker at government and industry security forums.

In penetration tests and red team engagements, we frequently exploit privilege escalation paths in Active Directory (AD). When discussing remediation, clients often think, “I’ll just disable that account to eliminate the risk.” However, in some instances, simply disabling the account does not remove the risk. When we explain this to customers, they tend to look confused because the account is indeed disabled. Still, the residual risk is tied to specific escalations involving permissions over a higher-privilege account from alternative access. For example, privileges such as “WriteDACL” or “GenericAll” can enable an attacker to exploit a disabled account by re-enabling it and leveraging the escalation path as if it were never disabled.

Basic Example:

During an engagement, the team gains access to several users through password guessing or by discovering cleartext passwords on shared drives and begins evaluating the access and privilege escalation paths of these compromised users within Active Directory. The team identifies one user (User A) who has (GenericAll) rights over User (B).

This is especially interesting because appears that User A’s access to User B is somewhat outside the normal permissions, as the rest of the permission paths reside with high-value targets.

Further investigation shows that User B can do a lot of interesting things (effectively has control over all the domain objects):

In this instance, User B is disabled. However, since the attackers have User A, who has “GenericAll” over User B, they can enable the user and leverage it to escalate their privileges.

This perfectly demonstrates how simply disabling the account of User B does not reduce the risk for this attack path; it just adds an extra step for the attacker to enable the account, which is trivial.

So What Should You Do?

Now that you understand simply disabling accounts within AD doesn’t always eliminate the attack surface, you should consider the following:

  1. Removing the account if it’s no longer needed
  2. Removing the access from the account if you are unable to remove it entirely
  3. Evaluate attack paths to this disabled account to see if paths exist that expose you to risk

Tips for Investigation:

We find it useful to assess the outbound rights of all disabled accounts (we do this for every account in the domain, but to focus on the topic of this blog, we will concentrate on disabled users) to identify those with significant access. From this point, examine who has rights to reach them (inbound permissions). This can assist in pinpointing potential escalation paths from lower targets to disabled accounts that possess considerable access. Below are some helpful Cypher Queries you can leverage within Neo4j against the Bloodhound database:

List all Disabled Users:

MATCH (m:User {enabled: false}) return m.name, m.objectid, m.admincount

Using the SID for each user (m.objectid), you can list out transitive permissions for each user based on a count:

MATCH (n) WHERE NOT n.objectid='"+objectid+"' MATCH p=shortestPath((u:User 
{objectid: '"+objectid+"'})-
[r1:MemberOf|AddSelf|WriteSPN|AddKeyCredentialLink|AddMember|AllExtendedRights
|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n)) RETURN count(p)

In practice, we find querying is best done in Python. Below is a more complete snippet of code:

def get_disabledUserOutboundRights_transitive():
    with GraphDatabase.driver(URI, auth=AUTH) as driver:
        result = do_query(driver, "MATCH (m:User {enabled: false}) return m.name, m.objectid, m.admincount")
        disabled_outbound_file=open("disabled_user_outbound_transitive_rights.txt", "w")
        for record in result:
          if record["m.name"]:
              username = record["m.name"]
              objectid = record["m.objectid"]
              admincount= record["m.admincount"]
              result1 = do_query(driver, "MATCH (n) WHERE NOT n.objectid='"+objectid+"' MATCH 
p=shortestPath((u:User {objectid: '"+objectid+"'})-[r1:MemberOf|AddSelf|WriteSPN|AddKeyCredentialLink|AddMember|
AllExtendedRights|ForceChangePassword|GenericAll|GenericWrite|WriteDacl|WriteOwner|Owns*1..]->(n)) RETURN count(p)")
              for record1 in result1:
                  if record1["count(p)"]:
                      trans_rights=str(record1["count(p)"])
                      disabled_outbound_file.write("[-] User: "+username+" Transitive Outbound Rights: "+trans_rights+" 
Admincount: "+str(admincount)+"\n")
        disabled_outbound_file.close()

    with open("disabled_user_outbound_transitive_rights.txt", "r") as fp:
        entries = str(len(fp.readlines()))
    print("[+] Generating a List of Disabled Users with Transitive Outbound Rights: disabled_user_outbound_trans_rights.txt 
("+entries+") lines")

The result will make a text file of all the disabled users and their transitive outbound rights, one per line. This can help us analyze the outbound rights of all disabled users quickly to find interesting targets for additional analysis:

The output will look like this:

[-] User: userb@BREAKPOINT.LABS First Degree Outbound Rights: 20771 Admincount: True

So to sort the data based on "who has the most rights", first we will use the Linux sort utility based on a number in reverse 
order on the 7th element in the line (which is the outbound right count).

└─$ cat disabled_users_outbound_transitive_rights.txt | sort -nr -k 7,7 | less -S
[-] User: userb@BREAKPOINT.LABS Transitive Outbound Rights: 20771 Admincount: True
[-] User: userc@BREAKPOINT.LABS Transitive Outbound Rights: 6165 Admincount: True
[-] User:userd@BREAKPOINT.LABS Transitive Outbound Rights: 5098 Admincount: True
[-] User: usere@BREAKPOINT.LABS Transitive Outbound Rights: 4821 Admincount: True
[-] User: userf@BREAKPOINT.LABS Transitive Outbound Rights: 4821 Admincount: True

From here, review the inbound rights for these accounts in Bloodhound, and you might discover an interesting way to control one of these disabled users who have extensive rights in the domain.

Key Takeaways

Disabling an Active Directory account does not always equate to eliminating risk. Attackers can potentially re-enable disabled accounts if they possess the necessary permissions, such as “GenericAll” or “WriteDACL,” on those accounts. To truly mitigate risks, organizations should go beyond simply disabling accounts. Consider removing the account entirely if it’s no longer needed, revoking its access rights, and thoroughly evaluating potential attack paths leading to these disabled accounts.

By proactively assessing outbound and inbound rights, especially for disabled accounts with significant access, and utilizing tools like Bloodhound and Cypher queries, you can uncover hidden vulnerabilities and strengthen your Active Directory security posture. Remember, a seemingly disabled account can still pose a threat if the underlying permissions are not properly addressed.

Author Image

About the Author

Andrew McNicol is the Chief Technology Officer and a co-founder of BreakPoint Labs, where he oversees the company’s technical strategy and manages the Cybersecurity Assessments line of business. He is a recognized expert in adversarial penetration testing, with more than 16 years of experience leading numerous technical teams on red team operations, vulnerability assessments, and penetration testing engagements to achieve each client’s specific objectives. Andrew holds numerous professional certifications and is a frequent speaker at government and industry security forums.

chevron-down