Skip to content

Commit 437ee16

Browse files
committed
Optimized PMKID & Fixed Association Request
1 parent ea28f84 commit 437ee16

File tree

9 files changed

+89
-63
lines changed

9 files changed

+89
-63
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
members = ["libs/libwifi", "libs/libwifi_macros", "libs/pcap-file"]
33

44
[workspace.package]
5-
version = "0.8.20"
5+
version = "0.8.27"
66
authors = ["Ryan Butler"]
77
description = "80211 Attack Tool"
88
license = "GPL"

libs/libwifi/src/frame/components/station_info.rs

+21-12
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,19 @@ pub struct StationInfo {
3737

3838
impl StationInfo {
3939
pub fn encode(&self) -> Vec<u8> {
40+
let mandatory_rates = [6.0f32, 12.0, 24.0];
4041
let mut bytes = Vec::new();
4142

43+
// Encode SSID (if present)
44+
if let Some(ssid) = &self.ssid {
45+
bytes.push(0); // ID
46+
bytes.push(ssid.len() as u8); // Length of SSID
47+
bytes.extend_from_slice(ssid.as_bytes()); // SSID as bytes
48+
} else {
49+
bytes.push(0);
50+
bytes.push(0);
51+
}
52+
4253
if !self.supported_rates.is_empty() {
4354
// Encode Supported Rates
4455
bytes.push(1); // ID
@@ -47,7 +58,11 @@ impl StationInfo {
4758
// Convert rate from Mbps to 500 kbps units and then to a byte
4859
let rate_byte = (rate_mbps * 2.0) as u8;
4960
let rate_byte_with_flag = rate_byte | 0x80; // Setting MSB
50-
bytes.push(rate_byte_with_flag);
61+
if mandatory_rates.contains(&rate_mbps) {
62+
bytes.push(rate_byte_with_flag);
63+
} else {
64+
bytes.push(rate_byte);
65+
}
5166
}
5267
}
5368

@@ -59,20 +74,14 @@ impl StationInfo {
5974
// Convert rate from Mbps to 500 kbps units and then to a byte
6075
let rate_byte = (rate_mbps * 2.0) as u8;
6176
let rate_byte_with_flag = rate_byte | 0x80; // Setting MSB
62-
bytes.push(rate_byte_with_flag);
77+
if mandatory_rates.contains(&rate_mbps) {
78+
bytes.push(rate_byte_with_flag);
79+
} else {
80+
bytes.push(rate_byte);
81+
}
6382
}
6483
}
6584

66-
// Encode SSID (if present)
67-
if let Some(ssid) = &self.ssid {
68-
bytes.push(0); // ID
69-
bytes.push(ssid.len() as u8); // Length of SSID
70-
bytes.extend_from_slice(ssid.as_bytes()); // SSID as bytes
71-
} else {
72-
bytes.push(0);
73-
bytes.push(0);
74-
}
75-
7685
// Encode DS Parameter Set (if present)
7786
if let Some(ds_param) = self.ds_parameter_set {
7887
bytes.push(3); // DS Parameter Set tag number

libs/libwifi/src/frame/management/association.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ impl AssociationRequest {
1919
// Encode the ManagementHeader
2020
bytes.extend(self.header.encode());
2121

22-
// Encode Beacon Interval
23-
bytes.extend_from_slice(&self.beacon_interval.to_le_bytes());
24-
2522
// Encode Capability Info
2623
bytes.extend_from_slice(&self.capability_info.to_le_bytes());
2724

25+
// Encode Beacon Interval
26+
bytes.extend_from_slice(&self.beacon_interval.to_le_bytes());
27+
2828
// Encode Station Info
2929
bytes.extend(self.station_info.encode());
3030

libs/libwifi/src/parsers/frame_types/management.rs

+19-12
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub fn parse_association_request(
3939
/// - Authentication Transaction Sequence Number
4040
/// - Status Code
4141
/// - Challenge Text (optional, dynamic length)
42+
/// - Station Info (optional, dynamic length)
4243
pub fn parse_authentication_frame(
4344
frame_control: FrameControl,
4445
input: &[u8],
@@ -51,19 +52,25 @@ pub fn parse_authentication_frame(
5152
let (input, status_code) = le_u16(input)?;
5253

5354
// Parse the optional challenge text, if present
54-
let (_, challenge_text) = if input.is_empty() {
55-
(input, None)
55+
let (challenge_text, station_info) = if auth_algorithm == 1 {
56+
let (_, challenge_text) = if input.is_empty() {
57+
(input, None)
58+
} else {
59+
let (input, length) = le_u16(input)?;
60+
let (input, text) = take(length)(input)?;
61+
(input, Some(text.to_vec()))
62+
};
63+
(challenge_text, None)
5664
} else {
57-
let (input, length) = le_u16(input)?;
58-
let (input, text) = take(length)(input)?;
59-
(input, Some(text.to_vec()))
60-
};
61-
62-
// Parse station info (extended capabilities) if present
63-
let station_info = if let Ok((input, info)) = parse_station_info(input) {
64-
Some(info)
65-
} else {
66-
None
65+
// Parse station info (extended capabilities) if present
66+
let station_info = if input.is_empty() {
67+
None
68+
} else if let Ok((_, info)) = parse_station_info(input) {
69+
Some(info)
70+
} else {
71+
None
72+
};
73+
(None, station_info)
6774
};
6875

6976
Ok(Frame::Authentication(Authentication {

src/attack.rs

+15-15
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,10 @@ pub fn m1_retrieval_attack(oxide: &mut OxideRuntime, ap_mac: &MacAddress) -> Res
264264
return Ok(());
265265
}
266266

267+
if !ap_data.auth_sequence.cts() {
268+
return Ok(());
269+
}
270+
267271
// Make an authentication frame (no_ack), so we don't over-send.
268272
// This will flip between sending params and not sending, hopefully one of them works.
269273
let frx = if oxide.counters.seq2 % 2 == 0 {
@@ -285,7 +289,7 @@ pub fn m1_retrieval_attack(oxide: &mut OxideRuntime, ap_mac: &MacAddress) -> Res
285289
ap_data.interactions += 1;
286290
oxide.status_log.add_message(StatusMessage::new(
287291
MessageType::Info,
288-
format!("M1 Retrieval - sent authentication [{}]", ap_mac),
292+
format!("M1 Retrieval - Sent Authentication Req [{}]", ap_mac),
289293
));
290294

291295
Ok(())
@@ -297,10 +301,12 @@ pub fn m1_retrieval_attack_phase_2(
297301
client_mac: &MacAddress,
298302
oxide: &mut OxideRuntime,
299303
) -> Result<(), String> {
304+
// Return if PMKID is disabled
300305
if oxide.config.disable_pmkid {
301306
return Ok(());
302307
}
303308

309+
// Return if no-transmit is on
304310
if oxide.config.notx {
305311
return Ok(());
306312
}
@@ -309,30 +315,23 @@ pub fn m1_retrieval_attack_phase_2(
309315
let ap_data = if let Some(ap) = oxide.access_points.get_device(ap_mac) {
310316
ap
311317
} else {
312-
oxide.status_log.add_message(StatusMessage::new(
313-
MessageType::Info,
314-
format!("M1 Retrieval - no AP [{}]", ap_mac),
315-
));
316318
return Ok(());
317319
};
318320

319321
if !oxide.target_data.targets.is_target(ap_data) {
320-
oxide.status_log.add_message(StatusMessage::new(
321-
MessageType::Info,
322-
format!("M1 Retrieval - not a target? [{}]", ap_mac),
323-
));
324322
return Ok(());
325323
}
326324

327325
if oxide.target_data.whitelist.is_whitelisted(ap_data) {
328-
oxide.status_log.add_message(StatusMessage::new(
329-
MessageType::Info,
330-
format!("M1 Retrieval - is whitelisted? [{}]", ap_mac),
331-
));
332326
return Ok(());
333327
}
334328

335-
if oxide.handshake_storage.has_m1_for_ap(ap_mac) {
329+
// if we already have a PMKID, return
330+
if ap_data.has_pmkid {
331+
return Ok(());
332+
}
333+
334+
if !ap_data.auth_sequence.cts() {
336335
return Ok(());
337336
}
338337

@@ -341,6 +340,7 @@ pub fn m1_retrieval_attack_phase_2(
341340
} else {
342341
RsnCipherSuite::CCMP
343342
};
343+
344344
let gs: RsnCipherSuite = if ap_data.information.gs_tkip.is_some_and(|f| f) {
345345
RsnCipherSuite::TKIP
346346
} else {
@@ -361,7 +361,7 @@ pub fn m1_retrieval_attack_phase_2(
361361
ap_data.interactions += 1;
362362
oxide.status_log.add_message(StatusMessage::new(
363363
MessageType::Info,
364-
format!("M1 Retrieval - sent association [{}]", ap_mac),
364+
format!("M1 Retrieval - Sent Association Req [{}]", ap_mac),
365365
));
366366

367367
Ok(())

src/devices.rs

+23-14
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,31 @@ use rand::seq::IteratorRandom;
99
use rand::thread_rng;
1010
use std::collections::HashMap;
1111

12-
use std::time::{Duration, SystemTime, UNIX_EPOCH};
12+
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
1313

1414
use crate::oui::OuiRecord;
1515
use crate::util::{epoch_to_iso_string, epoch_to_string, option_bool_to_json_string, wps_to_json};
1616
use crate::OxideRuntime;
1717

1818
// Constants for timeouts
1919
const CONST_T1_TIMEOUT: Duration = Duration::from_secs(5); // Do not change state unless five seconds has passed.
20-
const CONST_T2_TIMEOUT: Duration = Duration::from_millis(200); // Still need a purpose for this.
20+
const CONST_T2_TIMEOUT: Duration = Duration::from_millis(2); // Still need a purpose for this.
2121

2222
//////////////////////////////////////////////////////////////////////
2323
///
2424
#[derive(Clone, Debug)]
2525
pub struct AuthSequence {
26-
pub t1: SystemTime,
27-
pub t2: SystemTime,
26+
pub t1: Instant,
27+
pub t2: Instant,
2828
pub rogue_mac: MacAddress,
2929
pub state: u8,
3030
}
3131

3232
impl AuthSequence {
3333
fn new(rogue_mac: MacAddress) -> Self {
3434
AuthSequence {
35-
t1: SystemTime::UNIX_EPOCH,
36-
t2: SystemTime::now(),
35+
t1: Instant::now(),
36+
t2: Instant::now(),
3737
rogue_mac: MacAddress::random_with_oui(&rogue_mac),
3838
state: 0,
3939
}
@@ -43,27 +43,36 @@ impl AuthSequence {
4343
// Timer 1 is an interaction timer - elapsed means we have passed 1 second
4444
pub fn is_t1_timeout(&self) -> bool {
4545
self.t1
46-
.elapsed()
47-
.map_or(false, |elapsed| elapsed > CONST_T1_TIMEOUT)
46+
.elapsed() > CONST_T1_TIMEOUT
4847
}
4948

5049
// Checks if CONST_T2_TIMEOUT has elapsed since t2
5150
// Timer 2 is a timer of WHEN state last changed.
5251
pub fn is_t2_timeout(&self) -> bool {
5352
self.t2
54-
.elapsed()
55-
.map_or(false, |elapsed| elapsed > CONST_T2_TIMEOUT)
53+
.elapsed()> CONST_T2_TIMEOUT
54+
}
55+
56+
// Checks if CONST_T2_TIMEOUT has elapsed since t2
57+
// Timer 2 is a timer of WHEN state last changed.
58+
pub fn cts(&mut self) -> bool {
59+
if self.t2.elapsed() > CONST_T2_TIMEOUT {
60+
self.reset_t2();
61+
true
62+
} else {
63+
false
64+
}
5665
}
5766

5867
// Reset t1
59-
pub fn reset_t1(&mut self) -> SystemTime {
60-
self.t1 = SystemTime::now();
68+
pub fn reset_t1(&mut self) -> Instant {
69+
self.t1 = Instant::now();
6170
self.t1
6271
}
6372

6473
// Reset t2
65-
pub fn reset_t2(&mut self) -> SystemTime {
66-
self.t2 = SystemTime::now();
74+
pub fn reset_t2(&mut self) -> Instant {
75+
self.t2 = Instant::now();
6776
self.t2
6877
}
6978

src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1545,6 +1545,7 @@ fn process_frame(oxide: &mut OxideRuntime, packet: &[u8]) -> Result<(), String>
15451545
),
15461546
);
15471547
} else {
1548+
// lets print so we know we see it
15481549
let _ = m1_retrieval_attack_phase_2(
15491550
&ap_addr,
15501551
&oxide.target_data.rogue_client.clone(),

src/tx.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub fn build_authentication_response(
4040
protocol_version: 0,
4141
frame_type: libwifi::FrameType::Management,
4242
frame_subtype: libwifi::FrameSubType::Authentication,
43-
flags: 1u8,
43+
flags: 0u8,
4444
};
4545

4646
let header: ManagementHeader = ManagementHeader {
@@ -80,7 +80,7 @@ pub fn build_authentication_frame_noack(
8080
protocol_version: 0,
8181
frame_type: libwifi::FrameType::Management,
8282
frame_subtype: libwifi::FrameSubType::Authentication,
83-
flags: 1u8,
83+
flags: 0u8,
8484
};
8585

8686
let header: ManagementHeader = ManagementHeader {
@@ -117,7 +117,7 @@ pub fn build_authentication_frame_with_params(
117117
protocol_version: 0,
118118
frame_type: libwifi::FrameType::Management,
119119
frame_subtype: libwifi::FrameSubType::Authentication,
120-
flags: 1u8,
120+
flags: 0u8,
121121
};
122122

123123
let header: ManagementHeader = ManagementHeader {
@@ -229,7 +229,7 @@ pub fn build_association_request_rg(
229229
protocol_version: 0,
230230
frame_type: libwifi::FrameType::Management,
231231
frame_subtype: libwifi::FrameSubType::AssociationRequest,
232-
flags: 1u8,
232+
flags: 0u8,
233233
};
234234

235235
let header: ManagementHeader = ManagementHeader {
@@ -306,7 +306,7 @@ pub fn build_association_request(
306306
protocol_version: 0,
307307
frame_type: libwifi::FrameType::Management,
308308
frame_subtype: libwifi::FrameSubType::AssociationRequest,
309-
flags: 1u8,
309+
flags: 0u8,
310310
};
311311

312312
let header: ManagementHeader = ManagementHeader {

0 commit comments

Comments
 (0)