This detection identifies adversaries who have deployed obfuscated PHP webshells to establish a persistent foothold and execute commands on compromised web servers. Proactive hunting for this behavior in Azure Sentinel is critical because obfuscation techniques often evade standard signature-based detections, allowing attackers to maintain stealthy access while exfiltrating sensitive data or pivoting deeper into the network.
rule WEBSHELL_PHP_OBFUSC
{
meta:
description = "PHP webshell obfuscated"
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"
score = 75
date = "2021/01/12"
modified = "2025-09-22"
hash = "eec9ac58a1e763f5ea0f7fa249f1fe752047fa60"
hash = "181a71c99a4ae13ebd5c94bfc41f9ec534acf61cd33ef5bce5fb2a6f48b65bf4"
hash = "76d4e67e13c21662c4b30aab701ce9cdecc8698696979e504c288f20de92aee7"
hash = "1d0643927f04cb1133f00aa6c5fa84aaf88e5cf14d7df8291615b402e8ab6dc2"
id = "f66e337b-8478-5cd3-b01a-81133edaa8e5"
strings:
//strings from private rule php_false_positive
// try to use only strings which would be flagged by themselves as suspicious by other rules, e.g. eval
// a good choice is a string with good atom quality = ideally 4 unusual characters next to each other
$gfp1 = "eval(\"return [$serialised_parameter" // elgg
$gfp2 = "$this->assert(strpos($styles, $"
$gfp3 = "$module = new $_GET['module']($_GET['scope']);"
$gfp4 = "$plugin->$_POST['action']($_POST['id']);"
$gfp5 = "$_POST[partition_by]($_POST["
$gfp6 = "$object = new $_REQUEST['type']($_REQUEST['id']);"
$gfp7 = "The above example code can be easily exploited by passing in a string such as" // ... ;)
$gfp8 = "Smarty_Internal_Debug::start_render($_template);"
$gfp9 = "?p4yl04d=UNION%20SELECT%20'<?%20system($_GET['command']);%20?>',2,3%20INTO%20OUTFILE%20'/var/www/w3bsh3ll.php"
$gfp10 = "[][}{;|]\\|\\\\[+=]\\|<?=>?"
$gfp11 = "(eval (getenv \"EPROLOG\")))"
$gfp12 = "ZmlsZV9nZXRfY29udGVudHMoJ2h0dHA6Ly9saWNlbnNlLm9wZW5jYXJ0LWFwaS5jb20vbGljZW5zZS5waHA/b3JkZXJ"
$gfp13 = "assert(\\\""
$gfp14 = "PhutilUTF8TestCase"
$gfp15 = "chr(195).chr(128) => 'A'," // 3d413ceb54e929d6af2e64ebb8df7ba2452a7aac876dddcf6336c3445e7bcc91, wordpress formatter.php
//strings from private rule capa_php_old_safe
$php_short = "<?" wide ascii
// prevent xml and asp from hitting with the short tag
$no_xml1 = "<?xml version" nocase wide ascii
$no_xml2 = "<?xml-stylesheet" nocase wide ascii
$no_asp1 = "<%@LANGUAGE" nocase wide ascii
$no_asp2 = /<script language="(vb|jscript|c#)/ nocase wide ascii
$no_pdf = "<?xpacket"
// of course the new tags should also match
// already matched by "<?"
$php_new1 = /<\?=[^?]/ wide ascii
$php_new2 = "<?php" nocase wide ascii
$php_new3 = "<script language=\"php" nocase wide ascii
//strings from private rule capa_php_obfuscation_multi
$o1 = "chr(" nocase wide ascii
$o2 = "chr (" nocase wide ascii
// not excactly a string function but also often used in obfuscation
$o3 = "goto" fullword nocase wide ascii
$o4 = "\\x9" wide ascii
$o5 = "\\x3" wide ascii
// just picking some random numbers because they should appear often enough in a long obfuscated blob and it's faster than a regex
$o6 = "\\61" wide ascii
$o7 = "\\44" wide ascii
$o8 = "\\112" wide ascii
$o9 = "\\120" wide ascii
$fp1 = "$goto" wide ascii
//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
$cpayload5 = /\bsystem[\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:
not (
any of ( $gfp* )
)
and (
(
(
$php_short in (0..100) or
$php_short in (filesize-1000..filesize)
)
and not any of ( $no_* )
)
or any of ( $php_new* )
)
and (
// allow different amounts of potential obfuscation functions depending on filesize
not $fp1 and (
(
filesize < 20KB and
(
( #o1+#o2 ) > 50 or
#o3 > 10 or
( #o4+#o5+#o6+#o7+#o8+#o9 ) > 20
)
) or (
filesize < 200KB and
(
( #o1+#o2 ) > 200 or
#o3 > 30 or
( #o4+#o5+#o6+#o7+#o8+#o9 ) > 30
)
)
)
)
and (
any of ( $cpayload* ) or
all of ( $m_cpayload_preg_filter* )
)
}
This YARA rule can be deployed in the following contexts:
This rule contains 53 string patterns in its detection logic.
Here are 5 specific false positive scenarios for the PHP Webshell Obfuscated detection rule, including suggested filters and exclusions tailored for an enterprise environment:
Automated Backup Scripts with Base64 Encoding
cron or Windows Task Scheduler) execute a custom PHP script that reads configuration files, encodes them into Base64 strings to ensure safe transmission over HTTP, and then decodes them for processing. The heavy use of base64_decode() combined with string concatenation often mimics the obfuscation patterns typical of malicious webshells./var/www/html/scripts/daily_backup.php) and the user context (e.g., backup-service account). Additionally, filter out events where the decoded payload matches a known internal checksum or hash signature.CI/CD Pipeline Deployment Artifacts
eval() with complex string manipulation) to dynamically configure the runtime environment, triggering the rule during the deployment window.10.50.1.x subnet) or filter by User-Agent strings containing “Jenkins” or “GitLab”.Third-Party Analytics and Monitoring Agents