mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-04-26 07:29:59 -07:00
lib: add analyzer for a null cipher being set
This commit is contained in:
@@ -4,7 +4,7 @@ use serde::Serialize;
|
||||
|
||||
use crate::{diag::MessagesContainer, gsmtap_parser};
|
||||
|
||||
use super::{imsi_provided::ImsiProvidedAnalyzer, information_element::InformationElement, lte_downgrade::LteSib6And7DowngradeAnalyzer};
|
||||
use super::{imsi_provided::ImsiProvidedAnalyzer, information_element::InformationElement, lte_downgrade::LteSib6And7DowngradeAnalyzer, null_cipher::NullCipherAnalyzer};
|
||||
|
||||
/// Qualitative measure of how severe a Warning event type is.
|
||||
/// The levels should break down like this:
|
||||
@@ -101,6 +101,7 @@ impl Harness {
|
||||
let mut harness = Harness::new();
|
||||
harness.add_analyzer(Box::new(LteSib6And7DowngradeAnalyzer{}));
|
||||
harness.add_analyzer(Box::new(ImsiProvidedAnalyzer{}));
|
||||
harness.add_analyzer(Box::new(NullCipherAnalyzer{}));
|
||||
harness
|
||||
}
|
||||
|
||||
|
||||
@@ -2,3 +2,4 @@ pub mod analyzer;
|
||||
pub mod information_element;
|
||||
pub mod lte_downgrade;
|
||||
pub mod imsi_provided;
|
||||
pub mod null_cipher;
|
||||
|
||||
115
lib/src/analysis/null_cipher.rs
Normal file
115
lib/src/analysis/null_cipher.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use telcom_parser::lte_rrc::{CipheringAlgorithm_r12, DL_CCCH_MessageType, DL_CCCH_MessageType_c1, DL_DCCH_MessageType, DL_DCCH_MessageType_c1, PCCH_MessageType, PCCH_MessageType_c1, PagingUE_Identity, RRCConnectionReconfiguration, RRCConnectionReconfigurationCriticalExtensions, RRCConnectionReconfigurationCriticalExtensions_c1, RRCConnectionReconfiguration_r8_IEs, RRCConnectionRelease_v890_IEs, SCG_Configuration_r12, SecurityConfigHO_v1530HandoverType_v1530, SecurityModeCommand, SecurityModeCommandCriticalExtensions, SecurityModeCommandCriticalExtensions_c1};
|
||||
|
||||
use super::analyzer::{Analyzer, Event, EventType, Severity};
|
||||
use super::information_element::{InformationElement, LteInformationElement};
|
||||
|
||||
pub struct NullCipherAnalyzer {
|
||||
}
|
||||
|
||||
impl NullCipherAnalyzer {
|
||||
fn check_rrc_connection_reconfiguration_cipher(&self, reconfiguration: &RRCConnectionReconfiguration) -> bool {
|
||||
let RRCConnectionReconfigurationCriticalExtensions::C1(c1) = &reconfiguration.critical_extensions else {
|
||||
return false;
|
||||
};
|
||||
let RRCConnectionReconfigurationCriticalExtensions_c1::RrcConnectionReconfiguration_r8(c1) = c1 else {
|
||||
return false;
|
||||
};
|
||||
if let Some(handover) = &c1.security_config_ho {
|
||||
let maybe_security_config = match &handover.handover_type {
|
||||
telcom_parser::lte_rrc::SecurityConfigHOHandoverType::IntraLTE(lte) => lte.security_algorithm_config.as_ref(),
|
||||
telcom_parser::lte_rrc::SecurityConfigHOHandoverType::InterRAT(rat) => Some(&rat.security_algorithm_config),
|
||||
};
|
||||
if let Some(security_config) = maybe_security_config {
|
||||
if security_config.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Use map/flatten to dig into a long chain of nested Option types
|
||||
let maybe_v1250 = c1.non_critical_extension.as_ref()
|
||||
.map(|v890| v890.non_critical_extension.as_ref()).flatten()
|
||||
.map(|v920| v920.non_critical_extension.as_ref()).flatten()
|
||||
.map(|v1020| v1020.non_critical_extension.as_ref()).flatten()
|
||||
.map(|v1130| v1130.non_critical_extension.as_ref()).flatten();
|
||||
let Some(v1250) = maybe_v1250 else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if let Some(SCG_Configuration_r12::Setup(scg_setup)) = v1250.scg_configuration_r12.as_ref() {
|
||||
let maybe_cipher = scg_setup.scg_config_part_scg_r12.as_ref()
|
||||
.map(|scg| scg.mobility_control_info_scg_r12.as_ref()).flatten()
|
||||
.map(|mci| mci.ciphering_algorithm_scg_r12.as_ref()).flatten();
|
||||
if let Some(cipher) = maybe_cipher {
|
||||
if cipher.0 == CipheringAlgorithm_r12::EEA0 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let maybe_v1530_security_config = v1250.non_critical_extension.as_ref()
|
||||
.map(|v1310| v1310.non_critical_extension.as_ref()).flatten()
|
||||
.map(|v1430| v1430.non_critical_extension.as_ref()).flatten()
|
||||
.map(|v1510| v1510.non_critical_extension.as_ref()).flatten()
|
||||
.map(|v1530| v1530.security_config_ho_v1530.as_ref()).flatten();
|
||||
let Some(v1530_security_config) = maybe_v1530_security_config else {
|
||||
return false;
|
||||
};
|
||||
let maybe_security_algorithm = match &v1530_security_config.handover_type_v1530 {
|
||||
SecurityConfigHO_v1530HandoverType_v1530::Intra5GC(intra_5gc) => intra_5gc.security_algorithm_config_r15.as_ref(),
|
||||
SecurityConfigHO_v1530HandoverType_v1530::Fivegc_ToEPC(to_epc) => Some(&to_epc.security_algorithm_config_r15),
|
||||
SecurityConfigHO_v1530HandoverType_v1530::Epc_To5GC(to_5gc) => Some(&to_5gc.security_algorithm_config_r15),
|
||||
};
|
||||
if let Some(security_algorithm) = maybe_security_algorithm {
|
||||
if security_algorithm.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn check_security_mode_command_cipher(&self, command: &SecurityModeCommand) -> bool {
|
||||
let SecurityModeCommandCriticalExtensions::C1(c1) = &command.critical_extensions else {
|
||||
return false;
|
||||
};
|
||||
let SecurityModeCommandCriticalExtensions_c1::SecurityModeCommand_r8(r8) = &c1 else {
|
||||
return false;
|
||||
};
|
||||
if r8.security_config_smc.security_algorithm_config.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Analyzer for NullCipherAnalyzer {
|
||||
fn get_name(&self) -> Cow<str> {
|
||||
Cow::from("Null Cipher")
|
||||
}
|
||||
|
||||
fn get_description(&self) -> Cow<str> {
|
||||
Cow::from("Tests whether the cell suggests using a null cipher (EEA0)")
|
||||
}
|
||||
|
||||
fn analyze_information_element(&mut self, ie: &InformationElement) -> Option<Event> {
|
||||
let InformationElement::LTE(LteInformationElement::DlDcch(dcch_msg)) = ie else {
|
||||
return None;
|
||||
};
|
||||
let DL_DCCH_MessageType::C1(c1) = &dcch_msg.message else {
|
||||
return None;
|
||||
};
|
||||
let null_cipher_detected = match c1 {
|
||||
DL_DCCH_MessageType_c1::RrcConnectionReconfiguration(reconfiguration) => self.check_rrc_connection_reconfiguration_cipher(reconfiguration),
|
||||
DL_DCCH_MessageType_c1::SecurityModeCommand(command) => self.check_security_mode_command_cipher(command),
|
||||
_ => return None,
|
||||
};
|
||||
if null_cipher_detected {
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning { severity: Severity::High },
|
||||
message: "Cell suggested use of null cipher".to_string(),
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user