Why your path-traversal test silently passed: the curl dot-dot trap

  • web
  • path-traversal
  • lfi
  • tooling
  • methodology

Proof of concept on GitHub

You test a download or include parameter for path traversal with curl, the response looks innocent, and you move on. Before you write the parameter off as sanitized, check whether curl ever sent your payload. By default it does not send what you typed.

What actually happens

curl normalizes the URL path per RFC 3986 before sending: ../ segments are collapsed client-side. So http://target/download?file=../../etc/passwd can leave your machine with the ../../ already resolved away. The server receives a clean path, behaves normally, and you have produced a false negative on an endpoint that is genuinely vulnerable.

# what you think you sent, vs what actually goes out:
curl -v   'http://TARGET/download?file=../../etc/passwd'       # ../ may be stripped before sending
curl -v --path-as-is 'http://TARGET/download?file=../../etc/passwd'   # sent verbatim

Pass --path-as-is whenever the payload contains .., ., or //, and confirm with -v (or -D -) that the request line carries the traversal you typed. The flag costs nothing, so use it unconditionally for traversal work.

The real rule: read the request, not your intention

This is one instance of a larger self-inflicted failure: the tool quietly changed your payload, so you are testing something other than what you meant.

  • Normalization applies to the path, so a payload in a query parameter often passes through, but not always. Do not rely on the distinction; set the flag.
  • Shells eat backslashes, -G moves data into the query string, and -L follows a redirect that can drop your parameter. Each produces the same "looks not vulnerable" lie.
  • Encoded variants (%2e%2e%2f, ..%2f, ....//) defeat server-side filters, not curl's normalization. They are a different fix for a different problem. Try a plain ../ with --path-as-is first: if it works, there was no filter to bypass at all.
  • If a traversal works in a browser or Burp but "not in curl," this is almost always why.

For defenders

Two takeaways. First, do not trust a single tool's negative result in a test or a scanner: a clean curl line is not evidence the endpoint is safe, and a pipeline that concludes "not vulnerable" from normalized requests will pass a real bug straight through. Second, the server-side fix for the actual vulnerability is unchanged: resolve the path to a canonical absolute form and verify it stays inside the intended base directory before opening it, rather than filtering ../ substrings, and reject requests whose decoded path escapes the root.

Detection

Path-traversal attempts are noisy once you look for both the raw and encoded forms: ..%2f, %2e%2e, ....//, and literal ../ in request paths and query strings, plus reads of /etc/passwd, web.config, or . env-style files in the application logs. A WAF or access-log rule that normalizes the request the way the server will, then flags an escape above the web root, catches the real attempts that a naive client-side test would miss.

Use

Authorized and educational use only: your own systems, a lab you control, or an engagement with written permission. This is a testing-accuracy note so a real finding is not discarded, and a reminder for defenders not to trust a single tool's verdict.

Proof of concept on GitHub

Back to all techniques