In this Walkthrough, we will be hacking the machine Monteverde from HackTheBox.
To begin, we will preform domain specific enumeration, which leads to the discovery that we can dump a lot of information about the DC over LDAP with an anonymous session. From the info dump, we will discover all of the users, groups, and group memberships in the domain.
Once we have found a list of users and groups, we will continue with domain enumeration but find nothing we can work with. Low on options, we will turn to a brute force attempt, which will turn out fruitful and provide us with our first set of valid credentials.
Armed with a valid set of credentials, enumeration continues and an authenticated SMB session. In the users$ share, we’ll come across a single file called “azure.xml”, which contains a password inside for the user ‘mhope’. From the LDAP enumeration, we had found that the user ‘mhope’ is in the Remote Management Users group. As a result, we are able to quickly get a shell on the DC using evil-winrm.
Since there were many hints to Azure being important, we will jump right into enumerating from there. We will hit Google and find exploits that can be used against Azure AD. Most specifically, we will find a way to extract the Azure accounts credentials.
In the post we found on Google, it provides a PowerShell script to exploit this vulnerability. After troubleshooting and editing the script, we will finally get it working. To our surprise, the password we extract will end up belonging to the Domain Admin account!
— nmap TCP full —
nmap -A -sV -sC -T4 10.10.10.172 -p- -oN full_tcp.nmap
— Nmap UDP (top 1000) —
nmap -A -sV -sU 10.10.10.172 --script=*enum* -oN udp_top_1000.nmap
Review of Open Ports
Lots of interesting TCP ports open; and it has been observed that this is an AD machine, and even more specifically a Domain Controller (DC).
- Port 53 is open and is hosting a DNS service over TCP – version: Microsoft DNS 6.1.7601 (1DB15D39)
- Port 88 is open and is hosting the kerberos service.
- Ports 135 / 139 / 445 are open and are hosting the RPC / NetBIOS / SMB share services respectively.
- Ports 389 and 636 are open and hosting the LDAP/S services respectively
- Port 464 is open are hosting a Kerberos password change service, typically seen on DCs and generally not of much interest.
- Port 593 is open and are hosting RPC services.
- Port 5985 is hosting the WinRM service, which will be good if credentials are found.
- Port 9389 is hosting the .NET Message Framing service.
- Ports 49xxx and 61733 are hosting the high port RPC services.
From the nmap scan we can see this is a Domain Controller with a hostname of MONTEVERDE and that this is the DC for the domain MEGABANK.LOCAL.
Message signing is also enabled on the DC; however, we will not be doing any lateral movement in the example so, that will not be important.
Enumeration and Initial Exploit
Since this is a Domain controller, enumeration will be split into two sections: domain specific service enumeration and regular service enumeration.
Enumerating Services Specific to a Domain Controller
Enumeration will begin by attempting to get a Zone Transfer from the DNS server.
dig @10.10.10.172 AXFR megabank.local
The zone transfer attempt was unsuccessful, so next I will move on to the kerberos service.
Targeting the Kerberos service, I tested for a quick No-Preauth win without supplying a username.
GetNPUsers.py megabank.local/ -dc-ip 10.10.10.172
Dumping Domain Info with ldapsearch – LDAP Enumeration
Unable to find much from Kerberos without any credentials, I will move on to the LDAP service and see if I can dump information from an anonymous session.
ldapsearch -x -H ldap://10.10.10.172 -b "dc=megabank,dc=local"
The ldapsearch worked and A LOT of information was dumped. So much information that I had to redirect the output to a file for easier parsing.
ldapsearch -x -H ldap://10.10.10.172 -b "dc=megabank,dc=local" > ldapsearch.txt
I can start by grabbing all of the users in the domain.
cat ldapsearch.txt | grep 'userPrincipalName'
Next I can enumerate all groups as well as all groups that all users are in with the following command:
cat ldapsearch.txt | grep 'OU=Groups\|CN=Builtin\|sAMAccountName'
This grep will pull…
- sAMAccountName – the names of all users and groups in the domain
- OU=Groups – all global domain groups
- CN=Builtin – all local domain groups
Additionally, these keywords also show up in the “memberof” field, which will tell us which groups each user is a member of!
Since sAMAccountName appears low on the output of LDAP search, it is the first field that we need to examine, as well as our “break point”. This means, we want to view these results bottom up to better understand what we are seeing.
Breaking Down Users and Groups from ldapsearch Output
Reading from bottom up, it shows that user “smorgan” is in the “Operations” group, user “roleary” is in the “HelpDesk” group, and user “dgalanos” is in the “Trading” group.
Looking up a bit further, we can see an interesting group: Azure Admins and that the user “mhope” is in this group. Another big finding is that the user “mhope” is also in the “Remote Management Users” group, which means they can PSRemote into the DC over WinRM.
This hints heavily to me wanting to find credentials for user mhope.
Finally, another user: “AAD_987d7f2f57d2” is also in the Azure Admins group; however that account did not have a userPrincipalName. But… the account is also in the “Users” group, which indicates that this is a valid user account.
Domain Specific Enumeration Continued
After enumerating LDAP, I decided to craft a users.txt list to run kerbrute.py against and see if all of these users are valid. Also, this will check if any of the users have kerberos pre-authentication disabled.
In addition to the users enumerated from the ‘userPrincipalName’ grep, I also added default domain accounts as well as AAD_987d7f2f57d2 to the list.
kerbrute.py -users users.txt -dc-ip 10.10.10.172 -domain megabank.local
This shows that all of the users that were enumerated from LDAP are valid. Unfortunately, none of them got the NOT PREAUTH message so I won’t be able to AS-REP roast any of these users.
If you find a user has ‘NOT PREAUTH” next to their name it means they are AS-REP roastable. Check out my post here to learn more.
Since I did not get any AS-REP roast opportunities, I need to continue with enumeration, so I will head to the SMB shares.
First, I tested anonymous access to both the SMB shares and RPC server.
smbclient -L 10.10.10.172 -N rpcclient 10.10.10.172 -N
Anonymous login was successful to SMB; however, no shares were listed, which indicates that anonymous is denied access to all of the shares. Trying to access RPC with an anonymous session failed.
Brute-Forcing User ‘SABAtchJobs’ Password with Kerbrute.py
Since I am not finding much, I decided to craft a wordlist to try and brute force while I look over the ldapsearch results a bit more closely for hints.
To craft a quick dirty wordlist, I like to combine Users.txt with rockyou-25.txt.
cat ./users.txt > wordlist.txt | cat /usr/share/seclists/Passwords/Leaked-Databases/rockyou-25.txt >> wordlist.txt
The wordlist is only 942 words, so it should run fairly quickly. If this gets no hits, I will try combining users.txt with rockyou-50.txt, and so on.
For the brute force attempt, I decided to use kerbrute.py again.
kerbrute.py -users users.txt -passwords wordlist.txt -dc-ip 10.10.10.172 -domain megabank.local
Amazing! Right away it found a password, which is the same as one of the account names.
SABatchJobs : SABatchJobs
It is common for service accounts to have the same username and password, which is why its always a good idea to add all usernames you find to a wordlist you are going to use when trying to brute force.
Great! With a valid set of credentials, I can now perform much deeper enumeration with an authenticated session to services like kerberos, LDAP, SMB, and WinRM.
At this point, I decided to let the brute force keep running in case I get lucky and find more passwords. I also added ‘SABatchJobs’ to a passwords.txt file.
Domain Enumeration with Valid Credentials
First, I want to test access on SMB and WinRM to see if there is a foothold opportunity.
crackmapexec smb 10.10.10.172 -u "SABatchJobs" -p "SABatchJobs" -d megabank.local crackmapexec winrm 10.10.10.172 -u "SABatchJobs" -p "SABatchJobs" -d megabank.local
The account does have access to SMB, but not command execution. This means I can likely list the shares with this account, but first I want to look for any kerberoastable users.
GetUserSPNs.py megabank.local/SABatchJobs:SABatchJobs -dc-ip 10.10.10.172 -request
Finding a Password in the ‘Users’ Share – SMB Enumeration
No kerberoastable users were found so I will move along and check the shares.
smbclient -L 10.10.10.172 -U "megabank.local/SABatchJobs"
Some interesting shares are revealed!
First, there are two custom shares “azure_uploads” and “users$“. Next, there are two disk drives on this host “C$”, which is default, but also “E$”. Finally, SYSVOL is interesting if this is a version of Windows Server that uses the Groups.xml file.
I checked SYSVOL for the quick win but the Groups.xml file was not in there and just the typical policy information files were found.
smbclient \\\\10.10.10.172\\SYSVOL -U "megabank.local/SABatchJobs"
Moving on, I checked E$ but access was denied.
smbclient "\\\\10.10.10.172\\E$" -U "megabank.local/SABatchJobs"
Next I checked the users$ share and found that there were 4 user profiles and one of the mhope has a file called azure.xml inside.
smbclient "\\\\10.10.10.172\\users$" -U "megabank.local/SABatchJobs"
This is particularly interesting because I have already enumerated that user mhope is in the Azure Admins group form the LDAP search.
Lots of hints towards Azure here, so I grabbed the XML file using the command mget and then headed to the last share, which also references Azure.
smbclient "\\\\10.10.10.172\\azure_uploads" -U "megabank.local/SABatchJobs"
The azure_uploads share was empty, so now I will check this XML file I found.
Amazing! There is a password in here!
Finding who the Password Belongs to with Kerbrute.py
Although this password is most likely for “mhope”, I will add it to my passwords.txt file and spray the password using kerbrute.py to confirm.
kerbrute.py -users users.txt -passwords passwords.txt -dc-ip 10.10.10.172 -domain megabank.local
Great! The password belongs to mhope! That’s just what I was hoping for after what I have found from enumerating LDAP.
mhope : 4n0therD4y@n0th3r$
Getting a Foothold on the DC as User ‘mhope’ – evil-winrm
After finding mhope’s credentials, I know that this user is part of the Remote Management Users group so they should be able to execute commands on the DC via PSRemoting. To confirm this, I can use crackmapexec.
crackmapexec winrm 10.10.10.172 -u 'mhope' -p '4n0therD4y@n0th3r$' -d megabank.local
BOOM! This confirms that this user can execute commands on the DC through the WinRM service.
Now that I have confirmed access through WinRM, I grabbed a shell using evil-winrm.
evil-winrm -i 10.10.10.172 -u 'mhope' -p '4n0therD4y@n0th3r$'
Post Exploitation Enumeration and Privilege Escalation
Since I already know this user is part of the Azure Admins group, I decided to go straight to Google to find out more about this group.
Understanding Azure AD Connect and the Azure Admins Group
I want to see if there is a way to exploit the DC as a result of being a member of this group. If nothing turns up, I will continue enumerating, but the breadcrumbs really point to this group being the key to privilege escalation.
Since this is my first time encountering Azure, I gathered some information about the groups that are created when Azure AD is being setup.
There are four default groups that get created when you install Azure AD Connect: ADSyncAdmins ; ADSyncBrowse ; ADSyncOperators ; ADSyncPasswordSet
Also, I found that If you install Azure AD Connect to a member server, then these groups are created as local groups. Alternatively, if you install Azure AD Connect on a domain controller, these groups are created as domain local groups.
Azure AD Connect is an on-premises Microsoft application that’s designed to meet and accomplish your hybrid identity goals.
Checking local groups first, I can see the four default groups have been created.
When Googling for information about the “Azure Admins” group, it appears that this group is not default and is a custom group that was created.
Since this is a custom group, I am curious what the purpose of this group is.
I found a blog post here that shows how to create Azure AD users and groups, and it looks like in their example, the Azure Admins group is a custom group that was created for users who have privileges to administer the Azure AD environment (Azure Administrator privilege’s). This is likely the same scenario here.
After doing some good research about Azure, I want to confirm that this is “Azure AD Connect” by checking in the Program Files folder.
Great! This confirms that Azure AD Connect is running and this is a hybrid solution.
Hunting for an Exploit against Azure AD Connect
Now that I know what is running, I went back to Google to look up “hacking Azure AD Connect”.
The first link was interesting, but did not seem like it would work on this machine. However, the second link had some really interesting info on it.
To summarize, there are a few different attack methods in the post; however, the “DCSync” attack is the most appealing to me.
Essentially, the post goes on to explain how Azure AD Connect synchronizes with on-prem AD to retrieve data through replication (DRS – Directory Replication Services). This is the same service that Mimikatz utilizes (interesting!!!!)
Next, the post mentions an account that is created when setting up Azure: ‘MSOL__[HEX]’; however, I already discovered the account that was created with this instance: AAD_987d7f2f57d2
Based on the post, this account should have the “Replicating Directory Changes” and be able to perform a DCSync attack to dump all of the hashes in the domain. And checking the account, there is an interesting comment that hints to a DCSync attack being possible.
net user 'AAD_987d7f2f57d2' /domain
Amazing! This is just what I was hoping to find! From the post along with this comment, it is pretty safe to assume this account has the ability to replicate the DC.
Attempting to Enumerate the Azure Accounts Permissions – FAIL
Although it seems pretty clear, instead of assuming, I can try to confirm these permissions by downloading accesschk64.exe onto the victim from the Sysinternals Suite of Tools and then check the permissions on account AAD_987d7f2f57d2.
I grabbed a copy of accesschk64.exe and then moved it to my working directory and then used the upload feature of evil-winrm to transfer the file to the victim. Once on the victim, I attempted to check the user’s permissions but I was denied access.
At this point, I’ll just need to rely on blind faith and hope that the default configuration is in place here.
Attempting to Extract the Azure Account Password with a PowerShell Script
Finally, in the post it has a PowerShell script that is used to extract the password of the Azure account and decrypt the encrypted_configuration value, which will retrieve the keying material from the LocalDB instance before passing it to the mcrypt.dll assembly to decrypt.
A copy of the raw script can be found here.
After copying the raw script and pasting it into a file on my attacker machine named “azure_cred_decrpyt.ps1”, I uploaded it to the victim and tried to execute it.
Unfortunately, I got an error and it kicked me out of evil-winrm. Time to troubleshoot…
Troubleshooting and Editing the PowerShell Script
I found a second script made by the same person, but the script uses a different technique – it uses MSSQL to extract and decrypt the password with xp_cmdshell. However, that would indicate that I need admin privileges enabled in MSSQL.
Also, I did not see MSSQL open from the outside. I can easily check if its open internally though with the following command:
netstat -nao | findstr -i "1433"
Ok cool, this confirms that MSSQL is running internally and not visible from outside. Additionally, all of the connections are “ESTABLISHED”, which indicates that the MSSQL is likely in use for the Azure connections.
Even though this will require DB admin rights, I still decided to test the script because I don’t know if user “mhope” has DBA rights or not.
The second version of the script can be found here.
I transferred this script on to the victim, just the same as the last one and then tried to execute it.
Again, I got an error, but since this script uses a “try” statement at the start, it errored out gracefully when it could not connect to “localdb”.
It appears the issue stems around the “localdb”, which is determined in the ‘$client’ variable at the start of the script here:
- $client = new-object System.Data.SqlClient.SqlConnection -ArgumentList “Data Source=(localdb).\ADSync;Initial Catalog=ADSync”
Doing some Google searches, I came across this page here showing that the way “Data Source” is presented in the script is actually used only for MSSQL-Express.
As a result, I edited the $client variable to look like the following, and then tested again:
- $client = new-object System.Data.SqlClient.SqlConnection -ArgumentList “Data Source=(local);Initial Catalog=ADSync”
With the changes made to the script, I tested it again but I was presented with the same error!
Troubleshooting and Editing the Script Continued
Continuing with troubleshooting and Google searches, I came to the realization that the script was NOT trying to connect to the database as any specific user– or any user whatsoever for that matter.
This whole time I was wondering “who does this script connect to the database as”? because it is NOT specified. This seems to be the issue as to why I cannot connect to the database.
After doing some research on connecting to the database, I came across some good information here.
If any of these two settings is present (Trusted_Connection=true or Integrated Security=true/SSPI), then the Windows credentials of the current user are used to authenticate against SQL Server.
Essentially, I can either pass the credentials right in the script (any user) or I can use “Trusted_Connection=true” to connect using Windows authentication (current user).
With this new finding, I edited the $client variable once more, and ended up with the following:
- $client = new-object System.Data.SqlClient.SqlConnection -ArgumentList “Data Source=(local);Initial Catalog=ADSync;Trusted_Connection=True“
Testing the changes made, I sent this to the victim again and tried to execute it.
Alright, the script worked this time… kinda. After adding the Trusted_Connection=true string into the $client variable, the script was able to connect to the database as the current user (mhope). Unfortunately, inside the MSSQL server, the current user does NOT have the permissions necessary to execute xp_cmdshell (user is not a DBA).
Getting the Script Right and Extracting the Domain Administrator Password
The key here, is that I was able to connect to the database. This means that by editing the first script, which used a different non-cmdshell method to extract and decrypt the password, I may get lucky. One thing I do know, is that I can now edit it script #1 the same way I did script #2, and then I should connect to the DB successfully.
Back to editing the first script now, I added the “try” statement to error out gracefully when it cannot connect to the database. Then, I also edited the $client variable to the one that worked on the second script.
If you are interested in grabbing a copy of this edited exploit, the final copy of script #1 has been added to my GitHub repo here.
Now that everything is ready, I transferred an updated copy of the first script onto the victim and executed it.
BOOM!!! The Domain Administrator accounts credentials were dumped! I was expecting to get the credentials for the Azure account, but this works even better!
Administrator : d0m@in4dminyeah!
Getting a Shell as the Domain Administrator – psexec.py
Armed with the Domain Administrators credentials, I officially own the DC. Now I can simply login with the DA credentials using psexec.py from the Impacket Collections of Scripts, which will drop me into a SYSTEM shell on the DC.