blst-security / cherrybomb

Stop half-done APIs! Cherrybomb is a CLI tool that helps you avoid undefined user behaviour by auditing your API specifications, validating them and running API security tests.
https://www.blstsecurity.com/cherrybomb
Apache License 2.0
1.08k stars 78 forks source link

SSRF active check #61

Open RazMag opened 1 year ago

RazMag commented 1 year ago

This check was written by @DeliciousBounty, waiting on improvements to GET requests that should be added soon and will make these easier to write and test

pub async fn check_for_ssrf(&self, auth: &Authorization) -> (CheckRetVal, Vec<String>) {
        let mut ret_val = CheckRetVal::default();
        let mut provider_vec = vec![];
        let provider_hash = HashMap::from([
            ("Amazon", "http://169.254.169.254/"),
            ("Google", "http://169.254.169.254/computeMetadata/v1/"),
            ("Digital", "http://169.254.169.254/metadata/v1.json"),
            ("Azure", "http://169.254.169.254/metadata/v1/maintenance"),
        ]);
        let base_url = self.oas.servers().unwrap().get(0).unwrap().clone(); //todo ouch
        for (path, item) in &self.oas.get_paths() {
            for (m, op) in item.get_ops().iter().filter(|(m, _)| m == &Method::GET) {

                let mut param_is_good_to_send = false;

                for (provider_item, value_to_send) in &provider_hash {
                    let mut params_vec = vec![];
                    let payload_get_param = create_payload_for_get(
                        &self.oas_value,
                        op,
                        Some(value_to_send.to_string()),
                    );
                    for parameter_item in payload_get_param {
                        if parameter_item.dm == QuePay::Query {
                            if LIST_PARAM.contains(&parameter_item.name.as_str()) {
                                params_vec.push(parameter_item);
                                param_is_good_to_send = true;
                            }
                        } else {
                            params_vec.push(parameter_item);
                        }
                    }

                    if param_is_good_to_send {
                        provider_vec.push(provider_item.to_string());
                        println!("SSRF GET: ----");
                        let req = AttackRequest::builder()
                            .uri(&base_url.url, path)
                            .parameters(params_vec.clone())
                            .auth(auth.clone())
                            .method(m.to_owned())
                            .headers(vec![])
                            .auth(auth.clone())
                            .build();

                        if let Ok(res) = req.send_request(true).await {
                            //logging
                            //logging request/response/description
                            ret_val
                                .1
                                .push(&req, &res, "Testing ssrf for get ".to_string());
                            ret_val.0.push((
                                ResponseData{
                                    location: path.clone(),
                                    alert_text: format!("The parameter {:?} seems to be vulerable to open-redirect on the {} endpoint",&params_vec.last().unwrap(),path)//TODO Chekc if is it the correct parameter
                                },
                            res.clone(),
                            ));
                        } else {
                            println!("REQUEST FAILED");
                        }
                    }

                }
            }
        }
        (ret_val, provider_vec)
    }

    pub async fn check_ssrf_post(&self, auth: &Authorization) -> (CheckRetVal, Vec<String>) {
        println!("-------------------------POST SSRF-----------------------");
        let mut ret_val = CheckRetVal::default();
        let mut provider_vec = vec![];
        let provider_hash = HashMap::from([
            ("Amazon", "http://169.254.169.254/"),
            ("Google", "http://169.254.169.254/computeMetadata/v1/"),
            ("Digital", "http://169.254.169.254/metadata/v1.json"),
            ("Azure", "http://169.254.169.254/metadata/v1/maintenance"),
        ]);
        for oas_map in self.payloads.iter() {
            for json_path in oas_map.payload.map.keys() {
                for (m, _) in oas_map
                    .path
                    .path_item
                    //.filter(|| path_item==p)
                    .get_ops()
                    .iter()
                    .filter(|(m, _)| m == &Method::POST)
                //947
                {
                    let param_to_test =
                        &json_path.last().unwrap_or(&"empty".to_string()).to_owned()[..];
                    if LIST_PARAM.contains(&param_to_test) {
                        for (provider_item, provider_value) in &provider_hash {
                            if let Some(server) = self.oas.servers().and_then(|servers| servers.first().cloned()){

                                provider_vec.push(provider_item.to_string());
                                let req = AttackRequest::builder()
                                    .uri(&server.url, &oas_map.path.path)
                                    .method(*m)
                                    .headers(vec![])
                                    .parameters(vec![])
                                    .auth(auth.clone())
                                    .payload(
                                        &change_payload(
                                            &oas_map.payload.payload,
                                            json_path,
                                            json!(provider_value),
                                        )
                                        .to_string(),
                                    )
                                    .build();

                                print!("POST SSRF : ");

                                if let Ok(res) = req.send_request(true).await {
                                    //logging request/response/description
                                    ret_val
                                        .1
                                        .push(&req, &res, "Testing SSRF VALUES".to_string());
                                    ret_val.0.push((
                                        ResponseData {
                                            location: oas_map.path.path.clone(),
                                            alert_text: format!(
                                                "This {} parameter on the {} endpoint seems to be vulerable to ssrf.", json_path[json_path.len() - 1],&param_to_test// json_path[json_path.len() - 1]
                                            ),
                                        },
                                        res.clone(),
                                    ));
                                    println!(
                                        "{}:{}",
                                        "Status".green().bold(),
                                        res.status.to_string().magenta()
                                    );
                                } else {
                                    println!("REQUEST FAILED");
                                }
                            }
                        }
                    }
                }
            }
        }
        (ret_val, provider_vec)
    }