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%00causes the.phpto be dropped in the C-level string, so/etc/passwdis included directly. - Before a fake file extension — to make file-type checks pass while still reaching a different file.
evil.php%00.jpgpasses a naive “ends with .jpg” check but resolves toevil.phpon 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
- OWASP WSTG — Testing for File Inclusion
- Common PHP attacks: Poison Null Byte
- PHP 5.3.4 Release Announcement
Related: local-file-inclusion-attacks, php, directory-traversal