mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-06-11 07:13:30 -07:00
imsi revealing message diagnostic heuristic
This commit is contained in:
committed by
Cooper Quintin
parent
e27da68b5d
commit
e44230c043
@@ -335,6 +335,17 @@
|
||||
Test Heuristic (noisy!)
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input
|
||||
id="imsi_attach_analyzer"
|
||||
type="checkbox"
|
||||
bind:checked={config.analyzers.imsi_attach_analyzer}
|
||||
class="h-4 w-4 text-rayhunter-blue focus:ring-rayhunter-blue border-gray-300 rounded"
|
||||
/>
|
||||
<label for="imsi_attach_analyzer" class="ml-2 block text-sm text-gray-700">
|
||||
IMSI Attach Analyzer
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ export interface AnalyzerConfig {
|
||||
nas_null_cipher: boolean;
|
||||
incomplete_sib: boolean;
|
||||
test_analyzer: boolean;
|
||||
imsi_attach_analyzer: boolean;
|
||||
}
|
||||
|
||||
export enum enabled_notifications {
|
||||
|
||||
@@ -4,6 +4,7 @@ use pcap_file_tokio::pcapng::blocks::enhanced_packet::EnhancedPacketBlock;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::analysis::imsi_attach::ImsiAttachAnalyzer;
|
||||
use crate::gsmtap::{GsmtapHeader, GsmtapMessage, GsmtapType};
|
||||
use crate::util::RuntimeMetadata;
|
||||
use crate::{diag::MessagesContainer, gsmtap_parser};
|
||||
@@ -19,13 +20,14 @@ use super::{
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(default)]
|
||||
pub struct AnalyzerConfig {
|
||||
pub imsi_requested: bool,
|
||||
pub imsi_attach: bool,
|
||||
pub connection_redirect_2g_downgrade: bool,
|
||||
pub lte_sib6_and_7_downgrade: bool,
|
||||
pub null_cipher: bool,
|
||||
pub nas_null_cipher: bool,
|
||||
pub incomplete_sib: bool,
|
||||
pub test_analyzer: bool,
|
||||
pub imsi_requested: bool,
|
||||
}
|
||||
|
||||
impl Default for AnalyzerConfig {
|
||||
@@ -38,6 +40,7 @@ impl Default for AnalyzerConfig {
|
||||
nas_null_cipher: true,
|
||||
incomplete_sib: true,
|
||||
test_analyzer: false,
|
||||
imsi_attach: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -346,6 +349,10 @@ impl Harness {
|
||||
harness.add_analyzer(Box::new(TestAnalyzer {}))
|
||||
}
|
||||
|
||||
if analyzer_config.imsi_attach {
|
||||
harness.add_analyzer(Box::new(ImsiAttachAnalyzer {}));
|
||||
}
|
||||
|
||||
harness
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
use crate::analysis::analyzer::{Analyzer, Event, EventType};
|
||||
use crate::analysis::information_element::{InformationElement, LteInformationElement};
|
||||
use pycrate_rs::nas::NASMessage;
|
||||
use pycrate_rs::nas::emm::EMMMessage;
|
||||
use pycrate_rs::nas::generated::emm::emm_attach_reject::EMMCauseEMMCause as AttachRejectEMMCause;
|
||||
use pycrate_rs::nas::generated::emm::emm_detach_request_mt::EPSDetachTypeMTType;
|
||||
use pycrate_rs::nas::generated::emm::emm_identity_request::IDTypeV;
|
||||
use pycrate_rs::nas::generated::emm::emm_service_reject::EMMCauseEMMCause as ServiceRejectEMMCause;
|
||||
use pycrate_rs::nas::generated::emm::emm_tracking_area_update_reject::EMMCauseEMMCause as TAURejectEMMCause;
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub struct ImsiAttachAnalyzer;
|
||||
|
||||
impl ImsiAttachAnalyzer {
|
||||
pub fn new() -> Self {
|
||||
ImsiAttachAnalyzer
|
||||
}
|
||||
|
||||
fn is_imsi_exposing_nas(&self, nas_msg: &NASMessage) -> bool {
|
||||
match nas_msg {
|
||||
NASMessage::EMMMessage(emm_msg) => match emm_msg {
|
||||
EMMMessage::EMMIdentityRequest(req) => req.id_type.inner == IDTypeV::IMSI,
|
||||
|
||||
EMMMessage::EMMTrackingAreaUpdateReject(reject) => {
|
||||
matches!(
|
||||
reject.emm_cause.inner,
|
||||
TAURejectEMMCause::IllegalUE
|
||||
| TAURejectEMMCause::IllegalME
|
||||
| TAURejectEMMCause::EPSServicesNotAllowed
|
||||
| TAURejectEMMCause::EPSServicesAndNonEPSServicesNotAllowed
|
||||
| TAURejectEMMCause::TrackingAreaNotAllowed
|
||||
| TAURejectEMMCause::EPSServicesNotAllowedInThisPLMN
|
||||
| TAURejectEMMCause::RequestedServiceOptionNotAuthorizedInThisPLMN
|
||||
)
|
||||
}
|
||||
|
||||
EMMMessage::EMMAttachReject(reject) => {
|
||||
matches!(
|
||||
reject.emm_cause.inner,
|
||||
AttachRejectEMMCause::IllegalUE
|
||||
| AttachRejectEMMCause::IllegalME
|
||||
| AttachRejectEMMCause::EPSServicesNotAllowed
|
||||
| AttachRejectEMMCause::EPSServicesAndNonEPSServicesNotAllowed
|
||||
| AttachRejectEMMCause::PLMNNotAllowed
|
||||
| AttachRejectEMMCause::TrackingAreaNotAllowed
|
||||
| AttachRejectEMMCause::RoamingNotAllowedInThisTrackingArea
|
||||
| AttachRejectEMMCause::EPSServicesNotAllowedInThisPLMN
|
||||
| AttachRejectEMMCause::NoSuitableCellsInTrackingArea
|
||||
| AttachRejectEMMCause::RequestedServiceOptionNotAuthorizedInThisPLMN
|
||||
)
|
||||
}
|
||||
|
||||
EMMMessage::EMMDetachRequestMT(req) => {
|
||||
// Original implementation: !(nas_eps.emm.detach_type_dl == 3)
|
||||
req.eps_detach_type.inner.typ != EPSDetachTypeMTType::IMSIDetach
|
||||
}
|
||||
|
||||
EMMMessage::EMMServiceReject(reject) => {
|
||||
matches!(
|
||||
reject.emm_cause.inner,
|
||||
ServiceRejectEMMCause::IllegalUE
|
||||
| ServiceRejectEMMCause::IllegalME
|
||||
| ServiceRejectEMMCause::EPSServicesNotAllowed
|
||||
| ServiceRejectEMMCause::UEIdentityCannotBeDerivedByTheNetwork
|
||||
| ServiceRejectEMMCause::TrackingAreaNotAllowed
|
||||
| ServiceRejectEMMCause::EPSServicesNotAllowedInThisPLMN
|
||||
| ServiceRejectEMMCause::RequestedServiceOptionNotAuthorizedInThisPLMN
|
||||
)
|
||||
}
|
||||
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Analyzer for ImsiAttachAnalyzer {
|
||||
fn get_name(&self) -> Cow<'_, str> {
|
||||
"IMSI-Exposed Message Detector".into()
|
||||
}
|
||||
|
||||
fn get_description(&self) -> Cow<'_, str> {
|
||||
"Catches any and all messages that may expose IMSI. Can be quite noisy. \
|
||||
Based on the detection logic from the Marlin paper (\"They Know Where You Are: Tracking Mobile \
|
||||
Devices Using Cellular Infrastructure\"). Since we don't have traffic of many devices, we \
|
||||
cannot implement the original exposure ratio calculation, and naively trigger an event on \
|
||||
every exposure.".into()
|
||||
}
|
||||
|
||||
fn get_version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn analyze_information_element(&mut self, ie: &InformationElement, _packet_num: usize) -> Option<Event> {
|
||||
let lte_ie = match ie {
|
||||
InformationElement::LTE(inner) => inner,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
match lte_ie.as_ref() {
|
||||
LteInformationElement::NAS(nas_msg) => {
|
||||
if self.is_imsi_exposing_nas(nas_msg) {
|
||||
let message_type = match nas_msg {
|
||||
NASMessage::EMMMessage(emm_msg) => match emm_msg {
|
||||
EMMMessage::EMMIdentityRequest(_) => "EMM Identity Request (IMSI)",
|
||||
EMMMessage::EMMTrackingAreaUpdateReject(_) => {
|
||||
"EMM Tracking Area Update Reject"
|
||||
}
|
||||
EMMMessage::EMMAttachReject(_) => "EMM Attach Reject",
|
||||
EMMMessage::EMMDetachRequestMT(_) => "EMM Detach Request (MT)",
|
||||
EMMMessage::EMMServiceReject(_) => "EMM Service Reject",
|
||||
_ => "Unknown EMM Message",
|
||||
},
|
||||
_ => "Unknown NAS Message",
|
||||
};
|
||||
|
||||
Some(Event {
|
||||
event_type: EventType::Informational,
|
||||
message: format!(
|
||||
"IMSI-exposing NAS message detected: {message_type}."
|
||||
),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod analyzer;
|
||||
pub mod connection_redirect_downgrade;
|
||||
pub mod imsi_attach;
|
||||
pub mod imsi_requested;
|
||||
pub mod incomplete_sib;
|
||||
pub mod information_element;
|
||||
|
||||
Reference in New Issue
Block a user