mirror of
https://github.com/s-b-repo/rustsploit
synced 2026-06-27 09:54:12 +00:00
creds bruteforce: hydra-style -e nsr (null/same/reversed) + expose wordlist/cred_extras options
Adds the highest-yield hydra feature to the shared creds_helper: for each username, optionally also try an empty password (null), the username as its own password (same), and the reversed username. Opt-in via 'setg cred_extras' with a subset of n/s/r (e.g. nsr); default off. Appended to the defaults-first rows so they run before the wordlist; the engine's existing combo dedup avoids retrying a pair already in defaults. Skipped for password-only services. Also surfaced username_wordlist/password_wordlist/cred_extras in 'show options'. Verified: elasticsearch_bruteforce + cred_extras=nsr + 1 username -> 6 defaults + null + reversed (same deduped against the admin/admin default) = 8 attempts.
This commit is contained in:
@@ -1308,6 +1308,9 @@ async fn display_all_options(ctx: &ShellContext) {
|
||||
("exclusions", "Extra networks to skip in mass scans (CIDR,CIDR,...)", ""),
|
||||
("target_rps", "Per-target rate limit (req/s, 0 = unlimited)", ""),
|
||||
("module_rps", "Global module rate limit (req/s, 0 = unlimited)", ""),
|
||||
("username_wordlist", "Bruteforce username wordlist path", ""),
|
||||
("password_wordlist", "Bruteforce password wordlist path", ""),
|
||||
("cred_extras", "Bruteforce extra creds: hydra-style n/s/r (null/same/reversed), e.g. nsr", ""),
|
||||
];
|
||||
|
||||
let mut displayed = std::collections::HashSet::new();
|
||||
|
||||
@@ -184,12 +184,37 @@ where
|
||||
// Defaults are the highest-yield rows. In the eager path they go first; in
|
||||
// the streaming path they're passed as `extra_combos` (the engine runs them
|
||||
// after the streamed batches).
|
||||
let defaults: Vec<(String, String)> = cfg
|
||||
let mut defaults: Vec<(String, String)> = cfg
|
||||
.defaults
|
||||
.iter()
|
||||
.map(|(u, p)| (u.to_string(), p.to_string()))
|
||||
.collect();
|
||||
|
||||
// Hydra-style `-e nsr`: for each username also try an empty password (null),
|
||||
// the username as its own password (same), and the reversed username. Opt in
|
||||
// via `setg cred_extras <subset of n/s/r>` (e.g. "nsr"); default off. Appended
|
||||
// to the high-yield "defaults first" rows so they run before the wordlist.
|
||||
// Skipped for password-only services (no username to derive from).
|
||||
if !cfg.password_only {
|
||||
let extras = cred_extras();
|
||||
if extras.any() {
|
||||
for u in &usernames {
|
||||
if u.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if extras.null {
|
||||
defaults.push((u.clone(), String::new()));
|
||||
}
|
||||
if extras.same {
|
||||
defaults.push((u.clone(), u.clone()));
|
||||
}
|
||||
if extras.reversed {
|
||||
defaults.push((u.clone(), u.chars().rev().collect::<String>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let bf_config = BruteforceConfig {
|
||||
target: host.clone(),
|
||||
port,
|
||||
@@ -339,3 +364,35 @@ async fn is_port_open(host: &str, port: u16) -> bool {
|
||||
};
|
||||
crate::utils::tcp_port_open(ip, port, Duration::from_secs(2)).await
|
||||
}
|
||||
|
||||
/// Hydra-style `-e` extra-credential flags: null password, password == login
|
||||
/// (same), and reversed login.
|
||||
struct CredExtras {
|
||||
null: bool,
|
||||
same: bool,
|
||||
reversed: bool,
|
||||
}
|
||||
|
||||
impl CredExtras {
|
||||
fn any(&self) -> bool {
|
||||
self.null || self.same || self.reversed
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse `setg cred_extras` (a hydra-style subset of `n`/`s`/`r`, e.g. "nsr").
|
||||
/// Empty / "none" / "off" / "no" disables it (the default when unset).
|
||||
fn cred_extras() -> CredExtras {
|
||||
let none = CredExtras { null: false, same: false, reversed: false };
|
||||
let raw = match crate::tenant::resolve().global_options().try_get("cred_extras") {
|
||||
Some(s) => s.to_lowercase(),
|
||||
None => return none,
|
||||
};
|
||||
if matches!(raw.trim(), "" | "none" | "off" | "no" | "0") {
|
||||
return none;
|
||||
}
|
||||
CredExtras {
|
||||
null: raw.contains('n'),
|
||||
same: raw.contains('s'),
|
||||
reversed: raw.contains('r'),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user