mirror of
https://github.com/epi052/feroxbuster.git
synced 2026-06-06 17:31:12 -03:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8775e3c8c | ||
|
|
427efdef3b | ||
|
|
45815ff796 | ||
|
|
0dbc3bee23 | ||
|
|
9e143d9f19 | ||
|
|
bd2bd2035c | ||
|
|
6e71f4e039 | ||
|
|
f5229a1ddd |
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "feroxbuster"
|
name = "feroxbuster"
|
||||||
version = "1.5.0"
|
version = "1.5.2"
|
||||||
authors = ["Ben 'epi' Risher <epibar052@gmail.com>"]
|
authors = ["Ben 'epi' Risher <epibar052@gmail.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
@@ -33,6 +33,7 @@ openssl = { version = "0.10", features = ["vendored"] }
|
|||||||
dirs = "3.0"
|
dirs = "3.0"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
crossterm = "0.18"
|
crossterm = "0.18"
|
||||||
|
rlimit = "0.5"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.1"
|
tempfile = "3.1"
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ pub type FeroxChannel<T> = (UnboundedSender<T>, UnboundedReceiver<T>);
|
|||||||
/// Version pulled from Cargo.toml at compile time
|
/// Version pulled from Cargo.toml at compile time
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
/// Maximum number of file descriptors that can be opened during a scan
|
||||||
|
pub const DEFAULT_OPEN_FILE_LIMIT: usize = 8192;
|
||||||
|
|
||||||
/// Default wordlist to use when `-w|--wordlist` isn't specified and not `wordlist` isn't set
|
/// Default wordlist to use when `-w|--wordlist` isn't specified and not `wordlist` isn't set
|
||||||
/// in a [ferox-config.toml](constant.DEFAULT_CONFIG_NAME.html) config file.
|
/// in a [ferox-config.toml](constant.DEFAULT_CONFIG_NAME.html) config file.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use feroxbuster::{
|
|||||||
utils::{ferox_print, get_current_depth, module_colorizer, status_colorizer},
|
utils::{ferox_print, get_current_depth, module_colorizer, status_colorizer},
|
||||||
FeroxError, FeroxResponse, FeroxResult, SLEEP_DURATION, VERSION,
|
FeroxError, FeroxResponse, FeroxResult, SLEEP_DURATION, VERSION,
|
||||||
};
|
};
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
use feroxbuster::{utils::set_open_file_limit, DEFAULT_OPEN_FILE_LIMIT};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
@@ -294,6 +296,10 @@ fn main() {
|
|||||||
// setup logging based on the number of -v's used
|
// setup logging based on the number of -v's used
|
||||||
logger::initialize(CONFIGURATION.verbosity);
|
logger::initialize(CONFIGURATION.verbosity);
|
||||||
|
|
||||||
|
// this function uses rlimit, which is not supported on windows
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
set_open_file_limit(DEFAULT_OPEN_FILE_LIMIT);
|
||||||
|
|
||||||
if let Ok(mut runtime) = tokio::runtime::Runtime::new() {
|
if let Ok(mut runtime) = tokio::runtime::Runtime::new() {
|
||||||
let future = wrapped_main();
|
let future = wrapped_main();
|
||||||
runtime.block_on(future);
|
runtime.block_on(future);
|
||||||
|
|||||||
117
src/utils.rs
117
src/utils.rs
@@ -1,8 +1,10 @@
|
|||||||
use crate::FeroxResult;
|
use crate::{FeroxError, FeroxResult};
|
||||||
use console::{strip_ansi_codes, style, user_attended};
|
use console::{strip_ansi_codes, style, user_attended};
|
||||||
use indicatif::ProgressBar;
|
use indicatif::ProgressBar;
|
||||||
use reqwest::Url;
|
use reqwest::Url;
|
||||||
use reqwest::{Client, Response};
|
use reqwest::{Client, Response};
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
use rlimit::{getrlimit, setrlimit, Resource, Rlim};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
/// Helper function that determines the current depth of a given url
|
/// Helper function that determines the current depth of a given url
|
||||||
@@ -153,6 +155,27 @@ pub fn format_url(
|
|||||||
extension
|
extension
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if Url::parse(&word).is_ok() {
|
||||||
|
// when a full url is passed in as a word to be joined to a base url using
|
||||||
|
// reqwest::Url::join, the result is that the word (url) completely overwrites the base
|
||||||
|
// url, potentially resulting in requests to places that aren't actually the target
|
||||||
|
// specified.
|
||||||
|
//
|
||||||
|
// in order to resolve the issue, we check if the word from the wordlist is a parsable URL
|
||||||
|
// and if so, don't do any further processing
|
||||||
|
let message = format!(
|
||||||
|
"word ({}) from the wordlist is actually a URL, skipping...",
|
||||||
|
word
|
||||||
|
);
|
||||||
|
log::warn!("{}", message);
|
||||||
|
|
||||||
|
let mut err = FeroxError::default();
|
||||||
|
err.message = message;
|
||||||
|
|
||||||
|
log::trace!("exit: format_url -> {}", err);
|
||||||
|
return Err(Box::new(err));
|
||||||
|
}
|
||||||
|
|
||||||
// from reqwest::Url::join
|
// from reqwest::Url::join
|
||||||
// Note: a trailing slash is significant. Without it, the last path component
|
// Note: a trailing slash is significant. Without it, the last path component
|
||||||
// is considered to be a “file” name to be removed to get at the “directory”
|
// is considered to be a “file” name to be removed to get at the “directory”
|
||||||
@@ -238,10 +261,89 @@ pub async fn make_request(client: &Client, url: &Url) -> FeroxResult<Response> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempts to set the soft limit for the RLIMIT_NOFILE resource
|
||||||
|
///
|
||||||
|
/// RLIMIT_NOFILE is the maximum number of file descriptors that can be opened by this process
|
||||||
|
///
|
||||||
|
/// The soft limit is the value that the kernel enforces for the corresponding resource.
|
||||||
|
/// The hard limit acts as a ceiling for the soft limit: an unprivileged process may set only its
|
||||||
|
/// soft limit to a value in the range from 0 up to the hard limit, and (irreversibly) lower its
|
||||||
|
/// hard limit.
|
||||||
|
///
|
||||||
|
/// A child process created via fork(2) inherits its parent's resource limits. Resource limits are
|
||||||
|
/// per-process attributes that are shared by all of the threads in a process.
|
||||||
|
///
|
||||||
|
/// Based on the above information, no attempt is made to restore the limit to its pre-scan value
|
||||||
|
/// as the adjustment made here is only valid for the scan itself (and any child processes, of which
|
||||||
|
/// there are none).
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub fn set_open_file_limit(limit: usize) -> bool {
|
||||||
|
log::trace!("enter: set_open_file_limit");
|
||||||
|
|
||||||
|
if let Ok((soft, hard)) = getrlimit(Resource::NOFILE) {
|
||||||
|
if hard.as_usize() > limit {
|
||||||
|
// our default open file limit is less than the current hard limit, this means we can
|
||||||
|
// set the soft limit to our default
|
||||||
|
let new_soft_limit = Rlim::from_usize(limit);
|
||||||
|
|
||||||
|
if setrlimit(Resource::NOFILE, new_soft_limit, hard).is_ok() {
|
||||||
|
log::debug!("set open file descriptor limit to {}", limit);
|
||||||
|
|
||||||
|
log::trace!("exit: set_open_file_limit -> {}", true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if soft != hard {
|
||||||
|
// hard limit is lower than our default, the next best option is to set the soft limit as
|
||||||
|
// high as the hard limit will allow
|
||||||
|
if setrlimit(Resource::NOFILE, hard, hard).is_ok() {
|
||||||
|
log::debug!("set open file descriptor limit to {}", limit);
|
||||||
|
|
||||||
|
log::trace!("exit: set_open_file_limit -> {}", true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// failed to set a new limit, as limit adjustments are a 'nice to have', we'll just log
|
||||||
|
// and move along
|
||||||
|
log::warn!("could not set open file descriptor limit to {}", limit);
|
||||||
|
|
||||||
|
log::trace!("exit: set_open_file_limit -> {}", false);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// set_open_file_limit with a low requested limit succeeds
|
||||||
|
fn utils_set_open_file_limit_with_low_requested_limit() {
|
||||||
|
let (_, hard) = getrlimit(Resource::NOFILE).unwrap();
|
||||||
|
let lower_limit = hard.as_usize() - 1;
|
||||||
|
assert!(set_open_file_limit(lower_limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// set_open_file_limit with a high requested limit succeeds
|
||||||
|
fn utils_set_open_file_limit_with_high_requested_limit() {
|
||||||
|
let (_, hard) = getrlimit(Resource::NOFILE).unwrap();
|
||||||
|
let higher_limit = hard.as_usize() + 1;
|
||||||
|
// calculate a new soft to ensure soft != hard and hit that logic branch
|
||||||
|
let new_soft = Rlim::from_usize(hard.as_usize() - 1);
|
||||||
|
setrlimit(Resource::NOFILE, new_soft, hard).unwrap();
|
||||||
|
assert!(set_open_file_limit(higher_limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// set_open_file_limit should fail when hard == soft
|
||||||
|
fn utils_set_open_file_limit_with_fails_when_both_limits_are_equal() {
|
||||||
|
let (_, hard) = getrlimit(Resource::NOFILE).unwrap();
|
||||||
|
// calculate a new soft to ensure soft == hard and hit the failure logic branch
|
||||||
|
setrlimit(Resource::NOFILE, hard, hard).unwrap();
|
||||||
|
assert!(!set_open_file_limit(hard.as_usize())); // returns false
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
/// base url returns 1
|
/// base url returns 1
|
||||||
fn get_current_depth_base_url_returns_1() {
|
fn get_current_depth_base_url_returns_1() {
|
||||||
@@ -352,6 +454,19 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
/// word that is a fully formed url, should return an error
|
||||||
|
fn format_url_word_that_is_a_url() {
|
||||||
|
let url = format_url(
|
||||||
|
"http://localhost",
|
||||||
|
"http://schmocalhost",
|
||||||
|
false,
|
||||||
|
&Vec::new(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
assert!(url.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
/// status colorizer uses red for 500s
|
/// status colorizer uses red for 500s
|
||||||
fn status_colorizer_uses_red_for_500s() {
|
fn status_colorizer_uses_red_for_500s() {
|
||||||
|
|||||||
Reference in New Issue
Block a user