In part 1 I described the capabilities of the default domain admin account, and basically how “special” that account is.
This part of the series will focus on actually securing the default domain administrator account against being misused in any way while still allowing it to be used when really needed. Please be aware this applies to the default domain administrator account in any AD domain within an AD forest.
To secure the default domain admin account, do consider the following:
- Make sure it…:
REMARK: This can be achieved through GPOs, but not through Authentication Silos/Policies.- …CAN ONLY log on to writable domain controllers
- …CANNOT log on to any read-only domain controllers (interactive, service, batch, RDP)
- …CANNOT log on to any servers (interactive, service, batch, RDP)
- …CANNOT log on to any workstations or laptops. (interactive, service, batch, RDP)
- Configure it with a very strong password with at least 64 characters long. The first 50% of characters of the password is stored on a piece of paper within a sealed envelope, and the remaining 50% of characters of the password is stored on another piece of paper within another sealed envelope. You could apply the logic using 2 separate digital vaults. This is done so that at any time, not a single person knows the complete password. When updating the password on a regular basis (e.g. every 6 months), again 2 persons are required, each knowing only part of the password. At any time, only authorized persons should be able to access the partial password in any of the vaults, but not both vaults. Access to any of the vaults should also be monitored and audit at any time.
- It must not be in scope of any synchronization system or tool to any other identity store, whether that identity store is on-premises or in the cloud (e.g. Entra ID).
- The “servicePrincipalName” attribute is empty, i.e. no SPNs defined. This is the default and keep it like that! Adding at least 1 service principal to the account will make it vulnerable for the Kerberoasting Attack. DO NOT add any SPN!
- The “mail” attribute is empty, i.e. no e-mail address is defined.
REMARK: as described by a reader in the comments it is better to say the default domain administrator account should not be configured with anything Exchange related (i.e. a mailbox), nor Skype, nor teams. In other words: no app specific services should be enabled/configured for that account. - Do not configure or target the account with any logon or logoff script, through the “Log On Script” (Attribute “scriptPath”) on the account itself, or through any GPO.
- The “msDS-SupportedEncryptionTypes” attribute is configured with the value 28. This is equivalent to supporting RC4_HMAC_MD5 (4), AES128_CTS_HMAC_SHA1_96 (8) and AES256_CTS_HMAC_SHA1_96 (16)
REMARK: just checking the options “This Account Supports Kerberos AES 128 bit encryption” and “This Account Supports Kerberos AES 256 bit encryption” in Active Directory Users And Computers will ONLY configure AES128_CTS_HMAC_SHA1_96 (8) and AES256_CTS_HMAC_SHA1_96 (16 on the account. Configuring the “msDS-SupportedEncryptionTypes” attribute with the value 28 will still check the options “This Account Supports Kerberos AES 128 bit encryption” and “This Account Supports Kerberos AES 256 bit encryption” in Active Directory Users And Computers, but it will also support RC4_HMAC_MD5 - The account is made a member of only the following groups with the same AD domain, and nothing else:
REMARK: Do NOT add the default domain administrator account to the “Protected Users” group!- Administrators (SID S-1-5-32-544) (Available in any AD domain)
- Domain Admins (SID DomainSID-512) (Available in any AD domain)
- Enterprise Admins (SID ForestRootDomainSID-519) (Available in Forest Root Domain Only!)
- Denied RODC Password Replication Group” (SID DomainSID-572) (Available in any AD domain)
- The account can be renamed to something else, but this is not required and does not add any security
REMARK: This is what I did in my environment as you can see in the screenshots. It has been renamed to ADM.TEC - The “userAccountControl” attribute is configured with the value 1114626. This is equivalent to configuring the options ACCOUNTDISABLE (2) (Account Is Disabled), NORMAL_ACCOUNT (512) (Normal Account), DONT_EXPIRE_PASSWD (65536) (Password Never Expires) and NOT_DELEGATED (1048576) (Account Is Sensitive And Cannot Be Delegated)
REMARK: before disabling the default domain administrator account, test interactive and RDP logon first. Make sure to do this only highly trusted and privileged computers, like writable domain controllers, and nothing else.
REMARK: before disabling the default domain administrator account, make sure to have another personal domain admin account to be able to logon.
REMARK: In the Microsoft recommendations (https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/plan/security-best-practices/appendix-d–securing-built-in-administrator-accounts-in-active-directory) it is mentioned to not disable the account as during Forest Recovery it is needed. Even when disabling the account, after following the correct steps to enable it again after having done the restore of the backup, the account can still be used. It is just an additional step to execute. This obviously only applies when a forest recovery tool or process is used that requires the use of the default administrator account. For example, when using Semperis ADFR, this account is not necessarily needed during or after the recovery. - “Denied RODC Password Replication Group” (SID DomainSID-572) (Available in any AD domain)
Let’s have a quick look at the configuration of the default domain administrator account.
Get-ADUser -Identity "$((Get-ADDomain).DomainSID.value)-500" -Properties description,mail,memberOf,"msDS-SupportedEncryptionTypes",scriptPath,servicePrincipalNames,userAccountControl -Server $((Get-ADDomain).PDCEmulator)
Looking at Figure 1, the following configurable attributes to note as explained earlier:
- Account is DISABLED (Enabled = False)
- I added a GivenName and a Surname, but that is not required
- It is a member of:
- Administrators (SID S-1-5-32-544) (Available in any AD domain)
- Domain Admins (SID DomainSID-512) (Available in any AD domain)
- Enterprise Admins (SID ForestRootDomainSID-519) (Available in Forest Root Domain Only!)
- Denied RODC Password Replication Group (SID DomainSID-572) (Available in any AD domain)
- Supported Encryption Types: 28 (msDS-SupportedEncryptionTypes = 28)
- 4: RC4_HMAC_MD5
- 8: AES128_CTS_HMAC_SHA1_96
- 16: AES256_CTS_HMAC_SHA1_96
- Service Principals: NONE! (ServicePrincipalNames = { }) This is the default and keep it like that! Adding at least 1 service principal to the account will make it vulnerable for the Kerberoasting Attack
- User Account Control: 1114626
- 2: ACCOUNTDISABLE
- 512: NORMAL_ACCOUNT
- 65536: DONT_EXPIRE_PASSWD
- 1048576: NOT_DELEGATED
To secure the default domain administrator account, you can use the following PowerShell code in an elevated PowerShell command prompt window while logged on as another account that is a member of Domain Admins
Invoke-Command -ScriptBlock {
Clear-Host
Write-Host ""
Write-Host "#################################################" -Foregroundcolor Yellow
Write-Host "### SECURING THE DEFAULT DOMAIN ADMIN ACCOUNT ###" -Foregroundcolor Yellow
Write-Host "#################################################" -Foregroundcolor Yellow
$defaultDomainAdm = Get-ADUser -Identity "$((Get-ADDomain).DomainSID.value)-500" -Properties description,mail,memberOf,"msDS-PrincipalName","msDS-SupportedEncryptionTypes",servicePrincipalNames,userAccountControl -Server $((Get-ADDomain).PDCEmulator)
If ($defaultDomainAdm."msDS-PrincipalName" -ne ([Security.Principal.WindowsIdentity]::GetCurrent()).Name) {
$pwdPart1 = $(-join (33..126 | ForEach-Object {[char]$_} | Get-Random -Count 32))
$pwdPart2 = $(-join (33..126 | ForEach-Object {[char]$_} | Get-Random -Count 32))
$pwd = $pwdPart1 + $pwdPart2
$allowedGroupMemberShips = @()
$allowedGroupMemberShips += (New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-544")).Translate([System.Security.Principal.NTAccount]).Value # Administrators
$allowedGroupMemberShips += (New-Object System.Security.Principal.SecurityIdentifier("$((Get-ADDomain).DomainSID.value)-512")).Translate([System.Security.Principal.NTAccount]).Value # Domain Admins
$allowedGroupMemberShips += (New-Object System.Security.Principal.SecurityIdentifier("$((Get-ADDomain).DomainSID.value)-572")).Translate([System.Security.Principal.NTAccount]).Value # Denied RODC Password Replication Group
If ((Get-ADDomain).DNSRoot -eq (Get-ADForest).Name) {$allowedGroupMemberShips += (New-Object System.Security.Principal.SecurityIdentifier("$((Get-ADDomain).DomainSID.value)-519")).Translate([System.Security.Principal.NTAccount]).Value} # Enterprise Admins (Forest Root AD Domain ONLY!)
Write-Host ""
Write-Host "Clearing Any Values From 'mail', 'msDS-AllowedToDelegateTo', 'servicePrincipalName', 'scriptPath'" -Foregroundcolor Yellow
Write-Host ""
Write-Host "Configuring The Account To Support RC4, AES128 And AES256" -Foregroundcolor Yellow
Set-ADUser -Identity $defaultDomainAdm.SID.Value -EmailAddress $null -Clear "msDS-AllowedToDelegateTo",scriptPath -ServicePrincipalNames $null -KerberosEncryptionType RC4,AES128,AES256 -Server $((Get-ADDomain).PDCEmulator)
Write-Host ""
Write-Host "Configuring The Account With:" -Foregroundcolor Yellow
Write-Host " > 'AccountNotDelegated = true'" -Foregroundcolor Yellow
Write-Host " > 'PasswordNeverExpires = true'" -Foregroundcolor Yellow
Write-Host " > 'TrustedToAuthForDelegation = false'" -Foregroundcolor Yellow
Set-ADAccountControl -Identity $defaultDomainAdm.SID.Value -AccountNotDelegated $true -PasswordNeverExpires $true -TrustedToAuthForDelegation $false -Server $((Get-ADDomain).PDCEmulator)
Write-Host ""
Write-Host "Removing The Account From Any Group It Is A Member Of" -Foregroundcolor Yellow
$defaultDomainAdm.MemberOf | ForEach-Object {Try {Remove-ADGroupMember -Identity $_ -Members $defaultDomainAdm.DistinguishedName -Server $((Get-ADDomain).PDCEmulator)} Catch {}} # An Error Is given When Removing From The "Administrators" Group, But That Can Be And Is Ignored
Write-Host ""
Write-Host "Adding The Account As A Member Of:" -Foregroundcolor Yellow
$allowedGroupMemberShips | ForEach-Object {Write-Host " > '$($_)'" -Foregroundcolor Yellow}
$allowedGroupMemberShips | ForEach-Object {Add-ADGroupMember -Identity $($_.Split("\")[1]) -Members $defaultDomainAdm.DistinguishedName -Server $((Get-ADDomain).PDCEmulator)}
Write-Host ""
Write-Host "Setting The New Password On The Account" -Foregroundcolor Yellow
Set-ADAccountPassword -Identity $defaultDomainAdm.DistinguishedName -Reset -NewPassword $(ConvertTo-SecureString $pwd -AsPlainText -Force) -Server $((Get-ADDomain).PDCEmulator)
Write-Host ""
Write-Host "Enabling The Account" -Foregroundcolor Yellow
Enable-ADAccount -Identity $defaultDomainAdm.SID.Value -Server $((Get-ADDomain).PDCEmulator)
Write-Host ""
Write-Host "Waiting For 30 Seconds To Make Sure The Changes Have Replicated To Other DCs" -Foregroundcolor Yellow
Start-Sleep -s 30
Write-Host ""
Write-Host "Testing Authentication" -Foregroundcolor Yellow
If ((New-Object DirectoryServices.DirectoryEntry "",$($defaultDomainAdm."msDS-PrincipalName"),$pwd).psbase.name -ne $null) {Write-Host " > Authentication Successful" -Foregroundcolor Green} Else {Write-Host " > Authentication Failed" -Foregroundcolor Red}
Write-Host ""
Write-Host "Disabling The Account" -Foregroundcolor Yellow
#Disable-ADAccount -Identity $defaultDomainAdm.SID.Value -Server $((Get-ADDomain).PDCEmulator)
Write-Host ""
$oldPos = $host.UI.RawUI.CursorPosition
Write-Host "To Display The Password In Parts, Press Any Random Key TWICE To See The 1st Part" -Foregroundcolor Magenta
$host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null
$host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null
$host.UI.RawUI.CursorPosition = $oldPos
Write-Host "The First Part Of The Password.............: $pwdPart1 " -Foregroundcolor Cyan
Write-Host ""
Write-Host "Copy The Partial Password Shown Above Into A Password Vault. When Done, Press Any Random Key TWICE To See The 2nd Part" -Foregroundcolor Magenta
$host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null
$host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null
Write-Host ""
$host.UI.RawUI.CursorPosition = $oldPos
Write-Host "The Second Part Of The Password............: $pwdPart2 " -Foregroundcolor Cyan
Write-Host ""
Write-Host "Copy The Partial Password Shown Above Into A Password Vault. When Done, Press Any Random Key TWICE " -Foregroundcolor Magenta
$host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null
$host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | Out-Null
Write-Host ""
$host.UI.RawUI.CursorPosition = $oldPos
Write-Host " "
Write-Host ""
Write-Host " "
$newPos = [System.Management.Automation.Host.Coordinates]::new($oldpos.X, $oldpos.Y + 3)
$host.UI.RawUI.CursorPosition = $newPos
} Else {
Write-Host ""
Write-Host "The Logged In Account Is The Default Domain Administrator Account." -Foregroundcolor Red
Write-Host "This Code Should Only Be Executed When Logged In With Another Account That Is A Member Of Domain Admins." -Foregroundcolor Red
Write-Host "Aborting Script..." -Foregroundcolor Red
Write-Host ""
}
}
Cheers,
Jorge
————————————————————————————————————————————————————-
This posting is provided “AS IS” with no warranties and confers no rights!
Always evaluate/test everything yourself first before using/implementing this in production!
This is today’s opinion/technology, it might be different tomorrow and will definitely be different in 10 years!
DISCLAIMER: https://jorgequestforknowledge.wordpress.com/disclaimer/
————————————————————————————————————————————————————-
########################### IAMTEC | Jorge’s Quest For Knowledge ##########################
#################### https://jorgequestforknowledge.wordpress.com/ ###################
————————————————————————————————————————————————————
Identity | Security | Recovery
————————————————————————————————————————————————————-