« Back to home

Azure AD Connect for Red Teamers

With clients increasingly relying on cloud services from Azure, one of the technologies that has been my radar for a while is Azure AD. For those who have not had the opportunity to work with this, the concept is simple, by extending authentication beyond on-prem Active Directory, users can authenticate with their AD credentials against Microsoft services such as Azure, Office365, Sharepoint, and hundreds of third party services which support Azure AD.

If we review the available documentation, Microsoft show a number of ways in which Azure AD can be configured to integrate with existing Active Directory deployments. The first, and arguably the most interesting is Password Hash Synchronisation (PHS), which uploads user accounts and password hashes from Active Directory into Azure. The second method is Pass-through Authentication (PTA) which allows Azure to forwarded authentication requests onto on-prem AD rather than relying on uploading hashes. Finally we have Federated Authentication, which is the traditional ADFS deployment which we have seen numerous times.

Now of course some of these descriptions should get your spidey sense tingling, so in this post we will explore just how red teamers can leverage Azure AD (or more specifically, Azure AD Connect) to meet their objectives.

Before I continue I should point out that this post is not about exploiting some cool 0day. It is about raising awareness of some of the attacks possible if an attacker is able to reach a server running Azure AD Connect. If you are looking for tips on securing your Azure AD Connect deployment, Microsoft has done a brilliant job of documenting not only configuration and hardening recommendations, but also a lot about the internals of how Azure AD's options work under the hood.

Setting up our lab

Before we start to play around with Azure AD, we need a lab to simulate our attacks. To create this, we will use:

  1. A VM running Windows Server 2016
  2. An Azure account with the Global administrator role assigned within Azure AD
  3. Azure AD Connect

First you'll need to set up an account in Azure AD with Global administrator privileges, which is easily done via the management portal:

Once we have an account created, we will need to install the Azure AD Connect application on a server with access to the domain. Azure AD Connect is the service installed within the Active Directory environment. It is responsible for syncing and communicating with Azure AD and is what the majority of this post will focus on.

To speed up the installation process within our lab we will use the "Express Settings" option during the Azure AD Connect installation which defaults to Password Hash Synchronisation:

With the installation of Azure AD Connect complete, you should get a notification like this:

And with that, let's start digging into some of the internals, starting with PHS.

PHS... smells like DCSync

To begin our analysis of PHS, we should look at one of the assemblies responsible for handling the synchronisation of password hashes, Microsoft.Online.PasswordSynchronization.dll. This assembly can be found within the default installation path of Azure AD Sync C:\Program Files\Microsoft Azure AD Sync\Bin.

Hunting around the classes and methods exposed, there are a few interesting references:

As you are likely aware, DRS (Directory Replication Services) prefixes a number of API's which facilitate the replication of objects between domain controllers. DRS is also used by another of our favourite tools to recover password hashes... Mimikatz.

So what we are actually seeing here is just how Azure AD Connect is able to retrieve data from Active Directory to forward it onto Azure AD. So what does this mean to us? Well as we know, to perform a DCSync via Mimikatz, an account must possess the "Replicating Directory Changes" permission within AD. Referring back to Active Directory, we can see that a new user is created during the installation of Azure AD Connect with the username MSOL_[HEX]. After quickly reviewing its permissions, we see what we would expect of an account tasked with replicating AD:

So how do we go about gaining access to this account? The first thing that we may consider is simply nabbing the token from the Azure AD Connect service or injecting into the service with Cobalt Strike... Well Microsoft have already thought of this, and the service responsible for DRS (Microsoft Azure AD Sync) actually runs as NT SERVICE\ADSync, so we're going to have a work a bit harder to gain those DCSync privileges.

Now by default when deploying the connector a new database is created on the host using SQL Server's LOCALDB. To view information on the running instance, we can use the installed SqlLocalDb.exe tool:

The database supports the Azure AD Sync service by storing metadata and configuration data for the service. Searching we can see a table named mms_management_agent which contains a number of fields including private_configuration_xml. The XML within this field holds details regarding the MSOL user:

As you will see however, the password is omitted from the XML returned. The encrypted password is actually stored within another field, encrypted_configuration. Looking through the handling of this encrypted data within the connector service, we see a number of references to an assembly of C:\Program Files\Microsoft Azure AD Sync\Binn\mcrypt.dll which is responsible for key management and the decryption of this data:

To decrypt the encrypted_configuration value I created a quick POC which will retrieve the keying material from the LocalDB instance before passing it to the mcrypt.dll assembly to decrypt:

And when executed, the decrypted password for the MSOL account will be revealed:

So what are the requirements to complete this exfiltration of credentials? Well we will need to have access to the LocalDB (if configured to use this DB), which by default holds the following security configuration:

This means that if you are able to compromise a server containing the Azure AD Connect service, and gain access to either the ADSyncAdmins or local Administrators groups, what you have is the ability to retrieve the credentials for an account capable of performing a DCSync:

