CWE-177, Improper Handling of URL Encoding (Hex Encoding), covers products that mishandle input that is all or partly URL encoded. Any service that takes paths, parameters or filenames from a URL is exposed.
How it happens
URL encoding writes a byte as a percent sign and two hex digits. A dot is %2e, a forward slash is %2f, a backslash is %5c. A filter that looks for the literal text ".." will miss "%2e%2e". If the application decodes the value after the filter ran, the traversal sequence reappears.
MITRE's examples for this weakness include hex encoded traversal variants such as "%2e%2e", "%2e%2e%2f" and "%5c%2e%2e", seen in CVE-2000-0900, CVE-2005-2256 and CVE-2004-2121. Another, CVE-2004-0280, involved an encoded space ("%20").
A small illustration of the vulnerable ordering, in Python:
# Vulnerable: check first, decode later
def read_file(raw_name):
if ".." in raw_name:
raise ValueError("bad name")
name = urllib.parse.unquote(raw_name) # "%2e%2e/secret" becomes "../secret"
return open(BASE + name).read()
# Fixed: decode once, then validate the final path
def read_file(raw_name):
name = urllib.parse.unquote(raw_name)
path = os.path.realpath(os.path.join(BASE, name))
if not path.startswith(os.path.realpath(BASE) + os.sep):
raise ValueError("bad name")
return open(path).read()
What an attacker gains
MITRE's listed consequence is integrity, an unexpected state. In practice the examples point to access to files outside the intended directory.
How to find it
Request a known blocked resource with its characters hex encoded, then double encoded, and compare against the plain request. Code review should look for any place where a check runs before a decode step.
How to fix it
The record lists no mitigations, so this is general guidance.
- Decode exactly once, at the edge.
- Validate the decoded value against an allowlist or resolve it to a real path and confirm it stays inside the allowed directory.
- Do not decode again further down.
Quick check
Why decode before validating?
Validation must see the value the rest of the program will use. If decoding happens afterward, the check inspected something different.