
Postdissector per Wireshark basato su Lua che decrittografa e analizza gli IE vendor 802.11 di Ubiquiti AirMAX/RouterBoard in campi filtrabili.
Un dissector Wireshark/tshark per gli Information Element vendor Ubiquiti AirMAX (AC + M) e il compagno Mikrotik / RouterBoard, scritto in Lua.
È la controparte over-the-wire di pyrmax: la logica di decodifica rispecchia quel pacchetto Python campo-per-campo. Il formato dei pacchetti AC è stato ricavato tramite reverse engineering dal firmware binario AirMAX AC; i layout M e RouterBoard provengono da precedenti note pubblicate sul più vecchio vendor IE AirMAX-M.
AirMAX viaggia dentro gli IE vendor-specific 802.11 (tag 221). Wireshark già analizza il frame 802.11 e mostra il vendor IE come un blob opaco; wiremax prende quel blob, lo decripta (AES-128-ECB, chiave derivata dai MAC del frame) e renderizza un sottoalbero analizzato e filtrabile.
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)
Il layout decriptato dipende dal Message Type: i beacon trasportano mac_0c / cap_flags /
mixed_mode + TLV di nome (sopra); assoc req/resp trasportano chainmasks, cap_flags e
campi di coda controllati dalla versione (field_9c, rssi, fwname, txpower); probe req è
solo header; deauth trasporta un nonce di jiffies e un token di autenticazione opaco (e il suo
MAC sorgente è mascherato con XOR, cosa che wiremax smaschera).
| Variante | Copertura |
|---|---|
| AirMAX AC | header esterno + head condiviso decriptato (version + src_mac) + corpo per-tipo-di-messaggio, controllato dalla versione (beacon, assoc req/resp, probe req, deauth) + TLV di nome |
| AirMAX M | shell esterna + 9 byte documentati decriptati (version, msg_type, src_mac, enable) + resto grezzo |
| RouterBoard (Mikrotik) | OUI/type + sub-IE + nome del dispositivo (in chiaro) |
Le regioni di byte non documentate sono mostrate grezze (Unknown [n:len]), mai inventate —
stessa disciplina di pyrmax e della knowledge base ac/.
Un postdissector (non sostituisce il dissector 802.11 integrato, gira dopo di esso). Per ogni frame:
wlan.tag.oui più wlan.sa / wlan.da tramite estrattori Field;00:27:22), M (00:15:6d) o RouterBoard (00:0c:42)
— per AC/M richiede anche l'OUI-type FF FF FF, perché 00:15:6d è
condiviso con Mikrotik e con un altro IE Ubiquiti che appare nello stesso
frame;AES-128-ECB, chiave = HMAC-SHA1(dst_mac, src_mac)[:16], provando prima la
destinazione 802.11 catturata e ripiegando sul broadcast (i frame di identità usano
la chiave broadcast anche quando sono unicast) — vince quello il cui
MAC sorgente incorporato nel payload decriptato corrisponde alla sorgente 802.11;La crittografia (SHA-1, HMAC-SHA1, decrypt AES-128) è Lua puro autocontenuto — Wireshark non espone alcuna API crittografica a Lua. I payload sono pochi blocchi da 16 byte, quindi le prestazioni sono irrilevanti. La chiave è derivata da MAC pubblici, quindi si tratta di offuscamento, non di sicurezza.
Uso singolo (senza installazione):
tshark -X lua_script:airmax.lua -r capture.pcap -V
wireshark -X lua_script:airmax.lua capture.pcap
Permanente (GUI di Wireshark + tshark): metti l'intera cartella wiremax/ in una
directory Personal Lua Plugins. Il percorso esatto è mostrato in Wireshark sotto
Guida ▸ Informazioni su Wireshark ▸ Cartelle ▸ Personal Lua Plugins; i default sono:
| OS | Percorso predefinito Personal Lua Plugins |
|---|---|
| Linux | ~/.local/lib/wireshark/plugins/ |
| macOS | ~/.local/lib/wireshark/plugins/ (or ~/.config/wireshark/plugins/) |
| Windows | %APPDATA%\Wireshark\plugins\ |
Un symlink è ideale durante lo sviluppo — le modifiche vengono recepite al reload
(Ctrl+Shift+L) senza passaggi di copia:
ln -s "$PWD" ~/.local/lib/wireshark/plugins/wiremax
Wireshark carica ricorsivamente ogni .lua sotto la cartella dei plugin; i sottomoduli
sotto wsairmax/ sono semplici moduli (innocui se caricati da soli) e
test/selftest.lua non fa nulla dentro Wireshark.
| Filtro | Significato |
|---|---|
airmax.ac.msg_type | tipo di messaggio: 1 Beacon, 2 Assoc Req, 3 Assoc Resp, 4 Probe Req, 0xC Deauth |
airmax.ac.flags.encrypted | bit di cifratura (0x02) |
airmax.ac.version | versione del formato decriptato (il gate; TX emette 9) |
airmax.ac.src_mac | MAC sorgente dall'head decriptato (deauth lo smaschera) |
airmax.ac.mac_0c | MAC radio del beacon (BSSID) |
airmax.ac.cap_flags + airmax.ac.cap_flags.chanbw/.high_density/.auth_deauth/.compat_11ax | bitfield delle capability (§11) |
airmax.ac.radioname, airmax.ac.ssid, airmax.ac.fwname | stringhe nome / firmware AC |
airmax.ac.jiffies_nonce, airmax.ac.enc_token | nonce deauth + token di autenticazione opaco |
airmax.ac.tlv.tag / .len / .data | percorso TLV grezzo |
airmax.m.version, airmax.m.msg_type, airmax.m.src_mac, airmax.m.enable | campi documentati M |
airmax.m.unknown_rest | coda M non documentata |
airmax.rb.device_name | nome del dispositivo RouterBoard |
airmax.ac.key / airmax.m.key | chiave AES derivata (generata, per verifica) |
Esempi:
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
Le catture devono essere in modalità monitor 802.11 (radiotap); i frame di gestione che trasportano gli IE (beacon / probe response / assoc) non sono cifrati a livello 802.11 — solo il payload AirMAX dentro l'IE è avvolto in AES, e wiremax lo apre.
test/selftest.lua esegue la crittografia e i decoder sotto l'interprete Lua 5.4
standalone (senza Wireshark), verificando contro vettori ground-truth più i vettori
standard SHA-1 / RFC-2202 HMAC:
lua test/selftest.lua # from this directory
End-to-end contro una cattura reale (pyrmax include pcap di esempio sotto
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
msg_type di M etichetta 1 = Beacon secondo le vecchie note AirMAX-M, ma il valore
1 è osservato anche su probe response — lo spazio dei tipi di messaggio M non è
completamente mappato, quindi fidati del numero grezzo.enc_len, un u16 big-endian (09b §3); la vecchia
lettura a 1 byte era accidentalmente corretta solo per payload < 256.version < 9 non sono verificati (TX emette solo 9). Vedi i vettori sintetici in
test/selftest.lua.Quando un campo viene aggiunto/rinominato in pyrmax
(src/pyrmax/ac.py o i suoi schemi ksy/*.ksy), rispecchialo nel decoder
wsairmax/*.lua corrispondente e, se visibile all'utente, aggiungi un ProtoField in
airmax.lua. I vettori ground-truth in test/selftest.lua provengono dai
pcap di esempio di pyrmax, quindi una divergenza emergerà lì.