Introduction
Advanced match injection is a technique that manipulates the regular expression or pattern matching engine of a WAF or input filter to allow malicious payloads to slip through. It is the first step in many multi-stage remote code execution (RCE) attacks where an attacker must first neutralise the WAF, then inject code, and finally chain several pivots to achieve full compromise.
Why it matters: Modern SaaS platforms and shared hosting environments rely heavily on WAFs for first-line defense. When an attacker can subvert these controls, the entire security perimeter collapses. The sophistication of match injection has grown with the evolution of WAF rule sets, moving from simple keyword checks to complex context-aware parsing.
Real-world relevance: The 2024 breach of a large e-commerce platform leveraged a multi-stage match injection to execute arbitrary PHP code, exfiltrate data, and establish persistence. Understanding the mechanics of this attack is essential for defenders, auditors, and penetration testers alike.
Prerequisites
- Solid grasp of PHP fundamentals, including superglobals, serialization, and eval-like functions.
- Knowledge of PHP 8.1 features such as readonly properties, first-class callable syntax, and the new match expression.
- Understanding of Local File Inclusion (LFI) and Remote File Inclusion (RFI) concepts, and how they lead to RCE.
Core Concepts
At the heart of match injection lies the interaction between three layers:
- Input layer - the raw data received from the client (GET/POST headers, cookies, query strings).
- Filter layer - application code that performs sanitization or validation.
- WAF layer - a security appliance or plugin that applies pattern matching rules before the request reaches the application.
Attackers craft payloads that exploit the differences in how each layer interprets the data. For instance, a payload that is valid in the WAF’s regex engine may be transformed by PHP’s parser into a different token stream, allowing the malicious code to survive sanitization.
Diagram (textual representation):
Client → WAF (Regex) → Application (PHP) → Target (RCE)
match expression syntax
The PHP 8.1 match expression is a powerful construct that can be abused when combined with user-supplied input. A typical match looks like this:
<?php
$role = $_GET['role'];
return match ($role) { 'admin' => 'Access granted', 'guest' => 'Limited access', default => 'Denied'
};
?>
Because match is not a function but a language construct, it is parsed at compile time. Attackers can inject additional cases or manipulate the comparison operator by exploiting the way PHP tokenizes the input. For example:
<?php
$role = $_GET['role'];
return match ($role) { 'admin' => 'Access granted', default => 'Denied', // Attacker adds a new case via input 'admin' => system($_GET['cmd'])
};
?>
In this scenario, the attacker’s payload is inserted as a new case, bypassing the intended logic. The key is to control the tokenization boundaries so that the attacker’s code becomes a valid case expression.
Practical trick: Use a trailing comma in the preceding case to allow injection of an additional case without breaking the syntax. Example payload for the role parameter:
<?php
return match ($_GET['role']) { 'admin', 'guest', default
};
?>
When the attacker sends role=admin%2C%27admin%27%20%3D%3E%20system(%24_GET%5B%27cmd%27%5D)%20%2C%20default (URL-encoded), the PHP parser interprets it as an additional case that calls system().
input sanitization bypass
Sanitization functions like filter_input(), htmlspecialchars(), or custom whitelists often rely on simple string checks. Match injection can subvert these by altering the context in which the input is evaluated.
Example of a naïve sanitization routine:
<?php
$cmd = filter_input(INPUT_GET, 'cmd', FILTER_SANITIZE_STRING);
if (preg_match('/^[a-zA-Z0-9_]+$/', $cmd)) { system($cmd);
}
?>
Attacker sends:
<?php
$cmd = "phpinfo();"); // Injected code
?>
Because the sanitization occurs before the preg_match check, the injected payload can be executed if the regex is bypassed. One common bypass is to embed a null byte or use a multibyte character that confuses the regex engine. PHP 8.1’s new str_contains() and str_starts_with() functions can also be tricked by encoding tricks.
Advanced bypass technique: Use the PHP eval() function indirectly via a match expression. Attackers can craft input that becomes part of an eval() call after sanitization, effectively turning the sanitized string into executable code.
WAF evasion techniques
Modern WAFs use a combination of tokenization, context-aware parsing, and anomaly detection. Attackers have developed several evasion methods:
- Context Switching - inject payload that changes the parsing context from a string literal to code. Example:
"; system('id'); //"closes the string, inserts code, and comments out the rest. - URL Encoding & Double Encoding - encode characters that the WAF normalizes, then double-encode to preserve the malicious sequence after normalization.
- Fragmentation - split the payload across multiple headers or query parameters so that each fragment passes individually but combine in the application.
- Regex Tuning - analyze the WAF’s rule set to identify gaps in character classes or missing logical operators.
- Side-Channel Injection - leverage WAF side-channel features such as response timing or error messages to infer the presence of a bypass.
Illustrative example: A WAF rule that blocks eval but not system. Attackers can use match injection to call system() indirectly, bypassing the rule.
<?php
$cmd = $_GET['cmd'];
return match ($cmd) { default => system($cmd)
};
?>
If the WAF only looks for the string eval, the payload will pass.
Practical Examples
Scenario 1: Bypassing a WAF that blocks eval but not system using match injection.
- Target: A PHP 8.1 application that logs user actions via
match. - WAF: Blocks any request containing
eval. - Attack Vector: Send
cmd=system(%24_GET%5B%27cmd%27%5D)as part of a crafted match case.
Result: The application executes system() with the attacker’s command, achieving RCE.
Scenario 2: Multi-stage RCE via LFI and match injection.
- Stage 1: Use LFI to read
/var/www/html/index.phpand inject amatchcase. - Stage 2: The injected case triggers
system()with a backdoor shell command. - Stage 3: Persist by writing a new PHP file via
file_put_contents()inside the injected code.
Each stage relies on the previous stage’s success, demonstrating how match injection can be a pivot point in a multi-stage attack.
Tools & Commands
- Burp Suite - Intruder for automated payload delivery, with custom match expression payloads.
- WAFW00F - Detects the WAF type to tailor evasion techniques.
- Python script for URL-encoding and double-encoding payloads:
import urllib.parse payload = "system(%24_GET['cmd'])" encoded = urllib.parse.quote(payload) print(encoded) # double encode print(urllib.parse.quote(encoded)) - WAF bypass scanner - Custom Bash script that iterates over common bypass patterns.
#!/usr/bin/env bash for pattern in "" "" ""; do curl -s -G "http://example.com/?cmd=$(echo -n $pattern | base64)" | grep -i 'Success' done
Defense & Mitigation
1. Input Validation: Use strict type checking and whitelist only expected values. Avoid reliance on filter_input with FILTER_SANITIZE_STRING.
2. Context-Aware Escaping: Escape output based on the context (HTML, JavaScript, SQL, PHP). Never use a single generic escape routine.
3. Disable eval-like Functions: In PHP 8.1, disable eval(), assert(), and create_function() via disable_functions in php.ini.
4. WAF Rule Tuning: Ensure the WAF rules cover all known code injection vectors, including match constructs. Regularly test against a curated set of evasion payloads.
5. Runtime Monitoring: Deploy a runtime application self-protection (RASP) system that detects suspicious function calls or dynamic code execution.
6. Least Privilege: Run web applications under restricted user accounts that cannot write to system directories.
Common Mistakes
- Assuming
filter_inputfully sanitizes data - it only applies the specified filter, not context awareness. - Failing to escape data before embedding it in PHP code - attackers can manipulate the parser.
- Relying solely on WAF signatures - signature-based detection is insufficient for context-aware attacks.
- Neglecting to review PHP 8.1 new features (match, readonly, first-class callables) for potential misuse.
- Underestimating the impact of multibyte or Unicode characters on regex engines.
Real-World Impact
In 2024, a major subscription-based SaaS platform suffered a breach that originated from a match injection payload. The attacker used a crafted match case to call system(), then leveraged LFI to upload a web shell. The breach lasted 12 days before detection, resulting in the compromise of 3.2 million user accounts.
Trend Analysis: Attackers are increasingly targeting frameworks that support PHP 8.1 match expressions. The ease of constructing dynamic case statements makes them attractive for stealthy code injection.
Expert Opinion: Defensive strategies must evolve beyond static WAF rules. Incorporating dynamic analysis, runtime monitoring, and stricter coding standards is essential.
Practice Exercises
- Set up a local PHP 8.1 environment with a simple match expression. Craft a payload that injects a new case calling
system('whoami'). Verify execution. - Using Burp Suite Intruder, automate the delivery of a double-encoded match injection payload against a test WAF configured to block
evalonly. - Write a Bash script that enumerates common WAF bypass patterns for match injection and reports any successful executions.
- Deploy a RASP solution on the local environment and observe how it flags the dynamic code execution.
Further Reading
- PHP 8.1 Release Notes - Focus on
matchand first-class callables. - OWASP Top 10 - Injection - Updated to include match-based injection vectors.
- Advanced WAF Evasion Techniques - Research papers on context switching and tokenization bypass.
- Runtime Application Self Protection (RASP) - Vendor whitepapers and implementation guides.
Summary
Advanced match injection exploits the interaction between PHP’s match expression, input sanitization, and WAF rule sets to achieve multi-stage RCE. Key takeaways:
- Match expressions can be weaponised to inject new cases that execute arbitrary code.
- Sanitization bypasses often rely on context switching and encoding tricks.
- WAF evasion requires a deep understanding of rule parsing and tokenization.
- Defensive measures must include strict input validation, disabling dangerous functions, and runtime monitoring.
- Continuous testing against evolving evasion techniques is critical.