From 06c3c5a3d3d1b8f83c95b20b964895cf2d058a16 Mon Sep 17 00:00:00 2001 From: Ryan Rage Date: Wed, 17 Jan 2024 11:26:57 -0500 Subject: [PATCH] whitelisting inital (probably buggy) push --- src/attack.rs | 28 +++++ src/devices.rs | 29 +++++- src/main.rs | 104 +++++++++++++++++-- src/targets.rs | 25 +++-- src/ui.rs | 3 +- src/whitelist.rs | 260 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 429 insertions(+), 20 deletions(-) create mode 100644 src/whitelist.rs diff --git a/src/attack.rs b/src/attack.rs index 3592923..3b70a5a 100644 --- a/src/attack.rs +++ b/src/attack.rs @@ -49,6 +49,10 @@ pub fn csa_attack(oxide: &mut OxideRuntime, beacon: Beacon) -> Result<(), String return Ok(()); } + if oxide.target_data.whitelist.is_whitelisted(ap_data) { + return Ok(()); + } + // If we already have a 4whs, don't continue. if oxide .handshake_storage @@ -111,6 +115,10 @@ pub fn m1_retrieval_attack(oxide: &mut OxideRuntime, ap_mac: &MacAddress) -> Res return Ok(()); } + if oxide.target_data.whitelist.is_whitelisted(ap_data) { + return Ok(()); + } + // If we already have a 4whs, don't continue. if oxide .handshake_storage @@ -182,6 +190,10 @@ pub fn m1_retrieval_attack_phase_2( return Ok(()); } + if oxide.target_data.whitelist.is_whitelisted(ap_data) { + return Ok(()); + } + // Is our sequence state 1? if ap_data.auth_sequence.state != 1 { return Ok(()); @@ -252,6 +264,10 @@ pub fn deauth_attack(oxide: &mut OxideRuntime, ap_mac: &MacAddress) -> Result<() return Ok(()); } + if oxide.target_data.whitelist.is_whitelisted(ap_data) { + return Ok(()); + } + if oxide .handshake_storage .has_complete_handshake_for_ap(ap_mac) @@ -356,6 +372,10 @@ pub fn anon_reassociation_attack( return Ok(()); } + if oxide.target_data.whitelist.is_whitelisted(ap) { + return Ok(()); + } + let pcs = if ap.information.cs_ccmp.is_some_and(|x| x) { RsnCipherSuite::CCMP } else if ap.information.cs_tkip.is_some_and(|x| x) { @@ -434,6 +454,10 @@ pub fn rogue_m2_attack_directed( // If we have an AP for this SSID, we will use as many of the same details as possible if let Some(ap) = oxide.access_points.get_device_by_ssid(&ssid) { + if oxide.target_data.whitelist.is_whitelisted(ap) { + return Ok(()); + } + // Make sure this AP is a target and that this AP is if !oxide.target_data.targets.is_target(ap) || oxide @@ -498,6 +522,10 @@ pub fn rogue_m2_attack_undirected( { return Ok(()); } + + if oxide.target_data.whitelist.is_whitelisted(ap) { + return Ok(()); + } } // Do we already have a rogue-M2 from this station (for this SSID) diff --git a/src/devices.rs b/src/devices.rs index bee5264..1a4205c 100644 --- a/src/devices.rs +++ b/src/devices.rs @@ -98,6 +98,7 @@ pub struct AccessPoint { pub has_hs: bool, pub has_pmkid: bool, pub is_target: bool, + pub is_whitelisted: bool, } impl WiFiDeviceType for AccessPoint {} @@ -124,6 +125,7 @@ impl Default for AccessPoint { has_hs: false, has_pmkid: false, is_target: false, + is_whitelisted: false, } } } @@ -167,6 +169,7 @@ impl AccessPoint { has_hs: false, has_pmkid: false, is_target: false, + is_whitelisted: false, } } @@ -208,6 +211,7 @@ impl AccessPoint { has_hs: false, has_pmkid: false, is_target: false, + is_whitelisted: false, } } @@ -241,6 +245,11 @@ impl AccessPoint { pub fn is_target(&self) -> bool { self.is_target } + + pub fn is_whitelisted(&self) -> bool { + self.is_whitelisted + } + } #[derive(Clone, Debug, Default)] @@ -566,7 +575,23 @@ impl WiFiDeviceList { .map(|(_, access_point)| access_point) .collect(); match sort { - 0 => access_points.sort_by_key(|b| std::cmp::Reverse(b.is_target())), // TGT + 0 => access_points.sort_by(|a, b| { // TGT + match (a.is_target(), a.is_whitelisted(), b.is_target(), b.is_whitelisted()) { + // Highest priority: is_target() = true, is_whitelist() = false + (true, false, _, _) => std::cmp::Ordering::Less, + (_, _, true, false) => std::cmp::Ordering::Greater, + + // Middle priority: is_target() = false, is_whitelist() = false + (false, false, false, true) => std::cmp::Ordering::Less, + (false, true, false, false) => std::cmp::Ordering::Greater, + + // Lowest priority: is_target() = false, is_whitelist() = true + // This case is covered implicitly by the previous matches + + // Fallback for equal cases + _ => std::cmp::Ordering::Equal, + } + }), 1 => access_points.sort_by(|a, b| b.channel.cmp(&a.channel)), // CH 2 => access_points.sort_by(|a, b| { // RSSI @@ -602,7 +627,7 @@ impl WiFiDeviceList { let mut rows: Vec<(Vec, u16)> = Vec::new(); for (idx, ap) in access_points.iter().enumerate() { let mut ap_row = vec![ - format!("{}", if ap.is_target() { "\u{274E}" } else { "" }), // TGT + format!("{}", if ap.is_target() { "\u{274E}" } else if ap.is_whitelisted() { "\u{2B1C}" } else { "" }), // TGT format!("{}", ap.mac_address), // MAC Address ap.channel .as_ref() diff --git a/src/main.rs b/src/main.rs index 315f7b1..4f084dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ mod snowstorm; mod status; mod tabbedblock; mod targets; +mod whitelist; mod tx; mod ui; mod util; @@ -42,7 +43,7 @@ use nix::unistd::geteuid; use nl80211_ng::attr::Nl80211Iftype; use nl80211_ng::channels::{map_str_to_band_and_channel, WiFiBand, WiFiChannel}; -use nl80211_ng::{set_interface_chan, Interface, Nl80211}; +use nl80211_ng::{set_interface_chan, Interface, Nl80211, get_interface_info_idx}; use flate2::write::GzEncoder; use flate2::Compression; @@ -64,6 +65,7 @@ use tx::{ }; use ui::UiState; use uuid::Uuid; +use whitelist::WhiteList; use crate::ascii::get_art; use crate::auth::HandshakeStorage; @@ -74,6 +76,7 @@ use crate::snowstorm::Snowstorm; use crate::status::*; use crate::ui::{print_ui, MenuType}; use crate::util::parse_ip_address_port; +use crate::whitelist::{White, WhiteMAC, WhiteSSID}; use libwifi::{Addresses, Frame}; @@ -113,6 +116,9 @@ struct Arguments { /// Optional - Target (MAC or SSID) to attack - will attack everything if none specified. target: Option>, #[arg(short, long)] + /// Optional - Whitelist (MAC or SSID) to NOT attack. + whitelist: Option>, + #[arg(short, long)] /// Optional - Output filename. output: Option, #[arg(short, long)] @@ -228,6 +234,7 @@ pub struct IfHardware { } pub struct TargetData { + whitelist: WhiteList, targets: TargetList, rogue_client: MacAddress, rogue_m1: EapolKey, @@ -264,6 +271,7 @@ impl OxideRuntime { let rogue = cli_args.rogue.clone(); let interface_name = cli_args.interface.clone(); let targets = cli_args.target.clone(); + let wh_list = cli_args.whitelist.clone(); let mut notransmit = cli_args.notransmit.clone(); // Setup initial lists / logs @@ -339,6 +347,61 @@ impl OxideRuntime { let targ_list = TargetList::from_vec(target_vec.clone()); + // Setup Whitelist + let whitelist_vec: Vec = if let Some(vec_whitelist) = wh_list { + vec_whitelist + .into_iter() + .filter_map(|f| { + match MacAddress::from_str(&f) { + Ok(mac) => { + if targ_list.is_target_mac(&mac) { + println!("Whitelist {} is a target. Cannot add to whitelist.", mac); + None + } else { + Some(White::MAC(WhiteMAC::new(mac))) + } + }, + Err(_) => { + if targ_list.is_target_ssid(&f) { + println!("Whitelist {} is a target. Cannot add to whitelist.", f); + None + } else { + Some(White::SSID(WhiteSSID::new(&f))) + } + }, + } + }) + .collect() + } else { + vec![] + }; + + if !whitelist_vec.is_empty() { + println!(); + println!("========= White List ========="); + for (index, device) in whitelist_vec.iter().enumerate() { + let tree = if index == whitelist_vec.len() - 1 { + "└" + } else { + "├" + }; + match device { + White::MAC(dev) => { + println!(" {} MAC: {}", tree, dev.addr) + } + White::SSID(dev) => { + println!(" {} SSID: {}", tree, dev.ssid) + } + } + } + println!("========== Total: {:<2} ==========", whitelist_vec.len()); + println!(); + } else { + println!("💲 No whitelist list provided."); + } + + let white_list = WhiteList::from_vec(whitelist_vec.clone()); + ///////////////////////////////////////////////////////////////////// //// Setup Channels //// @@ -698,6 +761,7 @@ impl OxideRuntime { }; let target_data: TargetData = TargetData { + whitelist: white_list, targets: targ_list, rogue_client, rogue_m1, @@ -927,20 +991,30 @@ fn process_frame(oxide: &mut OxideRuntime, packet: &[u8]) -> Result<(), String> ), ); + // Proliferate whitelist + let _ = oxide.target_data.whitelist.get_whitelisted(ap); + // Proliferate the SSID / MAC to targets (if this is a target) // Also handle adding the target channel to autohunt params. - if let Ok(targets) = oxide.target_data.targets.get_targets(ap) { + + let targets = oxide.target_data.targets.get_targets(ap); + if !targets.is_empty() { + // This is a target_data target if let Some(channel) = station_info.ds_parameter_set { + // We have a channel in the broadcast (real channel) if oxide.config.autohunt && oxide .if_hardware .hop_channels .contains(&(band.to_u8(), channel)) { + // We are autohunting and our current channel is real (band/channel match) for target in targets { + // Go through all the target matches we got (which could be a Glob SSID, Match SSID, and MAC!) if let Some(vec) = oxide.if_hardware.target_chans.get_mut(&target) { + // This target is inside hop_chans // Update the target with this band/channel (if it isn't already there) if !vec.contains(&(band.to_u8(), channel)) { vec.push((band.to_u8(), channel)); @@ -1121,9 +1195,15 @@ fn process_frame(oxide: &mut OxideRuntime, packet: &[u8]) -> Result<(), String> oxide.target_data.rogue_client, ), ); + + // Proliferate whitelist + let _ = oxide.target_data.whitelist.get_whitelisted(ap); + // Proliferate the SSID / MAC to targets (if this is a target) // Also handle adding the target channel to autohunt params. - if let Ok(targets) = oxide.target_data.targets.get_targets(ap) { + + let targets = oxide.target_data.targets.get_targets(ap); + if !targets.is_empty() { // This is a target_data target if let Some(channel) = station_info.ds_parameter_set { // We have a channel in the broadcast (real channel) @@ -2275,7 +2355,7 @@ fn main() -> Result<(), Box> { let start_time = Instant::now(); - let mut err: Option = None; + let mut err: Option = None; let mut exit_on_succ = false; let mut terminal = Terminal::new(CrosstermBackend::new(stdout())).expect("Cannot allocate terminal"); @@ -2296,6 +2376,18 @@ fn main() -> Result<(), Box> { } while running.load(Ordering::SeqCst) { + // Update our interface + oxide.if_hardware.interface = match get_interface_info_idx(oxide.if_hardware.interface.index.unwrap()) { + Ok(interface) => interface, + Err(e) => { + // Uh oh... no interface + err = Some(e); + running.store(false, Ordering::SeqCst); + break; + } + }; + + // Handle Hunting let target_chans = oxide.if_hardware.target_chans.clone(); if oxide.config.autohunt @@ -2462,8 +2554,8 @@ fn main() -> Result<(), Box> { } Err(code) => { // This will result in "a serious packet read error" message. - err = Some(code.kind()); - running.store(false, Ordering::SeqCst); + // err = Some(code.kind()); + // running.store(false, Ordering::SeqCst); } }; diff --git a/src/targets.rs b/src/targets.rs index c07c1e7..960a6f1 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -117,7 +117,9 @@ impl TargetList { })); } } - ap.is_target = true; + if !ap.is_whitelisted() { + ap.is_target = true; + } return true; } } @@ -128,7 +130,9 @@ impl TargetList { addr: ap.mac_address, })) } - ap.is_target = true; + if !ap.is_whitelisted() { + ap.is_target = true; + } return true; } } @@ -137,9 +141,9 @@ impl TargetList { false } - pub fn get_targets(&mut self, ap: &mut AccessPoint) -> Result, ()> { + pub fn get_targets(&mut self, ap: &mut AccessPoint) -> Vec { if self.empty() { - return Err(()); + return vec![]; }; let mut matches: Vec = Vec::new(); @@ -154,7 +158,9 @@ impl TargetList { })); } } - ap.is_target = true; + if !ap.is_whitelisted() { + ap.is_target = true; + } matches.push(target); } } @@ -165,16 +171,15 @@ impl TargetList { addr: ap.mac_address, })) } - ap.is_target = true; + if !ap.is_whitelisted() { + ap.is_target = true; + } matches.push(target); } } } } - if !matches.is_empty() { - return Ok(matches); - } - Err(()) + return matches; } pub fn is_target_mac(&self, mac: &MacAddress) -> bool { diff --git a/src/ui.rs b/src/ui.rs index 3fc6eef..47a6ff6 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -244,6 +244,7 @@ pub fn print_ui( ) -> Result<()> { terminal.hide_cursor()?; terminal.draw(|frame| { + if frame.size().width < 105 || frame.size().height < 20 { let area = frame.size(); @@ -326,8 +327,6 @@ fn create_status_bar( start_time: Instant, framerate: u64, ) { - oxide.if_hardware.interface = - get_interface_info_idx(oxide.if_hardware.interface.index.unwrap()).unwrap(); // Top Bar Layout let top_layout: std::rc::Rc<[Rect]> = Layout::default() diff --git a/src/whitelist.rs b/src/whitelist.rs new file mode 100644 index 0000000..be10d99 --- /dev/null +++ b/src/whitelist.rs @@ -0,0 +1,260 @@ +use globset::Glob; +use libwifi::frame::components::MacAddress; +use rand::seq::SliceRandom; + +use crate::devices::AccessPoint; + +trait IsWhitelisted { + fn whitelist_match(&self, ap: &AccessPoint) -> bool; +} + +#[derive(Eq, PartialEq, Hash, Clone, Debug)] +pub struct WhiteSSID { + pub ssid: String, +} + +impl IsWhitelisted for WhiteSSID { + fn whitelist_match(&self, ap: &AccessPoint) -> bool { + if let Some(ssid) = ap.ssid.clone() { + if Glob::new(&self.ssid) + .unwrap() + .compile_matcher() + .is_match(ssid) + { + return true; + } + } + false + } +} + +impl WhiteSSID { + pub fn new(ssid: &str) -> Self { + WhiteSSID { + ssid: ssid.to_owned(), + } + } + + fn match_ssid(&self, ssid: String) -> bool { + if ssid == self.ssid { + return true; + } + false + } +} + +#[derive(Eq, PartialEq, Hash, Clone, Debug)] +pub struct WhiteMAC { + pub addr: MacAddress, +} + +impl IsWhitelisted for WhiteMAC { + fn whitelist_match(&self, ap: &AccessPoint) -> bool { + if ap.mac_address == self.addr { + return true; + } + false + } +} + +impl WhiteMAC { + pub fn new(addr: MacAddress) -> Self { + WhiteMAC { addr } + } +} +#[derive(Eq, PartialEq, Hash, Clone, Debug)] +pub enum White { + MAC(WhiteMAC), + SSID(WhiteSSID), +} + +impl White { + pub fn get_string(&self) -> String { + match self { + White::MAC(tgt) => tgt.addr.to_string(), + White::SSID(tgt) => tgt.ssid.clone(), + } + } +} + +pub struct WhiteList { + devices: Vec, +} + +impl WhiteList { + pub fn new() -> Self { + WhiteList { + devices: Vec::new(), + } + } + + pub fn from_vec(devices: Vec) -> Self { + WhiteList { devices } + } + + pub fn add(&mut self, device: White) { + self.devices.push(device); + } + + pub fn empty(&self) -> bool { + self.devices.is_empty() + } + + /// Will check if the AP is a target, but will also mark the + pub fn is_whitelisted(&mut self, ap: &mut AccessPoint) -> bool { + if self.empty() { + return false; + }; + + for device in &self.devices { + match device { + White::MAC(tgt) => { + if tgt.whitelist_match(ap) { + if let Some(ssid) = &ap.ssid { + if !self.is_whitelisted_ssid(ssid) { + self.add(White::SSID(WhiteSSID { + ssid: ssid.to_string(), + })); + } + } + if ! ap.is_target() { + ap.is_whitelisted = true; + } + return true; + } + } + White::SSID(tgt) => { + if tgt.whitelist_match(ap) { + if !self.is_whitelisted_mac(&ap.mac_address) { + self.add(White::MAC(WhiteMAC { + addr: ap.mac_address, + })) + } + if ! ap.is_target() { + ap.is_whitelisted = true; + } + return true; + } + } + } + } + false + } + + pub fn get_whitelisted(&mut self, ap: &mut AccessPoint) -> Vec { + if self.empty() { + return vec![]; + }; + let mut matches: Vec = Vec::new(); + + for target in self.devices.clone() { + match target { + White::MAC(ref tgt) => { + if tgt.whitelist_match(ap) { + if let Some(ssid) = &ap.ssid { + if !self.is_whitelisted_ssid(ssid) { + self.add(White::SSID(WhiteSSID { + ssid: ssid.to_string(), + })); + } + } + if ! ap.is_target() { + ap.is_whitelisted = true; + } + matches.push(target); + } + } + White::SSID(ref tgt) => { + if tgt.whitelist_match(ap) { + if !self.is_whitelisted_mac(&ap.mac_address) { + self.add(White::MAC(WhiteMAC { + addr: ap.mac_address, + })) + } + if ! ap.is_target() { + ap.is_whitelisted = true; + } + matches.push(target); + } + } + } + } + return matches; + } + + pub fn is_whitelisted_mac(&self, mac: &MacAddress) -> bool { + if self.empty() { + return true; + }; + + for target in &self.devices { + match target { + White::MAC(tgt) => { + if tgt.addr == *mac { + return true; + } + } + White::SSID(_) => {} // do nothing + } + } + false + } + + pub fn is_whitelisted_ssid(&self, ssid: &str) -> bool { + if self.empty() { + return true; + }; + + for target in &self.devices { + match target { + White::MAC(_) => {} // do nothing, we don't have anything to compare to here. + White::SSID(tgt) => { + if tgt.match_ssid(ssid.to_owned()) { + return true; + } + } + } + } + false + } + + pub fn has_ssid(&self) -> bool { + for device in &self.devices { + match device { + White::MAC(_) => continue, + White::SSID(_) => return true, + } + } + false + } + + pub fn get_random_ssid(&self) -> Option { + if self.empty() { + return None; + } + if !self.has_ssid() { + return None; + } + loop { + let tgt = self.devices.choose(&mut rand::thread_rng()).unwrap(); + if let White::SSID(tgt) = tgt { + return Some(tgt.ssid.clone()); + } + } + } + + pub fn get_string(&self) -> String { + self.devices + .iter() + .map(|target| match target { + White::MAC(mac_target) => format!("MAC: {}", mac_target.addr), + White::SSID(ssid_target) => format!("SSID: {}", ssid_target.ssid), + }) + .collect::>() + .join(", ") + } + + pub fn get_ref(&self) -> &Vec { + &self.devices + } +}