This detection identifies potential ASP webshells that leverage indirect eval/exec functions on untrusted user input or perform file write operations to establish persistent command execution capabilities. Proactive hunting for this behavior in Azure Sentinel is critical because these generic patterns often indicate the initial foothold of a remote code execution attack, allowing analysts to isolate compromised endpoints before adversaries can escalate privileges or exfiltrate sensitive data.
rule WEBSHELL_ASP_Generic : FILE {
meta:
description = "Generic ASP webshell which uses any eval/exec function indirectly on user input or writes a file"
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-03-07"
modified = "2025-08-18"
score = 60
hash = "a8c63c418609c1c291b3e731ca85ded4b3e0fba83f3489c21a3199173b176a75"
hash = "4cf6fbad0411b7d33e38075f5e00d4c8ae9ce2f6f53967729974d004a183b25c"
hash = "a91320483df0178eb3cafea830c1bd94585fc896"
hash = "f3398832f697e3db91c3da71a8e775ebf66c7e73"
id = "0904cefb-6e0f-5e5f-9986-cf83d409ce46"
strings:
$asp_much_sus7 = "Web Shell" nocase
$asp_much_sus8 = "WebShell" nocase
$asp_much_sus3 = "hidded shell"
$asp_much_sus4 = "WScript.Shell.1" nocase
$asp_much_sus5 = "AspExec"
$asp_much_sus14 = "\\pcAnywhere\\" nocase
$asp_much_sus15 = "antivirus" nocase
$asp_much_sus16 = "McAfee" nocase
$asp_much_sus17 = "nishang"
$asp_much_sus18 = "\"unsafe" fullword wide ascii
$asp_much_sus19 = "'unsafe" fullword wide ascii
$asp_much_sus28 = "exploit" fullword wide ascii
$asp_much_sus30 = "TVqQAAMAAA" wide ascii
$asp_much_sus31 = "HACKED" fullword wide ascii
$asp_much_sus32 = "hacked" fullword wide ascii
$asp_much_sus33 = "hacker" wide ascii
$asp_much_sus34 = "grayhat" nocase wide ascii
$asp_much_sus35 = "Microsoft FrontPage" wide ascii
$asp_much_sus36 = "Rootkit" wide ascii
$asp_much_sus37 = "rootkit" wide ascii
$asp_much_sus38 = "/*-/*-*/" wide ascii
$asp_much_sus39 = "u\"+\"n\"+\"s" wide ascii
$asp_much_sus40 = "\"e\"+\"v" wide ascii
$asp_much_sus41 = "a\"+\"l\"" wide ascii
$asp_much_sus42 = "\"+\"(\"+\"" wide ascii
$asp_much_sus43 = "q\"+\"u\"" wide ascii
$asp_much_sus44 = "\"u\"+\"e" wide ascii
$asp_much_sus45 = "/*//*/" wide ascii
$asp_much_sus46 = "(\"/*/\"" wide ascii
$asp_much_sus47 = "eval(eval(" wide ascii
$asp_much_sus48 = "Shell.Users" wide ascii
$asp_much_sus49 = "PasswordType=Regular" wide ascii
$asp_much_sus50 = "-Expire=0" wide ascii
$asp_much_sus51 = "sh\"&\"el" wide ascii
$asp_gen_sus1 = /:\s{0,20}eval}/ nocase wide ascii
$asp_gen_sus2 = /\.replace\(\/\w\/g/ nocase wide ascii
$asp_gen_sus6 = "self.delete"
$asp_gen_sus9 = "\"cmd /c" nocase
$asp_gen_sus10 = "\"cmd\"" nocase
$asp_gen_sus11 = "\"cmd.exe" nocase
$asp_gen_sus12 = "%comspec%" wide ascii
$asp_gen_sus13 = "%COMSPEC%" wide ascii
//TODO:$asp_gen_sus12 = ".UserName" nocase
$asp_gen_sus18 = "Hklm.GetValueNames();" nocase
// bonus string for proxylogon exploiting webshells
$asp_gen_sus19 = "http://schemas.microsoft.com/exchange/" wide ascii
$asp_gen_sus21 = "\"upload\"" wide ascii
$asp_gen_sus22 = "\"Upload\"" wide ascii
$asp_gen_sus25 = "shell_" wide ascii
//$asp_gen_sus26 = "password" fullword wide ascii
//$asp_gen_sus27 = "passw" fullword wide ascii
// own base64 or base 32 func
$asp_gen_sus29 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" fullword wide ascii
$asp_gen_sus30 = "abcdefghijklmnopqrstuvwxyz234567" fullword wide ascii
$asp_gen_sus31 = "serv-u" wide ascii
$asp_gen_sus32 = "Serv-u" wide ascii
$asp_gen_sus33 = "Army" fullword wide ascii
$asp_slightly_sus1 = "<pre>" wide ascii
$asp_slightly_sus2 = "<PRE>" wide ascii
// "e"+"x"+"e"
$asp_gen_obf1 = "\"+\"" wide ascii
$fp1 = "DataBinder.Eval"
$fp2 = "B2BTools"
$fp3 = "<b>Failed to execute cache update. See the log file for more information" ascii
$fp4 = "Microsoft. All rights reserved."
$fp5 = "\"unsafe\"," ascii wide
//strings from private rule capa_asp
$tagasp_short1 = /<%[^"]/ wide ascii
// also looking for %> to reduce fp (yeah, short atom but seldom since special chars)
$tagasp_short2 = "%>" wide ascii
// classids for scripting host etc
$tagasp_classid1 = "72C24DD5-D70A-438B-8A42-98424B88AFB8" nocase wide ascii
$tagasp_classid2 = "F935DC22-1CF0-11D0-ADB9-00C04FD58A0B" nocase wide ascii
$tagasp_classid3 = "093FF999-1EA0-4079-9525-9614C3504B74" nocase wide ascii
$tagasp_classid4 = "F935DC26-1CF0-11D0-ADB9-00C04FD58A0B" nocase wide ascii
$tagasp_classid5 = "0D43FE01-F093-11CF-8940-00A0C9054228" nocase wide ascii
$tagasp_long10 = "<%@ " wide ascii
// <% eval
$tagasp_long11 = /<% \w/ nocase wide ascii
$tagasp_long12 = "<%ex" nocase wide ascii
$tagasp_long13 = "<%ev" nocase wide ascii
// <%@ LANGUAGE = VBScript.encode%>
// <%@ Language = "JScript" %>
// <%@ WebHandler Language="C#" class="Handler" %>
// <%@ WebService Language="C#" Class="Service" %>
// <%@Page Language="Jscript"%>
// <%@ Page Language = Jscript %>
// <%@PAGE LANGUAGE=JSCRIPT%>
// <%@ Page Language="Jscript" validateRequest="false" %>
// <%@ Page Language = Jscript %>
// <%@ Page Language="C#" %>
// <%@ Page Language="VB" ContentType="text/html" validaterequest="false" AspCompat="true" Debug="true" %>
// <script runat="server" language="JScript">
// <SCRIPT RUNAT=SERVER LANGUAGE=JSCRIPT>
// <SCRIPT RUNAT=SERVER LANGUAGE=JSCRIPT>
// <msxsl:script language="JScript" ...
$tagasp_long20 = /<(%|script|msxsl:script).{0,60}language="?(vb|jscript|c#)/ nocase wide ascii
$tagasp_long32 = /<script\s{1,30}runat=/ wide ascii
$tagasp_long33 = /<SCRIPT\s{1,30}RUNAT=/ wide ascii
// avoid hitting php
$php1 = "<?php"
$php2 = "<?="
// avoid hitting jsp
$jsp1 = "=\"java." wide ascii
$jsp2 = "=\"javax." wide ascii
$jsp3 = "java.lang." wide ascii
$jsp4 = "public" fullword wide ascii
$jsp5 = "throws" fullword wide ascii
$jsp6 = "getValue" fullword wide ascii
$jsp7 = "getBytes" fullword wide ascii
$perl1 = "PerlScript" fullword
//strings from private rule capa_bin_files
$dex1 = "dex\n0"
$dex2 = "dey\n0"
$pack = { 50 41 43 4b 00 00 00 02 00 }
//strings from private rule capa_asp_input
// Request.BinaryRead
// Request.Form
$asp_input1 = "request" fullword nocase wide ascii
$asp_input2 = "Page_Load" fullword nocase wide ascii
// base64 of Request.Form(
$asp_input3 = "UmVxdWVzdC5Gb3JtK" fullword wide ascii
$asp_xml_http = "Microsoft.XMLHTTP" fullword nocase wide ascii
$asp_xml_method1 = "GET" fullword wide ascii
$asp_xml_method2 = "POST" fullword wide ascii
$asp_xml_method3 = "HEAD" fullword wide ascii
// dynamic form
$asp_form1 = "<form " wide ascii
$asp_form2 = "<Form " wide ascii
$asp_form3 = "<FORM " wide ascii
$asp_asp = "<asp:" wide ascii
$asp_text1 = ".text" wide ascii
$asp_text2 = ".Text" wide ascii
//strings from private rule capa_asp_payload
$asp_payload0 = "eval_r" fullword nocase wide ascii
$asp_payload1 = /\beval\s/ nocase wide ascii
$asp_payload2 = /\beval\(/ nocase wide ascii
$asp_payload3 = /\beval\"\"/ nocase wide ascii
// var Fla = {'E':eval}; Fla.E(code)
$asp_payload4 = /:\s{0,10}eval\b/ nocase wide ascii
$asp_payload8 = /\bexecute\s?\(/ nocase wide ascii
$asp_payload9 = /\bexecute\s[\w"]/ nocase wide ascii
$asp_payload11 = "WSCRIPT.SHELL" fullword nocase wide ascii
$asp_payload13 = "ExecuteGlobal" fullword nocase wide ascii
$asp_payload14 = "ExecuteStatement" fullword nocase wide ascii
$asp_payload15 = "ExecuteStatement" fullword nocase wide ascii
$asp_multi_payload_one1 = "CreateObject" nocase fullword wide ascii
$asp_multi_payload_one2 = "addcode" fullword wide ascii
$asp_multi_payload_one3 = /\.run\b/ wide ascii
$asp_multi_payload_two1 = "CreateInstanceFromVirtualPath" fullword wide ascii
$asp_multi_payload_two2 = "ProcessRequest" fullword wide ascii
$asp_multi_payload_two3 = "BuildManager" fullword wide ascii
$asp_multi_payload_three1 = "System.Diagnostics" wide ascii
$asp_multi_payload_three2 = "Process" fullword wide ascii
$asp_multi_payload_three3 = "Start" fullword wide ascii
// this is about "MSXML2.DOMDocument" but since that's easily obfuscated, lets not search for it
$asp_multi_payload_four1 = "CreateObject" fullword nocase wide ascii
$asp_multi_payload_four2 = "TransformNode" fullword nocase wide ascii
$asp_multi_payload_four3 = "loadxml" fullword nocase wide ascii
// execute cmd.exe /c with arguments using ProcessStartInfo
$asp_multi_payload_five1 = "ProcessStartInfo" fullword nocase wide ascii
$asp_multi_payload_five2 = ".Start" nocase wide ascii
$asp_multi_payload_five3 = ".Filename" nocase wide ascii
$asp_multi_payload_five4 = ".Arguments" nocase wide ascii
//strings from private rule capa_asp_write_file
// $asp_write1 = "ADODB.Stream" wide ascii # just a string, can be easily obfuscated
$asp_always_write1 = /\.write/ nocase wide ascii
$asp_always_write2 = /\.swrite/ nocase wide ascii
//$asp_write_way_one1 = /\.open\b/ nocase wide ascii
$asp_write_way_one2 = "SaveToFile" fullword nocase wide ascii
$asp_write_way_one3 = "CREAtEtExtFiLE" fullword nocase wide ascii
$asp_cr_write1 = "CreateObject(" nocase wide ascii
$asp_cr_write2 = "CreateObject (" nocase wide ascii
$asp_streamwriter1 = "streamwriter" fullword nocase wide ascii
$asp_streamwriter2 = "filestream" fullword nocase wide ascii
//strings from private rule capa_asp_classid
$tagasp_capa_classid1 = "72C24DD5-D70A-438B-8A42-98424B88AFB8" nocase wide ascii
$tagasp_capa_classid2 = "F935DC22-1CF0-11D0-ADB9-00C04FD58A0B" nocase wide ascii
$tagasp_capa_classid3 = "093FF999-1EA0-4079-9525-9614C3504B74" nocase wide ascii
$tagasp_capa_classid4 = "F935DC26-1CF0-11D0-ADB9-00C04FD58A0B" nocase wide ascii
$tagasp_capa_classid5 = "0D43FE01-F093-11CF-8940-00A0C9054228" nocase wide ascii
condition:
//any of them or
(
(
any of ( $tagasp_long* ) or
// TODO : yara_push_private_rules.py doesn't do private rules in private rules yet
any of ( $tagasp_classid* ) or
(
$tagasp_short1 and
$tagasp_short2 in ( filesize-100..filesize )
) or (
$tagasp_short2 and (
$tagasp_short1 in ( 0..1000 ) or
$tagasp_short1 in ( filesize-1000..filesize )
)
)
) and not (
(
any of ( $perl* ) or
$php1 at 0 or
$php2 at 0
) or (
( #jsp1 + #jsp2 + #jsp3 ) > 0 and ( #jsp4 + #jsp5 + #jsp6 + #jsp7 ) > 0
)
)
)
and not (
uint16(0) == 0x5a4d or
$dex1 at 0 or
$dex2 at 0 or
$pack at 0 or
// fp on jar with zero compression
uint16(0) == 0x4b50
)
and (
any of ( $asp_input* ) or
(
$asp_xml_http and
any of ( $asp_xml_method* )
) or
(
any of ( $asp_form* ) and
any of ( $asp_text* ) and
$asp_asp
)
)
and (
any of ( $asp_payload* ) or
all of ( $asp_multi_payload_one* ) or
all of ( $asp_multi_payload_two* ) or
all of ( $asp_multi_payload_three* ) or
all of ( $asp_multi_payload_four* ) or
all of ( $asp_multi_payload_five* )
)
and not any of ( $fp* ) and
( ( filesize < 3KB and
( 1 of ( $asp_slightly_sus* ) ) ) or
( filesize < 25KB and
( 1 of ( $asp_much_sus* ) or 1 of ( $asp_gen_sus* ) or
( #asp_gen_obf1 > 2 ) ) ) or
( filesize < 50KB and
( 1 of ( $asp_much_sus* ) or 3 of ( $asp_gen_sus* ) or
( #asp_gen_obf1 > 6 ) ) ) or
( filesize < 150KB and
( 1 of ( $asp_much_sus* ) or 4 of ( $asp_gen_sus* ) or
( #asp_gen_obf1 > 6 ) or
( (
any of ( $asp_always_write* ) and
(
any of ( $asp_write_way_one* ) and
any of ( $asp_cr_write* )
) or (
any of ( $asp_streamwriter* )
)
)
and
( 1 of ( $asp_much_sus* ) or 2 of ( $asp_gen_sus* ) or
( #asp_gen_obf1 > 3 ) ) ) ) ) or
( filesize < 100KB and (
any of ( $tagasp_capa_classid* )
)
) )
}
This YARA rule can be deployed in the following contexts:
This rule contains 145 string patterns in its detection logic.
Here are 5 specific false positive scenarios for the “Generic ASP Webshell” detection rule, including suggested filters and exclusions tailored for an enterprise environment:
Scenario: Scheduled Backup Script Execution
.asp script (e.g., BackupManager.asp) located in the C:\InetPub\wwwroot\Apps directory. This script accepts user input via a form to select backup paths and uses Server.CreateObject("Scripting.FileSystemObject") combined with Eval() to dynamically construct file paths before writing logs or backup manifests.C:\InetPub\wwwroot\Apps\BackupManager.asp) and restrict the trigger to only occur during the defined maintenance window (e.g., 02:00–04:00 UTC). Additionally, filter out events where the UserAgent matches the internal backup service account (e.g., NT SERVICE\MyBackupService).Scenario: Dynamic Report Generation via BI Tool Integration
Execute() functions to render dynamic charts and write temporary report files to a shared network drive (\\FileServer\Reports).10.20.30.5-8) accessing the specific application pool named BI_Report_Pool. Furthermore, add a filter to ignore events where the HTTP status code is 200 and the request duration exceeds