Files
sabia/crates/sabia-core/src/hashcat.rs
T
Lucas Rayan f83654506e feat: dicionário completo, regras de mutação, máscara, .hc22000 e WPS (reaver)
- 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).
2026-10-24 14:53:54 -03:00

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***"
);
}
}