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

ImpactDetails
Access ControlScope: Bypass

Security filters fail to detect malicious content.
IntegrityScope: File System

Unauthorized file access or uploads.
ConfidentialityScope: 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

  1. MITRE. "CWE-626: Null Byte Interaction Error (Poison Null Byte)." https://cwe.mitre.org/data/definitions/626.html

  2. OWASP. "Embedding Null Code."