AiTM for WHFB persistence
Recently we held an internal EntraIDiots CTF where the challenge “HelloThere” required the CTF contestant to device-code phish a user. Then use the acquired refreshtokens to register a device, acquire a PRT and use that to register Windows Hello For Business. As the flag site only allowed access using phishing-resistant MFA.
Making the challenge work
In order to make that challenge work we added a CA policy that forces MFA when performing the device code flow authentication. This is neccesary because in order to do the whole flow toward WHFB registration. We need the user to have just performed MFA. The ngcmfa attribute needs to be present inside of the JWT token.
Forcing MFA in the real world
In the real world we wouldnt have the option to implement a CA policy inside our victims tenant. To make sure the users has just performed MFA. So we were thinking how could we force mfa? Looking at the Phishing the Phishing Resistant – Phishing for Primary Refresh Tokens in Microsoft Entra slides by Dirk Jan we found the “amr_values” parameter which, according to this techcommunity post said:
The MFA requirement is enforced by the Azure AD WAM plugin(Microsoft Authentication broker) via the following request parameters “amr_values=ngcmfa”.
Which made us assume we could use this to force users to perform MFA. After a quick test our assumption was correct!
Constructing the URL
We need to create a login user that has the follow characteristics:
- Authentication Broker client (id: 29d9ed98-a469-4536-ade2-f981bc1d605e)
- The correct resource url: “”
- The amr_values=ngcmfa parameter to force MFA
- A correct redirect_uri: ms-appx-web://Microsoft.AAD.BrokerPlugin
Which result in the following URL:
https://login.microsoftonline.com/common/oauth2/authorize
?response_type=code # OAuth2 response type
&client_id=29d9ed98-a469-4536-ade2-f981bc1d605e # Authentication Broker client
&resource=https://enrollment.manage.microsoft.com # Resource URL
&amr_values=ngcmfa # Force MFA
&redirect_uri=ms-appx-web://Microsoft.AAD.BrokerPlugin # Redirect URI
The flow
The flow we are building is very similar to our phishing for refreshtokens implementation. The steps:
- User visits the AiTM page
- Worker retrieves the login page using our prepared url.
- User is presented with the standard login flow, mfa is now forced, regardless of the active session
- After login user receives a form asking: “Are you trying to sign in to Microsoft Authentication Broker?”.
- When the users clicks “Continue” we receive the code for the code flow.

After that we can exchange our code for access- and refreshtoken. When we have the tokens we can use that to perform the required steps to get WHFB registered:
- Add a device to EntraID
- Request a PRT for that device
- Enrich the PRT with our ngcmfa claim
- Register a WHFB key
- Request a new PRT using our device, and newly created WHFB key
For details on the last steps read the Phishing for Primary Refresh Tokens and Windows Hello keys blog by Dirk-Jan.
So what’s new?
Not much this is built on lots of previous work, mostly by Dirk-Jan. But the core is:
- AiTM for PRT -> Phishing resistant implementation
- Using the amr_values=ngcmfa parameter to make sure MFA is always performed.
Demo
We can use the script for our refreshtoken blog and modify the parameters to match the client and resources we use. After which the sign in works and the user is always forced to perform MFA.
Because the user performed MFA we can now use that to register a new device, request a PRT, enrich that PRT with our ngcmfa claim and register a WHFB key.
After this we can use interactively browse like our victim using the browserprtauth module in roadtx: roadtx browserprtauth -url https://myaccount.microsoft.com.
Stealth
From a user perspective its quite difficult to detect that a new WHFB key was added. Its not shown in the My Account page that lists the other authentication methods. The newly added device will show up here, but its not clear an auth method is bound ot it. Administrators can see the WHFB keys in EntraID.

The thing is an admin cant view his own authentication methods in EntraID. For some reason Microsoft decided to force the admin to use his own My Account page to manage those. Which requires another admin to check if they are suspicious.

You can use graph api to lis the authentication method.
Prevention
The following options exist to prevent against this attack:
Enforce phishing resistant MFA for everyone. Onboarding can be a pain. But it would prevent this attack. Be aware that device code flow could still be used to gain access and register WHFB.
AiTM mitigation, it will warn the user and alert administrators. When a user visits a company login page from a non-microsoft URL the page will change color and show DO NOT LOGIN in big letters. At the same time the administrators will receive an alert of what just happened.
Disallow device registration in Entra. As far as we are aware the WHFB are bound to a device. Without the ability to register a new one attacker would need to steal existing device certificate and key. Be aware that its still possible to add a fido key directy.
Enforce device compliance. It wont prevent the registration of a device or the registration of the WHFB keys. But the moment you use the keys the device that was registered is not compliant. Be aware that there are tools to spoof intune device compliance.
Block or limit the device code flow, wont prevent this attack. But doing so will prevent the original device code flow attack and is sage advice in general.
Detection
We were having a hard time detecting this. We initially wanted to match an interactive sign in into the “29d9ed98-a469-4536-ade2-f981bc1d605e” client to the creation of a device or WHFB-key. We assumed the newly added UTI attribute would allow us to do so. However it seems EntraID Audit logging doesnt contain the new attributes. Then we thought roadtx registers windows hello with a static User-Agent “Dsreg/10.0 (Windows 10.0.19044.1826)”. But that information is not ingested into Sentinel (it is present in the EntraID auditlog view). Also we are unsure how much this is used.
So if any of you have a good idea on how to detect this. Please let us know 🙂