Only Filtering Special Elements at an Absolute Position

Description

Only Filtering Special Elements at an Absolute Position is an input validation vulnerability where software filters dangerous elements only when they appear at specific byte offsets or character positions within the input. The filter checks fixed positions (like "byte 0", "characters 0-2", or "position 10") rather than searching the entire input for dangerous sequences. This approach fails when attackers position malicious content at any location other than the specifically checked positions, allowing the dangerous elements to pass through the filter unchanged.

Risk

This vulnerability allows straightforward bypass by simply positioning dangerous content at any unchecked location. If a filter only checks positions 0-2 for "../", an attacker can use "x/../../../etc/passwd" where the traversal sequences start at position 1 instead of 0. The rigidity of position-based checking makes this even easier to exploit than marker-relative filtering, as any input structure that shifts the dangerous content by even one byte defeats the protection. The vulnerability indicates a fundamental misunderstanding of how input validation should work—dangerous content must be neutralized regardless of where it appears.

Solution

Never rely on absolute positions for security filtering. Implement pattern searches that examine the entire input from start to end. Use string search functions or regular expressions without position constraints. For path validation, resolve inputs to canonical form and verify they remain within allowed directories. Apply allowlist validation that only permits known-safe characters at any position. Loop filtering operations until no more matches are found to handle nested patterns. Test with dangerous content at various positions including the beginning, middle, and end of inputs.

Common Consequences

ImpactDetails
IntegrityScope: Integrity

Unexpected State - Dangerous elements at non-checked positions pass through filters, causing unintended application behavior.
ConfidentialityScope: Confidentiality

Read Application Data - Path traversal at unchecked positions enables unauthorized file access.
IntegrityScope: Integrity

Execute Unauthorized Code - Injection sequences positioned outside checked offsets enable attacks.

Example Code

Vulnerable Code

# Vulnerable: Only checks substring at absolute position 0-2
my $Username = GetUntrustedInput();

# Vulnerable: Only checks if "../" at positions 0-2
if (substr($Username, 0, 3) eq '../') {
    $Username = substr($Username, 3);
}

my $filename = "/home/user/" . $Username;
ReadAndSendFile($filename);

# Attack: "../../../etc/passwd" - only first "../" removed
# Attack: "x/../../../etc/passwd" - "../" at position 1, not checked
# Attack: "a/../../../etc/passwd" - "../" at position 2, not checked
# Vulnerable: Checks specific byte positions
def vulnerable_filter(data):
    # Vulnerable: Only checks first 4 bytes for null
    if data[0:4] == b'\x00\x00\x00\x00':
        return data[4:]
    return data

# Attack: Any input with nulls after position 3
# Attack: b'aaaa\x00\x00\x00\x00malicious'
// Vulnerable: Only checks specific character positions
char* vulnerable_filter(char* input) {
    // Vulnerable: Only checks positions 0-2 for "../"
    if (input[0] == '.' && input[1] == '.' && input[2] == '/') {
        return input + 3;  // Skip first 3 characters
    }
    return input;
}

// Attack: "x/../etc/passwd" - "../" at position 1
// Attack: "a/../etc/passwd" - "../" at position 2
// Vulnerable: Uses indexOf with position constraint
public String vulnerableFilter(String input) {
    // Vulnerable: Only removes "../" if at position 0
    if (input.indexOf("../") == 0) {
        return input.substring(3);
    }
    return input;
}

// Attack: "dir/../../../etc/passwd" - "../" at position 4
// Attack: "./../../../etc/passwd" - ".." at position 2
// Vulnerable: Only checks specific offset
<?php
function vulnerable_filter($input) {
    // Vulnerable: Only checks first 3 characters
    if (substr($input, 0, 3) === '../') {
        $input = substr($input, 3);
    }
    return $input;
}

// Attack: "x/../../../etc/passwd" - unchanged
// Attack: "subdir/../../../etc/passwd" - unchanged
// Vulnerable: Checks at fixed positions
function vulnerableFilter(input) {
    // Vulnerable: Only checks character at position 0
    if (input.charAt(0) === '<') {
        // Remove first character
        return input.substring(1);
    }
    return input;
}

// Attack: "x<script>alert(1)</script>" - < not at position 0
// Attack: " <script>alert(1)</script>" - space at position 0

Fixed Code

# Fixed: Search entire string, not specific positions
my $Username = GetUntrustedInput();

