The URL with the exercise is: https://portswigger.net/web-security/ssrf/lab-ssrf-with-whitelist-filter

The solution is:
http://localhost:80%[email protected]/admin/delete?username=carlos

A little simplified (no port specified):
http://localhost%[email protected]/admin/delete?username=carlos

We can concentrate simply on:
http://localhost%[email protected]

Why is the credential component (everything before the @) processed as if everything after the @ is part of a URL fragment? Before double encoding the # character, the URL looks like this:

Why is the @ even necessary, even after we double encode the #? Why does http://localhost%2523stock.weliketoshop.net (not no '@') not work? Does the url fragment statement take priority over the credential statement? Why is the @ ignored in favor of the #?

  • 1
    I'm not sure this is a security question. This looks like a basic URL standards question.
    – schroeder
    Mar 3, 2020 at 8:24

2 Answers 2


As per the RFC 3986:-

The userinfo subcomponent may consist of a user name and, optionally, scheme-specific information about how to gain authorization to access the resource. The user information, if present, is followed by a commercial at-sign ("@") that delimits it from the host.

RFC3986 section-3.2.1


I played with this recently. The SSRF filter is checking to see if the parameter value is an actual URL with a stock.weliketoshop.net hostname. It's not a simple string check. That's why if we use http://localhost%2523stock.weliketoshop.net it sees it as http://localhost#... and not working.

If you add '@' then the filter sees http://[email protected] which is the accepted hostname. Now if we use http://localhost%[email protected] that passes the filter and the web server directs it to http://localhost#...

The mysterious part is why http://localhost%[email protected]/admin works. It looks like there's another filter that strips everything from %25 until it sees / and concatenates, which leads tohttp://localhost/admin. The normal web server should ignore /admin at the end as it's after the # segment.

I enumerated all the characters and discovered that %252f and $253f also work which are / and ? respectively. I tried http://localhost%252fadmin%[email protected] which still passes the filter and makes more sense but again the result was that everything after %25 was stripped so it goes to http://localhost only.

My conclusion is that Portswigger created a custom filter for the sake of the tutorial which doesn't exactly resemble a real-world SSRF filter.

You must log in to answer this question.

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