October 27, 2026
I Bypassed a Whitelist-Based SSRF Filter PortSwigger Expert Lab Walkthrough
I Bypassed a Whitelist-Based SSRF Filter โ PortSwigger Expert Lab Walkthrough

By Sahariarhasan
5 min read
One URL, a few parsing experiments, and a double-encoded character. A practical lesson in why URL validation can fail.
A whitelist is supposed to be a strong line of defense.
If an application only allows requests to trusted hosts, an attacker should not be able to redirect its backend toward an internal admin panel simply by changing a parameter.
But what happens when the application and the component processing the URL don't interpret that URL in exactly the same way?
That was the central challenge in PortSwigger Web Security Academy's SSRF with whitelist-based input filter lab.
The objective was to bypass the application's SSRF defense, access the internal admin interface at http://localhost/admin, and delete the user carlos.
The interesting part wasn't just finding a working payload. It was understanding how the application's responses revealed clues about its URL parsing and validation behavior.
Lab details
- Platform: PortSwigger Web Security Academy
- Vulnerability: Server-Side Request Forgery (SSRF)
- Difficulty: Expert
- Tool: Burp Suite
- Status: Solved
1. Finding the attack surface
I started by opening a product page and clicking Check stock.
The application used this functionality to retrieve inventory information from another service. I intercepted the request in Burp Suite and sent it to Repeater, where I could modify the destination and compare the responses.
The parameter of interest was stockApi.
This gave me a controlled way to investigate how the backend handled user-influenced URLs.
My first question was:
Is the application validating the actual destination, or is it relying on a hostname extracted from the supplied URL?
That distinction matters because a URL contains several components, and security decisions can go wrong when those components are interpreted inconsistently.
2. First test: Can I reach localhost directly?
I replaced the stock API URL with:
The application rejected the request.
This was my first useful observation: a direct request to the loopback address was blocked by the filter.
However, I didn't treat this as proof that every route to localhost was impossible. It only showed that this particular representation was rejected.
The next step was to investigate how the application parsed URLs and decided whether a hostname was allowed.
In security testing, a blocked payload is not necessarily a dead end. It can help narrow down the application's validation logic.
[Screenshot 3 โ The direct loopback request and its rejection response.]
3. The @ character changes the picture
Next, I tried:
http://username@stock.weliketoshop.net/
This request was accepted.
Why was this interesting?
In standard URL syntax, the @ character separates user information from the host component. The URL can be broken down as follows:
ComponentValueSchemehttpUser informationusernameHostnamestock.weliketoshop.netPath/
The hostname still belonged to the application's trusted domain, so the URL passed the filter.
This revealed that the application accepted a URL containing embedded user information. It also gave me a new direction to investigate: whether special characters could alter the way the URL was interpreted.
One important distinction: acceptance of this URL did not, by itself, prove that the whitelist was vulnerable. It was a clue about the parser's behavior.
[Screenshot 4 โ The URL containing embedded credentials and the accepted response.]
4. Introducing a URL fragment
I then changed the URL to:
http://username#@stock.weliketoshop.net/
The application rejected it.
The reason this experiment mattered is that # is a URL fragment delimiter. Under standard URL parsing, the fragment begins at the first literal #, and the characters following it are not ordinarily part of the host component.
That changes how the URL is interpreted.
The rejection suggested that introducing this delimiter affected the validation process. Rather than guessing more hostnames, I focused on whether encoding the delimiter would change the behavior.
This was the turning point in the investigation: I was no longer testing only which hostname the application allowed. I was testing how it interpreted the URL.
[Screenshot 5 โ The URL containing the literal fragment delimiter and the rejection response.]
5. Double URL encoding: a different response
The next experiment involved the fragment character.
Its URL-encoded representation is %23. Encoding the percent sign again gives %2523.
I tested the following URL:
http://username%2523@stock.weliketoshop.net/
This time, the application returned an Internal Server Error.
That was a different outcome from the explicit rejection I had seen earlier.
A server error is not proof of a successful SSRF attack, and it doesn't establish exactly which host the backend attempted to contact. But the change in behavior was a useful clue: the modified input was triggering a different processing path.
The key concept here is decoding order.
Consider these representations:
-
โ the literal fragment delimiter
%23โ its URL-encoded representation%2523โ an encoded percent sign followed by23, which can become%23after one decoding pass
If different components decode or parse the URL at different stages, they may not agree on its meaning.
That inconsistency can undermine security checks that depend on a single interpretation of the destination.
6. Putting the observations together
At this point, I had gathered several pieces of evidence:
- A direct request to
127.0.0.1was rejected. - A URL containing embedded user information was accepted.
- Introducing a literal # changed the result.
- Double encoding the delimiter produced a different error response.
Together, these observations suggested that the application's URL validation and subsequent request processing were sensitive to parsing and encoding behaviour.
The next step was to apply that understanding to the lab's intended target: the internal admin interface.
The goal was not simply to make the filter accept a string. It was to get the backend to process a request that reached the intended internal endpoint.
7. Reaching the admin endpoint
The final stockApi value was:
http://localhost:80%2523@stock.weliketoshop.net/admin/delete?username=carlos
I submitted the request in Burp Repeater.
The payload combined the internal destination, the URL structure, and the double-encoded delimiter explored in the previous steps.
The lab completed successfully, confirming that the input-filter bypass had worked in this environment and that the target user had been deleted.
8. What this lab actually teaches
The biggest lesson is not that a particular encoded string bypasses a filter. It is that security validation must agree with the component that ultimately uses the input.
A whitelist can look correct while still being vulnerable if it validates one interpretation of a URL and the HTTP client processes another.
There are several defensive lessons here.
Validate structured URLs, not arbitrary strings. Parse the URL with a reliable library and validate the normalized scheme, hostname, and port against a strict allowlist.
Reject unnecessary ambiguity. If embedded credentials, malformed URLs, or unusual encoding sequences are not required, reject them instead of attempting to sanitize them.
Control where the server can connect. Network-level egress restrictions should prevent application components from reaching sensitive internal services, even if application-level validation fails.
Handle redirects and DNS resolution carefully. Validation must account for the actual destination contacted, not just the initial hostname supplied by the user.
Protect internal administrative functionality independently. An internal endpoint should still enforce authentication and authorization. Being reachable only from an internal network is not an adequate security boundary.
Final thoughts
This lab was a useful reminder that SSRF testing is about more than replacing a hostname with localhost.
The more valuable skill is learning to reason from application behaviour:
- Establish a baseline.
- Change one part of the input at a time.
- Compare rejection, acceptance, and error responses.
- Form a hypothesis about how the input is parsed.
- Test that hypothesis before drawing conclusions.
The double-encoding experiment was particularly instructive because it changed the application's response and provided a new lead. The final success demonstrated why seemingly small differences in URL interpretation can have significant security consequences.
For me, the takeaway is simple: don't just test whether a filter blocks a payload. Investigate what the application is actually validating.
This walkthrough covers an intentionally vulnerable PortSwigger Web Security Academy lab. Only test systems you own or have explicit permission to assess.