# Fixed: Remove from anywhere, loop until stable
my $prev;
do {
    $prev = $Username;
    # Search entire string with global replacement
    $Username =~ s/\.\.[\\/]//g;
} while ($Username ne $prev);

# Better: Allowlist validation
unless ($Username =~ /^[a-zA-Z0-9_.-]+$/) {
    die "Invalid username";
}

my $filename = "/home/user/" . $Username;
# Fixed: Check all positions, not just specific bytes
import os.path

def fixed_filter(data):
    # Fixed: Remove null bytes from anywhere
    if isinstance(data, bytes):
        return bytes(b for b in data if b != 0)
    else:
        return data.replace('\x00', '')

def fixed_filter_path(path):
    # Fixed: Remove traversal from anywhere
    prev = None
    while path != prev:
        prev = path
        path = path.replace('../', '').replace('..\\', '')
    return path

# Better: Canonical path validation
def fixed_validate_path(base_dir, user_path):
    full_path = os.path.realpath(os.path.join(base_dir, user_path))
    base_path = os.path.realpath(base_dir)

    if not full_path.startswith(base_path + os.sep):
        raise ValueError("Path traversal detected")
    return full_path
// Fixed: Search entire string
#include <string.h>
#include <stdlib.h>

char* fixed_filter(const char* input) {
    size_t len = strlen(input);
    char* result = malloc(len + 1);
    size_t j = 0;

    // Fixed: Check every position, not just position 0
    for (size_t i = 0; i < len; i++) {
        // Check for "../" at current position
        if (i + 2 < len &&
            input[i] == '.' && input[i+1] == '.' &&
            (input[i+2] == '/' || input[i+2] == '\\')) {
            i += 2;  // Skip "../" (loop will add 1 more)
        } else {
            result[j++] = input[i];
        }
    }
    result[j] = '\0';

    // Loop until stable (handle nested patterns)
    char* final = result;
    while (strstr(final, "../") || strstr(final, "..\\")) {
        char* temp = fixed_filter(final);
        if (final != result) free(final);
        final = temp;
    }

    return final;
}
// Fixed: Search entire string with proper loop
import java.nio.file.Path;

public String fixedFilter(String input) {
    // Fixed: Remove from anywhere, loop until stable
    String result = input;
    String prev;
    do {
        prev = result;
        result = result.replace("../", "").replace("..\\", "");
    } while (!result.equals(prev));

    return result;
}

// Better: Canonical path validation
public Path fixedValidatePath(Path base, String userInput) throws Exception {
    Path resolved = base.resolve(userInput).normalize().toRealPath();
    Path realBase = base.toRealPath();

    if (!resolved.startsWith(realBase)) {
        throw new SecurityException("Path traversal detected");
    }
    return resolved;
}
// Fixed: Search and remove from entire string
<?php
function fixed_filter($input) {
    // Fixed: Loop until no changes
    do {
        $prev = $input;
        // strpos searches entire string
        $input = str_replace('../', '', $input);
        $input = str_replace('..\\', '', $input);
    } while ($input !== $prev);

    return $input;
}

// Better: Canonical path validation
function fixed_validate_path($base, $userPath) {
    $basePath = realpath($base);
    $fullPath = realpath($base . DIRECTORY_SEPARATOR . $userPath);

    if ($fullPath === false ||
        strpos($fullPath, $basePath . DIRECTORY_SEPARATOR) !== 0) {
        throw new Exception("Path traversal detected");
    }

    return $fullPath;
}
// Fixed: Search entire input
function fixedFilter(input) {
    // Fixed: Global search with loop
    let result = input;
    let prev;
    do {
        prev = result;
        result = result.replace(/\.\.[\\/]/g, '');
    } while (result !== prev);

    return result;
}

// For XSS, use proper encoding
function fixedEncodeHtml(input) {
    const div = document.createElement('div');
    div.textContent = input;
    return div.innerHTML;
}

  • CWE-795: Only Filtering Special Elements at a Specified Location (parent)
  • CWE-796: Only Filtering Special Elements Relative to a Marker (sibling)
  • CWE-791: Incomplete Filtering of Special Elements (grandparent)
  • CWE-22: Improper Limitation of a Pathname to a Restricted Directory (related exploitation)

References

  1. MITRE Corporation. "CWE-797: Only Filtering Special Elements at an Absolute Position." https://cwe.mitre.org/data/definitions/797.html
  2. OWASP. "Path Traversal." https://owasp.org/www-community/attacks/Path_Traversal
  3. OWASP. "Input Validation Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html