{
	"id": "eb046f5b-5c08-46b0-8c76-e3cb0972c7ea",
	"created_at": "2026-04-06T00:07:52.974697Z",
	"updated_at": "2026-04-10T03:21:26.612483Z",
	"deleted_at": null,
	"sha1_hash": "049a3f1a609ea2ef1d4c728a3d68310349ffd5d9",
	"title": "Microsoft 365 OAuth Device Code Flow and Phishing",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 90777,
	"plain_text": "Microsoft 365 OAuth Device Code Flow and Phishing\r\nBy By: Daniel Min\r\nPublished: 2021-08-17 · Archived: 2026-04-05 23:34:17 UTC\r\nDuring a recent red team engagement, we found that the target organization used a well-known identity access management\r\n(IAM) product for their multi-factor authentication (MFA) solution. Most of their Internet-facing login portals, such as\r\nOffice365 and Citrix portals, were behind this IAM platform; however, their users were allowed to enable multiple MFA\r\noptions, including Email OTP (one-time passcode). Given these situations, we thought a phishing campaign abusing\r\nMicrosoft 365 OAuth device code flow would be a great attack vector for potentially bypassing their MFA and gaining\r\naccess to the applications from the Internet.\r\nFigure 1: Email OTP Enabled for MFA\r\nMicrosoft OAuth Authentication Flow\r\nAccording to Microsoft, it uses OAuth device authorization grant to allow users to sign in to input-constrained devices such\r\nas smart TVs, IoT devices or printers. To enable this authorization flow, the device will have the user visit a webpage from\r\nanother device’s browser to sign in. Once the user signs in, the device can request access tokens as needed.\r\nTo explain what this is doing:\r\n1. A user initiates an application on a device, which supports this device authorization grant flow.\r\n2. The application connects the /devicecode endpoint with the client_id of the application and the permission scopes,\r\nsuch as Files.ReadWrite.\r\n3. The /devicecode endpoint sends back user_code, device_code and verification_uri.\r\n4. The user now visits the verification_uri and submits the user_code.\r\n5. Azure will prompt the user to complete the authentication process and accept the presented permission scopes.\r\n6. The device continuously connects to the /token endpoint with client_id and device_code. After the user’s successful\r\nlogin, it gets access_token, refresh_token (if offline_access scope was consent) and id_token.\r\nHow Can We Abuse This for Phishing?\r\n1. An attacker creates a fake Azure App leveraging Azure Active Directory.\r\n2. The attacker requests user_code, device_code and verification_uri by connecting to /devicecode endpoint with\r\nclient_id of the created Azure App.\r\n3. After receiving necessary items, the attacker sends a phishing email to a victim soliciting them to visit the\r\nverification_uri and enter the user_code.\r\n4. The victim submits the user_code and completes the normal Microsoft sign-in, and accepts the permission consent.\r\n5. The attacker requests access_token and refresh_token from /token endpoint to impersonate the victim.\r\na. The access_token can then be used to access the victim’s Office365 products, including Outlook mail\r\nleveraging Microsoft Graph API.\r\nb. By default, the access_tokens are valid for 60 days and refresh_tokens are valid for a year Thus, with\r\nrefresh_token, one can continuously re-request for the victim’s access_token for persistence purposes.\r\nPractical Example of Microsoft OAuth Device Code Phishing\r\nOne perk about this phishing technique is that we do not need to build custom phishing infrastructure (e.g., creating a\r\nphishing server and landing page). We can use pretty much everything provided by Microsoft since we are technically\r\nhttps://www.optiv.com/insights/source-zero/blog/microsoft-365-oauth-device-code-flow-and-phishing\r\nPage 1 of 5\n\nleveraging their legitimate OAuth authentication flow.\r\nCreating Azure Application\r\nFirst, we need to create an Azure application. This application will serve as an endpoint where we will generate client_id\r\nand device_code, which are later used for obtaining the victim’s access_token, refresh_token and id_token.\r\nSteps: Azure Portal → Azure Active Directory → App registrations → New registration\r\nFigure 3: Azure App Registrations\r\nNext, enter a name for the application. This can be anything such as “Optiv Remote User Management”, etc. For Supported\r\naccount types, select “Accounts in any organizational directory (Any Azure AD directory – Multitenant) and personal\r\nMicrosoft accounts (e.g., Skype, Xbox)” so that target users could use their business Microsoft accounts to allow the\r\napplication consents.\r\nFigure 4: Azure Register an Application\r\nThis will create our Azure application and make a note for the Application (client) ID value. This will serve as client_id\r\nvalue.\r\nFigure 5: Azure App client_id\r\nFinally, change Allow public client flows to “Yes” under the Authentication section and select “Save.”\r\nFigure 6: Azure App Authentication\r\nLaunching Phishing Campaign\r\nTo conduct the device code phishing, we first need to make a POST request to /devicecode endpoint to obtain user_code\r\nand device_code. Postman, an API testing and development platform, can be very useful for this.\r\n1) HTTP POST Request to /devicecode\r\nEndpoint https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode\r\nclient_id 54c9f794-489c-4473-8407-XXXXX (*Application ID from the Azure App)\r\nscope\r\nContacts.Read Files.ReadWrite Mail.Read Notes.Read Mail.ReadWrite openid profile User.Read email\r\noffline_access\r\n*Note: A full list of the scope can be found at Microsoft.com. Make sure to carefully check permissions required for each\r\nscope since some require admin consent.\r\nFigure 7: POST Request to /devicecode Endpoint\r\nNext, upon obtaining the user_code, we need to send a phishing email to the victim. This is the only tricky part of this\r\nphishing attack since you need to lure the victim to visit the verification_uri and enter the user_code. Another caveat is that\r\nobtained user_code and device_code will expire 15 minutes after they have been generated. Thus, if they expire, you will\r\nbe required to regenerate them and resend the phishing email.\r\nFor our engagement, we came up with the following phishing email template:\r\nFigure 8: Example Phishing Email\r\nhttps://www.optiv.com/insights/source-zero/blog/microsoft-365-oauth-device-code-flow-and-phishing\r\nPage 2 of 5\n\nWe registered our phishing domain to send emails from Microsoft Outlook. Also, the reason why we did not embed the\r\nhyperlink for the verification_uri (https://microsoft.com/devicelogin) was that our phishing email kept landing in the spam\r\nfolder. Though we are not 100% sure, we think Microsoft may have done some subtle filtering for hyperlinked\r\nverification_uri and user_code combinations not originating from a Microsoft email domain. We observed this behavior\r\nfrom our dynamic testing at that time, so we decided to make our phishing email like the above.\r\nIf the phishing attempt was successful and the victim visited the verification_uri, entered the user_code, and completed\r\nauthentication, the victim would have seen the following devicelogin and permission consent pages:\r\nFigure 9: Microsoft Devicelogin Page for Entering user_code\r\nFigure 10: Example Sign in Page\r\nFigure 11: Example Device Permissions Requested Page\r\nFigure 12: Example Final Page After Accepting Permissions\r\nAs for the attacker, they will need to keep checking whether the victim completed the authorization process by sending a\r\nPOST request to /token API endpoint after the phishing email is sent. Once the authorization process is completed, it will\r\npresent a bearer token formatted access_token and refresh_token, which you can use to mimic the victim’s access.\r\n2) HTTP POST Request to /token\r\nEndpoint https://login.microsoftonline.com/organizations/oauth2/v2.0/token\r\nclient_id 54c9f794-489c-4473-8407-XXXXX (*Application ID from the Azure App)\r\ncode\r\n\u003cdevice_code\u003e (*Example: FAQABAAEAAAD--\r\nDLA3VO7QrddgJg7WevrafD5YlswpVDFrAq_avAeRuloI07HQehfaP-8QEeTzrP0k24tIrsNKgrltMt7RIsWieb_OJhqJInked8PpsCh5CMhwFqakM0y2sG3fphtxTevtxozvVk5rR_xOCKPmxg\r\nW5WkvHGOjuQwlAOFB7Qo4ni8PjfAYRKx6tGqv39c9_sIgAA)\r\ngrant_type urn:ietf:params:oauth:grant-type:device_code\r\nFigure 13: Authorization Pending\r\nFigure 14: Authorization Granted \u0026 Gain Bearer access_token\r\nFor example, with the access_token, the attacker can now access the victim’s email inbox using Microsoft Graph API.\r\nFigure 15: Accessing Victim's Outlook Mail Inbox\r\nAutomation of Device Code Phishing\r\nGoing back to our red team scenario, our goals were to:\r\n1. Launch a Microsoft 365 device code phishing attack\r\n2. Gain access to the target user’s inbox (*The targets were ones whom we had already compromised passwords for via\r\npassword spraying against their MDM solution. We used airCross created by Matt Burch to perform a password\r\nguessing attack.)\r\n3. Trigger the Email OTP for MFA options\r\n4. Read the created Email OTP code from the victim’s inbox\r\n5. Bypass MFA\r\nhttps://www.optiv.com/insights/source-zero/blog/microsoft-365-oauth-device-code-flow-and-phishing\r\nPage 3 of 5\n\nWe originally found a great open-source framework, TokenTactics created by revrsh3ll to conduct our phishing attack. The\r\ntool was designed to contain all the necessary functions and tactics to abuse Azure JSON Web Token (JWT). We highly\r\nrecommend trying it out. Leveraging his tool, however, we decided to create a simple Python script to automate some of the\r\ndevice code phishing flow to solely meet our goals. The first function getDeviceCode() was created to request for\r\nuser_code and device_code.\r\nFigure 16: getDeviceCode\r\nFigure 17: Example of Running devicePhish.py – Getting user_code \u0026 device_code\r\nNext, using the newly obtained device_code, the getAccessToken() function continuously loops through the authorization\r\nprocess. If the phishing attack is successful, it will yield an access_token and refresh_token; otherwise, the loop will end\r\nafter 15 minutes due to the token expiration.\r\nFigure 18: getAccessToken\r\nFigure 19: Example of Victim Entering user_code\r\nFigure 20: Example of Running devicePhish.py – Obtaining access_token \u0026 refresh_token\r\nWith access_token, the getMail() function keeps looking for an email containing specific text. For our case, it was looking\r\nfor a string “MFA – One Time Code” within the email text, so if we were to trigger the Email OTP, it would have sent the\r\ntemporary code to the victim’s inbox. If it finds the right email, it prints out the email context with the OTP code and saves\r\nthe email_id value.\r\nFigure 21: getMail\r\nFigure 22: Example OTP Email\r\nFigure 23: Example of Running devicePhish.py – Reading OTP Email\r\nFinally, with the stored email_id value, the deleteMail() function will delete the OTP email from the victim’s inbox.\r\nFigure 24: deleteMail\r\nFigure 25: Example of Running devicePhish.py – Deleting OTP Email\r\nThe script used in this demo can be found here: Microsoft365-devicePhish\r\nMitigation of Microsoft OAuth Device Code Phishing\r\nThis device code phishing attack can be mitigated by making configuration changes for Azure AD. Under the Enterprise\r\napplications settings, prevent users from giving consent to all applications.\r\nFigure 26: Mitigation\r\nIf the above solution is too restrictive, then please check out the following best practices provided by Microsoft:\r\nConfigure how end-users consent to applications\r\nManaging consent to applications and evaluating consent requests\r\nReferences\r\nhttps://o365blog.com/post/phishing/\r\nhttps://threatpost.com/microsoft-seizes-domains-office-365-phishing-scam/157261/\r\nhttps://www.optiv.com/insights/source-zero/blog/microsoft-365-oauth-device-code-flow-and-phishing\r\nPage 4 of 5\n\nhttps://github.com/rvrsh3ll/TokenTactics\r\nSource: https://www.optiv.com/insights/source-zero/blog/microsoft-365-oauth-device-code-flow-and-phishing\r\nhttps://www.optiv.com/insights/source-zero/blog/microsoft-365-oauth-device-code-flow-and-phishing\r\nPage 5 of 5",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"references": [
		"https://www.optiv.com/insights/source-zero/blog/microsoft-365-oauth-device-code-flow-and-phishing"
	],
	"report_names": [
		"microsoft-365-oauth-device-code-flow-and-phishing"
	],
	"threat_actors": [],
	"ts_created_at": 1775434072,
	"ts_updated_at": 1775791286,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/049a3f1a609ea2ef1d4c728a3d68310349ffd5d9.pdf",
		"text": "https://archive.orkl.eu/049a3f1a609ea2ef1d4c728a3d68310349ffd5d9.txt",
		"img": "https://archive.orkl.eu/049a3f1a609ea2ef1d4c728a3d68310349ffd5d9.jpg"
	}
}