DOCUMENTATION / PART 5 — TECHNIQUE REFERENCE5.1

Part 5 — Technique reference

Part 4 told you what exists. This part tells you how to do it: one page per distinct attack and per distinct passive analysis, numbered by family. The numbering is stable, so a page can be cited as §5.2.3 from anywhere in the manual.

Two rules hold for every page here.

A page describes a technique, not a module. Where one module owns several techniques it appears on several pages; where one technique spans modules (a capture decoded by one module and scored by another) the page names both. The modules themselves are catalogued in Part 4 and are only mentioned here to say which verb to type.

Every page states what the technique needs. Three need-levels recur:

Need Meaning
nothing Runs on any machine, no radio, no capture. Most of the analysis in this toolkit is like this.
a capture Needs a pcap of GSMTAP, NAS or SIP traffic — from 03_gsmtap, from a fleet node, or from someone else's network.
a radio Needs a transmitting or receiving SDR this machine can key. Every such verb refuses with a stated WHY when it cannot, and 05_field_kit --guard-check proves that refusal.

The three exploit-kit modules (20_gsm_cve, 21_lte_cve, 22_nr_cve) are covered at kit level, one page each, in §5.10 — never one page per CVE, because a CVE is not a technique: it is one message shape inside a kit whose reach depends on a testbed this toolkit does not ship.


5.1 Reading the air without touching it

Everything in this family is passive. Nothing here transmits, nothing here associates, and every one of these techniques is legal to run against a network you are authorised to observe. A receiver that never keys a transmitter is the only instrument in this toolkit that cannot be detected as an instrument.

5.1.1 Decode a broadcast channel

Need: nothing. Every cell tells you, in the clear, before it knows who you are: which operator it belongs to, which tracking area it is in, which cell it is, what it will let you do, and what its neighbours are. That broadcast is the one part of the network that never has to be attacked — only read.

GSM first, because its broadcast is the most explicit:

modules/06_gsm_air.sh --decode-si 15061b000162f2100000
# message  SI3 / cell_id 1 / mcc 262 / mnc 01 / lac 0 ...

The hex is an L2 BCCH block: a pseudo-length octet, the protocol discriminator, the RR message type, then the fields. --decode-si names the message it finds rather than assuming one, so the same command reads SI1 through SI13.

LTE and 5G put the same information behind ASN.1 rather than a fixed layout:

modules/10_lte_air.sh --decode-mib  <24-bit hex>   # bandwidth, PHICH, SFN
modules/10_lte_air.sh --decode-sib  <hex>          # PLMN list, TAC, cell id, barring
modules/12_nr_air.sh --decode-mib  <hex>           # 5G MIB: SCS, SFN, subCarrierSpacingCommon
modules/12_nr_air.sh --decode-sib1 <hex>           # 5G SIB1: PLMN, TAC, cellIdentity, slices
modules/08_umts_air.sh --decode-sib <hex>          # UMTS SIB blob, field map via --sib-fields

What you read: the operator identity (MCC/MNC), the tracking area, the cell identity and — the part that matters for everything else in this manual — whether the cell is barred, whether it permits emergency calls only, and which ciphering and integrity algorithms it advertises support for. A cell that advertises A5/0 or NEA0 is telling you it will not encrypt, which makes every technique in §5.1.5 free.

When it refuses: the decoder refuses a block whose length does not match its own framing, with a stated reason. A --decode-si that answers "no field map for message type 0x00" means the message type is real but this decoder has no named layout for it — not that the block is invalid. modules/06_gsm_air.sh and tools/gsm_mod.py are the referees; tools/check-gsm-si.sh proves the four SI layouts against tshark and pycrate on every gate run.

Build rather than read: 10_lte_air.sh --build-sib1 and 15_downgrade.sh --build-sib1 construct the same message from arguments, which is what you want when you are the one broadcasting (§5.3.1).

Read next: §5.1.2 to turn a receiver into a capture, then §5.2 for what a catcher does with the identity the broadcast just published.

5.1.2 Capture the air and decode it into a store

Need: a radio, then nothing.

