This detection identifies adversaries who establish persistent backdoors by deploying PHP webshells that dynamically execute obfuscated code through direct eval() calls to evade static analysis. A proactive hunt is essential in Azure Sentinel because these obfuscated payloads often bypass signature-based defenses, allowing attackers to maintain stealthy command-and-control channels for lateral movement and data exfiltration within the organization’s web infrastructure.
rule WEBSHELL_PHP_Gzinflated
{
meta:
description = "PHP webshell which directly eval()s obfuscated string"
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 = "2023-07-05"
hash = "49e5bc75a1ec36beeff4fbaeb16b322b08cf192d"
hash = "6f36d201cd32296bad9d5864c7357e8634f365cc"
hash = "ab10a1e69f3dfe7c2ad12b2e6c0e66db819c2301"
hash = "a6cf337fe11fe646d7eee3d3f09c7cb9643d921d"
hash = "07eb6634f28549ebf26583e8b154c6a579b8a733"
id = "9cf99ae4-9f7c-502f-9294-b531002953d6"
strings:
$payload2 = /eval\s?\(\s?("\?>".)?gzinflate\s?\(\s?base64_decode\s?\(/ wide ascii nocase
$payload4 = /eval\s?\(\s?("\?>".)?gzuncompress\s?\(\s?(base64_decode|gzuncompress)/ wide ascii nocase
$payload6 = /eval\s?\(\s?("\?>".)?gzdecode\s?\(\s?base64_decode\s?\(/ wide ascii nocase
$payload7 = /eval\s?\(\s?base64_decode\s?\(/ wide ascii nocase
$payload8 = /eval\s?\(\s?pack\s?\(/ wide ascii nocase
// api.telegram
$fp1 = "YXBpLnRlbGVncmFtLm9"
//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"
//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
condition:
filesize < 700KB and 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 1 of ( $payload* ) and not any of ( $fp* )
}
This YARA rule can be deployed in the following contexts:
This rule contains 29 string patterns in its detection logic.
Here are 4 specific false positive scenarios for the PHP webshell which directly eval()s obfuscated string detection rule, along with suggested filters and exclusions:
Automated Backup & Reporting Scripts
eval() on base64-encoded configuration strings (e.g., eval(base64_decode("..."))) to dynamically render report headers without hardcoding them in the source file.File.Path contains /var/www/html/reports/daily_generator.php OR Process.User is backup-service.CI/CD Pipeline Deployment Artifacts
eval() block to parse complex JSON payloads or apply temporary feature flags during the rollout phase.Event.Time is between 01:00–05:00 AND Process.User matches jenkins-deployer OR gitlab-runner.Third-Party Plugin Initialization
eval() to decode and execute encrypted license keys upon server startup.