Published
- 15 min read
HackRF passive GSM Sniffing

The longer we’re out of work, the harder we try to become better Hackers. That’s only partly true. Reality is: we’re preparing for a very hard time, a long time we probably will need to distract ourselfs. That why we chose a topic we don’t know yet much about: SS7
This has been the first step.
Passive HackRF GSM Basestation
Privacy and Security
Why nobody cares about us doing some garage-SS7 stuff:
https://www.transunion.com/solution/trulookup/locate-recover
Things have moved on much further long ago. Former API providers now sell access to “100 Billion records”, “11 Billion addresses and names” and “4 Billion phone records” to easily “locate individuals and recover assets” including “hard-to-find subjects”.
When it’s about recovering credit, everything is fair game.
She works hard for the money
So hard for it, honey
She works hard for the money
So you better treat her right
Links
- https://vasanthavanan.medium.com/ss7-the-deadliest-attack-6423de7fe8c0
- Dragon OS - Out of the box OS for SDRs
- https://github.com/SigPloiter/SigPloit
- https://github.com/0xc0decafe/ss7MAPer/
- https://osmocom.org/projects/cellular-infrastructure/wiki/Osmocom_Network_In_The_Box
- https://osmocom.org/projects/cellular-infrastructure/wiki/Latest_Builds
- https://rs-ojict.pubpub.org/pub/u71m7a67/release/2
- https://www.mail-archive.com/gerrit-log@lists.osmocom.org/msg06549.html
- https://www.hlr-lookups.com/
- https://www.hlrlookup.com/
- https://www.msisdn.net/
- https://www.msisdn.net/api/ (didn’t work when we tried)
- https://www.free-hlr.com/
- https://www.vianett.com/en/products/overview/hlr-number-lookup
- https://mcc-mnc.net/
- https://github.com/pbakondy/mcc-mnc-list
- https://github.com/SigPloiter/HLR-Lookups
- https://www.youtube.com/@cemaxecuter7783
- https://github.com/b4rdia/HackTricks/blob/master/generic-methodologies-and-resources/pentesting-network/README.md#sctp-scan
To Catch an IMSI
Some SS7 routes (like IP1 Asia or IP4 Africa, DV8, MS9) still provide some IMSI and provider info, but it’s getting harder to access. It’s no longer free and typically works only in the Number -> IMSI direction, not the other way around. Additionally, many IMSIs are now “home routed” or “scrambled,” making them largely unusable. Home routing means that IMSI queries are redirected to the home network, preventing direct access to subscriber details, while IMSI scrambling replaces the real IMSI with a temporary or randomized one to obscure tracking. Requesting a large dataset, such as 5 million numbers for partial IMSI resolution, would cost around 50,000€.
$ basic_hash=$(echo -n "$API_KEY:$API_SECRET" | sha256sum | cut -d' ' -f1)
$ curl -X POST https://www.hlr-lookups.com/api/v2/hlr-lookup \
-H "X-Basic: $basic_hash" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"msisdn": "+<phonenr>", "route":"IP4", "storage":null}'
One cent per request is at least reasonable. If we had a few bucks left, we could potentially validate, that none of our phone stalkers
is nearby.
SS7 Home Routing
is a security mechanism designed to prevent unauthorized access to subscriber details, particularly IMSI numbers and roaming status. Instead of directly responding to SS7 queries with subscriber data (such as IMSI and location), the request is redirected to a home network node, which acts as a gateway and hides the real information. This method prevents third parties from easily extracting IMSI data or tracking users via SS7 queries.
IMSI Scrambling
(or IMSI Encryption) is another protective measure where a temporary, randomized IMSI is assigned to a subscriber for use in signaling requests. This prevents external parties from obtaining the real IMSI and tracking a subscriber over time.
In the past…
…some things were simpler:
Today you’re lucky to find a free HLR lookup service at all. While web scraping has made advances, most APIs and providers limit the data they tell more striktly, or sell it B2B-only - contact us for a quote.
SMS Data
TL; DR:
Nope - at least not here.
Reviewing older scripts, it seems that in the past it was also possible to grab SMS
data. We could over several hours not catch a single piece of such data on any frequency and assume, today it’s save (in Germany) against passive sniffing
.
Frequencies
Manual
We made a manual list using scan outputs and added a rating, which we use as multiplier for scan duration during hopping.
# Use arrow keys in GUI to scan and watch for output on stdout
$ grgsm_livemon -f 925M
Freq. HZ | MCC | MNC | LAC | CellId | Rating | Country | Brand | Operator |
---|---|---|---|---|---|---|---|---|
934.2M | 262 | 03 | 59067 | 199 | 6 | USA China | T-Mobile China Mobile | China Mobile |
934.8M | 262 | 03 | 59067 | 194 | 6 | Germany | O2 | Telefónica Germany GmbH & Co. oHG |
936.2M | 262 | 02 | 7229 | 21722 | 5 | United States of America | Verizon | Verizon Wireless |
936.8M | 262 | 03 | 59067 | 194 | 5 | Germany | Lycamobile | Lycamobile |
943.4M | 262 | 02 | 7228 | 19233 | 4 | Netherlands | Vodafone | Vodafone Libertel B.V. |
944.0M | 262 | 02 | 7228 | 19231 | 4 | Italy | Vodafone | Vodafone Italia S.p.A. |
945.6M | 262 | 01 | 21015 | 10594 | 4 | Germany Poland | Telekom Plus | Telekom Deutschland GmbH Polkomtel Sp. z o.o. |
948.2M | 262 | 01 | 21015 | 5015 | 4 | Germany | Telekom | Telekom Deutschland GmbH |
948.6M | 262 | 01 | 21015 | 11376 | 6 | Germany | Telekom | Telekom Deutschland GmbH |
950.2M | 262 | 01 | 21015 | 5015 | 3 | Poland | Orange | Polska Telefonia Komórkowa Centertel Sp. z o.o. |
Secondary values on Country, Brand, Operator are either misreads or roaming, not sure atm.
Auto
Manually testing revealed that lots of these don’t work. The thing with air traffic
is - no matter WiFi, GSM or amateur / HAM radio - radiowaves can travel huge distances, reflect on the atmosphere, on building walls, etc. You may receive some data packets but will never be able to actually tune in on that frequency. We caught WiFi stations from the other end of town, but to connect to the station we would need to travel at least 3km by car and get very close.
TL;DR: Manual verification of each auto-found frequency is needed, or otherwise your frequency hopping will idle most of the time.
$ grgsm_scanner | grep -Poe "Freq:.*[\d\.]*M" | sort | uniq | cut -d ":" -f 2 | cut -d "," -f 1
Freq: 925.6M, CID: 50017, LAC: 59067, MCC: 262, M
Freq: 926.0M, CID: 50748, LAC: 59067, MCC: 262, M
Freq: 926.2M, CID: 50747, LAC: 59067, MCC: 262, M
Freq: 926.4M, CID: 50742, LAC: 59067, MCC: 262, M
Freq: 934.2M, CID: 199, LAC: 59067, MCC: 262, M
Freq: 934.8M, CID: 194, LAC: 59067, MCC: 262, M
Freq: 936.2M, CID: 21722, LAC: 7229, MCC: 262, M
Freq: 938.2M, CID: 21722, LAC: 7229, MCC: 262, M
Freq: 942.8M, CID: 6181, LAC: 7228, MCC: 262, M
Freq: 943.4M, CID: 19233, LAC: 7228, MCC: 262, M
Freq: 943.8M, CID: 6182, LAC: 7228, MCC: 262, M
Freq: 944.0M, CID: 19231, LAC: 7228, MCC: 262, M
Freq: 944.6M, CID: 19781, LAC: 7228, MCC: 262, M
Freq: 945.6M, CID: 10594, LAC: 21015, MCC: 262, M
Freq: 947.2M, CID: 2583, LAC: 21015, MCC: 262, M
Freq: 948.2M, CID: 5015, LAC: 21015, MCC: 262, M
Freq: 948.6M, CID: 11376, LAC: 21015, MCC: 262, M
Freq: 950.2M, CID: 5015, LAC: 21015, MCC: 262, M
Freq: 959.8M, CID: 35639, LAC: 21015, MCC: 262, M
HackRF Passive IMSI Catcher
This is not a MitM
IMSI Catcher, just a passive downstream data collector. We’re not intercepting phone network traffic or faking a base station, we only catch what’s in the air.
To use an analogy: It’s like turning on an oldschool Radio Receiver FM (German: UKW Radio), your car stereo, and listening to a station. In this case, the stations are digital though and they don’t play music there. Using software we can read and translate the data.
An active
Catcher or Fake Basestation would be like a two-way radio, where you could also press a button to talk back to another person. We’re not doing that, it would require better hardware and it would be way beyond the privacy & legal baseline. When people talk about IMSI Catchers and Stingrays, they mean those active, two-way radios.
I got Nodes…
I got Nodes, with different area codes
, area codes.
Ok, that’s not really true. It’s mostly the same area codes (LAC), as we only got few basestation nearby.
See also: https://blog.network-sec.de/post/spoofing_gsm_sms_mobile_phone_silent_tracking/
# livemon_headless provides a Time parameter, but it didn't seem to work
$ while :; do for freq in $(cat ./freqs.txt | sort -u); do echo $freq; timeout 60s grgsm_livemon_headless -f $freq 2>&1>/dev/null; done; done
# The script has a comment: switching frequencies while dumping may
# result in incorrect data. If you care, maybe start / stop the script
# within the previous loop.
$ python3 simple_IMSI-catcher.py -s -w IMSI_catching.db
More complex loop that uses our manual frequency table and the rating value to calculate timeout for each frequency. We simply run “better” station signals for longer duration.
$ IFS=$'\n'; while :; do for entry in $(cat ./freqs.txt | cut -d "|" -f 2,7); do freq=$(echo -n $entry | cut -d "|" -f 1 | sed -r 's/ //g'); rating=$(echo -n $entry | cut -d "|" -f 2); duration=$((rating * 20))s; echo "Running Frequency: $freq | Rating: $rating | Duration: $duration"; timeout "$duration" grgsm_livemon_headless -f $freq ; done; done
Upper right part of the image:
ss command
hinting at new options in the SS7 realm. Bottom part shows the actual output data. We can see temporary & real IMSI numbers, provider Country, Brand, Operator - all pretty generic data - and finally MCC, MNC, LAC and CellId - the location of the basestation, not the connected phone or device. Lastly, a timestamp.
Unlike WiFi Beacons, we cannot see a connection history. Even though there are Temporary IMSIs, those are so-called Nounces: Number-Used-Once - they change every time and thus cannot be used to track someone continuously. You need to add the -a
param (all data) to the script to see those temp numbers and incomplete connection data, we didn’t do that in the screenshot.
GSM Data now and in the future
This is no longer such a big deal it was in the past. Without direct SS7 access, or being LE, you cannot transform the IMSI to “personally attributable data”. In theory, you could track someone that way, if you already know the connection IMSI <-> Phone Number / Person beforehand. You cannot mass-identify people via IMSI numbers, you need to be in the same location anyways.
We managed to scrape about 1000 IMSIs per hour via frequency hopping. That also means, with only a single HackRF we miss a lot of data. Compared to our WiFi project, these numbers seem pityful (by factor 10 to 100), and WiFI provides not only much more data today, but also personally identifiable data and more precise location data. This is not by design, but due to people using it in a “careless” way, “Oliver’s iPhone” and beacons as mentioned in our blog / BlackViz WiFi project. Let’s be happy that people could not customize their IMSI in a way they do with vehicle number plates…
Privacy-wise it looks like
, many issues (like Android apps collecting IMSIs) have been patched on the user-side. Meaning, we the people no longer get easy access to this data. Google does. Former monsters like “the NSA” and “XKeyScore” have shifted into a data-market, where companies can basically do what law-enforcement wanted to in the past, the data access is behind a stronger “B2B” wall - of course we don’t know, if or how gov agencies have access to that, too. They probably do, but likely not unlimited (the famous Apple case).
Right now I’m more scared about the further. Companies don’t care, if they need a “court order” to access a person’s data records, we experienced that first-hand for several years now. They can’t arrest you, but otherwise they can play god and make your life hell. XKey, financial and maybe even social scoring, privatized edition
. You have arrived in Cyberpunk Valey. Enjoy your stay.
Risk of Personal Attribution of IMSI numbers
and successive exploiting, for geo-location or stuff like OMA provisioning
:
With our current tools and knowledge we wouldn’t be able to make that connection from passive sniffing, as already explained. But this is also due to us being more “stationary” than other people.
If we wanted to attribute an IMSI to make a spear phishing
- style attack, target a specific person, we would have the option, be it GSM or WiFi, to run our dump in 2 or more locations, when we know, the target person could be in that area. Say, around the target’s home and workplace. The differential information of the collected dumps would in theory leave us with a number, that could be attributed to that person.
However, this requires beforehand knowledge of these locations, hour- to days-long deployment of the stations and results will be mixed, there will certainly be other random people / devices overlapping the two locations. You could add a third, triangulation style, or a fourth, … it’s definitely possible, if you know the target’s movement patterns and invest the neccessary time and money. Other things are then also possible, though. We could poison your lunch, once we know, where you eat. :D
As it’s completely passive / silent, of course it will be an option attacking teams may choose. It’s not possible to detect passive sniffing. If we wouldn’t have told anyone our current activies, nobody would have known or even suspected.
For your all’s piece of mind: It’s the first time we worked with this matter in this way, both GSM and WiFi and of course never tried to make that “personal connection”. We neither have the resources for a week-long field trip nor any reason.
Bonus 1: Wireshark Tapping
By reviewing the source code of grgsm_livemon and the entire GNU Radio
toolkit, we found that by default it will open a gsmtap
device, you can hook in with Wireshark, by using the GUI or:
$ wireshark -k -f udp -Y gsmtap -i lo
Bonus 2: RTL-SDR Blog v4
Since we published this article we got hold of a RTLv4. The smaller device is a lot cheaper than a HackRf, yet it also has some drawbacks:
- No Tx
- Limited range and signal strength (still good enough for GSM)
- Most notably: Bad driver support and issues with USB virtualisation
- On Windows, you need to install WinUSB
TL;DR: You may not be able to run the device with many common Hypervisors, but either need to use another OS - and miss out on software like GR (Windows, Android, Mac) - or a native Linux device with USB2.0 ports.
Supposedly KVM and VMWare should work - we haven’t tested those - but our daily-driver VMs wouldn’t properly USB pass-through the device. dmesg
shows it loaded without error, but lsusb
does not list it. We could run grgsm_livemon but encountered errors instead of data in the stream output. Finally we found some information, that this model works best with native Linux. Too bad.
Here’s some things to try anyways:
# Manually set a few options
$ export SDR_PLAY_TUNER=R828D
$ rtl_biast -b 1
# Test
$ rtl_test -t
# Run with different sample rate (-s) and a few more opts
$ grgsm_livemon -s 1e6 -f 934.8M --args="rtl=0,direct_samp=0"
This helped debugging but didn’t render the device “usable” for us. We’ll try native next.
Data Details
HLR lookup
{"id":"fe2d31******",
"msisdn":"+491521*********",
"connectivity_status":"CONNECTED",
"mccmnc":"***43",
"mcc":"***",
"mnc":"43",
"imsi":null,
"msin":null,
"msc":null,
"original_network_name":"Lycamobile Germany",
"original_country_name":"Germany",
"original_country_code":"DE",
"original_country_prefix":"+49",
"is_ported":"No",
"ported_network_name":null,
"ported_country_name":null,
"ported_country_code":null,
"ported_country_prefix":null,
"is_roaming":"No",
"roaming_network_name":null,
"roaming_country_name":null,
"roaming_country_code":null,
"roaming_country_prefix":null,
"cost":"0.0100",
"timestamp":"2025-02-02 22:15:37.920+0100",
"storage":"WEB-CLIENT-SOLO-2025-02",
"route":"E10",
"processing_status":"COMPLETED",
"error_code":574,
"interface":"Web Client Solo",
"data_source":"LIVE_HLR"}
NT lookup
{"id":"d2062e******",
"number":"+491521*********",
"number_type":"MOBILE",
"query_status":"OK",
"is_valid":"Yes",
"invalid_reason":null,
"is_possibly_ported":"Yes",
"is_vanity_number":"No",
"qualifies_for_hlr_lookup":"Yes",
"mccmnc":"***02",
"mcc":"***",
"mnc":"02",
"original_network_name":"Vodafone (Vodafone D2 GmbH)",
"original_country_name":"Germany",
"original_country_code":"DE",
"regions":["Germany"],
"timezones":["Europe/Berlin"],
"info_text":"This number qualifies for HLR lookups. Determine if this subscriber number is connected, absent or invalid by running an HLR lookup. This is a mobile number and might be in roaming state. Run an HLR lookup to obtain roaming information (if available). This number is possibly ported and the carrier information might be inaccurate. To obtain portability information run an HLR lookup.",
"cost":"0.0050",
"timestamp":"2025-02-02 22:18:38.493+0100",
"storage":"WEB-CLIENT-SOLO-2025-02",
"route":"LC1",
"interface":"Web Client Solo"}
MNP lookup
{"id":"6de23******",
"msisdn":"+491521*********",
"query_status":"OK",
"mccmnc":"***43",
"mcc":"***",
"mnc":"43",
"is_ported":"No",
"original_network_name":"Lycamobile Germany GmbH",
"original_country_name":"Germany",
"original_country_code":"DE",
"original_country_prefix":"+49",
"ported_network_name":null,
"ported_country_name":null,
"ported_country_code":null,
"ported_country_prefix":null,
"extra":"MOBILE",
"cost":"0.0050",
"timestamp":"2025-02-02 22:19:56.930+0100",
"storage":"WEB-CLIENT-SOLO-MNP-2025-02",
"route":"PTX",
"error_code":null,
"interface":"Web Client Solo",
"processing_status":"COMPLETED"}
MCC MNC Lookup
{
"type": "National",
"countryName": "Germany",
"countryCode": "DE",
"mcc": "262",
"mnc": "42",
"brand": "CCC Event",
"operator": "Chaos Computer Club",
"status": "Temporary operational",
"bands": "GSM 1800",
"notes": "Used on events like Chaos Communication Congress"
},
{
"type": "National",
"countryName": "Germany",
"countryCode": "DE",
"mcc": "262",
"mnc": "43",
"brand": "Lycamobile",
"operator": "Lycamobile",
"status": "Operational",
"bands": "MVNO",
"notes": "Uses Vodafone"
},
{
"type": "National",
"countryName": "Germany",
"countryCode": "DE",
"mcc": "262",
"mnc": "60",
"brand": null,
"operator": "DB Telematik",
"status": "Operational",
"bands": "GSM-R 900",
"notes": null
},
Frame Layers
Data is mostly uninteresting… formerly there was SMS data in here, clear-text. At least in our country, this issue seems to be fixed, even on 2G & 3G stations. Higher-rated stations employ further security measures like data encryption. Metadata is all that’s left by now, and to be honest, it’s not the kind of Metadata
you could do much with.
Found Layer GSM_A.CCCH
Packet Packet (Length: 81)
Layer ETH
: Destination: 00:00:00:00:00:00
Address: 00:00:00:00:00:00
.... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
Source: 00:00:00:00:00:00
.... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
.... ...0 .... .... .... .... = IG bit: Individual address (unicast)
Type: IPv4 (0x0800)
Address: 00:00:00:00:00:00
Layer IP
: 0100 .... = Version: 4
.... 0101 = Header Length: 20 bytes (5)
Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
0000 00.. = Differentiated Services Codepoint: Default (0)
.... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
Total Length: 67
Identification: 0x6600 (26112)
Flags: 0x40, Don't fragment
0... .... = Reserved bit: Not set
.1.. .... = Don't fragment: Set
..0. .... = More fragments: Not set
...0 0000 0000 0000 = Fragment Offset: 0
Time to Live: 64
Protocol: UDP (17)
Header Checksum: 0xd6a7 [validation disabled]
Header checksum status: Unverified
Source Address: 127.0.0.1
Destination Address: 127.0.0.1
Layer UDP
: Source Port: 45175
Destination Port: 4729
Length: 47
Checksum: 0xfe42 [unverified]
Checksum Status: Unverified
Stream index: 0
Timestamps
Time since first frame: 11.201968498 seconds
Time since previous frame: 0.055106161 seconds
UDP payload (39 bytes)
Layer GSMTAP
: Version: 2
Header Length: 16 bytes
Payload Type: GSM Um (MS<->BTS) (1)
Time Slot: 0
..00 0000 0010 1010 = ARFCN: 42
.0.. .... .... .... = Uplink: 0
0... .... .... .... = PCS band indicator: 0
Signal Level: -39 dBm
Signal/Noise Ratio: 0 dB
GSM Frame Number: 1411121
Channel Type: BCCH (1)
Antenna Number: 151
Sub-Slot: 0
Layer GSM_A.CCCH
: L2 Pseudo Length
0101 10.. = L2 Pseudo Length value: 22
.... 0110 = Protocol discriminator: Radio Resources Management messages (0x6)
0000 .... = Skip Indicator: No indication of selected PLMN (0)
Message Type: System Information Type 2
..0. .... = EXT-IND: The information element carries the complete BA (0)
...0 .... = BA-IND: 0
00.. 000. = Format Identifier: bit map 0 (0x00)
List of ARFCNs = 49 48 47 46 45 44 43 42 6 2 1
1111 1000 = NCC Permitted: 0xf8
10.. .... = Max retrans: Maximum 4 retransmissions (2)
..11 10.. = Tx-integer: 32 slots used to spread transmission (14)
.... ..0. = CELL_BARR_ACCESS: The cell is not barred (0)
.... ...0 = RE: Call Reestablishment allowed in the cell (0)
0000 0000 0000 0000 = ACC: 0x0000
Neighbour Cell Description - BCCH Frequency List
NCC Permitted
RACH Control Parameters
.... 0110 = Protocol discriminator: Radio Resources Management messages (0x6)