Filters tend to be written for the example in the textbook. CWE-33 is what happens when the attacker uses a different one. The product builds a pathname from external input and does not neutralize "...." (multiple dot) sequences, so the path can resolve to a location outside the restricted directory.
MITRE says this manipulation is useful for bypassing some path traversal protection schemes. On some Windows systems, "...." is equivalent to "......", and it may get past checks that assume only two dots are valid.
It belongs to the same family as the triple dot variant, CWE-32, and the plain parent directory forms. The record lists web based software among the affected platforms, alongside technology neutral ones.
How it happens
Two things have to line up.
First, the platform has to read a run of dots as more than a name. MITRE's description is specific about Windows: on some systems, extra dots stand for several parent directories at once. That behavior is a quirk of the platform, and most developers never learn it.
Second, the application's protection has to ignore it. A typical filter looks for ".." as a pair, or for "../" or "..", and rejects the request. Four dots contain pairs of dots, but a pattern that expects a pair bounded by separators may see nothing it recognizes.
MITRE also describes a second route to the same string. Incomplete filtering, such as removing "./" sequences, can produce valid ".." sequences afterward, because the leftover characters collapse into an unsafe value. That collapse is tracked as CWE-182. A filter written to help can create the dangerous string by cleaning.
Here is a naive filter in Python that treats only two dots as dangerous:
import os
import re
def looks_safe(name):
return re.search(r"(^|[\\/])\.\.([\\/]|$)", name) is None
path = os.path.join("C:\\srv\\files", name) if looks_safe(name) else None
The pattern wants exactly two dots between separators. A name made of four dots between separators does not match it, so looks_safe returns true. On a system that interprets those dots as multiple parent hops, the final path leaves the base.
The mistake is not the regular expression. The mistake is believing that a list of bad strings can describe every way a file system might interpret a name.
What an attacker gains
MITRE lists confidentiality and integrity impact: reading files or directories and modifying files or directories.
The observed cases in the record are old and spread across network services, which shows how long this pattern has existed:
- CVE-2000-0240: reading files through "/........../" in a URL.
- CVE-2000-0773: reading files through "...." in a web server.
- CVE-1999-1082: reading files through "......" in a web server, which MITRE notes as possibly a doubled triple dot.
- CVE-2004-2121: reading files through "......" in a web server, with the same note.
All four are file reads over HTTP. Three of them name different dot counts, which tells you something useful: the attackers were not relying on one magic string. They tried lengths until the filter missed.
How to find it
The record names automated static analysis, which can find some instances by modeling the flow of external input into file operations, without running the code.
Manual checks that help:
- Find every place a path is cleaned or screened with string rules. Each one is a candidate.
- Read each rule and ask what it does not cover. Rules that name specific strings (two dots, one separator) are the weakest.
- Add dot runs of several lengths to your traversal test list. Three, four, five and six dots, alone and between separators, with both slash types.
- Run those tests against a Windows deployment as well as a Linux one. A result that differs by platform is a sign that the filter and the file system disagree.
If the test passes on Linux and files leak on Windows, you have the same weakness.
How to fix it
The record carries no mitigation list. These steps follow from the cause.
- Do not enumerate bad sequences. A deny list cannot keep up with what a file system is willing to accept.
- Let the platform resolve the path, then check where it ended up. This works for any dot count because the check is on the result, not the input.
- Compare path components against the permitted base, not raw text.
- Where you only expect a plain file name, accept a narrow character set, such as letters, digits, an underscore, a hyphen and a single dot before the extension, and reject everything else.
- Prefer identifiers that you map to file names on the server.
- Run the process with access only to the folder it serves.
A repaired Python version:
import os
BASE = os.path.realpath("C:/srv/files")
def open_file(name):
full = os.path.realpath(os.path.join(BASE, name))
if os.path.commonpath([BASE, full]) != BASE:
raise PermissionError("outside base directory")
return open(full, "rb")
And an allow list for bare names:
import re
def valid_name(name):
return re.fullmatch(r"[A-Za-z0-9_\-]{1,64}(\.[A-Za-z0-9]{1,8})?", name) is not None
That second function rejects any run of dots in the name part, whatever its length, since dots appear only once, before the extension.
If you run older server software, check its patch level. The cases in the record are in web servers, and a server that still handles odd dot runs by itself is a risk that no application code can fix. A dependency scan helps with the libraries your own project pulls in. The free in-browser scan at gold.safeguard.sh/scan needs no login, the file stays on your machine, and it covers up to 100 dependencies.
Quick check
Why would four dots ever be treated as a parent directory?
MITRE notes that on some Windows systems, "...." is equivalent to "......". The file system interprets the dots, not the application.
Why is a longer deny list not the answer?
Attackers can try more dot counts and other separators. Checking the resolved path covers every variant at once.
How can cleaning input create the attack string?
If a filter removes "./" in one pass, the leftover characters can join into "..". MITRE tracks that as a collapse into an unsafe value.