flawopen.com/ssrf/C-cpp
How libcurl requests allow internal network pivots in C/C++, and how to enforce IP address validation with CURLOPT_RESOLVE.
कल्पना करें कि आप एक सहायक को सार्वजनिक दुकान से पार्सल लाने के लिए भेजते हैं, लेकिन उसे प्रबंधक के कार्यालय में रखी बंद तिजोरी का पता देते हैं। चूंकि सहायक के पास आंतरिक सुरक्षा बैज है, वह तिजोरी खोलकर कंपनी के रहस्य आपको सौंप देता है।
Web Application SecurityCWE-918.CWE-918CWE-918): Standard Common Weakness Enumeration classification for ssrf-c-cpp.Defense-in-Depthएप्लिकेशन इमेज फ़ेचिंग, वेबहुक या पीडीएफ़ जनरेशन के लिए उपयोगकर्ता द्वारा दिया गया URL स्वीकार करता है।
हमलावर क्लाउड मेटाडेटा सेवा (उदा. http://169.254.169.254/latest/meta-data/) या लूपबैक एड्रेस इनपुट करता है।
सर्वर बिना आईपी सत्यापन के आंतरिक वीपीसी नेटवर्क से अनुरोध भेज देता है।
आंतरिक मेटाडेटा सेवा सर्वर के अनुरोध पर विश्वास करके अस्थायी आईएएम टोकन और गुप्त कुंजियाँ लौटा देती है।
# VULNERABLE: Naive requests.get to user-provided URL
import requests
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/fetch-avatar')
def fetch_avatar():
url = request.args.get('url')
# Attacker passes: http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Server queries AWS metadata and returns cloud credentials!
resp = requests.get(url, timeout=5)
return resp.content, 200, {'Content-Type': resp.headers.get('Content-Type', 'image/png')}
# HARDENED: Resolve DNS, parse IP address, deny private/link-local CIDRs
import socket
import ipaddress
import urllib.parse
import requests
from flask import Flask, request, abort
app = Flask(__name__)
BLOCKED_NETWORKS = [
ipaddress.ip_network('127.0.0.0/8'), # Loopback
ipaddress.ip_network('10.0.0.0/8'), # RFC 1918 Private
ipaddress.ip_network('172.16.0.0/12'), # RFC 1918 Private
ipaddress.ip_network('192.168.0.0/16'), # RFC 1918 Private
ipaddress.ip_network('169.254.0.0/16'), # Link-local / Cloud IMDS
ipaddress.ip_network('::1/128'), # IPv6 Loopback
ipaddress.ip_network('fc00::/7'), # IPv6 Private ULA
]
def is_safe_ip(ip_str):
ip = ipaddress.ip_address(ip_str)
return not any(ip in net for net in BLOCKED_NETWORKS)
@app.route('/fetch-avatar')
def fetch_avatar():
url = request.args.get('url', '')
parsed = urllib.parse.urlparse(url)
if parsed.scheme not in ('http', 'https') or not parsed.hostname:
abort(400, "Invalid URL scheme")
# 1. Resolve all DNS IPs for hostname
try:
addr_info = socket.getaddrinfo(parsed.hostname, parsed.port or (443 if parsed.scheme == 'https' else 80))
resolved_ips = [ai[4][0] for ai in addr_info]
except socket.gaierror:
abort(400, "Unable to resolve host")
# 2. Verify all resolved IPs are public
for ip in resolved_ips:
if not is_safe_ip(ip):
abort(403, "Access to internal IP address is prohibited")
# 3. Fetch with redirects disabled to prevent DNS rebinding or redirect bypass
resp = requests.get(url, timeout=3, allow_redirects=False)
return resp.content, 200, {'Content-Type': resp.headers.get('Content-Type', 'image/png')}
allow_redirects=False) को अक्षम या प्रतिबंधित करें।