mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-05-05 11:29:08 -07:00
165 lines
6.1 KiB
Rust
165 lines
6.1 KiB
Rust
use std::borrow::Cow;
|
|
|
|
use telcom_parser::lte_rrc::{
|
|
CipheringAlgorithm_r12, DL_DCCH_MessageType, DL_DCCH_MessageType_c1,
|
|
RRCConnectionReconfiguration, RRCConnectionReconfigurationCriticalExtensions,
|
|
RRCConnectionReconfigurationCriticalExtensions_c1, 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
|
|
&& 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()
|
|
.and_then(|v890| v890.non_critical_extension.as_ref())
|
|
.and_then(|v920| v920.non_critical_extension.as_ref())
|
|
.and_then(|v1020| v1020.non_critical_extension.as_ref())
|
|
.and_then(|v1130| v1130.non_critical_extension.as_ref());
|
|
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()
|
|
.and_then(|scg| scg.mobility_control_info_scg_r12.as_ref())
|
|
.and_then(|mci| mci.ciphering_algorithm_scg_r12.as_ref());
|
|
if let Some(cipher) = maybe_cipher
|
|
&& cipher.0 == CipheringAlgorithm_r12::EEA0
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
let maybe_v1530_security_config = v1250
|
|
.non_critical_extension
|
|
.as_ref()
|
|
.and_then(|v1310| v1310.non_critical_extension.as_ref())
|
|
.and_then(|v1430| v1430.non_critical_extension.as_ref())
|
|
.and_then(|v1510| v1510.non_critical_extension.as_ref())
|
|
.and_then(|v1530| v1530.security_config_ho_v1530.as_ref());
|
|
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
|
|
&& 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 get_version(&self) -> u32 {
|
|
1
|
|
}
|
|
|
|
fn analyze_information_element(&mut self, ie: &InformationElement) -> Option<Event> {
|
|
let dcch_msg = match ie {
|
|
InformationElement::LTE(lte_ie) => match &**lte_ie {
|
|
LteInformationElement::DlDcch(dcch_msg) => dcch_msg,
|
|
_ => return None,
|
|
},
|
|
_ => 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
|
|
}
|
|
}
|