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
-
inlineinlineinline
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