I was working with this web-app, when someone pen-tested it and sent me a huge report that says my app is vulnerable to a Directory traversal attack.

Here is one sample:

Testing Path: <- VULNERABLE!

I put in my browser, but it gave me the home page, it didn't at all return the /etc/issue file.

Then I tried with curl and it too returned the homepage.

Could somebody please explain me how my app is vulnerable, if the /etc/issue file is not returned.

The app is coded in Python 2.7, with flask as the framework and Nginx as a reverse proxy.

Two more samples from the report, along with the corresponding response :-

  1. Testing Path: <- VULNERABLE!

    GET Request - app: 0|req: 1587/1587] () {34 vars in 488 bytes} [Tue Sep 6 15:47:13 2016] GET /??/etc/passwd => generated 982 bytes in 4 msecs (HTTP/1.1 200) 2 headers in 80 bytes1

  2. Testing Path: <- VULNERABLE!

    GET Request - app: 0|req: 1591/1591] () {34 vars in 493 bytes} [Tue Sep 6 15:47:14 2016] GET /??/??/etc/passwd => generated 982 bytes in 5 msecs (HTTP/1.1 200) 2 headers in 80 bytes

  • 17
    you can ask the pentester for proof. Exactly the contents of /etc/issue
    – Sravan
    Commented Sep 7, 2016 at 11:36
  • Thanks for the response @Sravan, could you please give a tiny hint about how should i check for that vulnerability. Nowhere i am asking for a file-path input in the app, and i have created a /var1/static/images directory for responding to static contents.
    – Batman
    Commented Sep 7, 2016 at 11:53
  • 12
    You keep using '/??/' in your path. That's probably not the actual path that would have been used for a directory traversal vulnerability. Are you redacting, or is that actually what the report contains?
    – Jesse K
    Commented Sep 7, 2016 at 17:54
  • 4
    I guess whatever tooling the pentester used got confused by the fact that your webapp returns a 200 OK for a nonsense URL instead of a redirect (3xx) to the home page or a 404. Commented Sep 8, 2016 at 12:27
  • 2
    @Batman The logs in your question certainly show that for the two cases you gave, the application returned 200. But I just now realised that ? is the leader for the query string, so from the perspective of the flask router, this is simply a request to /, so it’s legit to return the home page with a 200 OK (and this makes the report from the tool even weirder, if you indeed return HTML. HTML is not commonly found in /etc/issue). Disregard my earlier comment. Commented Sep 8, 2016 at 13:40

3 Answers 3


I sent a report for a similar vulnerability recently and got a similar response.

Turns out most browsers and CLI http clients remove path traversal components from the URL.

For instance if on Firefox you type the URL http://example.com/../../../etc/passwd the GET request that arrives at example.com will look like this:

GET /etc/passwd HTTP/1.1
[Ommitted headers]

Same deal with wget.

You should try with a lower level tool, like telnet or netcat:

$ telnet example.com 80
GET /../../../etc/issue HTTP/1.1

HTTP/1.1 400 Bad Request
Content-Type: text/html
Content-Length: 349
Connection: close
Date: Wed, 07 Sep 2016 12:38:13 GMT
Server: ECSF (fll/078B)

Then again, it might have been a false positive, your auditor should've included the contents of /etc/issue in the report. That's kind of the point of using issue and not passwd.

You should at least follow up with your auditor to confirm whether it was a false positive. If that's not possible, arrange a new pentest or perform your own with a path traversal fuzzer like dotdotpwn

Never assume you're secure, ensure you are. Especially after a report like that.

  • 3
    Thanks for the explanation! I did Telnet my localhost with GET /??/etc/issue and i got the HTML source code of my home page as the response. Could it be possible that the auditor's tool considered the returned HTML-content as the content of /etc/issue file and raised the vulnerable flag?
    – Batman
    Commented Sep 7, 2016 at 13:19
  • 8
    @Batman it's certainly possible. Some auditors will just run an automated tool and call it a day. Did the auditor test the live app or did they run their own instance? If they tested on live you could crosscheck with your logs for the exact query and try to replay it before following up with them.
    – GnP
    Commented Sep 7, 2016 at 13:27
  • 8
    @Batman that's a dangerous assumption. I've added a suggestion for a test tool you can easily use if following up with the auditor is not an option.
    – GnP
    Commented Sep 7, 2016 at 13:48
  • 2
    You'll want to ask a new question, on an applicable stack network. Commented Sep 7, 2016 at 15:55
  • 2
    I'm guessing that they got a non-4xx response to their request, so they're considering it a "successful" attack -- even if the result is not meaningful (e.g. your home page, instead of the expected file).
    – Doktor J
    Commented Sep 7, 2016 at 21:50

