key requires cert; accept multiple server certs

This commit is contained in:
epi
2023-05-05 07:07:06 -05:00
parent c307e6d56d
commit e094dab4a4
9 changed files with 84 additions and 71 deletions

View File

@@ -53,9 +53,9 @@ _feroxbuster() {
'*--status-codes=[Status Codes to include (allow list) (default\: All Status Codes)]:STATUS_CODE: ' \
'-T+[Number of seconds before a client'\''s request times out (default\: 7)]:SECONDS: ' \
'--timeout=[Number of seconds before a client'\''s request times out (default\: 7)]:SECONDS: ' \
'--server-cert=[Add a custom root certificate to connect to servers with a self-signed certificate]:PEM/DER:_files' \
'--client-cert=[Use a custom client SSL certificate for mutual authentication]:PEM:_files' \
'--client-key=[Use a custom client SSL key file for mutual authentication]:PEM:_files' \
'--server-certs=[Add custom root certificate(s) for servers with unknown certificates]:PEM|DER:_files' \
'--client-cert=[Add a PEM encoded certificate for mutual authentication (mTLS)]:PEM:_files' \
'--client-key=[Add a PEM encoded private key for mutual authentication (mTLS)]:PEM:_files' \
'-t+[Number of concurrent threads (default\: 50)]:THREADS: ' \
'--threads=[Number of concurrent threads (default\: 50)]:THREADS: ' \
'-d+[Maximum recursion depth, a depth of 0 is infinite recursion (default\: 4)]:RECURSION_DEPTH: ' \

View File

@@ -59,9 +59,9 @@ Register-ArgumentCompleter -Native -CommandName 'feroxbuster' -ScriptBlock {
[CompletionResult]::new('--status-codes', 'status-codes', [CompletionResultType]::ParameterName, 'Status Codes to include (allow list) (default: All Status Codes)')
[CompletionResult]::new('-T', 'T', [CompletionResultType]::ParameterName, 'Number of seconds before a client''s request times out (default: 7)')
[CompletionResult]::new('--timeout', 'timeout', [CompletionResultType]::ParameterName, 'Number of seconds before a client''s request times out (default: 7)')
[CompletionResult]::new('--server-cert', 'server-cert', [CompletionResultType]::ParameterName, 'Add a custom root certificate to connect to servers with a self-signed certificate')
[CompletionResult]::new('--client-cert', 'client-cert', [CompletionResultType]::ParameterName, 'Use a custom client SSL certificate for mutual authentication')
[CompletionResult]::new('--client-key', 'client-key', [CompletionResultType]::ParameterName, 'Use a custom client SSL key file for mutual authentication')
[CompletionResult]::new('--server-certs', 'server-certs', [CompletionResultType]::ParameterName, 'Add custom root certificate(s) for servers with unknown certificates')
[CompletionResult]::new('--client-cert', 'client-cert', [CompletionResultType]::ParameterName, 'Add a PEM encoded certificate for mutual authentication (mTLS)')
[CompletionResult]::new('--client-key', 'client-key', [CompletionResultType]::ParameterName, 'Add a PEM encoded private key for mutual authentication (mTLS)')
[CompletionResult]::new('-t', 't', [CompletionResultType]::ParameterName, 'Number of concurrent threads (default: 50)')
[CompletionResult]::new('--threads', 'threads', [CompletionResultType]::ParameterName, 'Number of concurrent threads (default: 50)')
[CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)')

View File

@@ -19,7 +19,7 @@ _feroxbuster() {
case "${cmd}" in
feroxbuster)
opts="-u -p -P -R -a -A -x -m -H -b -Q -f -S -X -W -N -C -s -T -r -k -t -n -d -e -L -w -D -E -B -g -I -v -q -o -U -h -V --url --stdin --resume-from --burp --burp-replay --smart --thorough --proxy --replay-proxy --replay-codes --user-agent --random-agent --extensions --methods --data --headers --cookies --query --add-slash --dont-scan --filter-size --filter-regex --filter-words --filter-lines --filter-status --filter-similar-to --status-codes --timeout --redirects --insecure --server-cert --client-cert --client-key --threads --no-recursion --depth --force-recursion --extract-links --dont-extract-links --scan-limit --parallel --rate-limit --time-limit --wordlist --auto-tune --auto-bail --dont-filter --collect-extensions --collect-backups --collect-words --dont-collect --verbosity --silent --quiet --json --output --debug-log --no-state --update --help --version"
opts="-u -p -P -R -a -A -x -m -H -b -Q -f -S -X -W -N -C -s -T -r -k -t -n -d -e -L -w -D -E -B -g -I -v -q -o -U -h -V --url --stdin --resume-from --burp --burp-replay --smart --thorough --proxy --replay-proxy --replay-codes --user-agent --random-agent --extensions --methods --data --headers --cookies --query --add-slash --dont-scan --filter-size --filter-regex --filter-words --filter-lines --filter-status --filter-similar-to --status-codes --timeout --redirects --insecure --server-certs --client-cert --client-key --threads --no-recursion --depth --force-recursion --extract-links --dont-extract-links --scan-limit --parallel --rate-limit --time-limit --wordlist --auto-tune --auto-bail --dont-filter --collect-extensions --collect-backups --collect-words --dont-collect --verbosity --silent --quiet --json --output --debug-log --no-state --update --help --version"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -177,7 +177,7 @@ _feroxbuster() {
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--server-cert)
--server-certs)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;

