use std::{collections::HashSet, fmt, iter::FromIterator};
use crate::{
report::{Finding, Severity},
Error,
};
use async_trait::async_trait;
use reqwest::Client;
use serde::{Deserialize, Serialize};
mod http;
mod subdomains;
pub fn all_http_modules() -> Vec<Box<dyn HttpModule>> {
return vec![
Box::new(http::DsStoreDisclosure::new()),
Box::new(http::DotEnvDisclosure::new()),
Box::new(http::DirectoryListingDisclosure::new()),
Box::new(http::TraefikDashboardUnauthenticatedAccess::new()),
Box::new(http::PrometheusDashboardUnauthenticatedAccess::new()),
Box::new(http::KibanaUnauthenticatedAccess::new()),
Box::new(http::GitlabOpenRegistrations::new()),
Box::new(http::GitHeadDisclosure::new()),
Box::new(http::GitDirectoryDisclosure::new()),
Box::new(http::GitConfigDisclosure::new()),
Box::new(http::EtcdUnauthenticatedAccess::new()),
Box::new(http::Cve2017_9506::new()),
Box::new(http::Cve2018_7600::new()),
Box::new(http::ElasticsearchUnauthenticatedAccess::new()),
];
}
pub fn get_http_modules(modules: &Vec<ModuleName>) -> Vec<Box<dyn HttpModule>> {
let modules: HashSet<ModuleName> = HashSet::from_iter(modules.iter().cloned());
all_http_modules()
.into_iter()
.filter(|module| modules.contains(&module.name()))
.collect()
}
pub fn all_subdomains_modules() -> Vec<Box<dyn SubdomainModule>> {
return vec![
Box::new(subdomains::Crtsh::new()),
Box::new(subdomains::WebArchive::new()),
];
}
pub fn get_subdomains_modules(modules: &Vec<ModuleName>) -> Vec<Box<dyn SubdomainModule>> {
let modules: HashSet<ModuleName> = HashSet::from_iter(modules.iter().cloned());
all_subdomains_modules()
.into_iter()
.filter(|module| modules.contains(&module.name()))
.collect()
}
#[derive(Debug, Clone, Eq, PartialEq, Copy, Deserialize, Serialize, Hash)]
#[serde(rename_all = "snake_case")]
pub enum ModuleName {
SubdomainsCrtsh,
SubdomainsWebArchive,
HttpCve2017_9506,
HttpCve2018_7600,
HttpDirectoryListingDisclosure,
HttpDotenvDisclosure,
HttpDsStoreDisclosure,
HttpElasticsearchUnauthenticatedAccess,
HttpEtcdUnauthenticatedAccess,
HttpGitConfigDisclosure,
HttpGitDirectoryDisclosure,
HttpGitHeadDisclosure,
HttpGitlabOpenRegistration,
HttpKibanaUnauthenticatedAccess,
HttpPrometheusDashboardUnauthenticatedAccess,
HttpTraefikDashboardUnauthenticatedAccess,
}
impl fmt::Display for ModuleName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ModuleName::SubdomainsCrtsh => write!(f, "subdomains/crtsh"),
ModuleName::SubdomainsWebArchive => write!(f, "subdomains/web_archive"),
ModuleName::HttpCve2017_9506 => write!(f, "http/cve_2017_9506"),
ModuleName::HttpCve2018_7600 => write!(f, "http/cve_2018_7600"),
ModuleName::HttpDirectoryListingDisclosure => {
write!(f, "http/directory_listing_disclosure")
}
ModuleName::HttpDotenvDisclosure => write!(f, "http/dotenv_disclosure"),
ModuleName::HttpDsStoreDisclosure => write!(f, "http/ds_store_disclosure"),
ModuleName::HttpElasticsearchUnauthenticatedAccess => {
write!(f, "http/elasticsearch_unauthenticated_access")
}
ModuleName::HttpEtcdUnauthenticatedAccess => {
write!(f, "http/etcd_unauthenticated_access")
}
ModuleName::HttpGitConfigDisclosure => write!(f, "http/git_config_disclosure"),
ModuleName::HttpGitDirectoryDisclosure => write!(f, "http/git_directory_disclosure"),
ModuleName::HttpGitHeadDisclosure => write!(f, "http/git_head_disclosure"),
ModuleName::HttpGitlabOpenRegistration => write!(f, "http/gitlab_open_registration"),
ModuleName::HttpKibanaUnauthenticatedAccess => {
write!(f, "http/kibana_unauthenticated_access")
}
ModuleName::HttpPrometheusDashboardUnauthenticatedAccess => {
write!(f, "http/prometheus_dashboard_unauthenticated_access")
}
ModuleName::HttpTraefikDashboardUnauthenticatedAccess => {
write!(f, "http/traefik_dashboard_unauthenticated_access")
}
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ModuleVersion(u8, u8, u8);
impl fmt::Display for ModuleVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}.{}.{}", self.0, self.1, self.2)
}
}
pub trait Module {
fn name(&self) -> ModuleName;
fn version(&self) -> ModuleVersion;
fn description(&self) -> String;
fn severity(&self) -> Severity;
fn is_aggressive(&self) -> bool;
}
#[async_trait]
pub trait SubdomainModule: Module {
async fn enumerate(&self, domain: &str) -> Result<Vec<String>, Error>;
}
#[async_trait]
pub trait HttpModule: Module {
async fn scan(&self, http_client: &Client, endpoint: &str) -> Result<Option<Finding>, Error>;
}