5.2 Being the network: catchers
The techniques in this family all share one trick: present a cell that is more attractive than the real one, so a handset talks to you instead of to its operator. That is the definition of an IMSI catcher, and everything in this section is a variation on it — a different generation, a different field of the protocol, or a different point at which you stop.
These need a radio and they transmit. Every one of them is detectable (§5.2.6 describes the detectors), and none of them should be pointed at a network you do not own or have written permission to test.
5.2.1 Run a GSM catcher sequence
Need: a radio. The 2G catcher is not one message, it is fourteen: a handset has to be told about the cell, told to camp, told to identify itself, and then told that its identity was accepted. Getting any of the middle steps wrong leaves a session that a handset abandons.
modules/07_gsm_attack.sh --imsi-catcher-sequence # the fourteen steps, in order
modules/07_gsm_attack.sh --plan acme # write the plan for this run
modules/07_gsm_attack.sh --plan-tsv # the plan as a table
The plan is the artefact to read before transmitting, because it names, per step, the channel, the direction, the message and the message type:
step channel direction message protocol message_type
1 BCCH downlink SYSTEM INFORMATION TYPE 1 RR 0x19
6 RACH uplink CHANNEL REQUEST - -
8 PCH downlink PAGING REQUEST TYPE 1 RR 0x21
11 SDCCH downlink IDENTITY REQUEST MM 0x18
12 SDCCH uplink IDENTITY RESPONSE MM 0x19
Each step has a builder, so a wrong octet can be fixed at the source rather than in a capture:
modules/07_gsm_attack.sh --build-identity-request 310150123456789
modules/07_gsm_attack.sh --build-immediate-assignment
modules/07_gsm_attack.sh --build-paging-request
modules/07_gsm_attack.sh --build-lu-accept
modules/07_gsm_attack.sh --build-tmsi-realloc 0x4a3f1c22
modules/07_gsm_attack.sh --build-cipher-mode A5/1
Every builder prints hex that the toolkit's own decoder accepts, and the four
System Information builders are refereed against tshark and pycrate by
tools/check-gsm-si.sh — so the frames this module transmits are the frames
the standard describes, not frames this toolkit invented. That referee exists
because they were once wrong: the LAPDm length indicator used a shift of three
instead of two, and the identity request carried the wrong protocol
discriminator, which Wireshark reported as SYSTEM INFORMATION TYPE 5bis.
What you read: the plan first, then the run. The plan is what a reviewer signs off; the run is what the handset did with it, and §5.2.2 is where the two are compared.
When it refuses: every transmitting verb asks 05_field_kit --guard-check
first and refuses with the reason when there is no keyed radio, no isolated lab
acknowledgement, or no compliance gate. The refusal is the feature.
Read next: §5.1.3 for the paging arithmetic this sequence depends on, and §5.4.1 for what a TMSI reallocation is worth.
5.2.2 Observe what a catcher actually got
Need: a capture, or a radio. A catcher that says "I got 34 IMSIs" is a catcher making a claim. The claim is checkable: every step that succeeded has a message on the air, and every step that did not has an absence.
modules/07_gsm_attack.sh --catcher-run-start field-1
modules/07_gsm_attack.sh --catcher-watch captures/catcher.pcap --run field-1
# run field-1 / frames 379 / decoded 5 / steps_seen 5 / steps_total 14 / store loot/gsm_attack/catcher.tsv
modules/07_gsm_attack.sh --catcher-progress-tsv --run field-1
modules/07_gsm_attack.sh --catcher-observations-tsv --run field-1 --limit 20
modules/07_gsm_attack.sh --catcher-run-stop
--catcher-watch delegates the pcap to 03_gsmtap --read (GSMTAP stays one
parser), then decodes each message against the plan's own numbering — it asks
--imsi-catcher-sequence which step a type belongs to rather than keeping a
second copy of the table. The progress table is a left join, so a step nobody
observed appears as unseen with its timestamp column empty:
step channel direction message state first_seen observations
3 BCCH downlink SYSTEM INFORMATION TYPE 3 seen 2024-06-01T10:42:19Z 1
9 SDCCH uplink PAGING RESPONSE seen 2024-06-01T10:42:20Z 1
11 SDCCH downlink IDENTITY REQUEST seen ... 1
12 SDCCH uplink IDENTITY RESPONSE seen ... 1
13 SDCCH downlink CIPHERING MODE COMMAND unseen - 0
What you read: seen vs unseen per step, and the count of observations, so
a second response from the same handset is visible as observations 2 rather
than as a new step. The GUI's Catch feed view redraws this same store every
two seconds while it is open (§3.3), which is how you watch a run instead of
interrogating it.
When it refuses: with nothing decodable it exits 1 with a note — the run is
not silently recorded as empty. A frame whose shape it cannot parse is counted
under unshaped rather than dropped, and an unnamed-but-legal message type is
counted under unknown_type rather than mislabelled.
Read next: §5.2.6 to check whether your own catcher was itself caught.
5.2.3 Drive a UMTS catcher
Need: a radio. 3G is the hardest generation to catch, because the handset authenticates the network with AKA before it will send anything identifying, and the ciphering and integrity keys come out of that exchange. The toolkit's approach is to model the exchange honestly and say where it stops.
modules/08_umts_air.sh --catcher-model # the eight-step model
modules/08_umts_air.sh --catcher-identity # where identity can be asked for
modules/08_umts_air.sh --catcher-auth # what an AKA exchange needs
modules/08_umts_air.sh --aka-flow # RAND/XRES/CK/IK through f1..f5
--aka-flow is the useful reference page in the whole module: it prints the
five functions, what each takes and returns, and which of them a catcher would
need to be able to invert — with the answer, for each, that no practical attack
is known. A catcher page that implied otherwise would be lying.
The message side is decoder-first, so you can build against frames you already have:
modules/08_umts_air.sh --parse-rrc dl-ccch:300022468acf0002010001048c48d1...
modules/08_umts_air.sh --build-rrc-setup
modules/08_umts_air.sh --build-security-mode
modules/08_umts_air.sh --build-rrc-reject
modules/08_umts_air.sh --parse-security-mode <hex>
--parse-rrc classifies and walks; it exits 0 for a message it both classified
and walked, and separately for classified-but-not-walked, truncated and
malformed, so a script can tell "unknown to me" from "invalid". The classifier
is refereed against tshark by tools/check-umts-rrc.sh on every gate run.
What you read: for --aka-flow, which steps a passive capture can and cannot
show (the answer is in §5.2.4). For --parse-rrc, the message name, the CHOICE
branch it came from and the integrity check info if present.
When it refuses: a frame whose direction contradicts its channel family is skipped rather than decoded — an UL-CCCH block carrying the downlink bit is not a message, it is a bug in whatever wrote the capture.
Read next: §5.2.4, which is the same model with the observing done for you.
5.2.4 Observe a UMTS catcher without a radio
Need: a capture. The 3G observation layer exists because the interesting question about a UMTS catcher is not "did it capture" but "which of the eight steps did the air actually carry".
modules/08_umts_air.sh --catcher-run-start umts-1
modules/08_umts_air.sh --observe captures/umts.pcap --run umts-1
modules/08_umts_air.sh --observe --frame dl-dcch:1400060a003002 --run umts-1
modules/08_umts_air.sh --catcher-progress-tsv --run umts-1
modules/08_umts_air.sh --catcher-observations-tsv --run umts-1
The progress table has nine rows: the eight model steps plus answer (the
uplink direct transfer that carries the response). Three steps are marked
unobservable and the table says why — physical sync leaves nothing to record,
a broadcast proves the network sent it and not that a handset read it, and the
authentication request rides inside the same downlink direct transfer as
registration, which this module does not parse NAS out of. Those rows are the
honest part of the table: they are the ones a capture cannot settle, so a run
that shows seven of nine is not seven ninths of a success.
This module reads the pcap itself rather than going through
03_gsmtap --read, because the GSMTAP store does not keep the RRC sub-type
byte — and without it a DL-CCCH block and a UL-CCCH block are
indistinguishable, which are different top-level CHOICEs. Message parsing still
goes through --parse-rrc; only the framing is local.
What you read: the same shape as §5.2.2, with an integrity column that says
none, not protected or the MAC status. A catcher that got as far as ciphering
would show protected with a failing MAC, which is the signature of an
interception that guessed the key wrong.
When it refuses: a capture with no UMTS GSMTAP frames exits 1 and says the capture was read but contained nothing it could classify.
Read next: §5.5.3, where the same RRC/NAS split matters for a handset.
5.2.5 Catch a 5G SUCI, and conceal one back
Need: a capture, then nothing. 5G was designed to make this family harder: the subscription identifier a handset sends is a SUCI (Subscription Concealed Identifier) instead of a SUPI (the IMSI), and no network — not even the one serving the handset — is supposed to learn the permanent identity.
Two things follow, and they are different things.
Catching a SUCI is easy. It travels in the clear inside the NAS identity response, so §5.2.4's methods work unchanged: a receiver that can read an RRC identity response can read a 5G one.
Turning a SUCI back into an IMSI is not. By home network this manual means the operator that issued the SIM — the HPLMN, the subscription's own network — not the network the handset happens to be visiting. The handset conceals the MSIN with that operator's public key using ECIES (TS 33.501 §6.12, profiles A and B on Curve25519 and secp256r1), generating a fresh ephemeral key pair per concealment. Only the matching private key can reverse it, and it lives in the home network's SIDF (Subscription Identifier De-concealing Function). The chain is handset → SUCI → visited network → home network's SIDF → SUPI, with the visited network receiving authentication results keyed by the SUCI and never seeing the IMSI. So a SUCI you catch is not a name you can compute your way out of; it is a stable pseudonym — the same handset returns the same SUCI for the same home key, which is why it still links sessions across days and cells even though it does not say who the subscriber is.
The exceptions are the ones worth auditing for, and --audit exists for them: a
null-scheme SUCI (scheme 0) carries the MSIN in the clear and is an IMSI in a
wrapper, and a home key that is published or shared across operators conceals
nothing from anyone who has it.
# what a 5G catcher asks for, and what it would see
modules/58_suci_catcher.sh --catch # plan + the real frames, in hex
modules/58_suci_catcher.sh --build-cell
modules/58_suci_catcher.sh --build-nas
modules/58_suci_catcher.sh --build-rrc
modules/58_suci_catcher.sh --start # keyed run
modules/58_suci_catcher.sh --capture captures/suci.pcap # harvest SUCIs from a trace
modules/58_suci_catcher.sh --classify-capture captures/suci.pcap
modules/58_suci_catcher.sh --check-trace captures/suci.pcap
modules/58_suci_catcher.sh --status-tsv
# and the other direction: build, conceal, deconceal, with a home-network key
modules/14_suci.sh --hn-create acme # a test home network
modules/14_suci.sh --build-suci 0 1234567890 # scheme 0 (null), MSIN; MCC/MNC follow
modules/14_suci.sh --conceal <suci-hex> --hn acme
modules/14_suci.sh --deconceal <suci-hex> --hn acme
modules/14_suci.sh --verify-hnkey # prove the key pair is what it claims
modules/14_suci.sh --audit # which SUCIs are weak (null scheme, short MSIN)
--catch is the page to read first: it prints the plan path, then the actual
RRC and NAS frames the catcher would transmit — RRC setup complete, registration
request, identity request — as hex you can replay into a decoder. The identity
request is unprotected by design (TS 24.501 8.2.6), which is why the plan says so
next to it.
14_suci.sh owns the cryptanalysis side: ECIES with the home network's public
key to conceal, the private key to deconceal, and --audit for the weaker
cases — a null-scheme SUCI is an IMSI in a wrapper, and --msin-decode shows
why a short MSIN field is a small search space.
What you read: from --classify-capture, one row per SUCI with its scheme
output, its PLMN and its MSIN field; from --audit, the ones that are not
really concealed. From --deconceal with a key you own, the IMSI — and, if the
SUCI was concealed with a different home key, a stated refusal rather than
garbage.
When it refuses: a SUCI whose scheme output is the wrong length for its
elliptic curve; a deconceal without a matching private key; a --capture file
with no NAS identity response in it.
Read next: §5.3 is where the passive side ends and §5.3 begins — the same cell, transmitting rather than listening.
5.2.6 Detect a catcher from the handset's side
Need: a capture. Everything above is one half of the field; this is the other half, and it is the half a defensive engineer should read first.
modules/18_imsi_exposure.sh --marlin <tsv> # identity-exposure ratio per cell
modules/06_gsm_air.sh --paging-group <imsi> 2 # is this cell paging in the right block
modules/28_paging_track.sh --detect <pcap> # paging anomalies and a verdict
modules/28_paging_track.sh --adaptover-detect <pcap>
A catcher is detectable not by its power but by its behaviour: it asks for an identity more often than a network needs to, it pages in the wrong block, it never pages the phone that it just identified, and it does not hand out the ciphering the network advertised. The identity-exposure ratio is the cleanest single signal — a real network needs a permanent identity roughly once per attach; a catcher needs it every time.
--detect and --adaptover-detect report named signals with counts and then a
verdict that states what it did not test. The published IMSI-catcher apps test
for a loud cell and nothing else; the module's own comment says so, and the
verdict repeats it, because "looks like a catcher" and "is a catcher" are
different sentences.
What you read: signals with evidence and a verdict bounded by what was measured. A cell that fails one signal out of four is reported as exactly that.
When it refuses: an input that is not a pcap or a TSV store; an empty capture, which reports zero observations rather than a clean verdict.
Read next: §4.13.4 for the window that draws these signals, and §5.3.1 for what the attacker does next once a handset attaches.