add client_key flag to separated PEM key file for identity from_pkcs8_pem

This commit is contained in:
Himadri Bhattacharjee 2023-05-04 08:35:25 +05:30
parent 4986ebdaae
commit 372f7c5cd4
11 changed files with 86 additions and 128 deletions

106
Cargo.lock generated
View File

@ -1292,19 +1292,6 @@ dependencies = [
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c"
dependencies = [
"http",
"hyper",
"rustls",
"tokio",
"tokio-rustls",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
@ -2277,7 +2264,6 @@ dependencies = [
"http",
"http-body",
"hyper",
"hyper-rustls",
"hyper-tls",
"ipnet",
"js-sys",
@ -2287,39 +2273,20 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls",
"rustls-pemfile",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-native-tls",
"tokio-rustls",
"tokio-socks",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots",
"winreg",
]
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
]
[[package]]
name = "rlimit"
version = "0.9.1"
@ -2352,27 +2319,6 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "rustls"
version = "0.20.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
dependencies = [
"log",
"ring",
"sct",
"webpki",
]
[[package]]
name = "rustls-pemfile"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
dependencies = [
"base64 0.21.0",
]
[[package]]
name = "rustversion"
version = "1.0.12"
@ -2417,16 +2363,6 @@ dependencies = [
"tendril",
]
[[package]]
name = "sct"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "seahash"
version = "4.1.0"
@ -2671,12 +2607,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@ -2914,17 +2844,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
dependencies = [
"rustls",
"tokio",
"webpki",
]
[[package]]
name = "tokio-socks"
version = "0.5.1"
@ -3089,12 +3008,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "url"
version = "2.3.1"
@ -3269,25 +3182,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "webpki-roots"
version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
dependencies = [
"webpki",
]
[[package]]
name = "winapi"
version = "0.3.9"

View File

@ -35,7 +35,7 @@ tokio = { version = "1.26", features = ["full"] }
tokio-util = { version = "0.7", features = ["codec"] }
log = "0.4"
env_logger = "0.10"
reqwest = { version = "0.11", features = ["socks", "rustls-tls"] }
reqwest = { version = "0.11", features = ["socks", "native-tls"] }
# uses feature unification to add 'serde' to reqwest::Url
url = { version = "2.2", features = ["serde"] }
serde_regex = "1.1"

View File

@ -54,7 +54,8 @@ _feroxbuster() {
'-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 key file for mutual authentication]:PEM:_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' \
'-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

@ -60,7 +60,8 @@ Register-ArgumentCompleter -Native -CommandName 'feroxbuster' -ScriptBlock {
[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 key file for mutual authentication')
[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('-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 --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-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"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@ -185,6 +185,10 @@ _feroxbuster() {
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--client-key)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--threads)
COMPREPLY=($(compgen -f "${cur}"))
return 0

View File

@ -57,7 +57,8 @@ set edit:completion:arg-completer[feroxbuster] = {|@words|
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 key file for mutual authentication'
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 -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

@ -58,6 +58,9 @@ pub struct Banner {
/// represents Configuration.proxy
proxy: BannerEntry,
/// represents Configuration.client_key
client_key: BannerEntry,
/// represents Configuration.client_cert
client_cert: BannerEntry,
@ -330,6 +333,7 @@ impl Banner {
let proxy = BannerEntry::new("💎", "Proxy", &config.proxy);
let server_cert = BannerEntry::new("🏅", "Server Certificate", &config.server_cert);
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());
let wordlist = BannerEntry::new("📖", "Wordlist", &config.wordlist);
let timeout = BannerEntry::new("💥", "Timeout (secs)", &config.timeout.to_string());
@ -410,6 +414,7 @@ impl Banner {
auto_tune,
proxy,
client_cert,
client_key,
server_cert,
replay_codes,
replay_proxy,
@ -569,6 +574,10 @@ by Ben "epi" Risher {} ver: {}"#,
writeln!(&mut writer, "{}", self.client_cert)?;
}
if !config.client_key.is_empty() {
writeln!(&mut writer, "{}", self.client_key)?;
}
if !config.server_cert.is_empty() {
writeln!(&mut writer, "{}", self.server_cert)?;
}

View File

@ -18,6 +18,7 @@ pub fn initialize(
proxy: Option<&str>,
server_cert: Option<&str>,
client_cert: Option<&str>,
client_key: Option<&str>,
) -> Result<Client> {
let policy = if redirects {
Policy::limited(10)
@ -75,24 +76,27 @@ pub fn initialize(
}
if let Some(cert_path) = client_cert {
let cert_path = Path::new(cert_path);
let mut buf = Vec::new();
if let Some(key_path) = client_key {
let cert_path = Path::new(cert_path);
// if the root certificate path is not empty, open it
// and read it into a buffer
File::open(cert_path)?.read_to_end(&mut buf)?;
// if the root certificate path is not empty, open it
// and read it into a buffer
// depending upon the extension of the file, create a
// certificate object from it using either the "pem" or "der" parser
// depending upon the extension of the file, create a
// certificate object from it using either the "pem" or "der" parser
// in either case, add the root certificate to the client
if let Some(extension) = cert_path.extension() {
if "pem" == extension.to_str().unwrap_or_default() {
let cert = reqwest::tls::Identity::from_pem(&buf)?;
client = client.identity(cert);
} else {
// if it is not a "pem" file or we cannot determine the extension
// TODO: spew an error
// in either case, add the root certificate to the client
if let Some(extension) = cert_path.extension() {
if "pem" == extension.to_str().unwrap_or_default() {
let cert = reqwest::tls::Identity::from_pkcs8_pem(
&std::fs::read(cert_path)?,
&std::fs::read(key_path)?,
)?;
client = client.identity(cert);
} else {
// if it is not a "pem" file or we cannot determine the extension
// TODO: spew an error
}
}
}
}
@ -118,6 +122,7 @@ mod tests {
Some("not a valid proxy"),
None,
None,
None,
)
.unwrap();
}
@ -127,6 +132,17 @@ mod tests {
fn client_with_good_proxy() {
let headers = HashMap::new();
let proxy = "http://127.0.0.1:8080";
initialize(0, "stuff", true, true, &headers, Some(proxy), None, None).unwrap();
initialize(
0,
"stuff",
true,
true,
&headers,
Some(proxy),
None,
None,
None,
)
.unwrap();
}
}

View File

@ -108,10 +108,14 @@ pub struct Configuration {
#[serde(default)]
pub server_cert: String,
/// Path to a custom client SSL key for mutual authentication with a server
/// Path to a custom client SSL certificate mutual authentication with a server
#[serde(default)]
pub client_cert: String,
/// Path to a custom client SSL key for mutual authentication with a server
#[serde(default)]
pub client_key: String,
/// The target URL
#[serde(default)]
pub target_url: String,
@ -341,6 +345,7 @@ impl Default for Configuration {
None,
None,
None,
None,
)
.expect("Could not build client");
let replay_client = None;
@ -388,6 +393,7 @@ impl Default for Configuration {
proxy: String::new(),
server_cert: String::new(),
client_cert: String::new(),
client_key: String::new(),
config: String::new(),
output: String::new(),
debug_log: String::new(),
@ -952,6 +958,12 @@ impl Configuration {
Some(configuration.client_cert.as_str())
};
let client_key = if configuration.client_key.is_empty() {
None
} else {
Some(configuration.client_key.as_str())
};
if proxy.is_some()
|| configuration.timeout != timeout()
|| configuration.user_agent != user_agent()
@ -961,6 +973,7 @@ impl Configuration {
|| configuration.resumed
|| server_cert.is_some()
|| client_cert.is_some()
|| client_key.is_some()
{
configuration.client = client::initialize(
configuration.timeout,
@ -971,6 +984,7 @@ impl Configuration {
proxy,
server_cert,
client_cert,
client_key,
)
.expect("Could not rebuild client");
}
@ -987,6 +1001,7 @@ impl Configuration {
Some(&configuration.replay_proxy),
server_cert,
client_cert,
client_key,
)
.expect("Could not rebuild client"),
);

View File

@ -653,6 +653,12 @@ impl<'a> Extractor<'a> {
Some(self.handles.config.client_cert.as_str())
};
let client_key = if self.handles.config.client_key.is_empty() {
None
} else {
Some(self.handles.config.client_key.as_str())
};
client = client::initialize(
self.handles.config.timeout,
&self.handles.config.user_agent,
@ -662,6 +668,7 @@ impl<'a> Extractor<'a> {
proxy,
server_cert,
client_cert,
client_key,
)?;
}

View File

@ -408,6 +408,16 @@ pub fn initialize() -> Command {
.value_hint(ValueHint::FilePath)
.num_args(1)
.help_heading("Client settings")
.help(
"Use a custom client SSL certificate for mutual authentication",
),
).arg(
Arg::new("client_key")
.long("client-key")
.value_name("PEM")
.value_hint(ValueHint::FilePath)
.num_args(1)
.help_heading("Client settings")
.help(
"Use a custom client SSL key file for mutual authentication",
),