Shieldlist

php-injection Free

PHP code injected in the query string — a web-shell drop or a remote-code-execution probe; one and out

Category
web
Threshold
1/10m
Ban policy
severe
Report
php-injection
Parsers
inline inline inline

Why it is written this way

PHP code injected in the query string: a webshell drop or an RCE
probe, whatever the CMS behind it (seen against SPIP's ?page=recherche,
ThinkPHP's ?s=, pearcmd) and whatever the server answered — the worst
case is the request that SUCCEEDED, so no status is required. One
request is the verdict: nothing legitimate carries a PHP open tag or
a file_put_contents() call in a URL.

Three shapes, each measured against 19.45M real shared-hosting
requests (2026-08-24) — every match was an attack, none was a visitor:
1. the open tag itself — raw, %-encoded or double-encoded — followed
by what PHP would execute (php, =, whitespace, echo/print: the
short-tag forms). The suffix is what keeps ?xml=<?xml … out of
the rule without a lookahead.
2. a sensitive PHP function called in a parameter — the measured
list only; system( and exec( are left out on purpose ("operating
system(s)" in a search box is a real query).
3. the campaign's declared scanner identity as its user agent.

The rule file View raw

# PHP code injected in the query string: a webshell drop or an RCE
# probe, whatever the CMS behind it (seen against SPIP's ?page=recherche,
# ThinkPHP's ?s=, pearcmd) and whatever the server answered — the worst
# case is the request that SUCCEEDED, so no status is required. One
# request is the verdict: nothing legitimate carries a PHP open tag or
# a file_put_contents() call in a URL.
#
# Three shapes, each measured against 19.45M real shared-hosting
# requests (2026-08-24) — every match was an attack, none was a visitor:
#   1. the open tag itself — raw, %-encoded or double-encoded — followed
#      by what PHP would execute (php, =, whitespace, echo/print: the
#      short-tag forms). The suffix is what keeps ?xml=<?xml … out of
#      the rule without a lookahead.
#   2. a sensitive PHP function called in a parameter — the measured
#      list only; system( and exec( are left out on purpose ("operating
#      system(s)" in a search box is a real query).
#   3. the campaign's declared scanner identity as its user agent.
rule: php-injection
description: "PHP code injected in the query string — a web-shell drop or a remote-code-execution probe; one and out"
about:
  purpose: >-
    Ban an attempt to run PHP through the query string — a web shell being dropped, or a remote execution being tried.
  blocks: >-
    ONE request whose query carries PHP code.
  applies_to: >-
    Any web server, whether or not it runs PHP: the attempt is the offence.
  requires:
    - >-
      The site's access log in combined format, declared as `log: http`.
  beware:
    - >-
      Nothing measured. A query string containing PHP has no innocent reading.
category: web
log: http
time_field: time
time_format: nginx
parsers:
  - regex: '^(?P<ip>\S+) \S+ \S+ \[(?P<time>[^\]]+)\] "(?P<method>[A-Z]+) (?P<path>[^ "?]*)\?(?P<query>[^ "]*(?i:<|%3C|%253C)(?i:\?|%3F|%253F)(?i:php|=|%3D|\+|%20|echo|print)[^ "]*) [^"]*" \d{3} '
    prefilter_any: ["%3C%3F", "%3c%3f", "%253C%253F", "%253c%253f", "<?"]
  - regex: '^(?P<ip>\S+) \S+ \S+ \[(?P<time>[^\]]+)\] "(?P<method>[A-Z]+) (?P<path>[^ "?]*)\?(?P<query>[^ "]*\b(?i:file_put_contents|base64_decode|shell_exec|passthru|proc_open|gethostbyname|eval)(?:\(|%28|%2528)[^ "]*) [^"]*" \d{3} '
    prefilter_any: [file_put_contents, base64_decode, shell_exec, passthru, proc_open, gethostbyname, eval]
  - regex: '^(?P<ip>\S+) \S+ \S+ \[(?P<time>[^\]]+)\] "(?P<method>[A-Z]+) (?P<path>[^ "?]*)(?:\?(?P<query>[^ "]*))? [^"]*" \d{3} \d+ "[^"]*" "(?P<ua>Mozilla/5\.0 \(security-research\))"$'
    prefilter: security-research
threshold: 1/10m
ban: severe
report: php-injection