modules/03_gsmtap.sh --listen 60 captures/gsm-2024-06-01.pcap   # 60 s from UDP 4729
modules/03_gsmtap.sh --read   captures/gsm-2024-06-01.pcap      # decode into the store
modules/03_gsmtap.sh --decode-tsv | head                        # what it decoded
modules/03_gsmtap.sh --identities-tsv                           # identities seen in the clear

--listen binds UDP 4729 and writes a pcap; it needs something else — a gr-gsm, srsRAN or osmocom flowgraph — actually feeding that port, which is why it reports what it is listening to and how long for. --read is the half that matters afterwards: it walks a GSMTAP capture, decodes each message, and appends to loot/gsmtap/decoded.tsv, which is the store half a dozen other modules read. A capture decoded twice does not double-count.

The store is the substrate for everything capture-driven in this manual: 18_imsi_exposure, 28_paging_track, 29_mac_layer, 32_nas_signaling and the catcher progress tables all read either this store or the raw pcap.

What you read: one row per message, with channel, uplink/downlink direction, message type, the payload hex and the capture timestamp. Direction is the column people skip: an identity in the downlink is the network asking, in the uplink is the handset answering, and only the second one is exposure.

When it refuses: with no capture file it says so and exits 1. With a capture that contains no GSMTAP (an ordinary IP capture, say) --read reports zero frames rather than inventing rows.

Referees: 17_pcap_analysis.sh --summary (independent header check) and 17_pcap_analysis.sh --hierarchy. --build-sample out.pcap writes a small synthetic capture when you want to test a verb with no radio at all.

Read next: §5.1.5, which is what you do with the store once it exists.

5.1.3 Paging arithmetic

Need: nothing. Paging is the network's way of calling a handset that is otherwise silent, and it is the only moment an idle phone must listen. If you can predict when it must listen, you can be listening then — and if you can predict it from an IMSI, an IMSI you already have is a tracking instrument.

# GSM: paging group from an IMSI and the cell's BS_PA_MFRMS
modules/06_gsm_air.sh --paging-group 310150123456789 2

# LTE: UE_ID = IMSI mod 1024, so pass the UE_ID, not the IMSI
modules/28_paging_track.sh --po 789 64 lte
# generation lte / pf_sfn 21 / po_subframe 9 / duplex fdd

# NR: same arithmetic with a PF_offset
modules/28_paging_track.sh --po 789 64 nr

That the LTE branch wants 789 and not the full IMSI is deliberate: the module will not guess which modulo you meant, and a full IMSI is not a UE_ID. It refuses with an out-of-range error rather than quietly reducing it, because a silently reduced identity produces a wrong paging frame that looks like an answer.

10_lte_air.sh --paging-frame <imsi> <tac> <ue-id> goes further and takes the TAC and any eDRX configuration, producing the hyperframe and paging time window as well as PF/PO.

What you read: the paging frame (an SFN), the subframe inside it, and for GSM the paging block and CCCH group. Those three numbers are the whole result: with them, a receiver that decodes only that one block has spent less than 1% of the airtime of a receiver that decodes continuously.

When it refuses: non-numeric or out-of-range identities, a T that is not one of 32/64/128/256, an nB that is not a legal fraction of T, a duplex mode that is neither fdd nor tdd.

Read next: §5.7.3, which is what this arithmetic is for when it is used against one subscriber rather than for scheduling a receiver.

5.1.4 Audit the cipher posture of the air

Need: nothing for the tables; a capture for the session view.

The interesting question about ciphering is never "is it encrypted" — it is "which algorithm did this session negotiate, and was that the best either side could do". Those are different questions and only the second one is an audit.

modules/19_cipher_audit.sh --table gsm          # every algorithm, with 3GPP refs and standing
modules/19_cipher_audit.sh --table 5g
modules/06_gsm_air.sh     --cipher-audit 35     # classify a Cipher Mode Command octet
modules/19_cipher_audit.sh --parse-smc <hex>    # what an SMC actually negotiated
modules/19_cipher_audit.sh --score lte EEA2 EIA2
modules/19_cipher_audit.sh --downgrade-check    # find downgrades in the session store