First, Nobody pen-tested it. They ran a scanner and handed you the results.

A pen-tester would have confirmed the vulnerability and explained how to recreate it.

It is possible that the scanner mistakenly flagged the fact that it got your home page as a response to these payload as a positive finding.

I also think, like Jesse, that the double question mark is hiding the real payload because I've never heard of ?? as part of a directory traversal payload and can't find anything to make me think it is one. Try substituting .. in all the places you see ??

The scanner would have used a browser version that didn't follow https://www.rfc-editor.org/rfc/rfc3986#section-5.2 which is the spec for removing/resolving those dots in the URL.

If the scanner had flagged just one payload as vulnerable while dozens of others were not, I'd be more concerned, but it looks like you got dozens of results with various payloads, right? Like @Gnp said, ask the scanner for proof (and ask about that ?? payload).

  • 1
    Thanks for responding, Spiderman! There are 21161 lines in the report and about 2800 lines have that /?. or /?? form, & only these 2800 lines are vulnerable ones. Rest of the lines with /.. or /.? or other patterns are showing either 400 or 404. My app returns the home page upon being hit by those /?. or /?? patterns and based upon your and GnP's answers it might be a possibility that the tool interprets it as a success in getting hidden file contents and raises the flag. Those 3 audit guys are mighty intelligent & big, i'll try my best to get an explanation about that /?? thing.
    – Batman
    Commented Sep 7, 2016 at 20:15
  • 7
    @Batman honestly it just sounds like ? is a unicode character that isn't displaying properly
    – d0nut
    Commented Sep 8, 2016 at 14:45
  • 1
    While it's true that .. (dotdot) is the most common path traversal method, there are others and ?., ??, null bytes and others have been seen to work on some webservers. Check dotdotpwn's TraversalEngine for a good sample of all the ways to screw it up there are. Particularly the Dots array.
    – GnP
    Commented Sep 8, 2016 at 20:20
  • @iismathwizard: ? is a legitimate character separating the resource on the server and the parameters. If the resource on left side of ? is there on the server, we'll get a valid response irrespective of the right hand-side content. That explains a 200 response to a /??anyunixfilepath. Try few urls' - http://security.stackexchange.com/??/etc/batman or http://security.stackexchange.com/??/etc/batwoman:-) , Remove ) if u r using curl. In all the cases you'll get home page. That means(possibly), my app wasn't vulnerable for ?? or .? or ? pattern in the report.Tools should look
    – Batman
    Commented Sep 9, 2016 at 11:41
  • Tools should look for those ?, otherwise they will raise vulnerable flags for these sites too upon scanning, as these sites(security.stackexchange, google) also return a 200 response if the left hand side of ? in a URL is a valid resource...That's just an opinion(which could be totally wrong).
    – Batman
    Commented Sep 9, 2016 at 11:48

This was most probably a false positive.

After seeing the below updated information in your question

GET Request -

app: 0|req: 1591/1591] () {34 vars in 493 bytes} [Tue Sep  6 15:47:14 2016] GET /??/??/etc/passwd => generated 982 bytes in 5 msecs (HTTP/1.1 200) 2 headers in 80 bytes

Its pretty clear it was produced by some automated scanner.

Then comes the question how the scanner decided its vulnerable?

As you mentioned,

Then I tried with curl and it too returned the homepage.

The automated scanner just assumed that since it got a HTTP/1.1 200(OK) as the server response it was able to read that file /etc/passwd on server. Silly Automated Scanner.

The automated scanner is expecting something like a HTTP/1.1 404 (Not Found) or HTTP/1.1 302 ( URL redirection) for that page to be not vulnerable.

  • Nice answer sir. Your 1st comment too was a great one, but honestly speaking at that time i didn't get what you really meant. Can i select multiple answers as useful ones?
    – Batman
    Commented Sep 9, 2016 at 12:28
  • @Batman np..cool :) . you can only upvote multiple answers.
    – Sravan
    Commented Sep 9, 2016 at 12:32
  • That should not be called pentesting, what a joke. Simply running scanners is not a serious pentest at all
    – niilzon
    Commented Dec 21, 2016 at 13:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .