subscan/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
#![forbid(unsafe_code)]
//! <!-- markdownlint-disable MD033 MD041 -->
//! <div align="center">
//! <picture>
//! <source media="(prefers-color-scheme: dark)" srcset="https://github.com/eredotpkfr/subscan/blob/main/assets/logo-light.png?raw=true">
//! <img alt="Subscan Logo" height="105px" src="https://github.com/eredotpkfr/subscan/blob/main/assets/logo-dark.png?raw=true">
//! </picture>
//! </div>
//! <br>
//! <p align="center">
//! <a href="https://github.com/eredotpkfr/subscan/?tab=readme-ov-file#install">Install</a> •
//! <a href="https://github.com/eredotpkfr/subscan/?tab=readme-ov-file#usage">Usage</a> •
//! <a href="https://docs.rs/subscan/latest/subscan/">Doc</a> •
//! <a href="https://www.erdoganyoksul.com/subscan/">Book</a> •
//! <a href="https://github.com/eredotpkfr/subscan/?tab=readme-ov-file#docker">Docker</a> •
//! <a href="https://github.com/eredotpkfr/subscan/?tab=readme-ov-file#development">Development</a>
//! </p>
//! <!-- markdownlint-enable MD033 MD041 -->
//!
//! Subscan is a powerful subdomain enumeration tool built with
//! [Rust](https://www.rust-lang.org/), specifically designed for penetration testing purposes.
//! It combines various discovery techniques into a single, lightweight binary, making
//! subdomain hunting easier and faster for security researchers
/// In-memory cache to store all modules
pub mod cache;
/// Includes CLI components
pub mod cli;
/// Project constants
pub mod constants;
/// Enumerations and project type definitions
pub mod enums;
/// Subscan error type
pub mod error;
/// Data extractors like
/// [`extractors::regex`], [`extractors::html`], etc.
pub mod extractors;
/// Trait implementations
pub mod interfaces;
/// Logger utilities
pub mod logger;
/// All modules listed under this module, core components for subscan
pub mod modules;
/// `Subscan` worker pool definitions, allows to run modules as asynchronously
pub mod pools;
/// HTTP requesters listed under this module
/// like [`requesters::chrome`], [`requesters::client`], etc.
pub mod requesters;
/// IP address resolver component
pub mod resolver;
/// Project core type definitions
pub mod types;
/// Utilities for the handle different stuff things
pub mod utilities;
use constants::LOG_TIME_FORMAT;
use resolver::Resolver;
use tokio::sync::OnceCell;
use crate::{
cache::CacheManager,
cli::Cli,
interfaces::module::SubscanModuleInterface,
pools::{brute::SubscanBrutePool, module::SubscanModulePool},
types::{config::subscan::SubscanConfig, core::SubscanModule, result::subscan::SubscanResult},
};
static INIT: OnceCell<()> = OnceCell::const_new();
/// Main [`Subscan`] object definition
#[derive(Default)]
pub struct Subscan {
/// Subscan configurations
pub config: SubscanConfig,
/// Cache manager instance to manage modules cache
pub manager: CacheManager,
}
impl From<Cli> for Subscan {
fn from(cli: Cli) -> Self {
Self {
config: cli.into(),
manager: CacheManager::default(),
}
}
}
impl From<SubscanConfig> for Subscan {
fn from(config: SubscanConfig) -> Self {
Self {
config,
manager: CacheManager::default(),
}
}
}
impl Subscan {
pub fn new(config: SubscanConfig) -> Self {
Self {
config,
manager: CacheManager::default(),
}
}
async fn init(&self) {
let rconfig = self.config.clone().into();
let inner = || async { self.manager.configure(rconfig).await };
INIT.get_or_init(inner).await;
}
pub async fn module(&self, name: &str) -> &SubscanModule {
self.manager.module(name).await.expect("Module not found!")
}
pub async fn modules(&self) -> &Vec<SubscanModule> {
self.manager.modules().await
}
pub async fn scan(&self, domain: &str) -> SubscanResult {
self.init().await;
let mut result = SubscanResult::from(domain);
let time = result.metadata.started_at.format(LOG_TIME_FORMAT);
let pool = SubscanModulePool::from(domain, self.config.clone());
log::info!("Started scan on {} ({})", domain, time);
pool.clone().start(self.modules().await).await;
result.update_with_pool_result(pool.result().await).await;
result.with_finished().await
}
pub async fn run(&self, name: &str, domain: &str) -> SubscanResult {
let mut result = SubscanResult::from(domain);
let time = result.metadata.started_at.format(LOG_TIME_FORMAT);
let pool = SubscanModulePool::from(domain, self.config.clone());
let module = self.module(name).await;
let rconfig = self.config.clone().into();
module.lock().await.configure(rconfig).await;
log::info!(
"Running {} module on {} ({})",
module.lock().await.name().await,
domain,
time
);
pool.clone().start(&vec![module.clone()]).await;
result.update_with_pool_result(pool.result().await).await;
result.with_finished().await
}
pub async fn brute(&self, domain: &str) -> SubscanResult {
let mut result = SubscanResult::from(domain);
let time = result.metadata.started_at.format(LOG_TIME_FORMAT);
let concurrency = self.config.resolver.concurrency;
let resolver = Resolver::boxed_from(self.config.resolver.clone());
let pool = SubscanBrutePool::new(domain.into(), concurrency, resolver);
let wordlist = self
.config
.wordlist
.clone()
.expect("Wordlist must be specified!");
log::info!("Started brute force attack on {} ({})", domain, time);
pool.clone().start(wordlist).await;
result.update_with_pool_result(pool.result().await).await;
result.with_finished().await
}
}