Nouvelle réglementation de cybersécurité 2026 en vigueur au Maroc. Obtenir un audit de conformité gratuit →
← Retour au blog
OWASP 2026-02-05 ⏱️ 12 min

PHP Filter Chains: Turning Local File Inclusion into Remote Code Execution

PHP Filter Chains: Turning Local File Inclusion into Remote Code Execution

Local file inclusion (LFI) is often treated as a read-only bug. An attacker can read /etc/passwd, maybe source code, and that is the end of the story. PHP filter chains changed that calculus. By abusing php://filter with a carefully constructed sequence of convert.iconv transformations, an attacker can synthesize arbitrary bytes inside a stream — including a valid PHP payload — without uploading a file or writing to disk. Combined with a file-inclusion sink, the result is remote code execution.


1. The LFI Sink

A typical vulnerable pattern passes a request parameter directly to include or require:

<?php
// vulnerable.php
$page = $_GET['page'] ?? 'home';
include(__DIR__ . '/pages/' . $page . '.php');

An attacker who controls page can traverse directories (../../etc/passwd) or, if URL wrappers are enabled, supply a wrapper such as php://filter. The suffix .php is appended, but wrappers and path normalization can still produce useful behavior depending on the code. The cleanest demonstration uses an include without a suffix:

<?php
include($_GET['file']);

2. What php://filter Does

php://filter applies stream filters to a resource. A simple example reads a file and base64-decodes it:

php://filter/convert.base64-decode/resource=/var/www/html/config.php

Filters can be chained. Each filter transforms the stream before the next one receives it. The convert.iconv.* filters convert between character encodings, and some encodings have stateful escape sequences that produce specific bytes. By chaining them, researchers found they could prepend controlled data to a stream.


3. The Synacktiv Technique

Synacktiv published a generator that builds a filter chain producing an arbitrary prefix. The chain starts with a base64-encoded payload and uses a sequence of iconv conversions to emit the desired bytes at the beginning of the stream. The generated URL looks like this (truncated):

php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|...|convert.base64-decode/resource=php://temp

The generator automates the search for a chain that yields the exact payload. In a lab, the workflow is:

# Install the generator in an isolated lab
git clone https://github.com/synacktiv/php_filter_chain_generator
python3 php_filter_chain_generator/php_filter_chain_generator.py \
  --chain '<?php system($_GET["c"]); ?>'

The tool prints a php://filter/... URL. Passing that URL to the vulnerable include causes PHP to compile and execute the synthesized payload. The payload never touches disk, which defeats file-upload restrictions, extension checks, and many integrity monitors.

Use this technique only in authorized assessments and isolated labs. The generator and the underlying bug are powerful; running them against systems you do not own is illegal.

4. CVE-2024-2961 and the glibc Connection

In 2024, Ambionics published research on a glibc iconv vulnerability tracked as CVE-2024-2961. The flaw is an out-of-bounds write in the handling of the ISO-2022-CN-EXT character set, affecting glibc versions before 2.40. It can be triggered through PHP's iconv filters and, in the right conditions, used to corrupt memory and achieve code execution.

The significance for LFI is that the filter-chain technique does not always need a clean “synthesize a PHP tag” primitive. A memory-corruption bug in the conversion library provides another path from a file-read primitive to code execution. The exact exploit depends on the PHP version, the glibc build, and the allocator state, which is why the research was notable: it turned a class of “read-only” bugs into a reliable RCE chain on many distributions.

PHP 8.5 was released on 20 November 2025. Organizations should verify their PHP and glibc versions together; upgrading one without the other may leave the vulnerable conversion path reachable. The durable fix is to remove the include sink, not merely to hope the filter chain fails.


5. Why This Bypasses Common Defenses

  • No upload required. The payload is generated in memory, so upload directories, MIME checks, and antivirus scans never see it.
  • No writable directory required. The attacker does not need to drop a webshell.
  • Signature-based WAF rules struggle. The URL is a long chain of legitimate filter names; blocking every variation is difficult without blocking legitimate use.
  • The payload can be small. A short PHP tag is enough to call system, assert, or a custom function.

6. Detection and Hardening

6.1 Remove dynamic includes

Never pass user input directly to include, require, include_once, or require_once. Use an allowlist map:

<?php
$pages = [
    'home' => __DIR__ . '/pages/home.php',
    'about' => __DIR__ . '/pages/about.php',
    'contact' => __DIR__ . '/pages/contact.php',
];

$key = $_GET['page'] ?? 'home';
if (!array_key_exists($key, $pages)) {
    http_response_code(404);
    exit;
}
include $pages[$key];

This eliminates the sink entirely. No filter chain, encoding trick, or traversal sequence can select a file outside the map.

6.2 Harden the PHP configuration

  • Set allow_url_include = Off and allow_url_fopen = Off unless explicitly required.
  • Use open_basedir to restrict filesystem access to the application directory.
  • Disable unused stream wrappers where the runtime permits it.
  • Run PHP as a low-privilege user with no shell access and no write permission outside the upload directory.

6.3 Patch the conversion stack

Keep glibc at 2.40 or later on affected distributions, and keep PHP current. Track CVE-2024-2961 in your vulnerability management system even if the application does not use iconv directly; PHP's filter system can reach it.

6.4 Detect the technique

Log request parameters containing php://filter, repeated convert.iconv tokens, or unusually long query strings. Alert on include errors that reveal wrapper usage. A WAF rule can block obvious chains, but it is a mitigation, not a fix.

# Example detection grep for web server logs (review before deploying)
grep -E 'php://filter|convert\.iconv' /var/log/nginx/access.log | head

7. Conclusion

PHP filter chains collapse the distinction between “file read” and “code execution.” The include sink is the vulnerability; the filter chain is the delivery mechanism. CVE-2024-2961 showed that even the conversion library can become an exploitation primitive. The correct response is not to blacklist a particular payload string but to eliminate dynamic includes, constrain the filesystem, patch the runtime, and monitor for wrapper abuse. If an application can be made to include a path chosen by the user, assume that an attacker can eventually turn that path into code.

Besoin d'un audit de sécurité ?

Contactez Cayvora pour une consultation gratuite et protégez votre entreprise contre les cybermenaces.

✉️ Contact via Email

Articles connexes