Null Byte Interaction Error (Poison Null Byte)
Description
Null Byte Interaction Error occurs when applications process user input containing null bytes (\x00) that cause string handling discrepancies between different components. Some languages (C, PHP) treat null bytes as string terminators while others (Java, Python) include them as regular characters. This mismatch allows attackers to bypass security filters and access unauthorized resources.
Risk
Attackers can bypass file extension validation to upload malicious files. Path traversal attacks can evade security filters. Access control mechanisms based on string matching can be circumvented. Log injection and poisoning becomes possible. SQL injection and XSS filters may be bypassed. The vulnerability is especially dangerous in web applications interfacing with C-based libraries.
Solution
Strip or reject null bytes from all user input. Use functions that handle null bytes correctly. Validate input before and after null byte processing. Use language-appropriate string handling. Implement defense in depth with multiple validation layers. Consider the entire data flow and all components involved.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Bypass Security filters fail to detect malicious content. |
| Integrity | Scope: File System Unauthorized file access or uploads. |
| Confidentiality | Scope: Information Disclosure Reading files outside intended scope. |
Example Code + Solution Code
Vulnerable Code
<?php
// VULNERABLE: Null byte injection in file inclusion
function includePageVulnerable($page) {
// Only allow .php files
if (substr($page, -4) !== '.php') {
$page .= '.php';
}
// Check for path traversal
if (strpos($page, '..') !== false) {
die('Invalid path');
}
include '/var/www/pages/' . $page;
}
// Attack: page=../../etc/passwd%00
// In older PHP, the null byte truncates the string
// include('/var/www/pages/../../etc/passwd') is executed!
// VULNERABLE: File upload extension check
function uploadFileVulnerable($filename, $content) {
$allowed = ['jpg', 'png', 'gif'];
// Get extension
$ext = pathinfo($filename, PATHINFO_EXTENSION);
if (!in_array(strtolower($ext), $allowed)) {
die('Invalid file type');
}
// Attack: filename=malware.php%00.jpg
// Passes check but creates .php file
file_put_contents('/uploads/' . $filename, $content);
}
// VULNERABLE: Access control bypass
function checkAccessVulnerable($path) {
$restricted = ['/admin/', '/config/', '/private/'];
foreach ($restricted as $dir) {
if (strpos($path, $dir) !== false) {
return false;
}
}
return true;
}
// Attack: path=/adm%00in/secret.txt
// strpos doesn't find '/admin/' but filesystem interprets as /admin/
?>
# VULNERABLE: Path validation bypass
import os
def read_file_vulnerable(user_path):
base_dir = '/var/www/public/'
# Simple path traversal check
if '..' in user_path:
raise ValueError('Invalid path')
# Check allowed extension
if not user_path.endswith('.txt'):
raise ValueError('Only .txt files allowed')
# Null byte not removed!
full_path = base_dir + user_path
# If passed to C library or certain OS calls:
# user_path = "secret.php\x00.txt"
# Passes Python checks but C sees "secret.php"
with open(full_path, 'r') as f:
return f.read()
# VULNERABLE: URL/command construction
def fetch_url_vulnerable(user_url):
# Validation
if not user_url.startswith('https://allowed.com/'):
raise ValueError('URL not allowed')
# Null byte can truncate in downstream processing
# user_url = "https://allowed.com/\x00https://evil.com/"
import subprocess
subprocess.run(['curl', user_url]) # May go to evil.com
// VULNERABLE: Java passing to native code
public class VulnerableFileHandler {
// Java strings include null bytes
public byte[] readFile(String userPath) throws IOException {
// Validation in Java
if (userPath.contains("..")) {
throw new SecurityException("Path traversal detected");
}
if (!userPath.endsWith(".txt")) {
throw new SecurityException("Invalid extension");
}
// If path is passed to native (JNI) code or C library:
// userPath = "secret.php\0.txt"
// Java sees "secret.php\0.txt" (11 chars)
// C sees "secret.php" (10 chars, stops at null)
return Files.readAllBytes(Paths.get(userPath));
}
// VULNERABLE: Command execution
public void runCommand(String filename) throws IOException {
// Validate filename
if (!filename.matches("[a-zA-Z0-9]+\\.sh")) {
throw new SecurityException("Invalid filename");
}
// Null byte in middle of string
// filename = "safe\0; rm -rf /.sh"
Runtime.getRuntime().exec("/scripts/" + filename);
}
}
Fixed Code
<?php
// SAFE: Strip null bytes from all input
function sanitizeInput($input) {
// Remove null bytes
return str_replace("\0", '', $input);
}
// SAFE: File inclusion with null byte protection
function includePageSafe($page) {
// Remove null bytes first
$page = str_replace(["\0", "\x00", "%00"], '', $page);
// Whitelist allowed pages
$allowed = ['home', 'about', 'contact', 'products'];
if (!in_array($page, $allowed)) {
die('Invalid page');
}
include '/var/www/pages/' . $page . '.php';
}
// SAFE: File upload with comprehensive validation
function uploadFileSafe($filename, $content) {
// Remove null bytes
$filename = str_replace(["\0", "\x00"], '', $filename);
// Validate filename format
if (!preg_match('/^[a-zA-Z0-9_-]+\.[a-zA-Z0-9]+$/', $filename)) {
die('Invalid filename format');
}
// Check extension
$allowed = ['jpg', 'png', 'gif'];
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (!in_array($ext, $allowed)) {
die('Invalid file type');
}
// Verify content matches claimed type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_buffer($finfo, $content);
finfo_close($finfo);
$allowedMimes = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($mime, $allowedMimes)) {
die('Content does not match extension');
}
// Generate safe filename
$safeFilename = bin2hex(random_bytes(16)) . '.' . $ext;
file_put_contents('/uploads/' . $safeFilename, $content);
return $safeFilename;
}
// SAFE: Access control with null byte handling
function checkAccessSafe($path) {
// Remove null bytes
$path = str_replace(["\0", "\x00"], '', $path);
// Normalize path
$path = realpath('/var/www' . $path);
// Must be within allowed directory
if ($path === false || strpos($path, '/var/www/public/') !== 0) {
return false;
}
return true;
}
?>
import os
import re
# SAFE: Remove null bytes from input
def sanitize_input(user_input):
if isinstance(user_input, str):
return user_input.replace('\x00', '')
elif isinstance(user_input, bytes):
return user_input.replace(b'\x00', b'')
return user_input
# SAFE: Path validation with null byte protection
def read_file_safe(user_path):
base_dir = '/var/www/public/'
# Remove null bytes
user_path = sanitize_input(user_path)
# Whitelist characters
if not re.match(r'^[a-zA-Z0-9_/-]+\.txt$', user_path):
raise ValueError('Invalid path format')
# Build full path
full_path = os.path.join(base_dir, user_path)
# Resolve to absolute path and verify within base
real_path = os.path.realpath(full_path)
real_base = os.path.realpath(base_dir)
if not real_path.startswith(real_base + os.sep):
raise ValueError('Path outside allowed directory')
with open(real_path, 'r') as f:
return f.read()
# SAFE: URL validation
def fetch_url_safe(user_url):
# Remove null bytes
user_url = sanitize_input(user_url)
# Parse and reconstruct URL
from urllib.parse import urlparse, urlunparse
parsed = urlparse(user_url)
# Validate components
if parsed.scheme != 'https':
raise ValueError('HTTPS required')
if parsed.netloc != 'allowed.com':
raise ValueError('Domain not allowed')
# Reconstruct clean URL
clean_url = urlunparse((
parsed.scheme,
parsed.netloc,
parsed.path,
'', '', '' # No params, query, fragment
))
import subprocess
subprocess.run(['curl', clean_url], check=True)
// SAFE: Null byte protection in Java
public class SafeFileHandler {
// Remove null bytes from string
private String sanitize(String input) {
if (input == null) return null;
return input.replace("\0", "");
}
public byte[] readFile(String userPath) throws IOException {
// Remove null bytes
userPath = sanitize(userPath);
// Validate path format
if (!userPath.matches("^[a-zA-Z0-9_/-]+\\.txt$")) {
throw new SecurityException("Invalid path format");
}
// Resolve and validate path
Path basePath = Paths.get("/var/www/public").toRealPath();
Path fullPath = basePath.resolve(userPath).normalize();
// Ensure within base directory
if (!fullPath.startsWith(basePath)) {
throw new SecurityException("Path outside allowed directory");
}
return Files.readAllBytes(fullPath);
}
// SAFE: Command execution with validation
public void runCommand(String filename) throws IOException {
// Remove null bytes
filename = sanitize(filename);
// Strict whitelist validation
if (!filename.matches("^[a-z]+\\.sh$")) {
throw new SecurityException("Invalid filename");
}
// Verify file exists in expected location
Path scriptPath = Paths.get("/scripts", filename).toRealPath();
if (!scriptPath.startsWith(Paths.get("/scripts").toRealPath())) {
throw new SecurityException("Script not in allowed directory");
}
// Use ProcessBuilder for safety
ProcessBuilder pb = new ProcessBuilder(scriptPath.toString());
pb.directory(new File("/scripts"));
pb.start();
}
}
Exploited in the Wild
PHP File Inclusion
Null byte attacks widely used against PHP include() and require().
File Upload Bypass
Extension validation bypassed to upload web shells.
WAF Bypass
Web Application Firewalls circumvented using null bytes.
Tools to test/exploit
-
Burp Suite — inject null bytes in requests.
-
Manual testing with %00 in URLs and parameters.
-
Fuzzing tools with null byte payloads.
CVE Examples
-
CVE-2006-7243: PHP null byte in file operations.
-
Multiple CVEs in web applications with null byte vulnerabilities.
References
-
MITRE. "CWE-626: Null Byte Interaction Error (Poison Null Byte)." https://cwe.mitre.org/data/definitions/626.html
-
OWASP. "Embedding Null Code."