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
@@ -55,7 +55,14 @@ pub fn parse(buf: &[u8]) -> Option<EapolKey> {
|
||||
if descriptor_type != 2 && descriptor_type != 254 {
|
||||
return None;
|
||||
}
|
||||
let key_info = u16::from_le_bytes([frame[5], frame[6]]);
|
||||
// Todo campo multi-byte do descritor EAPOL-Key (Key Information, Key
|
||||
// Length, Replay Counter, Key Data Length) é big-endian — é 802.1X,
|
||||
// segue a mesma ordem de rede do cabeçalho EAPOL em si. Ler como
|
||||
// little-endian (bug anterior) embaralhava os bits de ack/mic/secure e
|
||||
// fazia praticamente todo handshake real cair em message=0, descartado
|
||||
// silenciosamente — só não aparecia nos testes porque o gerador de
|
||||
// quadro sintético cometia o mesmo engano, simetricamente.
|
||||
let key_info = u16::from_be_bytes([frame[5], frame[6]]);
|
||||
let key_version = (key_info & 0x0007) as u8;
|
||||
let pairwise = key_info & KEY_INFO_PAIRWISE != 0;
|
||||
let ack = key_info & KEY_INFO_ACK != 0;
|
||||
@@ -75,12 +82,12 @@ pub fn parse(buf: &[u8]) -> Option<EapolKey> {
|
||||
0
|
||||
};
|
||||
|
||||
let replay_counter = u64::from_le_bytes(frame[9..17].try_into().ok()?);
|
||||
let replay_counter = u64::from_be_bytes(frame[9..17].try_into().ok()?);
|
||||
let mut nonce = [0u8; 32];
|
||||
nonce.copy_from_slice(&frame[17..49]);
|
||||
let mut mic_bytes = [0u8; 16];
|
||||
mic_bytes.copy_from_slice(&frame[MIC_OFFSET..MIC_OFFSET + 16]);
|
||||
let key_data_len = u16::from_le_bytes([frame[97], frame[98]]) as usize;
|
||||
let key_data_len = u16::from_be_bytes([frame[97], frame[98]]) as usize;
|
||||
let data_end = KEY_DATA_OFFSET.checked_add(key_data_len)?;
|
||||
if data_end > frame.len() {
|
||||
return None;
|
||||
@@ -144,14 +151,14 @@ mod tests {
|
||||
f[1] = EAPOL_TYPE_KEY;
|
||||
f[2..4].copy_from_slice(&((total - 4) as u16).to_be_bytes());
|
||||
f[4] = 2; // RSN descriptor
|
||||
f[5..7].copy_from_slice(&key_info.to_le_bytes());
|
||||
f[7..9].copy_from_slice(&16u16.to_le_bytes());
|
||||
f[9..17].copy_from_slice(&1u64.to_le_bytes());
|
||||
f[5..7].copy_from_slice(&key_info.to_be_bytes());
|
||||
f[7..9].copy_from_slice(&16u16.to_be_bytes());
|
||||
f[9..17].copy_from_slice(&1u64.to_be_bytes());
|
||||
f[17..49].copy_from_slice(&nonce);
|
||||
for (i, b) in f[MIC_OFFSET..MIC_OFFSET + 16].iter_mut().enumerate() {
|
||||
*b = i as u8;
|
||||
}
|
||||
f[97..99].copy_from_slice(&(key_data.len() as u16).to_le_bytes());
|
||||
f[97..99].copy_from_slice(&(key_data.len() as u16).to_be_bytes());
|
||||
f[KEY_DATA_OFFSET..].copy_from_slice(key_data);
|
||||
f
|
||||
}
|
||||
@@ -176,6 +183,32 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// Byte a byte, independente do helper `key_frame` (que usa a mesma
|
||||
/// codificação do parser): confere que um quadro Mensagem 1 real — key
|
||||
/// info 0x008a (Ack=1, versão CCMP=2, pairwise), big-endian como é no
|
||||
/// fio de verdade — é lido como message=1/key_version=2, não message=0.
|
||||
/// É exatamente o valor citado pela documentação de ferramentas de
|
||||
/// quebra de WPA pra mensagem 1 com CCMP.
|
||||
#[test]
|
||||
fn parses_real_wire_message1_key_info_as_big_endian() {
|
||||
let mut frame = vec![0u8; MIN_KEY_FRAME_LEN];
|
||||
frame[0] = 2; // versão EAPOL
|
||||
frame[1] = EAPOL_TYPE_KEY;
|
||||
frame[2..4].copy_from_slice(&((MIN_KEY_FRAME_LEN - 4) as u16).to_be_bytes());
|
||||
frame[4] = 2; // descritor RSN
|
||||
frame[5..7].copy_from_slice(&0x008au16.to_be_bytes()); // Ack=1, versão=2(CCMP), pairwise=1
|
||||
frame[7..9].copy_from_slice(&16u16.to_be_bytes()); // key length
|
||||
frame[9..17].copy_from_slice(&1u64.to_be_bytes()); // replay counter
|
||||
frame[17..49].fill(0xaa); // nonce
|
||||
frame[97..99].copy_from_slice(&0u16.to_be_bytes()); // sem key data
|
||||
|
||||
let parsed = parse(&frame).expect("mensagem 1 real deveria parsear");
|
||||
assert_eq!(parsed.message, 1);
|
||||
assert_eq!(parsed.key_version, 2);
|
||||
assert!(parsed.pairwise);
|
||||
assert_eq!(parsed.replay_counter, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_pmkid_from_key_data() {
|
||||
let mut data = vec![0xdd, 20, 0x00, 0x0f, 0xac, 0x04];
|
||||
|
||||
Reference in new issue
Block a user