
Lua-basierter Wireshark-Postdissector, der Ubiquiti AirMAX/RouterBoard-802.11-Vendor-IEs entschlüsselt und in filterbare Felder parst.
# wiremax
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](https://github.com/infobyte/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).
## Status
| 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.
## So funktioniert es
Ein **Postdissector** (er ersetzt nicht den eingebauten 802.11-Dissector, sondern läuft danach). Für jeden Frame:
1. liest jedes `wlan.tag.oui` plus `wlan.sa` / `wlan.da` über `Field`-Extraktoren;
2. matcht die AirMAX-AC- (`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;
3. lokalisiert die IE-Bytes im Frame und entschlüsselt für AC/M die Nutzdaten mit `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;
4. baut den Teilbaum auf und macht die entschlüsselten Bytes als eigene Datenquelle verfügbar, sodass die Feldauswahl sie hervorhebt.
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.
## Installation / Ausführung
**Einmalig (ohne Installation):**
```sh
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:
```sh
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.
## Felder (Anzeigefilter)
| 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:
```sh
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.
## Tests
`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:
```sh
lua test/selftest.lua # from this directory
```
End-to-End gegen eine echte Aufzeichnung (pyrmax enthält Beispiel-Pcaps unter `tests/samples/`):
```sh
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
```
## Layout
```
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
```
## Einschränkungen
- **M `msg_type`** kennzeichnet `1 = Beacon` laut den älteren AirMAX-M-Notizen, aber der Wert
`1` wird auch bei Probe Responses beobachtet — der M-Nachrichtentyp-Raum ist nicht
vollständig kartiert, vertrauen Sie also der rohen Zahl.
- Die AC-Klartextlänge ist `enc_len`, ein **u16 Big-Endian** (09b §3); die alte
1-Byte-Lesart war nur für Nutzdaten < 256 zufällig korrekt.
- Die AC-**assoc-req / assoc-resp / deauth**-Layouts werden direkt aus der
Spezifikation dekodiert, aber das Aufzeichnungskorpus enthält noch keine echten AC-Beispiele dafür — nur
**Beacon** und **Probe-Req** sind über die Leitung verifiziert. Die `version < 9`-Dekoder sind
ebenfalls unverifiziert (TX sendet nur 9). Siehe die synthetischen Vektoren in
`test/selftest.lua`.
- Die Entschlüsselung hängt davon ab, dass die 802.11-Quell-MAC in der Aufzeichnung vorhanden ist. Wenn ein
Frame nicht entschlüsselt werden kann, wird der Klartext-Außenheader trotzdem mit einem Expertenhinweis angezeigt.
## Synchron mit pyrmax bleiben
Wenn in [pyrmax](https://github.com/infobyte/pyrmax) (`src/pyrmax/ac.py` oder seinen `ksy/*.ksy`-Schemata) ein Feld hinzugefügt oder umbenannt wird, spiegeln Sie es im passenden `wsairmax/*.lua`-Dekoder wider und fügen Sie, falls für den Benutzer sichtbar, ein `ProtoField` in `airmax.lua` hinzu. Die Ground-Truth-Vektoren in `test/selftest.lua` stammen aus den pyrmax-Beispiel-Pcaps, daher wird eine Abweichung dort sichtbar.