This detection identifies potential PHP webshells that utilize eval functions to execute dynamically decoded payloads, leveraging high entropy analysis via YARA to distinguish obfuscated malicious code from legitimate traffic. Proactive hunting for this behavior in Azure Sentinel is critical because these encoded webshells often evade signature-based defenses by masking their payload structure, allowing adversaries to establish persistent command-and-control channels within web-facing applications before traditional alerts trigger.
rule WEBSHELL_PHP_Encoded_Big
{
meta:
description = "PHP webshell using some kind of eval with encoded blob to decode, which is checked with YARAs math.entropy module"
license = "Detection Rule License 1.1 https://github.com/Neo23x0/signature-base/blob/master/LICENSE"
author = "Arnim Rupp (https://github.com/ruppde)"
reference = "Internal Research"
date = "2021/02/07"
modified = "2024-12-16"
score = 50
hash = "1d4b374d284c12db881ba42ee63ebce2759e0b14"
hash = "fc0086caee0a2cd20609a05a6253e23b5e3245b8"
hash = "b15b073801067429a93e116af1147a21b928b215"
hash = "74c92f29cf15de34b8866db4b40748243fb938b4"
hash = "042245ee0c54996608ff8f442c8bafb8"
id = "c3bb7b8b-c554-5802-8955-c83722498f8b"
strings:
//strings from private rule capa_php_new
$new_php1 = /<\?=[\w\s@$]/ wide ascii
$new_php2 = "<?php" nocase wide ascii
$new_php3 = "<script language=\"php" nocase wide ascii
$php_short = "<?"
//strings from private rule capa_php_payload
// \([^)] to avoid matching on e.g. eval() in comments
$cpayload1 = /\beval[\n\t ]{0,500}(\([^)]|\/\*)/ nocase wide ascii
$cpayload2 = /\bexec[\n\t ]{0,500}(\([^)]|\/\*)/ nocase wide ascii
$cpayload3 = /\bshell_exec[\n\t ]{0,500}(\([^)]|\/\*)/ nocase wide ascii
$cpayload4 = /\bpassthru[\n\t ]{0,500}(\([^)]|\/\*)/ nocase wide ascii
$cpayload6 = /\bpopen[\n\t ]{0,500}(\([^)]|\/\*)/ nocase wide ascii
$cpayload7 = /\bproc_open[\n\t ]{0,500}(\([^)]|\/\*)/ nocase wide ascii
$cpayload8 = /\bpcntl_exec[\n\t ]{0,500}(\([^)]|\/\*)/ nocase wide ascii
$cpayload9 = /\bassert[\n\t ]{0,500}\([^)0]/ nocase wide ascii
$cpayload10 = /\bpreg_replace[\n\t ]{0,500}\([^\)]{1,100}\/[ismxADSUXju]{0,11}(e|\\x65)/ nocase wide ascii
$cpayload12 = /\bmb_ereg_replace[\t ]{0,500}\([^\)]{1,100}'e'/ nocase wide ascii
$cpayload13 = /\bmb_eregi_replace[\t ]{0,500}\([^\)]{1,100}'e'/ nocase wide ascii
$cpayload20 = /\bcreate_function[\n\t ]{0,500}(\([^)]|\/\*)/ nocase wide ascii
$cpayload21 = /\bReflectionFunction[\n\t ]{0,500}(\([^)]|\/\*)/ nocase wide ascii
$cpayload22 = /fetchall\(PDO::FETCH_FUNC[\n\t ]{0,500}[,}\)]/ nocase wide ascii
$m_cpayload_preg_filter1 = /\bpreg_filter[\n\t ]{0,500}(\([^\)]|\/\*)/ nocase wide ascii
$m_cpayload_preg_filter2 = "'|.*|e'" nocase wide ascii
// TODO backticks
condition:
//console.log(math.entropy(500, filesize-500)) and
//console.log(math.mean(500, filesize-500)) and
//console.log(math.deviation(500, filesize-500, 89.0)) and
//any of them or
filesize < 1000KB and (
any of ( $new_php* ) or
$php_short at 0
)
and (
any of ( $cpayload* ) or
all of ( $m_cpayload_preg_filter* )
)
and (
// file shouldn't be too small to have big enough data for math.entropy
filesize > 2KB and
(
// base64 :
// ignore first and last 500bytes because they usually contain code for decoding and executing
math.entropy(500, filesize-500) >= 5.7 and
// encoded text has a higher mean than text or code because it's missing the spaces and special chars with the low numbers
math.mean(500, filesize-500) > 80 and
// deviation of base64 is ~20 according to CyberChef_v9.21.0.html#recipe=Generate_Lorem_Ipsum(3,'Paragraphs')To_Base64('A-Za-z0-9%2B/%3D')To_Charcode('Space',10)Standard_Deviation('Space')
// lets take a bit more because it might not be pure base64 also include some xor, shift, replacement, ...
// 89 is the mean of the base64 chars
math.deviation(500, filesize-500, 89.0) < 24
) or (
// gzinflated binary sometimes used in php webshells
// ignore first and last 500bytes because they usually contain code for decoding and executing
math.entropy(500, filesize-500) >= 7.7 and
// encoded text has a higher mean than text or code because it's missing the spaces and special chars with the low numbers
math.mean(500, filesize-500) > 120 and
math.mean(500, filesize-500) < 136 and
// deviation of base64 is ~20 according to CyberChef_v9.21.0.html#recipe=Generate_Lorem_Ipsum(3,'Paragraphs')To_Base64('A-Za-z0-9%2B/%3D')To_Charcode('Space',10)Standard_Deviation('Space')
// lets take a bit more because it might not be pure base64 also include some xor, shift, replacement, ...
// 89 is the mean of the base64 chars
math.deviation(500, filesize-500, 89.0) > 65
)
)
}
This YARA rule can be deployed in the following contexts:
This rule contains 20 string patterns in its detection logic.
Here are 4 specific false positive scenarios for the PHP Webshell Detection Rule, including context and recommended filters/exclusions:
Scheduled Backup and Reporting Scripts
eval() before being rendered by a reporting engine like ReportLab. The high entropy of the encoded blob combined with the dynamic evaluation triggers the rule./var/www/html/reports/daily_generator.php) and the user context (e.g., svc-backup or a dedicated service account). Additionally, exclude files with extensions like .php.bak if they are part of a known backup rotation process.CI/CD Pipeline Artifact Deployment
eval() to configure environment variables immediately upon deployment before the main application starts.svc-jenkins-deploy) and the file modification timestamp matches the deployment trigger time.Third-Party Plugin or CMS Module Updates