View File

@@ -56,9 +56,9 @@ set edit:completion:arg-completer[feroxbuster] = {|@words|
cand --status-codes 'Status Codes to include (allow list) (default: All Status Codes)'
cand -T 'Number of seconds before a client''s request times out (default: 7)'
cand --timeout 'Number of seconds before a client''s request times out (default: 7)'
cand --server-cert 'Add a custom root certificate to connect to servers with a self-signed certificate'
cand --client-cert 'Use a custom client SSL certificate for mutual authentication'
cand --client-key 'Use a custom client SSL key file for mutual authentication'
cand --server-certs 'Add custom root certificate(s) for servers with unknown certificates'
cand --client-cert 'Add a PEM encoded certificate for mutual authentication (mTLS)'
cand --client-key 'Add a PEM encoded private key for mutual authentication (mTLS)'
cand -t 'Number of concurrent threads (default: 50)'
cand --threads 'Number of concurrent threads (default: 50)'
cand -d 'Maximum recursion depth, a depth of 0 is infinite recursion (default: 4)'

View File

@@ -65,7 +65,7 @@ pub struct Banner {
client_cert: BannerEntry,
/// represents Configuration.server_cert
server_cert: BannerEntry,
server_certs: BannerEntry,
/// represents Configuration.replay_proxy
replay_proxy: BannerEntry,
@@ -331,7 +331,7 @@ impl Banner {
let auto_bail = BannerEntry::new("🙅", "Auto Bail", &config.auto_bail.to_string());
let cfg = BannerEntry::new("💉", "Config File", &config.config);
let proxy = BannerEntry::new("💎", "Proxy", &config.proxy);
let server_cert = BannerEntry::new("🏅", "Server Certificate", &config.server_cert);
let server_certs = BannerEntry::new("🏅", "Server Certificates", &format!("[{}]", config.server_certs.join(", ")));
let client_cert = BannerEntry::new("🏅", "Client Certificate", &config.client_cert);
let client_key = BannerEntry::new("🔑", "Client Key", &config.client_key);
let threads = BannerEntry::new("🚀", "Threads", &config.threads.to_string());
@@ -415,7 +415,7 @@ impl Banner {
proxy,
client_cert,
client_key,
server_cert,
server_certs,
replay_codes,
replay_proxy,
headers,
@@ -578,8 +578,8 @@ by Ben "epi" Risher {} ver: {}"#,
writeln!(&mut writer, "{}", self.client_key)?;
}
if !config.server_cert.is_empty() {
writeln!(&mut writer, "{}", self.server_cert)?;
if !config.server_certs.is_empty() {
writeln!(&mut writer, "{}", self.server_certs)?;
}
if !config.replay_proxy.is_empty() {

View File

@@ -9,17 +9,21 @@ use std::time::Duration;
/// Create and return an instance of [reqwest::Client](https://docs.rs/reqwest/latest/reqwest/struct.Client.html)
/// For now, silence clippy for this one
#[allow(clippy::too_many_arguments)]
pub fn initialize(
pub fn initialize<I>(
timeout: u64,
user_agent: &str,
redirects: bool,
insecure: bool,
headers: &HashMap<String, String>,
proxy: Option<&str>,
server_cert: Option<&str>,
server_certs: Option<I>,
client_cert: Option<&str>,
client_key: Option<&str>,
) -> Result<Client> {
) -> Result<Client>
where
I: IntoIterator,
I::Item: AsRef<std::ffi::OsStr>,
{
let policy = if redirects {
Policy::limited(10)
} else {
@@ -46,33 +50,35 @@ pub fn initialize(
}
}
if let Some(cert_path) = server_cert {
let cert_path = Path::new(cert_path);
if let Some(cert_paths) = server_certs {
for cert_path in cert_paths {
let cert_path = Path::new(&cert_path);
// if the root certificate path is not empty, open it
// and read it into a buffer
// if the root certificate path is not empty, open it
// and read it into a buffer
let buf = std::fs::read(cert_path)?;
let cert = match cert_path
.extension()
.map(|s| s.to_str().unwrap_or_default())
{
// depending upon the extension of the file, create a
// certificate object from it using either the "pem" or "der" parser
Some("pem") => reqwest::Certificate::from_pem(&buf)?,
Some("der") => reqwest::Certificate::from_der(&buf)?,
let buf = std::fs::read(cert_path)?;
let cert = match cert_path
.extension()
.map(|s| s.to_str().unwrap_or_default())
{
// depending upon the extension of the file, create a
// certificate object from it using either the "pem" or "der" parser
Some("pem") => reqwest::Certificate::from_pem(&buf)?,
Some("der") => reqwest::Certificate::from_der(&buf)?,
// if we cannot determine the extension, do nothing
_ => {
log::warn!(
"unable to determine extension: assuming PEM format for root certificate"
);
reqwest::Certificate::from_pem(&buf)?
}
};
// if we cannot determine the extension, do nothing
_ => {
log::warn!(
"unable to determine extension: assuming PEM format for root certificate"
);
reqwest::Certificate::from_pem(&buf)?
}
};
// in either case, add the root certificate to the client
client = client.add_root_certificate(cert);
// in either case, add the root certificate to the client
client = client.add_root_certificate(cert);
}
}
if let (Some(cert_path), Some(key_path)) = (client_cert, client_key) {

View File

@@ -106,9 +106,9 @@ pub struct Configuration {
/// Path to a custom root certificate for connecting to servers with a self-signed certificate
#[serde(default)]
pub server_cert: String,
pub server_certs: Vec<String>,
/// Path to a client's PEM encoded X509 certificate(s) used during mutual authentication
/// Path to a client's PEM encoded X509 certificate used during mutual authentication
#[serde(default)]
pub client_cert: String,
@@ -336,7 +336,7 @@ impl Default for Configuration {
fn default() -> Self {
let timeout = timeout();
let user_agent = user_agent();
let client = client::initialize(
let client = client::initialize::<Vec<String>>(
timeout,
&user_agent,
false,
@@ -391,7 +391,6 @@ impl Default for Configuration {
force_recursion: false,
update_app: false,
proxy: String::new(),
server_cert: String::new(),
client_cert: String::new(),
client_key: String::new(),
config: String::new(),
@@ -401,6 +400,7 @@ impl Default for Configuration {
time_limit: String::new(),
resume_from: String::new(),
replay_proxy: String::new(),
server_certs: Vec::new(),
queries: Vec::new(),
extensions: Vec::new(),
methods: methods(),
@@ -857,7 +857,6 @@ impl Configuration {
// organizational breakpoint; all options below alter the Client configuration
////
update_config_if_present!(&mut config.proxy, args, "proxy", String);
update_config_if_present!(&mut config.server_cert, args, "server_cert", String);
update_config_if_present!(&mut config.client_cert, args, "client_cert", String);
update_config_if_present!(&mut config.client_key, args, "client_key", String);
update_config_if_present!(&mut config.replay_proxy, args, "replay_proxy", String);
@@ -930,6 +929,12 @@ impl Configuration {
}
}
if let Some(certs) = args.get_many::<String>("server_certs") {
for val in certs {
config.server_certs.push(val.to_string());
}
}
config
}
@@ -947,10 +952,10 @@ impl Configuration {
Some(configuration.proxy.as_str())
};
let server_cert = if configuration.server_cert.is_empty() {
let server_certs = if configuration.server_certs.is_empty() {
None
} else {
Some(configuration.server_cert.as_str())
Some(&configuration.server_certs)
};
let client_cert = if configuration.client_cert.is_empty() {
@@ -972,7 +977,7 @@ impl Configuration {
|| configuration.insecure
|| !configuration.headers.is_empty()
|| configuration.resumed
|| server_cert.is_some()
|| server_certs.is_some()
|| client_cert.is_some()
|| client_key.is_some()
{
@@ -983,7 +988,7 @@ impl Configuration {
configuration.insecure,
&configuration.headers,
proxy,
server_cert,
server_certs,
client_cert,
client_key,
)
@@ -1000,7 +1005,7 @@ impl Configuration {
configuration.insecure,
&configuration.headers,
Some(&configuration.replay_proxy),
server_cert,
server_certs,
client_cert,
client_key,
)
@@ -1037,7 +1042,11 @@ impl Configuration {
update_if_not_default!(&mut conf.target_url, new.target_url, "");
update_if_not_default!(&mut conf.time_limit, new.time_limit, "");
update_if_not_default!(&mut conf.proxy, new.proxy, "");
update_if_not_default!(&mut conf.server_cert, new.server_cert, "");
update_if_not_default!(
&mut conf.server_certs,
new.server_certs,
Vec::<String>::new()
);
update_if_not_default!(&mut conf.client_cert, new.client_cert, "");
update_if_not_default!(&mut conf.client_key, new.client_key, "");
update_if_not_default!(&mut conf.verbosity, new.verbosity, 0);

View File

@@ -641,10 +641,10 @@ impl<'a> Extractor<'a> {
Some(self.handles.config.proxy.as_str())
};
let server_cert = if self.handles.config.server_cert.is_empty() {
let server_certs = if self.handles.config.server_certs.is_empty() {
None
} else {
Some(self.handles.config.server_cert.as_str())
Some(&self.handles.config.server_certs)
};
let client_cert = if self.handles.config.client_cert.is_empty() {
@@ -666,7 +666,7 @@ impl<'a> Extractor<'a> {
self.handles.config.insecure,
&self.handles.config.headers,
proxy,
server_cert,
server_certs,
client_cert,
client_key,
)?;

View File

@@ -392,35 +392,33 @@ pub fn initialize() -> Command {
.help("Disables TLS certificate validation in the client"),
)
.arg(
Arg::new("server_cert")
.long("server-cert")
.value_name("PEM/DER")
Arg::new("server_certs")
.long("server-certs")
.value_name("PEM|DER")
.value_hint(ValueHint::FilePath)
.num_args(1)
.num_args(1..)
.help_heading("Client settings")
.help(
"Add a custom root certificate to connect to servers with a self-signed certificate",
),
).arg(
.help("Add custom root certificate(s) for servers with unknown certificates"),
)
.arg(
Arg::new("client_cert")
.long("client-cert")
.value_name("PEM")
.value_hint(ValueHint::FilePath)
.num_args(1)
.requires("client_key")
.help_heading("Client settings")
.help(
"Use a custom client SSL certificate for mutual authentication",
),
).arg(
.help("Add a PEM encoded certificate for mutual authentication (mTLS)"),
)
.arg(
Arg::new("client_key")
.long("client-key")
.value_name("PEM")
.value_hint(ValueHint::FilePath)
.num_args(1)
.requires("client_cert")
.help_heading("Client settings")
.help(
"Use a custom client SSL key file for mutual authentication",
),
.help("Add a PEM encoded private key for mutual authentication (mTLS)"),
);
/////////////////////////////////////////////////////////////////////