Poison Null Byte Attack

Some languages use null bytes (0x00) to know when a string terminates, rather than tracking the actual string length alongside the buffer. When application code is written in such a language — or calls down into a C library that is — a null byte embedded in user input causes everything after it to be silently dropped by the underlying string operation, even though the higher-level language (PHP, Java, etc.) tracks the “real” length separately.

On the web, a null byte is typically encoded as %00 in a URL. Because the % character itself is special in URL encoding, a null byte sometimes needs to be double-encoded as %2500 to survive one round of decoding by an intermediate layer (reverse proxy, framework, or WAF) before reaching the vulnerable code.

Typical uses

A null byte is usually injected in one of two positions:

  • At the end of a string — to prevent a suffix from being appended. Classic example: an application does include($page . ".php") to force an extension; requesting ?page=/etc/passwd%00 causes the .php to be dropped in the C-level string, so /etc/passwd is included directly.
  • Before a fake file extension — to make file-type checks pass while still reaching a different file. evil.php%00.jpg passes a naive “ends with .jpg” check but resolves to evil.php on the filesystem side.

PHP specifics

String parsing in PHP versions < 5.3.4 is susceptible to the poison null byte in many filesystem functions, because PHP passed user-controlled strings directly to C library calls that treat \0 as a terminator. PHP 5.3.4 and later sanitize null bytes in filesystem paths and emit a warning instead, so the attack is largely of historical interest — but it still matters on legacy targets and in the same pattern re-implemented in other languages or in PHP extensions that bypass the core fix.

This was a standard companion to LFI on older PHP, used to strip a forced suffix or bypass a simple path filter.

Defenses

The robust fix is to sanitize strings by explicitly removing any null bytes they contain before use:

$sanitized_string = str_replace(chr(0), '', $original_string);

More generally:

  • Reject (don’t just strip) input containing %00 / \0 — legitimate input almost never contains a null byte.
  • Canonicalize paths and enforce an allowlist of permitted files or directories, rather than relying on string manipulation.
  • Keep runtimes patched: the language-level fixes (PHP ≥ 5.3.4, modern JVMs, etc.) close the common cases, but custom native code and extensions can reintroduce the flaw.

Sources

Related: local-file-inclusion-attacks, php, directory-traversal