
Lua-basierter Wireshark-Postdissector, der Ubiquiti AirMAX/RouterBoard-802.11-Vendor-IEs entschlüsselt und in filterbare Felder parst.
Ein Wireshark/tshark-Dissector für Ubiquiti AirMAX (AC + M) und die zugehörigen Mikrotik-/RouterBoard-Vendor-Information-Elements, geschrieben in Lua.
Es ist das Gegenstück zu pyrmax auf der Leitung: Die Dekodierlogik spiegelt dieses Python-Paket Feld für Feld wider. Das AC-Paketformat wurde aus dem AirMAX-AC-Firmware-Binary rückentwickelt; die M- und RouterBoard-Layouts stammen aus früheren veröffentlichten Notizen zum älteren AirMAX-M-Vendor-IE.
AirMAX steckt in 802.11-vendor-spezifischen IEs (Tag 221). Wireshark parst den 802.11-Frame bereits und zeigt das Vendor-IE als undurchsichtigen Blob; wiremax nimmt diesen Blob, entschlüsselt ihn (AES-128-ECB, Schlüssel aus den Frame-MACs abgeleitet) und erzeugt einen geparsten, filterbaren Teilbaum.
Tag: Vendor Specific: Ubiquiti Inc (built-in 802.11 dissector)
AirMAX AC (Vendor Specific IE) ← added by wiremax
Flags: 0x02 ( .... ..1. = Encrypted: True )
Message Type: Beacon (1)
Encrypted Length: 48
[Decrypted payload (AES-128-ECB)]
[AES Key …: 1f162a13… (dst=broadcast)]
Version: 9
Source MAC: 24:5a:4c:44:57:fd
Radio MAC (mac_0c): 24:5a:4c:44:57:fd
Capability Flags: 0x0000003e
Mixed Mode: 0
Radioname: LB1
SSID: labalUBI2
TLV: Radioname (1), len 3
TLV: SSID (2), len 9
TLV: Padding (0)
Das entschlüsselte Layout hängt vom Message Type ab: Beacons tragen mac_0c / cap_flags / mixed_mode + Namens-TLVs (oben); Assoc Req/Resp tragen Chainmasks, cap_flags und versionsabhängige Endfelder (field_9c, rssi, fwname, txpower); Probe Req ist nur ein Header; Deauth trägt einen Jiffies-Nonce und ein undurchsichtiges Auth-Token (und seine Quell-MAC ist XOR-maskiert, was wiremax ent-maskt).
| Variant | Abdeckung |
|---|---|
| AirMAX AC | äußerer Header + entschlüsselter gemeinsamer Kopf (version + src_mac) + pro Nachrichtentyp, versionsabhängiger Body (beacon, assoc req/resp, probe req, deauth) + Namens-TLVs |
| AirMAX M | äußere Hülle + entschlüsselte 9 dokumentierte Bytes (version, msg_type, src_mac, enable) + roher Rest |
| RouterBoard (Mikrotik) | OUI/Typ + Sub-IEs + Gerätename (Klartext) |
Undokumentierte Bytebereiche werden roh angezeigt (Unknown [n:len]), niemals erfunden — dieselbe Disziplin wie pyrmax und die ac/-Wissensbasis.
Ein Postdissector (er ersetzt nicht den eingebauten 802.11-Dissector, sondern läuft danach). Für jeden Frame:
wlan.tag.oui plus wlan.sa / wlan.da über Field-Extraktoren;00:27:22), M- (00:15:6d) oder RouterBoard- (00:0c:42) OUI — für AC/M ist zusätzlich der OUI-Typ FF FF FF erforderlich, weil 00:15:6d mit Mikrotik und einem weiteren Ubiquiti-IE geteilt wird, das im selben Frame erscheint;AES-128-ECB, Schlüssel = HMAC-SHA1(dst_mac, src_mac)[:16], wobei zuerst das aufgezeichnete 802.11-Ziel versucht wird und dann auf Broadcast zurückgefallen wird (Identitäts-Frames verwenden den Broadcast-Schlüssel auch bei Unicast) — gewonnen hat derjenige, dessen eingebettete Quell-MAC in den entschlüsselten Nutzdaten mit der 802.11-Quelle übereinstimmt;Die Krypto (SHA-1, HMAC-SHA1, AES-128-Entschlüsselung) ist in sich geschlossenes reines Lua — Wireshark stellt Lua keine Crypto-API bereit. Nutzdaten sind nur ein paar 16-Byte-Blöcke, daher ist die Leistung irrelevant. Der Schlüssel wird aus öffentlichen MACs abgeleitet, also ist das Verschleierung, keine Sicherheit.
Einmalig (ohne Installation):
tshark -X lua_script:airmax.lua -r capture.pcap -V
wireshark -X lua_script:airmax.lua capture.pcap
Dauerhaft (Wireshark-GUI + tshark): Legen Sie den gesamten Ordner wiremax/ in ein Verzeichnis für persönliche Lua-Plugins. Der genaue Pfad wird in Wireshark unter Hilfe ▸ Über Wireshark ▸ Ordner ▸ Persönliche Lua-Plugins angezeigt; die Standardpfade sind:
| Betriebssystem | Standardpfad für persönliche Lua-Plugins |
|---|---|
| Linux | ~/.local/lib/wireshark/plugins/ |
| macOS | ~/.local/lib/wireshark/plugins/ (oder ~/.config/wireshark/plugins/) |
| Windows | %APPDATA%\Wireshark\plugins\ |
Ein Symlink ist beim Iterieren ideal — Änderungen werden beim Neuladen (Ctrl+Shift+L) ohne Kopierschritt übernommen:
ln -s "$PWD" ~/.local/lib/wireshark/plugins/wiremax
Wireshark lädt rekursiv jede .lua unter dem Plugin-Ordner; die Submodule unter wsairmax/ sind normale Module (harmlos, wenn sie eigenständig geladen werden) und test/selftest.lua ist eine No-op in Wireshark.
| Filter | Bedeutung |
|---|---|
airmax.ac.msg_type | Nachrichtentyp: 1 Beacon, 2 Assoc Req, 3 Assoc Resp, 4 Probe Req, 0xC Deauth |
airmax.ac.flags.encrypted | verschlüsseltes Bit (0x02) |
airmax.ac.version | entschlüsselte Formatversion (das Tor; TX sendet 9) |
airmax.ac.src_mac | Quell-MAC aus dem entschlüsselten Kopf (Deauth ent-maskt sie) |
airmax.ac.mac_0c | Beacon-Funk-MAC (BSSID) |
airmax.ac.cap_flags + airmax.ac.cap_flags.chanbw/.high_density/.auth_deauth/.compat_11ax | Fähigkeiten-Bitfeld (§11) |
airmax.ac.radioname, airmax.ac.ssid, airmax.ac.fwname | AC-Name-/Firmware-Strings |
airmax.ac.jiffies_nonce, airmax.ac.enc_token | Deauth-Nonce + undurchsichtiges Auth-Token |
airmax.ac.tlv.tag / .len / .data | roher TLV-Durchlauf |
airmax.m.version, airmax.m.msg_type, airmax.m.src_mac, airmax.m.enable | M-dokumentierte Felder |
airmax.m.unknown_rest | undokumentierter M-Rest |
airmax.rb.device_name | RouterBoard-Gerätename |
airmax.ac.key / airmax.m.key | abgeleiteter AES-Schlüssel (generiert, zur Verifizierung) |
Beispiele:
tshark -r cap.pcap -Y 'airmax.ac.ssid' -T fields -e wlan.sa -e airmax.ac.ssid
tshark -r cap.pcap -Y 'airmax.m.msg_type == 1' -T fields -e airmax.m.src_mac
tshark -r cap.pcap -Y 'airmax.rb.device_name' -T fields -e airmax.rb.device_name
Aufzeichnungen müssen im 802.11-Monitormodus (Radiotap) erfolgen; die Management-Frames, die die IEs tragen (Beacon / Probe Response / Assoc), sind auf der 802.11-Ebene unverschlüsselt — nur die AirMAX-Nutzlast im IE ist AES-gewrappt, und wiremax entpackt sie.
test/selftest.lua führt die Krypto und Dekoder unter dem eigenständigen Lua-5.4-Interpreter aus (ohne Wireshark) und prüft gegen Ground-Truth-Vektoren sowie Standard-SHA-1-/RFC-2202-HMAC-Vektoren:
lua test/selftest.lua # from this directory
End-to-End gegen eine echte Aufzeichnung (pyrmax enthält Beispiel-Pcaps unter tests/samples/):
tshark -X lua_script:airmax.lua -r airmax_ac_beacon.pcap -V
tshark -X lua_script:airmax.lua -r airmax_m_probe_response.pcap -V
wiremax/
├── airmax.lua # Wireshark entry: ProtoFields + postdissector + tree
├── wsairmax/ # pure-Lua modules (no Wireshark dependency)
│ ├── util.lua # hex / MAC helpers
│ ├── sha1.lua # SHA-1 + HMAC-SHA1
│ ├── aes.lua # AES-128 ECB decrypt
│ ├── crypto.lua # key derivation + decrypt (mirrors pyrmax/_crypto.py)
│ ├── ac.lua # AirMAX AC decoder (outer ksy: airmax_ac; payload hand-written per 09b)
│ ├── m.lua # AirMAX M decoder (ksy: airmax_m[_payload])
│ └── routerboard.lua # RouterBoard decoder (ksy: routerboard)
├── test/selftest.lua # standalone-Lua test harness
└── README.md