< back

A case-study of SSRF with whitelist-based input filter lab

A case-study of SSRF with whitelist-based input filter lab

This is just a “case-study”/observations/notes dump on the Portswigger lab SSRF with whitelist-based input filter.

After much experimentation of adding . @ # and reordering the positioning of target url and whitelist, the SSRF poc to access admin panel is: stockApi=http%3a//localhost%252f%40stock.weliketoshop.net%3a8080/admin

Seems like there is a parser differential of how the backend services handle stockApi. This lab is most probably inspired by OrangeTsai new era of SSRF presentation.

The first parsing layer most probably checks host defined after the @ user credential delimiter, in this case stock.weliketoshop.net:8080

The request is most probably handled by another http library that will validate the host in the http request sent by first parsing library

I will use parser, http library, and http handler interchangeably for this blog.

Based on my observations, if our payload is http://localhost@stock.weliketoshop.net:8080, then the first parser will take the host to be weliketoshop.net:8080 but it is hard to tell what the second parser will take the host to be (either localhost or stock.weliketoshop.net:8080).

But if we add a / after localhost? Will the second parser consider the path of localhost to be /@stock.weliketoshop.net:8080?

Based on the behaviour, it seems to be the case. Note that we will need to URL encode our / for each layer the request is sent internally as the request library will automatically decode onetime per request handle

In this case, we double url encode / to %252f. If our payload is http://localhost%252f@stock.weliketoshop.net:8080, then the first parser will take the host to be weliketoshop.net:8080 and the second parser will take the host to be localhost and the path to be /@stock.weliketoshop.net:8080

Note that, this payload will return the same shop page but this time we see the internal page to be /admin. This indicates that we managed to perform SSRF to access the web service running locally on the server on localhost.

We can try to access the admin panel by adding admin after %252f as such stockApi=http://localhost%252fadmin%252fadmin@stock.weliketoshop.net:8080 but it will not work.

Instead the following will work, stockApi=http://localhost%252f@stock.weliketoshop.net:8080/admin

I thought that this behaviour raises some question to our initial hypothesis of second parser ignoring what comes after / path delimiter

My assumption is that the whitelist mechanism is based on host. The backend implementation most probably extracted the host from the request object and then check against a whitelist.

Since we have stockApi=http://localhost%252f@stock.weliketoshop.net:8080/admin, my assumption is the following:

  1. first parser will take the host to be weliketoshop.net:8080 and check against a whitelist
  2. second parser will take the host to be localhost and the path to be /admin and not /@stock.weliketoshop.net:8080/admin

Maybe the first request handler sees the path as /admin and forwards the request to second handler. From there, the second handler will take the host to be localhost/ and the path to be /admin as specified by the first handler.

Anyways, on hindsight, using a fragment delimiter # works too instead of /. I guess as long as we can get the second parser to ignore the stock.weliketoshop.net:8080

Nicholas Ch • © 2025