On 2 November 2026, Google Threat Intelligence Group and Mandiant published their first full report on LLMjacking: criminals stealing cloud credentials, pointing them at enterprise model endpoints, and reselling the access. The numbers are the part to read twice. Fraudulent charges "exceeding $100,000 USD in a single day". Cumulative charges "reaching $1 million USD before access was revoked". One actor ran "more than 500,000 LLM API calls in less than eight hours" on stolen keys. The report names Azure Cognitive Services alongside Bedrock and Gemini as a platform defenders should be watching, and it lists the exact Azure operations to alert on. If your organisation runs Azure OpenAI or Foundry models with key-based authentication, this is the week to find out how many keys exist and who holds them.
What the report actually found
Cryptojacking taught a generation of cloud teams to watch for slow, steady compute theft. LLMjacking does not look like that. GTIG's framing is that it "can generate large spikes in fraudulent cloud charges in a matter of hours", because a frontier model bills per token and a stolen key has no natural ceiling other than your quota.
The initial access stories are familiar. Service account keys with "broad project permissions". GitHub personal access tokens used to reach private repositories. Corporate IAM credentials used to mint fresh access keys through federation and so "circumvent MFA requirements". One actor "opportunistically harvested exposed corporate LLM API keys from public GitHub repositories". Another scanned for Ollama endpoints left open to the internet. None of this is novel. What is new is the market on the other side.
GTIG calls that market "LLM Mixers": "illicit routing gateways that proxy, aggregate, and resell unauthorized LLM access to underground subscribers". The report describes a division of labour. One cluster steals credentials and validates them. A second cluster runs the resale infrastructure, built on open-source gateways such as new-api and gcli2api to pool keys and balance load across victim accounts. The service jet-night.com sold "an unlimited token plan for approximately $60 USD per month" against frontier models whose legitimate consumer plans cost $100 to $250. GTIG's conclusion: the price only works because someone else is paying the bill. The same service advertised repeated outages on Telegram, which GTIG attributes to victims revoking keys as they noticed.
Two tradecraft details matter for Azure defenders.
- Validation sweeps with max_tokens=1. Before any heavy use, one group confirmed stolen keys with single-token requests, "remaining undetected by the victim's endpoint detection and response (EDR) and SIEM volume anomaly alerts". A volume-based alert sees nothing. A cost-based alert sees nothing. The first visible signal is a new caller succeeding at all.
- Burn and rotate. Actors created and deleted short-lived service accounts "while auditing the cloud provider's billing interface to monitor financial impact". They watch your bill more closely than you do, and pace consumption to stay under whatever threshold they think you set.
There is also a gateway angle. After one cluster handed over pre-validated virtual keys, a follow-on cluster exploited CVE-2026-40217 in the victim's LiteLLM proxy, a user-ID rebind flaw in the key regeneration endpoint, to turn ordinary keys into full administrator credentials. Readers of our LiteLLM 1.107.0 audit already know that the gateway in front of your models is part of your attack surface. GTIG now has an incident to prove it.
Why Azure OpenAI is squarely in scope
The report's monitoring section gives Azure its own subsection. It tells defenders to watch the Microsoft.CognitiveServices namespace in the activity log for deployments being written, accounts and deployments being read as reconnaissance, and diagnostic settings being read, written or deleted as evasion. On the data plane it points at high volumes of POST requests to the completions and chat completions endpoints.
Azure has a precedent of its own. In February 2025 Microsoft's Digital Crimes Unit named four developers behind Storm-2139, a network that "exploited exposed customer credentials scraped from public sources" to drive Azure OpenAI deployments through a custom tool and resell the output. Microsoft sued in the Eastern District of Virginia and seized the group's website. The GTIG report describes the same business model, eighteen months later, industrialised and provider-agnostic.
The structural problem is what an Azure OpenAI key is. Every account resource has two keys. A key authenticates to every deployment in that resource, for every model, up to the tokens-per-minute allocation of each. It does not expire. It is not bound to a network, an application or a person. Rotating it breaks every client at once, so teams rotate rarely. It is, in other words, exactly the artefact a Mixer operator wants to buy.
The audit: six controls, in order of payoff
| Control | What it stops | Effort | Catch |
|---|---|---|---|
| Disable key auth, use Entra ID | Stolen keys become worthless | Per resource: minutes. Per client: a code change | Old keys can keep working for hours after the switch |
| Secret scanning with push protection | Keys reaching public or shared repos | Org setting | Azure OpenAI pattern has no validity check |
| Data-plane diagnostic logs | Blindness to new callers and sweeps | One diagnostic setting per resource | Log Analytics ingestion cost |
| Right-size TPM per deployment | Caps the maximum daily loss | Portal or Bicep | Legitimate bursts hit 429 too |
| Activity log alerts on config drift | Silent new deployments, disabled logging | Two alert rules | Noise from your own IaC unless filtered |
| Defender for AI Services | Credential-theft and anomaly alerts into XDR | Subscription toggle | Billed per 1,000 tokens scanned after the trial |
1. Turn the keys off
Azure AI services resources support Microsoft Entra authentication everywhere, and the account resource has a disableLocalAuth property that refuses key-based calls outright. Microsoft ships a built-in Azure Policy for it, named "Azure AI Services resources should have key access disabled (disable local authentication)", which you can assign at subscription or resource group scope to catch new resources as they appear.
# Per resource, today
Set-AzCognitiveServicesAccount -ResourceGroupName rg-ai-prod -Name aoai-prod-swc -DisableLocalAuth $true
# In Bicep, for every future resource
resource aoai 'Microsoft.CognitiveServices/accounts@2024-10-01' = {
name: 'aoai-prod-swc'
location: 'swedencentral'
kind: 'OpenAI'
sku: { name: 'S0' }
properties: {
disableLocalAuth: true
publicNetworkAccess: 'Disabled'
}
}
Read the propagation note in Microsoft's documentation before you rely on this during an incident. The control-plane change is immediate, but "the shared gateway that enforces authentication can continue to accept previously valid keys until its cached configuration refreshes", which "can take up to several hours depending on region, load, and gateway cache state". Microsoft's own instruction is to send a request with the old key and confirm a 401 before you consider the key dead. If you are responding to an active compromise, regenerate the keys as well. Regeneration invalidates the stolen value; disabling local auth closes the door behind it.
On the client side the change is a credential object, not an architecture. Grant the application's managed identity the Cognitive Services OpenAI User role on the resource and swap the key credential for a token credential. The same pattern we described for secret rotation with managed identity and Key Vault applies, with the bonus that there is no secret left to rotate.
2. Find the keys that already leaked
GitHub secret scanning has a partner pattern for the Azure OpenAI Key, with push protection, so a developer committing one to a protected repository is blocked before the push lands. Two gaps to know about. First, that pattern has no validity check, so GitHub will not tell you whether a found key still works; the sibling Azure AI Services Key pattern does have one. Second, push protection only helps on repositories where it is turned on. GTIG's actors harvested from public repositories, which means the question is not whether your main monorepo is protected but whether a contractor's demo repo from 2024 is. Run a historical scan across the organisation, treat every hit as live, and regenerate.
3. Log the data plane, then hunt for the sweep
Platform metrics are collected for free, but resource logs are not collected at all until you create a diagnostic setting. Azure OpenAI exposes the categories Audit, RequestResponse, Trace and AzureOpenAIRequestUsage, all landing in the AzureDiagnostics table. RequestResponse is the one that records who called, from where, which operation, and what the service answered. That is enough to catch the max_tokens=1 sweep, because a sweep is a new caller address succeeding against a resource it has never touched before.
// Callers first seen in the last 24 hours against any Azure OpenAI resource
let lookback = 30d;
let recent = 24h;
let known = AzureDiagnostics
| where ResourceProvider == "MICROSOFT.COGNITIVESERVICES"
| where Category == "RequestResponse"
| where TimeGenerated between (ago(lookback) .. ago(recent))
| distinct CallerIPAddress, _ResourceId;
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.COGNITIVESERVICES"
| where Category == "RequestResponse"
| where TimeGenerated > ago(recent)
| join kind=leftanti known on CallerIPAddress, _ResourceId
| summarize requests = count(), ok = countif(ResultSignature startswith "2"),
first = min(TimeGenerated), last = max(TimeGenerated)
by CallerIPAddress, _ResourceId, OperationName
| order by requests desc
Pair that with a metric alert on Processed Inference Tokens split by ModelDeploymentName, using a dynamic threshold rather than a fixed one so the baseline follows your own weekly pattern. Metrics are near real time; Cost Management is not. Azure's anomaly detection "runs 36 hours after the end of the day (UTC)", and budget data is "typically available within 8-24 hours" with evaluation "every 24 hours". Against an attack that GTIG measures in hours, a budget email is a post-mortem.
4. Use quota as the loss ceiling it already is
Azure budgets do not stop anything. Microsoft's documentation is explicit: "Resources aren't affected, and your consumption isn't stopped." The one hard limit you do have is the tokens-per-minute allocation on each deployment. When a deployment reaches its TPM, "further requests receive an HTTP 429 response until token capacity becomes available". That makes TPM a maximum loss rate, and the arithmetic is simple: multiply the allocation by 1,440 minutes and by the price per token.
Using the $1 per million input and $5 per million output list prices we covered for GPT-6.1 Terra in Foundry, a saturated deployment costs this much per day:
| TPM allocated | Tokens per day if saturated | Worst case, all input ($1/M) | Worst case, all output ($5/M) |
|---|---|---|---|
| 100,000 | 144 million | $144 | $720 |
| 500,000 | 720 million | $720 | $3,600 |
| 1,000,000 | 1.44 billion | $1,440 | $7,200 |
A single key reaches every deployment in the resource, so sum the rows for each resource. Then look at the sum and ask whether your finance team would notice it on a Tuesday. Most teams discover that a development resource carries the same generous TPM as production, because quota was assigned once and never revisited. Trim development deployments to what tests need. Give production what the workload needs plus headroom, not what the subscription allows. The 429s you see afterwards are a feature: they are the sound of a ceiling.
5. Alert on the control plane GTIG told you to watch
GTIG's Azure list translates directly into two activity log alert rules. One for Microsoft.CognitiveServices/accounts/deployments/write, because in "multiple incidents that Mandiant investigated, LLM services were not enabled at the time of compromise" and the attacker enabled them. One for Microsoft.Insights/diagnosticSettings/delete and write on your AI resources, because the first thing a careful operator does is turn the logs off. GTIG's guidance for the AWS equivalent applies verbatim: any such call by a principal that is not your infrastructure-as-code identity is high severity.
az monitor activity-log alert create \
--resource-group rg-sec-alerts \
--name aoai-new-deployment \
--scope /subscriptions/<subscription-id> \
--condition category=Administrative and operationName=Microsoft.CognitiveServices/accounts/deployments/write \
--action-group /subscriptions/<subscription-id>/resourceGroups/rg-sec-alerts/providers/microsoft.insights/actionGroups/ag-soc
az monitor activity-log alert create \
--resource-group rg-sec-alerts \
--name aoai-diagnostics-deleted \
--scope /subscriptions/<subscription-id> \
--condition category=Administrative and operationName=Microsoft.Insights/diagnosticSettings/delete \
--action-group /subscriptions/<subscription-id>/resourceGroups/rg-sec-alerts/providers/microsoft.insights/actionGroups/ag-soc
Wire the action group to something that acts, not only emails. A Function that sets disableLocalAuth to true and drops TPM on the affected resource turns a 02:00 alert into a 02:01 containment, with the caveat from control 1 that the gateway may honour old keys for a while longer.
6. Decide on Defender for AI Services
Microsoft Defender for Cloud's threat protection for AI services is generally available and covers Azure OpenAI and the Foundry model inference service. Its alert categories include credential theft, jailbreak, data leakage and data poisoning, and the alerts flow into Defender XDR alongside the rest of your incidents. It is priced per 1,000 tokens scanned, with a 30-day trial capped at 75 billion tokens, and it scans text tokens only. For a team that already runs Defender XDR, it is the fastest way to get LLMjacking signals in front of the analysts who will handle them. For a team that does not, the five controls above come first and cost nothing beyond log ingestion.
The Swedish and EU angle
Cybersäkerhetslagen makes this a reportable incident, not a billing dispute. Sweden's new cybersecurity law, SFS 2025:1506, transposes NIS2 and "träder i kraft den 15 januari 2026". For organisations in scope, a significant incident needs an initial notification "senast 24 timmar" after discovery, an incident report "senast 72 timmar" later for most operators, and a final report within a month. NIS2's own test for significance includes financial loss for the entity concerned. A six-figure fraudulent bill accumulated through a stolen credential meets that test comfortably, which means the clock starts the moment your SOC confirms the key was abused, not when finance disputes the invoice. Have the detection in place so that the 24 hours start on your terms.
The EU Data Zone does not help here. Swedish buyers chose Sweden Central and the EU Data Zone deployment type to keep processing inside the Union. That promise holds: the attacker's prompts are processed where your deployment is processed. But a key works from any IP on the planet, and the GTIG report describes relays "hosted on deceptive lookalike domains" built precisely to hide where traffic originates. Residency controls answer where data is processed. They say nothing about who is allowed to send it.
GDPR exposure depends on what else the credential could reach. A bare Azure OpenAI key gives inference access and nothing more, and the attacker's own prompts are not your personal data. But GTIG's cases mostly began with cloud identity credentials, not model keys, and those identities read storage, secrets and databases on the way to the model. If the credential that reached your deployments could also read your data, the 72-hour clock under Article 33 is running in parallel with the one under cybersäkerhetslagen. Scope the credential before you scope the notification.
Upphandling: ask the question before the contract. Public-sector buyers evaluating AI platforms or integration partners can add one line to the security questionnaire: "Is key-based authentication to model endpoints disabled, and if not, why?" A vendor who answers with a managed identity diagram has done the work. A vendor who answers with a Key Vault reference has moved the key, not removed it.
This week's checklist
- Monday. Inventory every Microsoft.CognitiveServices account in every subscription. Record whether
disableLocalAuthis true, the sum of TPM across its deployments, and whether a diagnostic setting exists. - Tuesday. Assign the "key access disabled" Azure Policy in audit mode. Read the non-compliance list. That is your migration backlog.
- Wednesday. Run a historical secret scan across all repositories for Azure OpenAI and Azure AI Services key patterns. Regenerate every hit, regardless of age.
- Thursday. Create diagnostic settings for RequestResponse on every resource, deploy the two activity log alerts above, and add a dynamic-threshold metric alert on Processed Inference Tokens per deployment.
- Friday. Right-size TPM on development resources. Run the new-caller query against the week's logs and look at every address you do not recognise.
- Next sprint. Move production clients to managed identity, then flip
disableLocalAuthand verify the 401. Decide on Defender for AI Services based on whether you already operate XDR.
GTIG expects criminals to "increasingly prioritize LLMJacking over traditional resource theft like cryptojacking" because the margins are better and the resale market already exists. The defence is not exotic. It is the same identity hygiene Azure has offered for years, applied to a resource type that most teams provisioned in a hurry in 2024 and have not looked at since.
subscribe # the AI news that matters, minus the noise
Sources
- Google Cloud: The Rise of LLMJacking: How Threat Actors Monetize Compromised Cloud and Model Resources (2 November 2026)
- Microsoft: Disrupting a global cybercrime network abusing generative AI (Storm-2139, 27 February 2025)
- Microsoft Learn: Disable local authentication in Foundry Tools
- Microsoft Learn: Monitoring data reference for Azure OpenAI
- Microsoft Learn: Manage Azure OpenAI quota
- Microsoft Learn: Create and manage budgets
- Microsoft Learn: Identify anomalies and unexpected changes in cost
- Microsoft Learn: AI threat protection in Microsoft Defender for Cloud
- GitHub Docs: Supported secret scanning patterns
- Riksdagen: Cybersäkerhetslag (2025:1506)