- crack.rs: dicionário padrão trocado pra wpa-full.txt.gz (rockyou + Probable-Wordlists top204k mesclados/deduplicados, 9,7M senhas, comprimido e embutido via LazyLock), PBKDF2 ~640x mais rápido (paralelização por núcleo + HMAC reaproveitado em vez de reconstruído a cada iteração), força bruta por máscara (rules.rs::Mask, sintaxe ?d?l?u?s?a) e regras de mutação tipo hashcat (maiúscula, sufixos numéricos/símbolos, leet simples). - hashcat.rs: exportação de handshake/PMKID em .hc22000 (individual ou em lote), pro hashcat de verdade em vez de só o dicionário embutido. - wps.rs: ataque WPS PIN via reaver (binário real do reaver-wps-fork-t6x, GPL-2, extraído em tempo de execução) e bully quando instalado no sistema. - assets/wordlists/LICENSE.wpa-full documenta a atribuição (CC BY-SA 4.0 do Probable-Wordlists; rockyou.txt como dado de vazamento de uso livre consolidado em pesquisa de segurança, mesmo padrão do Kali/SecLists).
87 lines
2.4 KiB
Rust
87 lines
2.4 KiB
Rust
//! Exportação no formato `.hc22000` do hashcat.
|
|
//!
|
|
//! Duas linhas possíveis, como no hcxtools:
|
|
//!
|
|
//! * `WPA*01*PMKID*AP*STA*SSID***`
|
|
//! * `WPA*02*MIC*AP*STA*SSID*ANONCE*EAPOL*MESSAGEPAIR`
|
|
//!
|
|
//! O EAPOL exportado é o quadro da mensagem 2 completo (cabeçalho 802.1X +
|
|
//! descritor), que é o que o hashcat espera no campo `EAPOL_CLIENT`.
|
|
|
|
use crate::eapol::MIC_OFFSET;
|
|
use crate::handshake::Handshake;
|
|
|
|
fn mac_hex(mac: &str) -> Option<String> {
|
|
let bytes: Vec<String> = mac
|
|
.split(':')
|
|
.map(|part| part.trim().to_ascii_lowercase())
|
|
.collect();
|
|
if bytes.len() != 6
|
|
|| !bytes
|
|
.iter()
|
|
.all(|b| b.len() == 2 && b.chars().all(|c| c.is_ascii_hexdigit()))
|
|
{
|
|
return None;
|
|
}
|
|
Some(bytes.concat())
|
|
}
|
|
|
|
fn ssid_hex(ssid: &str) -> String {
|
|
ssid.as_bytes().iter().map(|b| format!("{b:02x}")).collect()
|
|
}
|
|
|
|
fn hex(bytes: &[u8]) -> String {
|
|
bytes.iter().map(|b| format!("{b:02x}")).collect()
|
|
}
|
|
|
|
pub fn hashcat_line(hs: &Handshake) -> Option<String> {
|
|
let ssid = hs.ssid.as_deref().filter(|s| !s.is_empty())?;
|
|
let ap = mac_hex(&hs.bssid)?;
|
|
let sta = mac_hex(&hs.station)?;
|
|
let ssid = ssid_hex(ssid);
|
|
|
|
if let Some(pmkid) = hs.pmkid {
|
|
return Some(format!("WPA*01*{}*{ap}*{sta}*{ssid}***", hex(&pmkid)));
|
|
}
|
|
|
|
let eapol = hs.eapol_msg2.as_deref()?;
|
|
let anonce = hs.anonce?;
|
|
let mic = eapol.get(MIC_OFFSET..MIC_OFFSET + 16)?;
|
|
Some(format!(
|
|
"WPA*02*{}*{ap}*{sta}*{ssid}*{}*{}*00",
|
|
hex(mic),
|
|
hex(&anonce),
|
|
hex(eapol),
|
|
))
|
|
}
|
|
|
|
/// Linhas únicas, uma por handshake que tenha material suficiente.
|
|
pub fn export<'a>(handshakes: impl IntoIterator<Item = &'a Handshake>) -> Vec<String> {
|
|
let mut lines: Vec<String> = handshakes.into_iter().filter_map(hashcat_line).collect();
|
|
lines.sort();
|
|
lines.dedup();
|
|
lines
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::handshake::Handshake;
|
|
|
|
#[test]
|
|
fn exports_pmkid_line() {
|
|
let hs = Handshake {
|
|
bssid: "00:11:22:33:44:55".into(),
|
|
station: "66:77:88:99:aa:bb".into(),
|
|
ssid: Some("Casa".into()),
|
|
pmkid: Some([0xcd; 16]),
|
|
..Default::default()
|
|
};
|
|
let line = hashcat_line(&hs).expect("linha");
|
|
assert_eq!(
|
|
line,
|
|
"WPA*01*cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd*001122334455*66778899aabb*43617361***"
|
|
);
|
|
}
|
|
}
|