The table is the part worth reading once: it carries, per algorithm, the clause that defines it and its standing — null, broken, weak, ok — with the reason. A5/0 is null; A5/1 and A5/2 are broken with the attacks that broke them named; A5/3 is weak because KASUMI's structural weaknesses survive a nominal 64-bit key; A5/4 is ok because the longer key removes the practical recovery. "Weak" and "broken" are not synonyms and the table does not treat them as such.

--score turns a negotiated pair into a verdict about the session, and --downgrade-check looks for the case that matters: a session that negotiated something worse than both ends advertised they could do.

What you read: for a session, the negotiated pair and whether it is a downgrade; for a cell, whether its advertised set includes a null algorithm. A5/1 keystream is available as a known-answer instrument: 06_gsm_air.sh --a5-1-keystream <kc> <bits> <frame> prints the keystream the algorithm produces, which is what you compare a recovered keystream against.

When it refuses: a --parse-smc shorter than a Security Mode Command is refused by length with the byte count it got, rather than decoded into nonsense.

Read next: §5.3.3 for forcing a downgrade rather than observing one, and §5.2.1 for why a catcher cares.

5.1.5 Find where an IMSI crosses the air in the clear

Need: a capture. This is the single most consequential passive analysis in the toolkit, because it converts a passive capture into a persistent subscriber identifier without transmitting anything.

modules/06_gsm_air.sh --imsi-exposure-tsv          # where, in one store, an IMSI is readable
modules/06_gsm_air.sh --identity-exposure <hex>    # one Um message -> identity, type, exposure
modules/18_imsi_exposure.sh --audit <pcap|tsv>     # harvest and correlate
modules/18_imsi_exposure.sh --map                  # TMSI/GUTI -> IMSI with the evidence
modules/18_imsi_exposure.sh --subscribers-tsv      # one row per subscriber, with the score
modules/18_imsi_exposure.sh --marlin <tsv>         # identity-exposure ratio (the NDSS '25 detector)

--audit accepts several inputs and correlates across them, which is the whole point: a TMSI is only pseudonymous until the same capture (or a second one) contains the moment the network handed it out alongside a permanent identity. --cross answers the question that makes a capture archive dangerous — the same IMSI appearing in captures taken at different times and places.

What you read: one finding per exposure with the reason it counts, and a per-subscriber score that separates "identified" from "merely seen". The --map verb prints the binding with its evidence, so a mapping you cannot justify is visibly unjustified.

When it refuses: an input that is neither a pcap nor a TSV store; an empty store, which reports zero findings rather than a clean bill of health.

Read next: §5.2.6, which is the same exposure ratio used in the other direction — to detect a catcher rather than to be one.

5.1.6 Survey the cell layer

Need: a radio (or a capture). Which cells exist here, from which operator, on which frequencies, and where are they.

modules/02_cell_recon.sh --scan lte 20        # one pass, band 20
modules/02_cell_recon.sh --scan gsm
modules/02_cell_recon.sh --list-tsv           # surveyed cells
modules/02_cell_recon.sh --plmn 310-260       # operator and country for a PLMN
modules/02_cell_recon.sh --arfcn lte 6300     # EARFCN -> downlink MHz

modules/04_tower_map.sh --survey lte 20       # same pass, one point per cell, with a position
modules/04_tower_map.sh --history             # points with and without a fix, distinct cells
modules/04_tower_map.sh --export-kml          # a file a map program can open

The two modules are deliberately separate: 02_cell_recon answers "what is there", 04_tower_map answers "where is it". 04 records a point only when it has a position, and its --history line says how many points it could not place — a survey with no fix log is a survey whose map is a guess.

What you read: per cell, the RAT, the ARFCN/EARFCN, the PLMN, the physical cell id and the received power. The interesting anomaly is a cell that is loud and unlisted: an operator's own cells appear in its own neighbour lists.

When it refuses: with no LTE scanner installed, --scan lte says which tool it wanted (srsran/cell_search) and exits failing. With no GPS, 04_tower_map warns that points will be recorded without a position rather than silently recording them at zero.

Read next: §5.3.1, which takes this survey as its input.