From 437ee16ce0df4de398c5de3c295242af3af78b34 Mon Sep 17 00:00:00 2001 From: Ragnt Date: Sun, 11 Aug 2024 18:48:16 -0400 Subject: [PATCH] Optimized PMKID & Fixed Association Request --- Cargo.lock | 2 +- Cargo.toml | 2 +- .../src/frame/components/station_info.rs | 33 +++++++++++------ .../src/frame/management/association.rs | 6 +-- .../src/parsers/frame_types/management.rs | 31 ++++++++++------ src/attack.rs | 30 +++++++-------- src/devices.rs | 37 ++++++++++++------- src/main.rs | 1 + src/tx.rs | 10 ++--- 9 files changed, 89 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3acc72f..b79a106 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,7 +69,7 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "angry_oxide" -version = "0.8.20" +version = "0.8.27" dependencies = [ "anyhow", "byteorder", diff --git a/Cargo.toml b/Cargo.toml index 1166b53..23cb448 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ members = ["libs/libwifi", "libs/libwifi_macros", "libs/pcap-file"] [workspace.package] -version = "0.8.20" +version = "0.8.27" authors = ["Ryan Butler"] description = "80211 Attack Tool" license = "GPL" diff --git a/libs/libwifi/src/frame/components/station_info.rs b/libs/libwifi/src/frame/components/station_info.rs index ef979a5..506016f 100644 --- a/libs/libwifi/src/frame/components/station_info.rs +++ b/libs/libwifi/src/frame/components/station_info.rs @@ -37,8 +37,19 @@ pub struct StationInfo { impl StationInfo { pub fn encode(&self) -> Vec { + let mandatory_rates = [6.0f32, 12.0, 24.0]; let mut bytes = Vec::new(); + // Encode SSID (if present) + if let Some(ssid) = &self.ssid { + bytes.push(0); // ID + bytes.push(ssid.len() as u8); // Length of SSID + bytes.extend_from_slice(ssid.as_bytes()); // SSID as bytes + } else { + bytes.push(0); + bytes.push(0); + } + if !self.supported_rates.is_empty() { // Encode Supported Rates bytes.push(1); // ID @@ -47,7 +58,11 @@ impl StationInfo { // Convert rate from Mbps to 500 kbps units and then to a byte let rate_byte = (rate_mbps * 2.0) as u8; let rate_byte_with_flag = rate_byte | 0x80; // Setting MSB - bytes.push(rate_byte_with_flag); + if mandatory_rates.contains(&rate_mbps) { + bytes.push(rate_byte_with_flag); + } else { + bytes.push(rate_byte); + } } } @@ -59,20 +74,14 @@ impl StationInfo { // Convert rate from Mbps to 500 kbps units and then to a byte let rate_byte = (rate_mbps * 2.0) as u8; let rate_byte_with_flag = rate_byte | 0x80; // Setting MSB - bytes.push(rate_byte_with_flag); + if mandatory_rates.contains(&rate_mbps) { + bytes.push(rate_byte_with_flag); + } else { + bytes.push(rate_byte); + } } } - // Encode SSID (if present) - if let Some(ssid) = &self.ssid { - bytes.push(0); // ID - bytes.push(ssid.len() as u8); // Length of SSID - bytes.extend_from_slice(ssid.as_bytes()); // SSID as bytes - } else { - bytes.push(0); - bytes.push(0); - } - // Encode DS Parameter Set (if present) if let Some(ds_param) = self.ds_parameter_set { bytes.push(3); // DS Parameter Set tag number diff --git a/libs/libwifi/src/frame/management/association.rs b/libs/libwifi/src/frame/management/association.rs index 286aab2..88c4069 100644 --- a/libs/libwifi/src/frame/management/association.rs +++ b/libs/libwifi/src/frame/management/association.rs @@ -19,12 +19,12 @@ impl AssociationRequest { // Encode the ManagementHeader bytes.extend(self.header.encode()); - // Encode Beacon Interval - bytes.extend_from_slice(&self.beacon_interval.to_le_bytes()); - // Encode Capability Info bytes.extend_from_slice(&self.capability_info.to_le_bytes()); + // Encode Beacon Interval + bytes.extend_from_slice(&self.beacon_interval.to_le_bytes()); + // Encode Station Info bytes.extend(self.station_info.encode()); diff --git a/libs/libwifi/src/parsers/frame_types/management.rs b/libs/libwifi/src/parsers/frame_types/management.rs index 358313e..bee1782 100644 --- a/libs/libwifi/src/parsers/frame_types/management.rs +++ b/libs/libwifi/src/parsers/frame_types/management.rs @@ -39,6 +39,7 @@ pub fn parse_association_request( /// - Authentication Transaction Sequence Number /// - Status Code /// - Challenge Text (optional, dynamic length) +/// - Station Info (optional, dynamic length) pub fn parse_authentication_frame( frame_control: FrameControl, input: &[u8], @@ -51,19 +52,25 @@ pub fn parse_authentication_frame( let (input, status_code) = le_u16(input)?; // Parse the optional challenge text, if present - let (_, challenge_text) = if input.is_empty() { - (input, None) + let (challenge_text, station_info) = if auth_algorithm == 1 { + let (_, challenge_text) = if input.is_empty() { + (input, None) + } else { + let (input, length) = le_u16(input)?; + let (input, text) = take(length)(input)?; + (input, Some(text.to_vec())) + }; + (challenge_text, None) } else { - let (input, length) = le_u16(input)?; - let (input, text) = take(length)(input)?; - (input, Some(text.to_vec())) - }; - - // Parse station info (extended capabilities) if present - let station_info = if let Ok((input, info)) = parse_station_info(input) { - Some(info) - } else { - None + // Parse station info (extended capabilities) if present + let station_info = if input.is_empty() { + None + } else if let Ok((_, info)) = parse_station_info(input) { + Some(info) + } else { + None + }; + (None, station_info) }; Ok(Frame::Authentication(Authentication { diff --git a/src/attack.rs b/src/attack.rs index e85d38b..588fcf4 100644 --- a/src/attack.rs +++ b/src/attack.rs @@ -264,6 +264,10 @@ pub fn m1_retrieval_attack(oxide: &mut OxideRuntime, ap_mac: &MacAddress) -> Res return Ok(()); } + if !ap_data.auth_sequence.cts() { + return Ok(()); + } + // Make an authentication frame (no_ack), so we don't over-send. // This will flip between sending params and not sending, hopefully one of them works. let frx = if oxide.counters.seq2 % 2 == 0 { @@ -285,7 +289,7 @@ pub fn m1_retrieval_attack(oxide: &mut OxideRuntime, ap_mac: &MacAddress) -> Res ap_data.interactions += 1; oxide.status_log.add_message(StatusMessage::new( MessageType::Info, - format!("M1 Retrieval - sent authentication [{}]", ap_mac), + format!("M1 Retrieval - Sent Authentication Req [{}]", ap_mac), )); Ok(()) @@ -297,10 +301,12 @@ pub fn m1_retrieval_attack_phase_2( client_mac: &MacAddress, oxide: &mut OxideRuntime, ) -> Result<(), String> { + // Return if PMKID is disabled if oxide.config.disable_pmkid { return Ok(()); } + // Return if no-transmit is on if oxide.config.notx { return Ok(()); } @@ -309,30 +315,23 @@ pub fn m1_retrieval_attack_phase_2( let ap_data = if let Some(ap) = oxide.access_points.get_device(ap_mac) { ap } else { - oxide.status_log.add_message(StatusMessage::new( - MessageType::Info, - format!("M1 Retrieval - no AP [{}]", ap_mac), - )); return Ok(()); }; if !oxide.target_data.targets.is_target(ap_data) { - oxide.status_log.add_message(StatusMessage::new( - MessageType::Info, - format!("M1 Retrieval - not a target? [{}]", ap_mac), - )); return Ok(()); } if oxide.target_data.whitelist.is_whitelisted(ap_data) { - oxide.status_log.add_message(StatusMessage::new( - MessageType::Info, - format!("M1 Retrieval - is whitelisted? [{}]", ap_mac), - )); return Ok(()); } - if oxide.handshake_storage.has_m1_for_ap(ap_mac) { + // if we already have a PMKID, return + if ap_data.has_pmkid { + return Ok(()); + } + + if !ap_data.auth_sequence.cts() { return Ok(()); } @@ -341,6 +340,7 @@ pub fn m1_retrieval_attack_phase_2( } else { RsnCipherSuite::CCMP }; + let gs: RsnCipherSuite = if ap_data.information.gs_tkip.is_some_and(|f| f) { RsnCipherSuite::TKIP } else { @@ -361,7 +361,7 @@ pub fn m1_retrieval_attack_phase_2( ap_data.interactions += 1; oxide.status_log.add_message(StatusMessage::new( MessageType::Info, - format!("M1 Retrieval - sent association [{}]", ap_mac), + format!("M1 Retrieval - Sent Association Req [{}]", ap_mac), )); Ok(()) diff --git a/src/devices.rs b/src/devices.rs index de00cc3..12b6b28 100644 --- a/src/devices.rs +++ b/src/devices.rs @@ -9,7 +9,7 @@ use rand::seq::IteratorRandom; use rand::thread_rng; use std::collections::HashMap; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use crate::oui::OuiRecord; use crate::util::{epoch_to_iso_string, epoch_to_string, option_bool_to_json_string, wps_to_json}; @@ -17,14 +17,14 @@ use crate::OxideRuntime; // Constants for timeouts const CONST_T1_TIMEOUT: Duration = Duration::from_secs(5); // Do not change state unless five seconds has passed. -const CONST_T2_TIMEOUT: Duration = Duration::from_millis(200); // Still need a purpose for this. +const CONST_T2_TIMEOUT: Duration = Duration::from_millis(2); // Still need a purpose for this. ////////////////////////////////////////////////////////////////////// /// #[derive(Clone, Debug)] pub struct AuthSequence { - pub t1: SystemTime, - pub t2: SystemTime, + pub t1: Instant, + pub t2: Instant, pub rogue_mac: MacAddress, pub state: u8, } @@ -32,8 +32,8 @@ pub struct AuthSequence { impl AuthSequence { fn new(rogue_mac: MacAddress) -> Self { AuthSequence { - t1: SystemTime::UNIX_EPOCH, - t2: SystemTime::now(), + t1: Instant::now(), + t2: Instant::now(), rogue_mac: MacAddress::random_with_oui(&rogue_mac), state: 0, } @@ -43,27 +43,36 @@ impl AuthSequence { // Timer 1 is an interaction timer - elapsed means we have passed 1 second pub fn is_t1_timeout(&self) -> bool { self.t1 - .elapsed() - .map_or(false, |elapsed| elapsed > CONST_T1_TIMEOUT) + .elapsed() > CONST_T1_TIMEOUT } // Checks if CONST_T2_TIMEOUT has elapsed since t2 // Timer 2 is a timer of WHEN state last changed. pub fn is_t2_timeout(&self) -> bool { self.t2 - .elapsed() - .map_or(false, |elapsed| elapsed > CONST_T2_TIMEOUT) + .elapsed()> CONST_T2_TIMEOUT + } + + // Checks if CONST_T2_TIMEOUT has elapsed since t2 + // Timer 2 is a timer of WHEN state last changed. + pub fn cts(&mut self) -> bool { + if self.t2.elapsed() > CONST_T2_TIMEOUT { + self.reset_t2(); + true + } else { + false + } } // Reset t1 - pub fn reset_t1(&mut self) -> SystemTime { - self.t1 = SystemTime::now(); + pub fn reset_t1(&mut self) -> Instant { + self.t1 = Instant::now(); self.t1 } // Reset t2 - pub fn reset_t2(&mut self) -> SystemTime { - self.t2 = SystemTime::now(); + pub fn reset_t2(&mut self) -> Instant { + self.t2 = Instant::now(); self.t2 } diff --git a/src/main.rs b/src/main.rs index 83668a2..019d37f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1545,6 +1545,7 @@ fn process_frame(oxide: &mut OxideRuntime, packet: &[u8]) -> Result<(), String> ), ); } else { + // lets print so we know we see it let _ = m1_retrieval_attack_phase_2( &ap_addr, &oxide.target_data.rogue_client.clone(), diff --git a/src/tx.rs b/src/tx.rs index 6b93511..d0acdc9 100644 --- a/src/tx.rs +++ b/src/tx.rs @@ -40,7 +40,7 @@ pub fn build_authentication_response( protocol_version: 0, frame_type: libwifi::FrameType::Management, frame_subtype: libwifi::FrameSubType::Authentication, - flags: 1u8, + flags: 0u8, }; let header: ManagementHeader = ManagementHeader { @@ -80,7 +80,7 @@ pub fn build_authentication_frame_noack( protocol_version: 0, frame_type: libwifi::FrameType::Management, frame_subtype: libwifi::FrameSubType::Authentication, - flags: 1u8, + flags: 0u8, }; let header: ManagementHeader = ManagementHeader { @@ -117,7 +117,7 @@ pub fn build_authentication_frame_with_params( protocol_version: 0, frame_type: libwifi::FrameType::Management, frame_subtype: libwifi::FrameSubType::Authentication, - flags: 1u8, + flags: 0u8, }; let header: ManagementHeader = ManagementHeader { @@ -229,7 +229,7 @@ pub fn build_association_request_rg( protocol_version: 0, frame_type: libwifi::FrameType::Management, frame_subtype: libwifi::FrameSubType::AssociationRequest, - flags: 1u8, + flags: 0u8, }; let header: ManagementHeader = ManagementHeader { @@ -306,7 +306,7 @@ pub fn build_association_request( protocol_version: 0, frame_type: libwifi::FrameType::Management, frame_subtype: libwifi::FrameSubType::AssociationRequest, - flags: 1u8, + flags: 0u8, }; let header: ManagementHeader = ManagementHeader {