Utilities for processing LTE S1AP PCAP/PCAPNG captures:
anonymise_s1ap.py: Anonymise IMSIs inside S1AP traffic, optionally strip or anonymise NAS payloads, and filter by S1AP procedure codes.run_anonymise.sh: Convenience wrapper to run the anonymiser with sensible defaults and verification.s1ap_stats.py: Generate quick statistics (procedure codes, IPs, cell IDs, time range) from S1AP captures.
Published by Melrose Networks. Repository will be hosted under the organisation: https://github.com/melrosenetworks
- Python 3.9+ (tested on recent Python 3 versions)
- Wireshark/Tshark installed and on
$PATH(required bypyshark) - Python packages (from
requirements.txt):pysharkscapypycrate(providespycrate_mobile.NAS; used for best-effort NAS handling)
- Install Wireshark (for
tshark):
brew install wireshark- Create a virtual environment and install Python deps:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt- Verify
tsharkis available:
tshark -vsudo apt update
sudo apt install -y tshark python3-venv
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtNote: This tool reads from capture files; live-capture permissions are not needed. Only the tshark binary is required by pyshark for dissection.
source .venv/bin/activate
python anonymise_s1ap.py \
-i input.pcap \
-o output.pcap \
-m output.imsi_map.txt \
--include 9 11 12 13 18 21 23-
anonymise_s1ap.py- Reads PCAP/PCAPNG, scans S1AP frames using Tshark/PyShark, and writes a new capture containing only S1AP frames that pass filtering.
- For each frame, attempts best‑effort IMSI anonymisation by replacing:
- TBCD-encoded IMSIs (both nibble orders)
- ASCII decimal IMSI representations
- NAS-PDU bytes can be anonymised length‑preserving or zeroed if
--strip-nasis used.
- Generates an IMSI mapping file: original digits to anonymised digits (same length), with configurable anonymised MCC/MNC prefix.
- Supports pcapng input if your Scapy version provides
PcapNgReader. - Additional options:
--drop-encrypted-nas: Drop frames that carry security‑protected NAS (cannot be anonymised without keys).--no-prefilter: Disable Tshark prefilter step (S1AP‑only fast path); enabled by default.--s1ap-port: Decode S1AP on a specific SCTP port for tshark/pyshark (default36412).--fast-raw-mode(default) /--no-fast-raw-mode: Toggle fast raw‑byte replacement path vs Scapy‑based processing.--experimental-workers,--experimental-inflight: Experimental concurrency knobs for NAS anonymisation.
-
run_anonymise.sh- Wrapper for the Python anonymiser with:
- Default
--includeof common S1AP procedures (9, 11, 12, 13, 18, 21, 23) - Optional
--strip-nas(uncomment in the script) - Forwards additional flags to the Python script (e.g.,
--drop-encrypted-nas,--s1ap-port 38412) - Post‑run verification using Tshark:
- IMSI counts before/after and by field name
- MCC‑MNC table (input vs output)
- Procedure code distribution (input vs output)
- Pass/fail checks:
- When
--strip-nasis enabled, fails if any IMSIs remain in output. - Otherwise, fails if output IMSIs are not anonymised or equal to originals.
- When
- Default
- Usage:
./run_anonymise.sh -i INPUT.pcapng -o out/anonymised.pcap
- The IMSI map will be created alongside the output (e.g.,
out/anonymised.imsi_map.txt).
- Wrapper for the Python anonymiser with:
-
s1ap_stats.py- Computes quick S1AP stats using Tshark fields dynamically detected on your Wireshark version:
- Count of S1AP packets
- Time range (epoch seconds)
- Procedure code histogram
- IP addresses sent/received
- Detected cell identifiers (various field variants)
- Usage:
python s1ap_stats.py -i input.pcap --top 20
- Computes quick S1AP stats using Tshark fields dynamically detected on your Wireshark version:
python anonymise_s1ap.py [-h] -i INPUT [-o OUTPUT] [-m MAP]
[--mcc MCC] [--mnc MNC]
[--include INCLUDE [INCLUDE ...]]
[--exclude EXCLUDE [EXCLUDE ...]]
[--strip-nas]
[--anonymise-nas | --no-anonymise-nas]
[--drop-encrypted-nas]
[--no-prefilter]
[--s1ap-port S1AP_PORT]
[--experimental-workers N]
[--experimental-inflight N]
[--fast-raw-mode | --no-fast-raw-mode]-i/--input: Input PCAP/PCAPNG file (required)-o/--output: Output PCAP (default:output.pcap)-m/--map: IMSI mapping output (default:imsi_map.txt)--mcc/--mnc: MCC/MNC to prefix anonymised IMSIs (defaults: 999/99)--include: Only include these S1AP procedure codes (space‑separated)--exclude: Exclude these S1AP procedure codes (space‑separated)--anonymise-nas(default) |--no-anonymise-nas: Best‑effort IMSI anonymisation within NAS PDUs (unencrypted NAS only)--strip-nas: Zero out NAS PDUs; if an IMSI is still detected and cannot be safely replaced, the frame may be dropped--drop-encrypted-nas: Drop frames carrying security‑protected NAS (cannot be anonymised without keys)--no-prefilter: Disable Tshark S1AP‑only prefiltering (enabled by default for speed)--s1ap-port: SCTP port to decode as S1AP for tshark/pyshark (default: 36412). Use this if your S1AP runs on a non‑standard port so that procedure codes and NAS PDUs are decoded correctly.--experimental-workers: Experimental number of worker processes for NAS anonymisation--experimental-inflight: Experimental max in‑flight frame queue (default:workers*16)--fast-raw-mode(default) |--no-fast-raw-mode: Use fast raw‑byte replacement path (no checksum fixups) or Scapy‑based processing
Examples:
# Basic anonymisation with common procedures
python anonymise_s1ap.py -i input.pcap -o output.pcap -m output.imsi_map.txt \
--include 9 11 12 13 18 21 23
# Strip NAS payloads (best-effort zeroing) and drop frames where IMSI remains
python anonymise_s1ap.py -i input.pcap -o output.pcap --strip-nas
# Use a specific anonymised MCC/MNC prefix (length preserved)
python anonymise_s1ap.py -i input.pcap -o output.pcap --mcc 234 --mnc 30
# Disable NAS anonymisation (only do direct TBCD/ASCII replacements when found)
python anonymise_s1ap.py -i input.pcap --no-anonymise-nas
# Decode S1AP on a non-standard SCTP port (e.g., 38412)
python anonymise_s1ap.py -i input.pcap -o output.pcap --s1ap-port 38412
# Drop frames with security-protected NAS that cannot be anonymised
python anonymise_s1ap.py -i input.pcap -o output.pcap --drop-encrypted-nasOutputs:
output.pcap: New capture containing only allowed S1AP frames (others are dropped)output.imsi_map.txt: Lines oforiginal -> anonymisedIMSIs actually encountered
./run_anonymise.sh -i INPUT.pcap(,pcapng) -o OUTPUT.pcapNotes:
- The script activates
.venvautomatically if present. - Defaults include procedure codes
9 11 12 13 18 21 23. - To enable NAS stripping, open the script and uncomment the line that adds
--strip-nas. - After running, it prints:
- IMSI counts before/after (via
e212.imsi) - IMSI field occurrences by field name
- MCC‑MNC counts (input vs output)
- Procedure code distribution (input vs output)
- Pass/fail status:
- With
--strip-nas, fails if any IMSIs remain in output - Otherwise, fails if any output IMSIs are not anonymised or equal to originals
- With
- IMSI counts before/after (via
python s1ap_stats.py -i INPUT.pcapng --top 20Typical output (truncated):
=== S1AP Statistics ===
- Number of S1AP packets: 12345
- Time period: 2025-01-01T12:00:00+00:00 to 2025-01-01T12:05:00+00:00 (duration 300.00s)
- Procedure codes (code=count):
9=120
11=85
...
- IP addresses sent (ip=count):
10.0.0.1=500
...
- IP addresses received (ip=count):
10.0.0.2=480
...
- Cell IDs (id=count):
0123ABCD=200
...
Count IMSIs before/after:
tshark -r input.pcap -Y "s1ap && e212.imsi" -T fields -e e212.imsi | wc -l
tshark -r output.pcap -Y "s1ap && e212.imsi" -T fields -e e212.imsi | wc -lList sample frames with IMSIs:
tshark -r output.pcap -Y "s1ap && e212.imsi" -T fields -e frame.number -e e212.imsi | headCheck NAS-PDU zeroing (when --strip-nas is used):
tshark -r output.pcap -Y "s1ap" -T fields -e s1ap.nas_pdu 2>/dev/null \
| sed 's/[^0-9a-fA-F]//g' | grep -E -m1 '[1-9a-fA-F]' || echo "NAS-PDUs appear zeroed or absent"- Output contains only S1AP frames that pass filtering; non‑S1AP frames are not copied to the output capture.
- IMSI anonymisation is best‑effort:
- Works by replacing known TBCD encodings (both nibble orders) and ASCII occurrences when found.
- NAS anonymisation is attempted only for unencrypted NAS. Encrypted NAS cannot be decoded/anonymised.
- With
--strip-nas, NAS payloads are zeroed and frames with remaining detectable IMSIs may be dropped.
- Fast path is enabled by default (
--fast-raw-mode): raw‑byte replacements without Scapy checksum fixups. Disable with--no-fast-raw-modeto use Scapy‑based processing if you need protocol‑aware fixups (slower). - A Tshark prefilter step (S1AP‑only) is enabled by default for performance; disable it with
--no-prefilterif it causes issues in your environment. - Use
--drop-encrypted-nasto remove frames with security‑protected NAS that cannot be anonymised. - Depending on capture link‑types and Scapy/Wireshark versions, some tools may show decoding quirks.
- For protocol‑correct full re‑encoding, extend with ASN.1/PER aware handling (e.g.,
pycrate) and ensure length‑preserving updates of SCTP DATA segments.
This code is provided by Melrose Networks and will be published under the organisation on GitHub: https://github.com/melrosenetworks