CWE-158 is the failure to neutralize NUL characters, also called null bytes, when input is sent to a downstream component. It is one of the oldest tricks in web and native code, and it still matters whenever two layers of software disagree about where a string ends.
It affects any product that validates a string in one component and uses it in another. MITRE lists C and C++ among the platforms, and the weakness is not language specific.
How it happens
In C, a string ends at the first NUL byte. Many higher level languages store the length separately and let a NUL sit in the middle of a string like any other character. So a value can look one way to the high level code and another way to the C library underneath.
MITRE describes the effect: an injected NUL byte may cause the product to believe the input is terminated earlier than it actually is, or otherwise misinterpret the input. This can be used to inject dangerous input that occurs after the null byte, or to bypass validation routines and other protection mechanisms.
Here is the shape of the bug in a file handler.
# Vulnerable pattern: the check sees the whole string,
# a lower layer may stop at the first NUL byte
name = request.args["file"] # "secret.txt%00.png" after decoding
if name.endswith(".png"):
return send_file("/uploads/" + name)
The extension check passes because the string ends in .png. A lower layer that stops at the NUL byte opens secret.txt. Modern runtimes often reject embedded NUL bytes in file names, which is a good example of a language level defense, but the check can still be wrong for other consumers such as native extensions, other services or older runtimes.
The observed examples show this exact pattern in the wild. CVE-2008-1284 is a NUL byte in a theme name that makes the impact of a directory traversal worse. CVE-2005-2008 and CVE-2005-3293 are both source code disclosure using a trailing null. CVE-2005-2061 is a trailing null that allows a file include.
The recurring result is that a suffix check or filter looks at the full string while the file system sees a shorter one.
What an attacker gains
The record lists integrity impact as unexpected state. The examples are more specific.
- Reading source code that the server should execute instead of show.
- Including a file the attacker should not be able to name.
- Making a traversal bug more damaging by cutting off a forced suffix.
Each of these starts as a validation bypass. The attacker never breaks the check. The attacker hides from it.
How to find it
MITRE lists no detection methods for this variant, so rely on testing and review.
- Send encoded NUL bytes (percent encoded as %00, or a raw zero byte in binary fields) in file names, paths, theme or template names, and any parameter that ends up in a system call.
- Look for validation that depends on how a string ends, such as suffix checks, extension allowlists and forced file extensions appended by the program.
- Trace values across language boundaries. A string that goes from a managed language to native code, or from a web layer to another service, can change meaning at the boundary.
- Review code that decodes input after validating it. A percent encoded NUL byte only becomes a real one after decoding, so the order of decode and check matters. This overlaps with CWE-174 on double decoding.
How to fix it
The mitigation guidance in the record opens with a statement of expectation. Developers should anticipate that null characters or null bytes will be injected, removed or manipulated in the input vectors of their product. From there it asks for an appropriate combination of denylists and allowlists, so that only valid, expected and appropriate input is processed.
Turn that into steps.
- Decode input once, into the internal form your application uses, before you validate it.
- Reject any value that contains a NUL byte unless the field legitimately holds binary data.
def clean_filename(value: str) -> str:
if "\x00" in value:
raise ValueError("NUL byte in file name")
return value
- Use an accept known good strategy. Define what a valid file name looks like, such as letters, digits and a single dot, and reject anything else. Do not try to strip NUL bytes out and carry on, because removal changes meaning and can recreate problems.
- Do not append extensions or suffixes to user supplied names to force a file type. Look the name up in an allowlist of known files or use identifiers that map to paths on the server side.
- Make the validating layer and the using layer the same layer where possible, so there is no boundary where the length of the string can change.
- When a native extension or library is in the path, check what it does with embedded NUL bytes. Do not assume it matches the behavior of your language.
Try it yourself
A static scan can point at code that builds paths from input and where a NUL byte check is missing.
Click through it at your own pace, or open it full screen.
Libraries that carry the problem
Most of the observed examples come from products and libraries that handled strings across layers poorly. If one of those layers is a dependency in your project, you may inherit the behavior. The free in-browser dependency scan reads a manifest or lockfile with up to 100 dependencies, needs no login, and keeps the file on your machine. Reachability analysis then shows whether your code actually calls the affected function.
Quick check
Why does a suffix check not protect against a NUL byte?
The check sees the whole string and finds the expected suffix. A downstream component that stops at the NUL byte sees a shorter string with a different ending. CVE-2005-2008 is a source disclosure that used a trailing null.
Should I strip NUL bytes from input and continue?
Prefer rejecting. Removal changes the value after it was checked, and the record asks for strict validation: reject input that does not strictly conform, or transform it into something that does before validating the result.
Is this only a C problem?
No. C strings are where the termination rule lives, but the weakness appears when any two components disagree about the end of a string. MITRE lists it as not language specific.