Contents

Protect your users from Device Code Flow abuse

Device Code Flow is a great feature. You are signed in on a machine that does not have any UI but need to connect to an Azure or Microsoft 365 resource? No problem, device code flow to the rescue. All major PowerShell cmdlets, the az tools and many other tools support this authentication flow.

What makes it so flexible and great? You can go to https://microsoft.com/devicelogin enter a generated code that is displayed on another device and sign-in using your normal user and bam, you are in.

./images/SignInUsingDCF.gif

But the device code flow itself also has a dark side. Device Code phishing for example tries to convince the user to enter the device code and their credentials and the resulting access and refresh token are then used to access resources, without having to worry about reauthentication. The user already provided MFA, no phishing website had to be used, just something like TokenTactics or TokenTacticsV2 in the backend.

If you wanted to protect your users from this kind of attack there are already two powerful access controls in Conditional Access:

  • Require device to be marked as compliant
  • Require Microsoft Entra hybrid joined device

This security barrier is impossible, at least based on current knowledge, to bypass or fake and will prevent any users from using Device Code Flow and getting phished in the first place. (Looking at you evilginx).

/en/protect-users-device-code-flow-abuse/images/BlockedByDeviceCompliance.png
Device code flow blocked because the users has no compliant device

Even if you allow the access to a management portal for non-compliant devices and the attacker wants to get a new refresh token for Microsoft Graph using the wrong client ID, they would be out of luck. The Conditional Access Policy would immediately trigger and prevent the issuance of a new access token.

AADSTS53000: Device is not in required device state: compliant. Conditional Access policy requires a compliant device, and the device is not compliant. The user must enroll their device with an approved MDM provider like Intune.

No compliant device? Big problems!

But what if you can’t enforce a compliant device for a certain action? Like the device registration flow which is not protect at all because at this point you can’t have an compliant device yet. Dirk-jan Mollema (_dirkjan) demonstrated this attack in his blog post Phishing for Primary Refresh Tokens and Windows Hello keys.

Meet authentication flows in Conditional Access

/en/protect-users-device-code-flow-abuse/images/NewCAP.png
Block device code flow through an conditional access policy

With the newly released conditional access condition “Authentication Flows” you now can restrict certain authentication flows, e.g. control who is allowed to use Device Code Flow in your environment and who doesn’t. And of course you can scope it to applications and combine it with other conditions like trusted location.

Create using Graph API

Let’s create a new conditional access policy that will restrict the usage of device code flow completely for one particular user using the Graph endpoint https://graph.microsoft.com/beta/identity/conditionalAccess/policies

{
    "displayName": "Block Device Code Flow",
    "state": "enabled",
    "conditions": {
        "clientAppTypes": [
            "all"
        ],
        "applications": {
            "includeApplications": [
                "All"
            ]
        },
        "users": {
            "includeUsers": [
                "2382a810-87b4-43fa-a46a-bcdab69c46e8"
            ]
        },
        "authenticationFlows": {
            "transferMethods": "deviceCodeFlow"
        }
    },
    "grantControls": {
        "operator": "OR",
        "builtInControls": [
            "block"
        ]
    }
}

This will immediately block any further sign-ins using the device code flow.

./images/SignInUsingDCFBlocked.gif

/en/protect-users-device-code-flow-abuse/images/BlockedByCAP.png

You can also see the original transfer method mentioned as “Device code flow” which helps to track tokens that were generated using DCF over their lifetime. In the Microsoft Graph logs this is returned as “originalTransferMethod”. Sadly this is currently not a field forwarded to Microsoft Sentinel, or I was not able to identify it correctly.

/en/protect-users-device-code-flow-abuse/images/OriginalTransferMethod.png

/en/protect-users-device-code-flow-abuse/images/OriginalTransferMethodGraph.png

Note
Since this feature must work by tracking the original transfer method, which could be part of the refresh token, to identify tokens that where issued using device code flow, tools like TokenTactics will still work if the refresh token was transferred to client by another method.

Recommendation

If you have no apparent use case for device code flow, you should create a new Conditional Access policy right now. Blocking device code flow for everybody in you environment will protect you against attacks like the one Dirk-jan described as long as they rely on device code flow phishing for initial access. And in many cases does not impact your users at all.

And for the users that might be impacted you still can create an exclusion in this policy.

Of course I always recommend requiring to enforce device compliance or hybrid device join, but if you are not yet there this new control will help you to protect your environment.

./images/BlockedByCAP-roadtx.png
The roadtx command that uses device code flow is blocked

Identify device code usage

If you are unsure who in your environment is using device code flow, you can use the following KQL query to find any successful SignIns using DCF.

SigninLogs
| where TimeGenerated > ago(90d)
| where AuthenticationProtocol == "deviceCode"
| summarize by AppDisplayName, UserId
Info
While there is not actual filter for successful sign-ins in the query, the AuthenticationProtocol column is only set to deviceCode if the sign-in was successful.

/en/protect-users-device-code-flow-abuse/images/KQLresults.png
Example results of the KQL query