
Postdissector Wireshark basé sur Lua qui déchiffre et analyse les IEs de fournisseur 802.11 d'Ubiquiti AirMAX/RouterBoard en champs filtrables.
Un dissecteur Wireshark/tshark pour les AirMAX Ubiquiti (AC + M) et les Information Elements constructeur Mikrotik / RouterBoard associés, écrit en Lua.
C'est la contrepartie sur le fil de pyrmax : la logique de décodage reflète ce paquet Python champ pour champ. Le format de paquet AC a été rétro-ingénié à partir du binaire du firmware AirMAX AC ; les agencements M et RouterBoard proviennent de notes publiées antérieurement sur l'ancien IE constructeur AirMAX-M.
AirMAX voyage à l'intérieur des IE spécifiques au constructeur 802.11 (tag 221). Wireshark analyse déjà la trame 802.11 et affiche l'IE constructeur comme un bloc opaque ; wiremax prend ce bloc, le déchiffre (AES-128-ECB, clé dérivée des MAC de la trame) et rend un sous-arbre analysé et filtrable.
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)
L'agencement déchiffré dépend du Message Type : les beacons portent mac_0c / cap_flags / mixed_mode + des TLVs de nom (ci-dessus) ; les assoc req/resp portent les chainmasks, les cap_flags et des champs de queue conditionnés par la version (field_9c, rssi, fwname, txpower) ; probe req se limite à l'en-tête ; deauth porte un nonce jiffies et un jeton d'authentification opaque (et sa MAC source est masquée par XOR, ce que wiremax démasque).
| Variante | Couverture |
|---|---|
| AirMAX AC | en-tête externe + en-tête commun déchiffré (version + src_mac) + corps par type de message, conditionné par la version (beacon, assoc req/resp, probe req, deauth) + TLVs de nom |
| AirMAX M | coquille externe + les 9 octets documentés déchiffrés (version, msg_type, src_mac, enable) + reste brut |
| RouterBoard (Mikrotik) | OUI/type + sous-IE + nom de périphérique (en clair) |
Les régions d'octets non documentées sont affichées brutes (Unknown [n:len]), jamais inventées — même discipline que pyrmax et la base de connaissances ac/.
Un postdissector (il ne remplace pas le dissecteur 802.11 intégré, il s'exécute après lui). Pour chaque trame, il :
wlan.tag.oui ainsi que wlan.sa / wlan.da via des extracteurs Field ;00:27:22), M (00:15:6d) ou RouterBoard
(00:0c:42) — pour AC/M, il exige aussi le type d'OUI FF FF FF, car
00:15:6d est partagé avec Mikrotik et avec un autre IE Ubiquiti qui apparaît
dans la même trame ;AES-128-ECB, clé = HMAC-SHA1(dst_mac, src_mac)[:16], en essayant
d'abord la destination 802.11 capturée puis en retombant sur la diffusion (les
trames d'identité utilisent la clé de diffusion même en unicast) — le gagnant
est la clé dont la MAC source intégrée à la charge utile déchiffrée correspond
à la source 802.11 ;La cryptographie (SHA-1, HMAC-SHA1, déchiffrement AES-128) est du Lua pur autonome — Wireshark n'expose aucune API cryptographique à Lua. Les charges utiles ne font que quelques blocs de 16 octets, donc la performance est sans importance. La clé est dérivée de MAC publics, il s'agit donc d'obfuscation, pas de sécurité.
Ponctuel (sans installation) :
tshark -X lua_script:airmax.lua -r capture.pcap -V
wireshark -X lua_script:airmax.lua capture.pcap
Permanent (GUI Wireshark + tshark) : placez tout le dossier wiremax/ dans un répertoire de plugins Lua personnels. Le chemin exact est affiché dans Wireshark sous Aide ▸ À propos de Wireshark ▸ Dossiers ▸ Plugins Lua personnels ; les valeurs par défaut sont :
| OS | Chemin par défaut des plugins Lua personnels |
|---|---|
| Linux | ~/.local/lib/wireshark/plugins/ |
| macOS | ~/.local/lib/wireshark/plugins/ (ou ~/.config/wireshark/plugins/) |
| Windows | %APPDATA%\Wireshark\plugins\ |
Un lien symbolique est idéal pendant l'itération — les modifications sont prises en compte au rechargement (Ctrl+Shift+L) sans étape de copie :
ln -s "$PWD" ~/.local/lib/wireshark/plugins/wiremax
Wireshark charge récursivement chaque .lua sous le dossier de plugins ; les sous-modules sous wsairmax/ sont de simples modules (inoffensifs s'ils sont chargés seuls) et test/selftest.lua ne fait rien dans Wireshark.
| Filtre | Signification |
|---|---|
airmax.ac.msg_type | type de message : 1 Beacon, 2 Assoc Req, 3 Assoc Resp, 4 Probe Req, 0xC Deauth |
airmax.ac.flags.encrypted | bit chiffré (0x02) |
airmax.ac.version | version du format déchiffré (le verrou ; TX émet 9) |
airmax.ac.src_mac | MAC source de l'en-tête déchiffré (deauth le démasque) |
airmax.ac.mac_0c | MAC radio du beacon (BSSID) |
airmax.ac.cap_flags + airmax.ac.cap_flags.chanbw/.high_density/.auth_deauth/.compat_11ax | champ de bits de capacités (§11) |
airmax.ac.radioname, airmax.ac.ssid, airmax.ac.fwname | chaînes de nom / firmware AC |
airmax.ac.jiffies_nonce, airmax.ac.enc_token | nonce de deauth + jeton d'authentification opaque |
airmax.ac.tlv.tag / .len / .data | parcours TLV brut |
airmax.m.version, airmax.m.msg_type, airmax.m.src_mac, airmax.m.enable | champs documentés de M |
airmax.m.unknown_rest | queue de M non documentée |
airmax.rb.device_name | nom de périphérique RouterBoard |
airmax.ac.key / airmax.m.key | clé AES dérivée (générée, pour vérification) |
Exemples :
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
Les captures doivent être en mode moniteur 802.11 (radiotap) ; les trames de gestion portant les IE (beacon / probe response / assoc) ne sont pas chiffrées au niveau de la couche 802.11 — seule la charge utile AirMAX à l'intérieur de l'IE est enveloppée AES, et wiremax la déchiffre.
test/selftest.lua exécute la cryptographie et les décodeurs sous l'interpréteur Lua 5.4 autonome (sans Wireshark), en vérifiant les vecteurs de référence ainsi que les vecteurs SHA-1 / HMAC RFC-2202 standard :
lua test/selftest.lua # from this directory
De bout en bout contre une capture réelle (pyrmax fournit des pcaps d'exemple sous 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 attribue l'étiquette 1 = Beacon selon les anciennes notes AirMAX-M, mais la valeur 1 est aussi observée sur les probe responses — l'espace des types de message M n'est pas entièrement cartographié, faites donc confiance au nombre brut.enc_len, un u16 big-endian (09b §3) ; l'ancienne lecture sur 1 octet n'était correcte que par accident pour les charges utiles < 256.version < 9 sont également non vérifiés (TX n'émet que 9). Voir les vecteurs synthétiques dans test/selftest.lua.Lorsqu'un champ est ajouté/renommé dans pyrmax (src/pyrmax/ac.py ou ses schémas ksy/*.ksy), reflétez-le dans le décodeur wsairmax/*.lua correspondant et, s'il est visible par l'utilisateur, ajoutez un ProtoField dans airmax.lua. Les vecteurs de référence dans test/selftest.lua proviennent des pcaps d'exemple de pyrmax, donc une divergence s'y manifestera.