fix: captura de handshake real (endianness EAPOL) e enriquece o parsing 802.11
- eapol.rs: key_info/replay_counter/key_data_len liam little-endian; são big-endian (802.1X segue ordem de rede), então handshakes reais nunca fechavam mesmo com deauth funcionando. testutil.rs e os geradores de quadro de teste corrigidos junto, mais um teste contra bytes de wire reais (não só contra o próprio encoder). - inject.rs: deauth/disassoc agora grava Sequence Control crescente por quadro — reenviar o mesmo quadro era descartado como duplicata pelo driver/AP depois do primeiro envio. - dot11.rs/scan.rs: parsing de AP/cliente bem mais completo (PMF obrigatório, WPS detalhado, fabricante por OUI, país, largura de banda, padrão PHY, privacidade) e histórico de sinal/eventos por cliente. - handshake.rs/ccmp.rs: deriva a Temporal Key (CCMP) quando a senha é quebrada, o suficiente pra decifrar o tráfego em claro do par (BSSID, estação) cujo handshake foi capturado.
This commit is contained in:
1 parent
dab86d1fa7
commit
5477b8fb03
10 files changed
+2523
-19
No files matched your search
@@ -33,6 +33,12 @@ pub struct Handshake {
|
||||
pub first_seen_unix: u64,
|
||||
pub last_seen_unix: u64,
|
||||
pub password: Option<String>,
|
||||
/// Temporal Key (CCMP/AES) derivada quando `password` é preenchido.
|
||||
/// Só pra TKIP (key_version 1) fica None: a decriptação aqui só cobre
|
||||
/// CCMP. Serve pra enxergar o tráfego em claro desse par (BSSID,
|
||||
/// estação) específico — não abre o tráfego de outros clientes, já que
|
||||
/// a PTK é efêmera por associação.
|
||||
pub tk: Option<[u8; 16]>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
@@ -244,9 +250,17 @@ impl HandshakeTracker {
|
||||
pub fn set_password(&mut self, bssid: &str, station: &str, password: &str) {
|
||||
if let Some(hs) = self.get_mut(bssid, station) {
|
||||
hs.password = Some(password.to_string());
|
||||
hs.tk = derive_tk(hs, password);
|
||||
}
|
||||
}
|
||||
|
||||
/// TK já derivada pra esse par (BSSID, estação), se a senha da rede já
|
||||
/// foi quebrada e o handshake tinha os dois nonces (EAPOL completo — um
|
||||
/// handshake só de PMKID não dá pra derivar PTK/TK, só confirma a PSK).
|
||||
pub fn tk_for(&self, bssid: &str, station: &str) -> Option<[u8; 16]> {
|
||||
self.get(bssid, station).and_then(|hs| hs.tk)
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
@@ -256,6 +270,26 @@ impl HandshakeTracker {
|
||||
}
|
||||
}
|
||||
|
||||
/// Deriva a TK (bytes 32..48 da PTK) a partir da senha quebrada, pra poder
|
||||
/// decifrar CCMP do tráfego desse cliente. Só funciona com EAPOL/CCMP
|
||||
/// completo (key_version 2); PMKID sozinho não tem os dois nonces pra
|
||||
/// derivar PTK, e TKIP (key_version 1) usa RC4 + Michael, não implementado.
|
||||
fn derive_tk(hs: &Handshake, password: &str) -> Option<[u8; 16]> {
|
||||
if hs.key_version != 2 {
|
||||
return None;
|
||||
}
|
||||
let ssid = hs.ssid.as_deref().filter(|s| !s.is_empty())?;
|
||||
let bssid = dot11::parse_mac(&hs.bssid)?;
|
||||
let station = dot11::parse_mac(&hs.station)?;
|
||||
let anonce = hs.anonce?;
|
||||
let snonce = hs.snonce?;
|
||||
let pmk = crate::crack::pmk_from_candidate(password, ssid.as_bytes())?;
|
||||
let ptk = crate::crack::compute_ptk(&pmk, &bssid, &station, &anonce, &snonce);
|
||||
let mut tk = [0u8; 16];
|
||||
tk.copy_from_slice(&ptk[32..48]);
|
||||
Some(tk)
|
||||
}
|
||||
|
||||
fn apply_key(hs: &mut Handshake, key: &EapolKey) {
|
||||
match key.message {
|
||||
1 => {
|
||||
|
||||
Reference in new issue
Block a user