Pass Through Authentication

With the idea of password hashes being synced outside of an organisation being unacceptable to some, Azure AD also supports Pass Through Authentication (PTA). This option allows Azure AD to forward authentication requests onto the Azure AD Connect service via Azure ServiceBus, essentially transferring responsibility to Active Directory.

To explore this a bit further, let's reconfigure our lab to use Pass Through Authentication:

Once this change has pushed out to Azure, what we have is a configuration which allows users authenticating via Azure AD to have their credentials validated against an internal Domain Controller. This is nice compromise for customers who are looking to allow SSO but do not want to upload their entire AD database into the cloud.

There is something interesting with PTA however, and that is how authentication credentials are sent to the connector for validation. Let's take a look at what is happening under the hood.

The first thing that we can see are a number of methods which handle credential validation:

As we start to dig a bit further, we see that these methods actually wrap the Win32 API LogonUserW via pinvoke:

And if we attach a debugger, add a breakpoint on this method, and attempt to authenticate to Azure AD, we will see this:

This means that when a user enters their password via Azure AD with PTA configured, their credentials are being passed un-hashed onto the connector which then validates them against Active Directory. So what if we compromise a server responsible for Azure AD Connect? Well this gives us a good position to start syphoning off clear-text AD credentials each time someone tries to authenticate via Azure AD.

So just how do we go about grabbing data out of the connector during an engagement?

Hooking Azure AD Connect

As we saw above, although the bulk of the logic takes place in .NET, the actual authentication call to validate credentials passed from Azure AD is made using the unmanaged Win32 API LogonUserW. This gives us a nice place to inject some code and redirect calls into a function that we control.

To do this we will need to make use of the SeDebugPrivilege to grab a handle to the service process (as this is running under the NT SERVICE\ADSync). Typically SeDebugPrivilege is only available to local administrators, meaning that you will need to gain local admin access to the server to modify the running process.

Before we add our hook, we need to take a look at just how LogonUserW works to ensure that we can restore the call to a stable state once our code has been executed. Reviewing advapi32.dll in IDA, we see that LogonUser is actually just a wrapper around LogonUserExExW:

Ideally we don't want to be having to support differences between Windows versions by attempting to return execution back to this function, so going back to the connector's use of the API call we can see that all it actually cares about is if the authentication passes or fails. This allows us to leverage any other API which implements the same validation (with the caveat that the call doesn't also invoke LogonUserW). One API function which matches this requirement is LogonUserExW.

This means that we can do something like this:

  1. Inject a DLL into the Azure AD Sync process.
  2. From within the injected DLL, patch the LogonUserW function to jump to our hook.
  3. When our hook is invoked, parse and store the credentials.
  4. Forward the authentication request on to LogonUserExW.
  5. Return the result.

I won't go into the DLL injection in too much detail as this is covered widely within other blog posts, however the DLL we will be injecting will look like this:

And when executed, we can see that credentials are now harvested each time a user authenticates via Azure AD:

Backdoor LogonUser

OK, so we have seen how to retrieve credentials, but what about if we actually want to gain access to an Azure AD supported service? Well at this stage we control LogonUserW, and more importantly, we control its response, so how about we insert a backdoor to provide us access.

Within our DLL code, let's add a simple check for a hardcoded password:

BOOL LogonUserWHook(LPCWSTR username, LPCWSTR domain, LPCWSTR password, DWORD logonType, DWORD logonProvider, PHANDLE hToken) {
	PSID logonSID;
	void *profileBuffer = (void *)0;
	DWORD profileLength;
	QUOTA_LIMITS quota;
	bool ret;
	WCHAR pipeBuffer[1024];
	DWORD bytesWritten;

	swprintf_s(pipeBuffer, sizeof(pipeBuffer) / 2, L"%s\\%s - %s", domain, username, password);
	WriteFile(pipeHandle, pipeBuffer, sizeof(pipeBuffer), &bytesWritten, NULL);

	// Backdoor password
	if (wcscmp(password, L"ComplexBackdoorPassword") == 0) {
		// If password matches, grant access
		return true;
	}

	// Forward request to LogonUserExW and return result
	ret = LogonUserExW(username, domain, password, logonType, logonProvider, hToken, &logonSID, &profileBuffer, &profileLength, &quota);
	return ret;
}

Obviously you can implement a backdoor as complex or as simple as you want, but let's see how this looks when attempting to authenticate against O365:

So what are the takeaways from this? Well first of all, it means for us as red teamers, targeting Azure AD Connect can help to expedite the domain admin chase. Further, if the objectives of the assessment are within Azure or another services integrated with Azure AD, we have the potential to work around authentication for any account which passes an authentication request via PTA.

That being said, there is a lot of configuration and alternate options available when deploying Azure AD, so I'm keen to see any further research on just how red teamers can leverage this service.