flawopen.com/ssrf/Rust

● CWE-918 · उच्च
सुरक्षा अनुसंधान · FlawOpen

सुरक्षा भेद्यता: Server-Side Request Forgery in Rust

How reqwest allows internal network queries in Rust, and how to validate resolved socket addresses before connecting.

💡 आसान भाषा में (ELI5)

कल्पना करें कि आप एक सहायक को सार्वजनिक दुकान से पार्सल लाने के लिए भेजते हैं, लेकिन उसे प्रबंधक के कार्यालय में रखी बंद तिजोरी का पता देते हैं। चूंकि सहायक के पास आंतरिक सुरक्षा बैज है, वह तिजोरी खोलकर कंपनी के रहस्य आपको सौंप देता है।

इस पेज के मुख्य शब्द

Web Application Security
सुरक्षा अवधारणा (Web Application Security): Core architecture component affected by CWE-918.
CWE-918
सुरक्षा अवधारणा (CWE-918): Standard Common Weakness Enumeration classification for ssrf-rust.
Defense-in-Depth
सुरक्षा अवधारणा (Defense-in-Depth): Multi-layered engineering verification and runtime boundary isolation.

हमले का चरण-दर-चरण प्रवाह

Step 1

अविश्वसनीय रिमोट URL इनपुट

एप्लिकेशन इमेज फ़ेचिंग, वेबहुक या पीडीएफ़ जनरेशन के लिए उपयोगकर्ता द्वारा दिया गया URL स्वीकार करता है।

Step 2

आंतरिक नेटवर्क एड्रेसिंग

हमलावर क्लाउड मेटाडेटा सेवा (उदा. http://169.254.169.254/latest/meta-data/) या लूपबैक एड्रेस इनपुट करता है।

Step 3

अनियंत्रित सॉकेट आउटबाउंड कनेक्शन

सर्वर बिना आईपी सत्यापन के आंतरिक वीपीसी नेटवर्क से अनुरोध भेज देता है।

Step 4

क्लाउड क्रेडेंशियल चोरी

आंतरिक मेटाडेटा सेवा सर्वर के अनुरोध पर विश्वास करके अस्थायी आईएएम टोकन और गुप्त कुंजियाँ लौटा देती है।

सोर्स कोड: कमज़ोर बनाम सुरक्षित कार्यान्वयन

✕ कमज़ोर कार्यान्वयन
// VULNERABLE: Unchecked reqwest::get
use reqwest;

async fn fetch_remote_resource(user_url: &str) -> Result<Vec<u8>, reqwest::Error> {
    // Attacker passes: http://169.254.169.254/latest/meta-data/
    let resp = reqwest::get(user_url).await?;
    let bytes = resp.bytes().await?;
    Ok(bytes.to_vec())
}
✓ सुरक्षित और सुदृढ़ फ़िक्स
// HARDENED: Resolve DNS, assert non-private IP, and disable redirects
use std::net::{IpAddr, ToSocketAddrs};
use reqwest::redirect::Policy;
use url::Url;

fn is_safe_ip(ip: IpAddr) -> bool {
    match ip {
        IpAddr::V4(ipv4) => {
            !ipv4.is_loopback() &&
            !ipv4.is_private() &&
            !ipv4.is_link_local() &&
            !ipv4.is_unspecified()
        }
        IpAddr::V6(ipv6) => {
            !ipv6.is_loopback() &&
            !ipv6.is_unspecified()
        }
    }
}

async fn fetch_remote_resource(user_url: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    let parsed_url = Url::parse(user_url)?;
    if parsed_url.scheme() != "http" && parsed_url.scheme() != "https" {
        return Err("Invalid URL scheme".into());
    }
    
    let host = parsed_url.host_str().ok_or("Missing host")?;
    let port = parsed_url.port_or_known_default().unwrap_or(80);
    
    // 1. Resolve host and inspect all IP addresses
    let socket_addrs: Vec<_> = format!("{}:{}", host, port).to_socket_addrs()?.collect();
    for sa in &socket_addrs {
        if !is_safe_ip(sa.ip()) {
            return Err("Access to private/internal IP rejected".into());
        }
    }
    
    // 2. Build client with redirects disabled
    let client = reqwest::Client::builder()
        .redirect(Policy::none())
        .timeout(std::time::Duration::from_secs(3))
        .build()?;
        
    let resp = client.get(parsed_url).send().await?;
    let bytes = resp.bytes().await?;
    Ok(bytes.to_vec())
}

इंजीनियरिंग और सिस्टम सुरक्षा चेकलिस्ट

References