mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-05-31 10:13:35 -07:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| afeda3875d | |||
| ced4090be5 | |||
| 1471bb6f0b | |||
| ebc0ddb6b3 |
@@ -7,8 +7,13 @@ use super::analyzer::{Analyzer, Event, EventType};
|
|||||||
use super::information_element::{InformationElement, LteInformationElement};
|
use super::information_element::{InformationElement, LteInformationElement};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
|
use pycrate_rs::nas::generated::emm::emm_attach_reject::EMMCauseEMMCause as AttachRejectEMMCause;
|
||||||
|
use pycrate_rs::nas::generated::emm::emm_attach_request::TAI;
|
||||||
|
use telcom_parser::lte_rrc::{BCCH_DL_SCH_MessageType, BCCH_DL_SCH_MessageType_c1};
|
||||||
|
use telcom_parser::lte_rrc::{MCC_MNC_Digit, PLMN_Identity, PLMN_IdentityList};
|
||||||
use telcom_parser::lte_rrc::{
|
use telcom_parser::lte_rrc::{
|
||||||
DL_DCCH_MessageType, DL_DCCH_MessageType_c1, UL_CCCH_MessageType, UL_CCCH_MessageType_c1,
|
/* DL_DCCH_MessageType, DL_DCCH_MessageType_c1,*/ UL_CCCH_MessageType,
|
||||||
|
UL_CCCH_MessageType_c1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const TIMEOUT_THRESHHOLD: usize = 50;
|
const TIMEOUT_THRESHHOLD: usize = 50;
|
||||||
@@ -26,6 +31,8 @@ pub struct ImsiRequestedAnalyzer {
|
|||||||
state: State,
|
state: State,
|
||||||
timeout_counter: usize,
|
timeout_counter: usize,
|
||||||
flag: Option<Event>,
|
flag: Option<Event>,
|
||||||
|
likely_enb_plmn: String,
|
||||||
|
likely_ue_plmn: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ImsiRequestedAnalyzer {
|
impl Default for ImsiRequestedAnalyzer {
|
||||||
@@ -40,6 +47,10 @@ impl ImsiRequestedAnalyzer {
|
|||||||
state: State::Unattached,
|
state: State::Unattached,
|
||||||
timeout_counter: 0,
|
timeout_counter: 0,
|
||||||
flag: None,
|
flag: None,
|
||||||
|
// You will likely wonder why this isn't an Option<PLMN{mcc: u32, mnc: u32}>
|
||||||
|
// The answer is that I like strings.
|
||||||
|
likely_enb_plmn: "Unknown".to_string(),
|
||||||
|
likely_ue_plmn: "Unknown".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,10 +83,20 @@ impl ImsiRequestedAnalyzer {
|
|||||||
|
|
||||||
// IMSI to Disconnect without AuthAccept
|
// IMSI to Disconnect without AuthAccept
|
||||||
(State::IdentityRequest, State::Disconnect) => {
|
(State::IdentityRequest, State::Disconnect) => {
|
||||||
self.flag = Some(Event {
|
if self.likely_enb_plmn == self.likely_ue_plmn {
|
||||||
event_type: EventType::High,
|
self.flag = Some(Event {
|
||||||
message: "Disconnected after Identity Request without Auth Accept".to_string(),
|
event_type: EventType::High,
|
||||||
});
|
message: "Disconnected after Identity Request without Auth Accept on home network!".to_string(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.flag = Some(Event {
|
||||||
|
event_type: EventType::Low,
|
||||||
|
message: format!(
|
||||||
|
"Disconnected after Identity Request without Auth Accept, but this could be a false positive roaming issue - Tower PLMN: {}, UE PLMN: {}",
|
||||||
|
self.likely_enb_plmn, self.likely_ue_plmn
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(_, State::IdentityRequest) => {
|
(_, State::IdentityRequest) => {
|
||||||
@@ -92,6 +113,71 @@ impl ImsiRequestedAnalyzer {
|
|||||||
}
|
}
|
||||||
self.state = next_state;
|
self.state = next_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sometimes an ENB can have multiple PLMNS
|
||||||
|
fn format_plmn_list(&mut self, plmn_list: &PLMN_IdentityList) -> String {
|
||||||
|
plmn_list
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.map(|info| self.plmn_identity_to_str(&info.plmn_identity))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// PLMN is represented in two very different ways in the LTE spec so we need
|
||||||
|
// two very different functions to decode them. I hate this.
|
||||||
|
fn plmn_identity_to_str(&mut self, plmn: &PLMN_Identity) -> String {
|
||||||
|
let mcc_digits: String = plmn
|
||||||
|
.mcc
|
||||||
|
.as_ref()
|
||||||
|
.map(|mcc| {
|
||||||
|
mcc.0
|
||||||
|
.iter()
|
||||||
|
.map(|MCC_MNC_Digit(n)| n.to_string())
|
||||||
|
.collect::<String>()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let mnc_digits: String = plmn
|
||||||
|
.mnc
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.map(|MCC_MNC_Digit(n)| n.to_string())
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
|
format!("{}-{}", mcc_digits, mnc_digits)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn plmn_vec_to_str(&mut self, bytes: &[u8]) -> String {
|
||||||
|
let mcc_digit1 = bytes[0] & 0x0F;
|
||||||
|
let mcc_digit2 = (bytes[0] >> 4) & 0x0F;
|
||||||
|
let mcc_digit3 = bytes[1] & 0x0F;
|
||||||
|
|
||||||
|
let mnc_digit1 = bytes[2] & 0x0F;
|
||||||
|
let mnc_digit2 = (bytes[2] >> 4) & 0x0F;
|
||||||
|
let mnc_digit3 = (bytes[1] >> 4) & 0x0F;
|
||||||
|
|
||||||
|
let mcc = mcc_digit1 as u32 * 100 + mcc_digit2 as u32 * 10 + mcc_digit3 as u32;
|
||||||
|
|
||||||
|
let mcc_str = format!("{:03}", mcc);
|
||||||
|
let mnc_str = if mnc_digit3 == 0xF {
|
||||||
|
format!("{:02}", mnc_digit1 * 10 + mnc_digit2)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"{:03}",
|
||||||
|
mnc_digit1 as u32 * 100 + mnc_digit2 as u32 * 10 + mnc_digit3 as u32
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
format!("{}-{}", mcc_str, mnc_str)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_plmn(&mut self, old_tai: &Option<TAI>) -> String {
|
||||||
|
match old_tai {
|
||||||
|
Some(t) => self.plmn_vec_to_str(&t.plmn),
|
||||||
|
None => "Unknown".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Analyzer for ImsiRequestedAnalyzer {
|
impl Analyzer for ImsiRequestedAnalyzer {
|
||||||
@@ -106,7 +192,7 @@ impl Analyzer for ImsiRequestedAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_version(&self) -> u32 {
|
fn get_version(&self) -> u32 {
|
||||||
3
|
4
|
||||||
}
|
}
|
||||||
|
|
||||||
fn analyze_information_element(
|
fn analyze_information_element(
|
||||||
@@ -114,11 +200,29 @@ impl Analyzer for ImsiRequestedAnalyzer {
|
|||||||
ie: &InformationElement,
|
ie: &InformationElement,
|
||||||
packet_num: usize,
|
packet_num: usize,
|
||||||
) -> Option<Event> {
|
) -> Option<Event> {
|
||||||
|
// Set the enodeb plmn to the last sib1 we got, we should improve this once we have PCI data, this
|
||||||
|
// is a naive approach.
|
||||||
|
if let InformationElement::LTE(lte_ie) = ie
|
||||||
|
&& let LteInformationElement::BcchDlSch(sch_msg) = &**lte_ie
|
||||||
|
&& let BCCH_DL_SCH_MessageType::C1(c1) = &sch_msg.message
|
||||||
|
&& let BCCH_DL_SCH_MessageType_c1::SystemInformationBlockType1(sib1) = c1
|
||||||
|
{
|
||||||
|
let plmn = &sib1.cell_access_related_info.plmn_identity_list;
|
||||||
|
self.likely_enb_plmn = self.format_plmn_list(plmn);
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if let InformationElement::LTE(inner) = ie {
|
if let InformationElement::LTE(inner) = ie {
|
||||||
match &**inner {
|
match &**inner {
|
||||||
LteInformationElement::NAS(payload) => match payload {
|
LteInformationElement::NAS(payload) => match payload {
|
||||||
NASMessage::EMMMessage(EMMMessage::EMMExtServiceRequest(_))
|
NASMessage::EMMMessage(EMMMessage::EMMAttachRequest(request)) => {
|
||||||
| NASMessage::EMMMessage(EMMMessage::EMMAttachRequest(_)) => {
|
if self.likely_ue_plmn == "Unknown" {
|
||||||
|
self.likely_ue_plmn = self.extract_plmn(&request.old_tai.inner);
|
||||||
|
}
|
||||||
|
self.transition(State::AttachRequest, packet_num);
|
||||||
|
}
|
||||||
|
NASMessage::EMMMessage(EMMMessage::EMMExtServiceRequest(_)) => {
|
||||||
self.transition(State::AttachRequest, packet_num);
|
self.transition(State::AttachRequest, packet_num);
|
||||||
}
|
}
|
||||||
NASMessage::EMMMessage(EMMMessage::EMMIdentityRequest(_)) => {
|
NASMessage::EMMMessage(EMMMessage::EMMIdentityRequest(_)) => {
|
||||||
@@ -129,12 +233,22 @@ impl Analyzer for ImsiRequestedAnalyzer {
|
|||||||
self.transition(State::AuthAccept, packet_num);
|
self.transition(State::AuthAccept, packet_num);
|
||||||
}
|
}
|
||||||
NASMessage::EMMMessage(EMMMessage::EMMServiceReject(_))
|
NASMessage::EMMMessage(EMMMessage::EMMServiceReject(_))
|
||||||
| NASMessage::EMMMessage(EMMMessage::EMMAttachReject(_))
|
|
||||||
| NASMessage::EMMMessage(EMMMessage::EMMDetachRequestMO(_))
|
| NASMessage::EMMMessage(EMMMessage::EMMDetachRequestMO(_))
|
||||||
| NASMessage::EMMMessage(EMMMessage::EMMDetachRequestMT(_))
|
| NASMessage::EMMMessage(EMMMessage::EMMDetachRequestMT(_))
|
||||||
| NASMessage::EMMMessage(EMMMessage::EMMTrackingAreaUpdateReject(_)) => {
|
| NASMessage::EMMMessage(EMMMessage::EMMTrackingAreaUpdateReject(_)) => {
|
||||||
self.transition(State::Disconnect, packet_num);
|
self.transition(State::Disconnect, packet_num);
|
||||||
}
|
}
|
||||||
|
NASMessage::EMMMessage(EMMMessage::EMMAttachReject(reject)) => {
|
||||||
|
self.transition(State::Disconnect, packet_num);
|
||||||
|
if reject.emm_cause.inner
|
||||||
|
== AttachRejectEMMCause::EPSServicesAndNonEPSServicesNotAllowed
|
||||||
|
{
|
||||||
|
self.flag = Some(Event {
|
||||||
|
event_type: EventType::Low,
|
||||||
|
message: "Identity requested without authentication but its likely a false positive unless your SIM card has an active plan".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -148,6 +262,9 @@ impl Analyzer for ImsiRequestedAnalyzer {
|
|||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// This causes two messages in the event of a false positive when we should always get an attach reject anyway so
|
||||||
|
// I'm commentingit out until I figure out a smarter way to deal with it.
|
||||||
|
/*
|
||||||
LteInformationElement::DlDcch(rrc_payload) => {
|
LteInformationElement::DlDcch(rrc_payload) => {
|
||||||
if let DL_DCCH_MessageType::C1(DL_DCCH_MessageType_c1::RrcConnectionRelease(
|
if let DL_DCCH_MessageType::C1(DL_DCCH_MessageType_c1::RrcConnectionRelease(
|
||||||
_,
|
_,
|
||||||
@@ -156,6 +273,7 @@ impl Analyzer for ImsiRequestedAnalyzer {
|
|||||||
self.transition(State::Disconnect, packet_num)
|
self.transition(State::Disconnect, packet_num)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user