Improper Neutralization of Null Byte or NUL Character
This weakness occurs when software fails to remove or properly handle null bytes \0 in user input. Because many C-based file and string functions treat the…
This weakness occurs when software fails to remove or properly handle null bytes (\0) in user input. Because many C-based file and string functions treat the null byte as a string terminator, an attacker can use it to truncate filenames or bypass security checks — for example, uploading a file named shell.php\0.jpg that gets saved as shell.php. This is particularly dangerous in web applications that validate file extensions or paths.
02How It Happens
Most programming languages (especially those with C heritage) use null bytes to mark the end of strings in memory. When user-supplied input containing a null byte is passed to file operations, path validation, or extension checks, the null byte causes the string to be truncated at that point. A developer might validate that a filename ends with .jpg, but if the input is malicious.php\0.jpg, the null byte causes the filename to be treated as malicious.php by the underlying file system or C library, bypassing the extension whitelist. This is especially common in older code, legacy systems, and applications that mix high-level languages with C-based libraries.
03Real-World Impact
An attacker can upload executable files (PHP, ASP, JSP) disguised with safe extensions, execute arbitrary code on the server, and gain full system compromise. Path traversal attacks can be enhanced by null-byte injection to bypass directory restrictions. Authentication and authorization checks that rely on filename or path validation can be circumvented. The impact ranges from remote code execution to unauthorized file access and data exfiltration.
04Vulnerable & Fixed Patterns
Vulnerable pattern
import os
def save_uploaded_file(filename, content):
# Validate extension
if not filename.endswith('.jpg'):
raise ValueError("Only .jpg files allowed")
# Save to disk — null byte in filename bypasses check
filepath = os.path.join('/uploads', filename)
with open(filepath, 'wb') as f:
f.write(content)
Why it's vulnerable: The extension check happens on the full filename string, but if filename contains a null byte (e.g., shell.php\0.jpg), the underlying C file API truncates it at the null byte, saving the file as shell.php instead of shell.php\0.jpg.
Fixed pattern
import os
def save_uploaded_file(filename, content):
# Remove null bytes and other dangerous characters
filename = filename.replace('\0', '')
# Validate extension on cleaned filename
if not filename.endswith('.jpg'):
raise ValueError("Only .jpg files allowed")
# Use a safe, predictable filename instead of user input
import uuid
safe_filename = f"{uuid.uuid4()}.jpg"
filepath = os.path.join('/uploads', safe_filename)
with open(filepath, 'wb') as f:
f.write(content)
Why it's vulnerable: PHP's file_put_contents() passes the filename to C-level file APIs, which interpret the null byte as a string terminator. A filename like shell.php\0.jpg is truncated to shell.php at the file system level.
Fixed pattern
<?php
function save_uploaded_file($filename, $content) {
// Remove null bytes and control characters
$filename = str_replace("\0", "", $filename);
$filename = preg_replace('/[^\w\-\.]/', '', $filename);
// Validate extension on cleaned filename
if (substr($filename, -4) !== '.jpg') {
die("Only .jpg files allowed");
}
// Better: use a generated filename instead of user input
$safe_filename = bin2hex(random_bytes(16)) . '.jpg';
$filepath = '/uploads/' . $safe_filename;
file_put_contents($filepath, $content);
}
?>
05Prevention Checklist
Strip null bytes from all user input before passing it to file operations, path functions, or any C-based library. Use str.replace('\0', '') in Python or str_replace("\0", "", ...) in PHP.
Regenerate filenames server-side instead of trusting user-supplied names. Use UUIDs, hashes, or sequential IDs; store the original filename separately in a database if needed.
Validate file extensions on the cleaned filename , not the raw input. Perform validation *after* null-byte removal.
Use allowlists for file extensions , not blocklists. Only permit .jpg, .png, etc.; reject everything else.
Avoid passing user input directly to C-level APIs . Use language-native file operations where possible, and keep user input in the high-level language as long as possible.
Test with null-byte payloads in your security testing: filename.php\0.jpg, ../../../etc/passwd\0.txt, etc.
06Signs You May Already Be Affected
Check your upload directories for executable files with unexpected extensions (e.g., .php files in a directory that should only contain images). Review file upload logs for filenames containing unusual characters or patterns. If your application uses older PHP versions (pre-5.3.4) or legacy C-based file handling, audit all user-supplied filenames for null-byte filtering.