Incomplete Filtering of Multiple Instances of Special Elements
Description
Incomplete Filtering of Multiple Instances of Special Elements is an input validation vulnerability where software fails to filter all instances of special elements when they appear multiple times in the input. This applies to both sequential elements (dangerous sequences appearing adjacent to each other, like "....//") and non-sequential elements (the same dangerous sequence appearing in multiple locations throughout the input). When filtering doesn't address all instances, attackers can construct inputs where removal of some instances produces or reveals additional dangerous sequences.
Risk
This vulnerability enables sophisticated bypass techniques. With sequential elements, attackers can craft inputs like "....//file" where removing the middle ".." and "/" leaves behind a new "../" sequence. With non-sequential elements, inputs like "..%2f..%2f../file" may have some traversal sequences filtered while encoded or differently formatted ones survive. The risk is compounded when filtering is applied only once without checking if the result is now dangerous. These bypasses are particularly effective against naive filter implementations that assume a single pass is sufficient.
Solution
Implement recursive or iterative filtering that continues until no more dangerous sequences are found. Apply filtering in a loop, comparing the result to the previous iteration until they match (indicating no more changes). Handle all variations of dangerous elements including different encodings, case variations, and separator styles. Consider multiple filtering passes for different categories of dangerous elements. Use canonical representation before validation—for paths, resolve to absolute canonical form before checking. Prefer allowlist validation that only permits known-safe characters. Test with complex nested and encoded inputs.
Common Consequences
| Impact | Details |
|---|---|
| Integrity | Scope: Integrity Unexpected State - Unfiltered element instances cause unintended application behavior. |
| Confidentiality | Scope: Confidentiality Read Application Data - Sequential traversal patterns that survive filtering enable file access. |
| Integrity | Scope: Integrity Execute Unauthorized Code - Multiple encoding variations of dangerous elements may enable injection. |
Example Code
Vulnerable Code
# Vulnerable: Single pass doesn't handle sequential patterns
my $Username = GetUntrustedInput();
# Vulnerable: Only removes literal "../" sequences
$Username =~ s/\.\.\///g;
my $filename = "/home/user/" . $Username;
ReadAndSendFile($filename);
# Attack: "....//....//etc/passwd"
# After filter: each ".." followed by "." followed by "/" becomes different
# Actually: "...." becomes ".." and "//" becomes "/" after removing middle
# More precisely, "..../" contains "../" which is removed leaving "../"
# Result may still allow traversal depending on exact pattern
// Vulnerable: Single replacement pass
<?php
function vulnerable_filter($input) {
// Vulnerable: Doesn't loop until stable
$filtered = str_replace("../", "", $input);
return $filtered;
}
// Attack: "..././..././etc/passwd"
// After ONE pass: "..//..//etc/passwd"
// The "..././" contains "../" which is removed, leaving "./"
// But "./../" then becomes "../" - still traversal!
# Vulnerable: Doesn't handle double-encoded sequences
import urllib.parse
def vulnerable_filter(path):
# First pass: remove path traversal
filtered = path.replace('../', '')
# Vulnerable: Doesn't handle encoded versions
return filtered
# Attack: "..%2f..%2fetc%2fpasswd"
# After filter: unchanged because %2f is not /
# When URL-decoded by web server: "../../../etc/passwd"
// Vulnerable: Doesn't handle all variations
function vulnerableFilter(input) {
// Vulnerable: Only handles forward slash variant
return input.replace(/\.\.\//g, '');
}
// Bypass 1 (Windows): "..\\..\\etc\\passwd"
// Bypass 2 (Mixed): "..\\/etc/passwd"
// Bypass 3 (Encoded): "..%2Fetc/passwd"
// Vulnerable: Filters each type separately, doesn't loop
public String vulnerableFilter(String input) {
String result = input;
// Remove forward slash traversal
result = result.replaceAll("\\.\\./", "");
// Remove backslash traversal
result = result.replaceAll("\\.\\.\\\\", "");
return result;
}
// Attack: "..../\\" -> after removing "../" leaves "..\\"
// Attack: "..\\../file" -> after removing "..\" leaves "../file"
Fixed Code
# Fixed: Loop until no more changes
my $Username = GetUntrustedInput();
# Fixed: Recursive filtering
my $prev;
do {
$prev = $Username;
# Remove all variants
$Username =~ s/\.\.[\/\\]//g; # Literal traversal
$Username =~ s/%2e%2e[%2f%5c]//gi; # URL-encoded
$Username =~ s/%252e%252e[%252f%255c]//gi; # Double-encoded
} while ($Username ne $prev);
# Better: Validate against allowlist
unless ($Username =~ /^[a-zA-Z0-9_.-]+$/) {
die "Invalid username";
}
my $filename = "/home/user/" . $Username;
// Fixed: Multi-pass filtering with all variants
<?php
function fixed_filter($input) {
// Decode URL encoding first
$decoded = rawurldecode(rawurldecode($input)); // Double decode
// Loop until stable
do {
$prev = $decoded;
$decoded = str_replace("../", "", $decoded);
$decoded = str_replace("..\\", "", $decoded);
$decoded = str_replace(".." . DIRECTORY_SEPARATOR, "", $decoded);
} while ($decoded !== $prev);
return $decoded;
}
// Better: Canonical path validation
function fixed_validate_path($base, $userInput) {
// Decode first
$decoded = rawurldecode(rawurldecode($userInput));
// Resolve to canonical path
$realBase = realpath($base);
$fullPath = realpath($base . DIRECTORY_SEPARATOR . $decoded);
if ($fullPath === false ||
strpos($fullPath, $realBase . DIRECTORY_SEPARATOR) !== 0) {
throw new Exception("Path traversal detected");
}
return $fullPath;
}
# Fixed: Handle all encoding variants and loop
import urllib.parse
import os.path
def fixed_filter(path):
# Decode multiple times to handle double/triple encoding
decoded = path
for _ in range(3): # Handle up to triple encoding
prev = decoded
decoded = urllib.parse.unquote(decoded)
if decoded == prev:
break
# Loop until stable
prev = None
while decoded != prev:
prev = decoded
# Remove all traversal variants
decoded = decoded.replace('../', '')
decoded = decoded.replace('..\\', '')
return decoded
# Better: Use canonical path validation
def fixed_validate_path(base_dir, user_path):
# Decode the user input
decoded = user_path
for _ in range(3):
prev = decoded
decoded = urllib.parse.unquote(decoded)
if decoded == prev:
break
# Resolve to canonical path
full_path = os.path.realpath(os.path.join(base_dir, decoded))
base_path = os.path.realpath(base_dir)
# Verify it's under base directory
if not full_path.startswith(base_path + os.sep):
raise ValueError("Path traversal detected")
return full_path
// Fixed: Comprehensive filtering with loop
import java.net.URLDecoder;
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathValidator {
public String fixedFilter(String input) {
// Decode URL encoding (multiple passes for double encoding)
String decoded = input;
for (int i = 0; i < 3; i++) {
try {
String prev = decoded;
decoded = URLDecoder.decode(decoded, "UTF-8");
if (decoded.equals(prev)) break;
} catch (Exception e) {
break;
}
}
// Loop until stable
String result = decoded;
String prev;
do {
prev = result;
result = result.replaceAll("\\.\\.[/\\\\]", "");
} while (!result.equals(prev));
return result;
}
// Better: Canonical path validation
public Path fixedValidatePath(Path base, String userInput) throws Exception {
// Decode
String decoded = userInput;
for (int i = 0; i < 3; i++) {
String prev = decoded;
decoded = URLDecoder.decode(decoded, "UTF-8");
if (decoded.equals(prev)) break;
}
// Resolve and validate
Path resolved = base.resolve(decoded).normalize().toRealPath();
Path realBase = base.toRealPath();
if (!resolved.startsWith(realBase)) {
throw new SecurityException("Path traversal detected");
}
return resolved;
}
}
Related CWEs
- CWE-792: Incomplete Filtering of One or More Instances of Special Elements (parent)
- CWE-793: Only Filtering One Instance of a Special Element (sibling)
- CWE-23: Relative Path Traversal (related exploitation)
References
- MITRE Corporation. "CWE-794: Incomplete Filtering of Multiple Instances of Special Elements." https://cwe.mitre.org/data/definitions/794.html
- OWASP. "Path Traversal." https://owasp.org/www-community/attacks/Path_Traversal
- OWASP. "Double Encoding." https://owasp.org/www-community/Double_Encoding