Skip to content

YARA and Sigma rules for web shell detection

Write, test and tune YARA rules for suspicious server-side scripts and Sigma rules for web shell behaviour in logs and process telemetry — with annotated examples and a false-positive strategy.

Published on 3 min read

Detection rules turn the heuristics in the rest of this site into repeatable, shareable logic. YARA matches patterns in file content — ideal for sweeping web roots, backups and disk images. Sigma describes behaviour in log events — ideal for catching a shell in use. This guide shows how to write both for web shells, and, just as importantly, how to keep them from burying you in false positives.

The rules below are defensive detections built from generic language primitives. They do not contain, and are not derived from, working web shell code.

Part 1 — YARA for web shells at rest

Design principles

  1. Combine weak signals. eval alone is everywhere; eval fed directly from request input, inside a small file, is not.
  2. Anchor on file type. Match PHP only in files that open like PHP, ASPX only where page directives exist.
  3. Use file size. Loader-style shells are tiny; full-featured panels are large but carry many capability strings.
  4. Prefer structure over names. Family names and author banners are trivially changed.
  5. Scope the scan. Scan web-served paths, not the whole disk, to reduce noise.

Example: PHP request-to-eval pattern

rule WS_PHP_Request_To_Dynamic_Eval
{
    meta:
        description = "PHP file where request superglobals flow into dynamic code evaluation (heuristic)"
        author      = "WebShells"
        date        = "2026-09-25"
        reference   = "https://attack.mitre.org/techniques/T1505/003/"
        confidence  = "medium"

    strings:
        $php     = "<?php" nocase
        $in1     = "$_POST" nocase
        $in2     = "$_REQUEST" nocase
        $in3     = "$_COOKIE" nocase
        $in4     = "php://input" nocase
        $in5     = "$_SERVER['HTTP_" nocase
        $ev1     = /\beval\s*\(/ nocase
        $ev2     = /\bassert\s*\(/ nocase
        $ev3     = /\bcreate_function\s*\(/ nocase
        $dec1    = "base64_decode" nocase
        $dec2    = "gzinflate" nocase
        $dec3    = "str_rot13" nocase

    condition:
        $php in (0..1024)
        and filesize < 200KB
        and any of ($in*)
        and any of ($ev*)
        and ( filesize < 4KB or any of ($dec*) )
}

Why this shape: it requires a PHP opener, request input and an evaluation primitive, and then either a very small file (loader style) or a decoding function (obfuscation). Each condition alone is common in legitimate code; together they are rare.

Example: ASP.NET page that executes processes

rule WS_ASPX_Page_With_Process_Execution
{
    meta:
        description = "ASP.NET page or handler that reads request data and starts OS processes (heuristic)"
        author      = "WebShells"
        date        = "2026-09-25"
        confidence  = "medium"

    strings:
        $dir1  = "<%@ Page" nocase
        $dir2  = "<%@ WebHandler" nocase
        $dir3  = "<script runat=\"server\"" nocase
        $req1  = "Request.Form" nocase
        $req2  = "Request.QueryString" nocase
        $req3  = "Request.Headers" nocase
        $req4  = "Request.Cookies" nocase
        $req5  = "Request.InputStream" nocase
        $exec1 = "System.Diagnostics.Process" nocase
        $exec2 = "ProcessStartInfo" nocase
        $exec3 = "cmd.exe" nocase
        $exec4 = "powershell" nocase
        $refl  = "Assembly.Load" nocase

    condition:
        filesize < 500KB
        and any of ($dir*)
        and any of ($req*)
        and ( (any of ($exec1, $exec2) and any of ($exec3, $exec4)) or $refl )
}

Assembly.Load in a page that also reads request data is a strong indicator of client-driven .NET shells that receive compiled payloads in each request.

Example: JSP with runtime execution

rule WS_JSP_Request_To_Runtime_Exec
{
    meta:
        description = "JSP reading request parameters and invoking runtime process execution (heuristic)"
        author      = "WebShells"
        date        = "2026-09-25"
        confidence  = "medium"

    strings:
        $jsp1 = "<%@ page" nocase
        $jsp2 = "<jsp:" nocase
        $req  = "request.getParameter" nocase
        $ex1  = "Runtime.getRuntime" nocase
        $ex2  = "ProcessBuilder" nocase
        $cl1  = "defineClass" nocase
        $cl2  = "ClassLoader" nocase

    condition:
        filesize < 300KB
        and any of ($jsp*)
        and $req
        and ( any of ($ex*) or all of ($cl*) )
}

Example: script content hiding in image files

rule WS_Script_In_Image_Extension
{
    meta:
        description = "Server-side script markers inside a file that begins with an image header"
        author      = "WebShells"
        date        = "2026-09-25"

    strings:
        $php  = "<?php" nocase
        $asp  = "<%@" nocase
        $jsp  = "<%=" nocase

    condition:
        ( uint32be(0) == 0x89504E47            // PNG
          or uint16be(0) == 0xFFD8             // JPEG
          or uint32be(0) == 0x47494638 )       // GIF8
        and any of them
}

A match here does not mean the file executes — that depends on server configuration — but it does indicate someone crafted a polyglot, which is worth investigating.

Running and tuning

# Scan a web root recursively, print matching strings, skip files over 5 MB
yara -r -s -z 5242880 webshell_rules.yar /var/www/

Tuning workflow:

  1. Run against clean corpora first — current releases of the CMSs and frameworks you host, your own repositories, vendor product directories.
  2. Exclude by hash, not by path, where possible. Path exclusions are exactly where an attacker would hide.
  3. Record every exclusion with a reason and review date.
  4. Version rules in Git and run them in CI against the clean corpus to catch regressions.

Mature public rule sets — such as the web shell rules in Florian Roth's signature-base and the YARA-Forge packaged collections — are an excellent starting point; review their licences and tune them to your estate.

Part 2 — Sigma for web shells in use

Sigma rules describe log events generically and are converted to SIEM queries with sigma-cli (pySigma backends for Splunk, Microsoft Sentinel/Defender, Elastic, and others).

Process creation

The highest-value behavioural rule — web server workers spawning shells and recon tools — is shown in full in process-tree detection.

Web server logs: suspicious parameters on script files

title: Command-Like Parameters in Requests to Server-Side Scripts
id: 0d6f4c3e-8a57-4b0d-9f3a-2c1e7b5a9d10
status: experimental
description: Detects requests to server-side script files whose query string contains parameter names or values typical of web shell command execution. Only effective for shells using GET parameters.
author: WebShells
date: 2026-09-25
tags:
  - attack.persistence
  - attack.t1505.003
logsource:
  category: webserver
detection:
  script:
    cs-uri-stem|endswith:
      - '.php'
      - '.aspx'
      - '.ashx'
      - '.jsp'
  params:
    cs-uri-query|contains:
      - 'cmd='
      - 'exec='
      - 'command='
      - 'whoami'
      - 'ipconfig'
      - 'cat%20/etc/passwd'
  condition: script and params
falsepositives:
  - Legitimate applications using generic parameter names such as "cmd" or "command"
level: medium

File creation: scripts written by the web server

title: Web Server Process Writes Server-Side Script File
id: 9a2e7d14-5c3b-4f8e-a1d6-3b7c0e2f5a88
status: experimental
description: Detects IIS or other web server worker processes creating ASP/ASPX/PHP/JSP files, typical of web shell upload or exploitation.
author: WebShells
date: 2026-09-25
tags:
  - attack.persistence
  - attack.t1505.003
logsource:
  category: file_event
  product: windows
detection:
  selection:
    Image|endswith:
      - '\w3wp.exe'
      - '\httpd.exe'
      - '\php-cgi.exe'
      - '\java.exe'
    TargetFilename|endswith:
      - '.aspx'
      - '.ashx'
      - '.asmx'
      - '.asp'
      - '.php'
      - '.jsp'
  condition: selection
falsepositives:
  - Applications that generate or compile pages at runtime (review ASP.NET temporary files separately)
level: high

Converting

pip install sigma-cli
sigma plugin install splunk
sigma convert -t splunk -p sysmon rules/web_server_writes_script.yml

Layering the rules

LayerRule typeCatchesMisses
File at restYARADormant shells, backups, disk imagesHeavily obfuscated or in-memory shells
File creationSigma (file_event)Moment of plantingShells created by deployment identity
Web requestSigma (webserver)GET-parameter shells, probingPOST-body commands
ProcessSigma (process_creation)Any shell that runs OS commandsShells used only for file or DB access

Deploy all four. Each covers another's blind spot, and correlation across layers turns medium-confidence alerts into near-certain findings.

Related guides