Adversaries may be deploying web shells to establish persistent access and exfiltrate data from compromised web servers. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify and mitigate potential long-term persistence and data theft threats.
KQL Query
let scriptExtensions = dynamic([".php", ".jsp", ".js", ".aspx", ".asmx", ".asax", ".cfm", ".shtml"]);
SecurityAlert
| where ProviderName =~ "MDATP"
| extend alertData = parse_json(Entities)
| mvexpand alertData
// Get only the file type from the JSON, this gives us the file name
| where alertData.Type =~ "file"
// This can be expanded to include other script extensions
| where alertData.Name has_any(scriptExtensions)
| extend FileName = alertData.Name
| project TimeGenerated, tostring(FileName), alertData.Directory
| join (
W3CIISLog
| where csUriStem has_any(scriptExtensions)
| extend splitUriStem = split(csUriStem, "/")
| extend FileName = splitUriStem[-1]
| summarize StartTime=min(TimeGenerated), EndTime=max(TimeGenerated) by AttackerIP=cIP, AttackerUserAgent=csUserAgent, SiteName=sSiteName, ShellLocation=csUriStem, tostring(FileName)
) on FileName
| project StartTime, EndTime, AttackerIP, AttackerUserAgent, SiteName, ShellLocation
| extend timestamp = StartTime, IPCustomEntity = AttackerIP
id: d0a3cb7b-375e-402d-9827-adafe0ce386d
name: Web shell file alert enrichment
description: |
'Extracts MDATP Alert for a web shell being placed on the server and then enriches this event with information from W3CIISLog to idnetigy the Attacker that placed the shell'
requiredDataConnectors:
- connectorId: MicrosoftDefenderAdvancedThreatProtection
dataTypes:
- SecurityAlert
- connectorId: AzureMonitor(IIS)
dataTypes:
- W3CIISLog
tactics:
- PrivilegeEscalation
- Persistence
query: |
let scriptExtensions = dynamic([".php", ".jsp", ".js", ".aspx", ".asmx", ".asax", ".cfm", ".shtml"]);
SecurityAlert
| where ProviderName =~ "MDATP"
| extend alertData = parse_json(Entities)
| mvexpand alertData
// Get only the file type from the JSON, this gives us the file name
| where alertData.Type =~ "file"
// This can be expanded to include other script extensions
| where alertData.Name has_any(scriptExtensions)
| extend FileName = alertData.Name
| project TimeGenerated, tostring(FileName), alertData.Directory
| join (
W3CIISLog
| where csUriStem has_any(scriptExtensions)
| extend splitUriStem = split(csUriStem, "/")
| extend FileName = splitUriStem[-1]
| summarize StartTime=min(TimeGenerated), EndTime=max(TimeGenerated) by AttackerIP=cIP, AttackerUserAgent=csUserAgent, SiteName=sSiteName, ShellLocation=csUriStem, tostring(FileName)
) on FileName
| project StartTime, EndTime, AttackerIP, AttackerUserAgent, SiteName, ShellLocation
| extend timestamp = StartTime, IPCustomEntity = AttackerIP
version: 1.0.0
metadata:
source:
kind: Community
author:
name: Thomas McElroy
support:
tier: Community
categories:
domains: [ "Security - Threat Protection" ]
| Sentinel Table | Notes |
|---|---|
SecurityAlert | Ensure this data connector is enabled |
W3CIISLog | Ensure this data connector is enabled |
Scenario: Legitimate scheduled job deploying a web application update
Description: A system administrator uses a CI/CD pipeline (e.g., Jenkins, GitHub Actions) to deploy a web application update that includes a .php file.
Filter/Exclusion: Check for known deployment tools or IP addresses associated with internal CI/CD systems. Use a filter like: source_ip IN (internal_ci_cd_ips) or process_name IN ("jenkins.exe", "github-actions.exe").
Scenario: Admin manually uploading a configuration file
Description: An admin uploads a configuration file (e.g., config.php) via FTP or a web interface to update a CMS like WordPress or Drupal.
Filter/Exclusion: Filter by user agent or IP address of the admin’s workstation. Example: user_agent CONTAINS "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" or source_ip IN (admin_workstation_ips).
Scenario: Automated backup script generating a temporary PHP file
Description: A backup script (e.g., backup.sh or backup.bat) creates a temporary .php file during a backup process, which is later deleted.
Filter/Exclusion: Check for file deletion events within a short time frame. Example: file_deleted_time - file_created_time < 1 minute or file_name CONTAINS "temp_*.php".
Scenario: Web server log rotation creating a new log file with a .php extension
Description: During log rotation, a log file is renamed to include a .php extension (e.g., access.log.php) as part of a naming convention.
Filter/Exclusion: Filter by file path patterns common in log rotation. Example: file_path CONTAINS "logs/ or `