ADVANCED TECHNIQUE BRIEF

PHP filter chains as byte transducers

Reason about composable encoding transforms and prove suffix preservation before assembling a chain.

Also known as: PHP filter chains · iconv LFI to RCE

phpstream-filtersiconvlfi

A PHP stream filter transforms bytes before a consumer sees them. Prefix-generating encodings and permissive normalization can therefore compose into byte generation, but only when each step preserves the suffix already assembled.

Research anchor and scope

Rémi Matasse’s filter-chain research explains prefix introduction, base64 cleanup, padding differences, and unstable conversions. Its separate Laravel inclusion gadget uses v9.34.0. The byte-transform mechanism needs PHP wrappers and iconv; framework presence alone does not expose unsafe deserialization or inclusion.

A checked harmless byte transformation

This workspace’s PHP 8.5.10 has an installed iconv extension that is disabled by default; it was enabled only for the individual verification process. Converting the ASCII string QkM from UTF-8 to CSISO2022KR produced:

input:         51 6b 4d                   QkM
encoded:       1b 24 29 43 51 6b 4d       ESC $ ) C Q k M
base64 decode: 09 09 0c
base64 encode: 43 51 6b 4d                CQkM

The ISO-2022-KR designation introduces a fixed four-byte prefix. The first three prefix bytes are outside the base64 alphabet and the stream decoder ignores them; C is inside that alphabet and survives. Re-encoding the decoded bytes gives C || QkM, preserving the original suffix. This example generates harmless data, not executable source, and is not a complete generated chain.

Choosing a three-symbol suffix makes the normalized result a complete four-symbol quantum. Arbitrary suffix lengths can leave incomplete quanta, so the example does not prove a universal prepend gadget. Count alphabet symbols and verify end-of-stream output for every chain stage.

Preserve an invariant, not just the first byte

Model a candidate transform as T and the normalization as clean. The desired property is clean(T(s)) = c || s over the permitted suffix alphabet. A conversion that produces c but alters any earlier symbol breaks subsequent composition. Encoding names alone do not imply preservation: codecs may reinterpret the suffix rather than merely add a designation or byte-order mark.

Use a small alphabet-covering suffix corpus, including mixed case, digits, +, and /. Compare exact bytes and lengths after every stage. A successful one-character probe does not establish behavior over arbitrary base64 strings or across stream-buffer boundaries. Construct longer results only from individually verified transformations.

Padding and runtime constraints

The stream convert.base64-decode filter and the language function base64_decode() differ on malformed = placement. Do not substitute the function for the actual stream consumer during verification. UTF-8 to UTF-7 converted a literal = to +AD0- in the local test. This removes the literal padding character but introduces new alphabet symbols; it does not simply delete padding. Any cleanup strategy must account for those symbols and preserve the intended suffix through another normalization round.

Record PHP version, iconv implementation, available conversion aliases, warning handling, input-length limits, and empty-stream behavior. The illustrative designation was verified locally; another platform can have different codec availability. Base64 decode/encode acts on bytes and alphabet quanta, so floating assumptions about textual equivalence are unsafe.

Connect the primitive to its consumer

A read sink returns transformed data; an include or require sink may interpret it as source; a hash or image parser consumes it differently. Wrapper-string control and a reachable sink are separate prerequisites. Report byte generation separately from inclusion, object gadgets, and execution. No full chain or historical framework gadget should be treated as universal solely because this bounded prefix example works.

Sources & further reading

  1. PHP filters chain: What is it and how to use it ↗Rémi Matasse · synacktiv

Content license: CC BY 4.0. Source-derived sections retain their cited licenses and redistribution notices. How this reference is compiled →