SSRF Filter Bypasses

Server-Side Request Forgery (SSRF) vulnerabilities occur when an attacker can coerce the server to fetch remote resources using HTTP requests; this might allow an attacker to identify and enumerate services running on the local network of the web server, which an external attacker would generally be unable to access due to a firewall blocking access.


Confirming SSRF

Let us consider the following vulnerable web application to illustrate how a developer might address SSRF vulnerabilities.

Code Review - Identifying the Vulnerability

Our sample web application allows us to take screenshots of websites for which we provide URLs.

The web application contains two endpoints. The first one handles taking screenshots, while the second endpoint responds with a debug page and is only accessible from localhost:

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        return render_template('index.html')
   
    try:
        screenshot = screenshot_url(request.form.get('url'))
    except Exception as e:
        return f'Error: {e}', 400
    # b64 encode image
    image = Image.open(screenshot)
    buffered = BytesIO()
    image.save(buffered, format="PNG")
    img_data = base64.b64encode(buffered.getvalue())
    return render_template('index.html', screenshot=img_data.decode('utf-8'))

@app.route('/debug')
def debug():
    if request.remote_addr != '127.0.0.1':
            return 'Unauthorized!', 401
    return render_template('debug.html')

Since our target is to obtain unauthorized access to the debug page, we need to bypass the check in the /debug endpoint. However, we cannot manipulate the request.remote_addr variable, as this represents the IP address from which the request originates (i.e., our external IP address).

Screenshot Function

The web application performs basic checks including the scheme (blocking file://), but does not restrict the domain or IP address.

Exploitation

Since the web application only restricts us to the http and https schemes but does not restrict the domain or IP address, we can provide a URL pointing to the /debug endpoint:

The web application will visit its own debug endpoint such that the request originates from 127.0.0.1, granting access.


SSRF Basic Filter Bypasses

1. Obfuscation of localhost

The simplest SSRF filter explicitly blocks certain domains like localhost or 127.0.0.1:

Bypass Methods - Many ways exist to represent localhost:

Method
Value

Localhost Address Block

127.0.0.0 - 127.255.255.255

Shortened IP Address

127.1

Prolonged IP Address

127.000000000000000.1

All Zeroes

0.0.0.0

Shortened All Zeroes

0

Decimal Representation

2130706433

Octal Representation

0177.0000.0000.0001

Hex Representation

0x7f000001

IPv6 loopback address

0:0:0:0:0:0:0:1 (also ::1)

IPv4-mapped IPv6 loopback

::ffff:127.0.0.1

Example bypass:


2. Bypass via DNS Resolution

Improved filter that blocks private IP ranges:

Problem: The filter only blocks IP addresses, not domain names that resolve to private IPs.

Bypass: Use a domain that resolves to 127.0.0.1:

Example bypass:


3. Bypass via HTTP Redirect

Further improved filter that resolves domain names:

Problem: The filter does not account for HTTP redirects.

Bypass: Host a redirect on your server:

Then provide your server URL:

Note: Blocking redirects completely is difficult. Other redirect methods exist: JavaScript, meta tags, etc. Even if all redirects are prevented, DNS rebinding can still bypass the filter.


Prevention

The simplest and safest way to prevent SSRF is via firewall rules. The system running the vulnerable application should be separated from internal web applications, with firewall rules preventing incoming connections from the vulnerable system to internal services.


Question Walkthrough

Task: Bypass the SSRF filter to obtain the flag. The staging environment is behind a firewall that blocks all outgoing web requests.

Code Analysis

Download and analyze the source:

The /flag endpoint only allows localhost:

The check_domain function:

Vulnerability

The is_loopback function does not consider 0.0.0.0 as a loopback address (which includes all IPv4 addresses on a local machine, including 127.0.0.1).

Exploit

Or simply use 0:

Last updated