Null Byte Injection
debt(d5/e3/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list Semgrep as the tool, with automated detection possible for PHP <5.3.4 with user-controlled file paths and C extension patterns. This is not a default linter catch (d3) but requires a configured SAST tool like Semgrep. The residual risk in modern PHP (C extensions, regex, string comparisons) makes it harder than a simple pattern match, keeping it at d5.
Closest to 'simple parameterised fix' (e3). The quick_fix indicates: in modern PHP (5.3.4+) the file-path case is handled automatically; for C extension interactions, a single str_contains($input, "\0") check rejects null bytes. The common_mistakes show the fix is primarily adding a null byte stripping/rejection step to input handling — a small, localised change. Not quite a one-liner across all surfaces (multiple entry points may need patching), but no cross-cutting architectural rework required.
Closest to 'localised tax' (b3). The applies_to scope is web and cli contexts but is primarily a legacy issue (php_max 5.3.3 for the file-function variant). Modern codebases interacting with C extensions carry a persistent but narrow validation responsibility — it's a localised input-sanitisation tax on specific code paths (file uploads, path construction, C extension calls) rather than a systemic architectural burden.
Closest to 'serious trap' (t7). The misconception field explicitly states developers believe this is a legacy PHP 5 issue that no longer applies, when in fact null bytes still affect regex matching, string comparisons, and C-based library calls in modern PHP. This directly contradicts the common mental model — the 'obvious' conclusion (upgrade past 5.3.4 and you're safe) is wrong in a non-trivial set of cases, making it a serious trap that contradicts expectations set by the PHP fix itself.
Also Known As
TL;DR
Explanation
Null byte injection exploits the difference between PHP string handling and underlying C library functions. PHP strings can contain null bytes, but C functions treat null as a string terminator. Injecting %00 into a filename like shell.php%00.jpg causes PHP's extension check to see .jpg while the file system writes shell.php. This was patched in PHP 5.3.4 for filesystem functions, but can still affect custom validation logic or older systems. Always use pathinfo() for extension extraction rather than string manipulation.
How It's Exploited
# Null byte truncates the appended .txt, reads /etc/passwd on old PHP
Common Misconception
Why It Matters
Common Mistakes
- Relying solely on extension whitelisting without also stripping null bytes from filenames.
- Using old PHP (pre-5.3.4) where null bytes in filenames caused file function truncation.
- Not applying null byte stripping when constructing file paths from user input even in modern PHP where some extensions may still be vulnerable.
- Ignoring null bytes in database queries that use native C extensions.
Code Examples
// Null byte terminates filename in PHP < 5.3.4
$file = $_GET['file'] . '.txt';
readfile('/data/' . $file);
// ?file=secret.php%00 → reads 'secret.php' (ignores '.txt')
// Modern PHP raises ValueError on null bytes in filesystem calls
// Still sanitise defensively:
$file = str_replace("\0", '', $_GET['file'] ?? '');
// Best: realpath + prefix check
$base = realpath('/data');
$path = realpath($base . '/' . $file . '.txt');
if (!$path || !str_starts_with($path, $base . DIRECTORY_SEPARATOR)) abort(403);
readfile($path);