This detection identifies adversaries who establish a persistent foothold by uploading and writing malicious ASP webshells to the disk without executing them immediately, often as part of a multi-stage attack chain. Proactive hunting for this behavior in Azure Sentinel is critical because silent file creation can evade execution-based alerts, allowing attackers to maintain stealth while preparing for subsequent command-and-control or lateral movement activities.
rule WEBSHELL_ASP_Writer
{
meta:
description = "ASP webshell which only writes an uploaded file to disk"
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 = "2023-07-05"
score = 60
hash = "df6eaba8d643c49c6f38016531c88332e80af33c"
hash = "83642a926291a499916e8c915dacadd0d5a8b91f"
hash = "5417fad68a6f7320d227f558bf64657fe3aa9153"
hash = "97d9f6c411f54b56056a145654cd00abca2ff871"
hash = "fc44fd7475ee6c0758ace2b17dd41ed7ea75cc73"
id = "a1310e22-f485-5f06-8f1a-4cf9ae8413a1"
strings:
$sus1 = "password" fullword wide ascii
$sus2 = "pwd" fullword wide ascii
$sus3 = "<asp:TextBox" fullword nocase wide ascii
$sus4 = "\"upload\"" wide ascii
$sus5 = "\"Upload\"" wide ascii
$sus6 = "gif89" wide ascii
$sus7 = "\"&\"" wide ascii
$sus8 = "authkey" fullword wide ascii
$sus9 = "AUTHKEY" fullword wide ascii
$sus10= "test.asp" fullword wide ascii
$sus11= "cmd.asp" fullword wide ascii
$sus12= ".Write(Request." wide ascii
$sus13= "<textarea " wide ascii
$sus14= "\"unsafe" fullword wide ascii
$sus15= "'unsafe" fullword wide ascii
$sus16= "Army" fullword wide ascii
//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_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_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
condition:
(
(
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 (
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_always_write* ) and
(
any of ( $asp_write_way_one* ) and
any of ( $asp_cr_write* )
) or (
any of ( $asp_streamwriter* )
)
)
and
( filesize < 400 or
( filesize < 6000 and 1 of ( $sus* ) ) )
}
This YARA rule can be deployed in the following contexts:
This rule contains 63 string patterns in its detection logic.
Here are 5 specific false positive scenarios for the “ASP webshell which only writes an uploaded file to disk” detection rule, including suggested filters and exclusions:
Scenario: Automated Backup Script Execution
SYSTEM or a dedicated service account (svc_backup) uploads configuration snapshots or log archives to an IIS application directory. The process writes these files directly to disk without triggering a web request that mimics a malicious upload flow.DOMAIN\svc_backup) AND the destination path matches the backup repository pattern (e.g., C:\ProgramData\BackupRepos\*).Scenario: Content Management System (CMS) Media Uploads
App_Data or Images folder immediately upon submission, which matches the “write-only” logic of the rule but represents normal business activity.SharePoint, Sitecore) AND the file extension is a standard media type (*.jpg, *.png, *.pdf, *.docx).Scenario: Development Deployment via CI/CD Pipeline