
Décodeur/encodeur pour les trames du protocole sans fil Ubiquiti AirMAX ; analyse des captures pcap/en direct, découverte de périphériques, recherche de firmware vulnérables, fabrication de paquets malformés et émulation de cibles AirMAX AC/M.
Décodeur et encodeur pour les trames du protocole de communication Ubiquiti AirMAX, couvrant les séries AC (firmware WA) et M (firmware XW/XM).
ksy/. La charge utile AC (sélectionnée selon la version, commutée sur msg_type, avec le démasquage XOR du deauth) est écrite à la main dans ac.py — cette logique ne convient pas à un Kaitai de simple parsing.| Variante | Décodage | Encodage | Itération pcap |
|---|---|---|---|
| AC | ✅ les 5 types de message (beacon, assoc req/resp, probe req, deauth) | ✅ inverse octet-exact + constructeurs + seal | ✅ |
| M | partiel (9 octets documentés ; le reste sous unknown_rest) | ✅ aller-retour de l'en-tête documenté + unknown_rest | ✅ |
| IE Routerboard.com (compagnon de M) | ✅ nom du périphérique + liste des sous-IE | n/a | ✅ |
Le format de transmission AC suit
docs/ac_wire_format.md
([P]-confirmé par rapport à ubnt_poll_host.ko). Tous les entiers multi-octets AC sont en big-endian.
pyrmax.ac.encode(AcPacket) -> bytes — inverse octet-exact écrit à la main de decode() (+ seal(), to_ie() et les constructeurs build_*).pyrmax.m.encode(MPacket) -> bytes — idem pour M (+ build_m, seal, to_ie).[open] — cap_flags carte de bits exacte, mixed_mode, field_14/field_9c (assoc_req), sta_field_68/ic_6b8 (assoc_resp), et le enc_token du deauth (extractible mais sa clé n'a pas encore été rétro-conçue, donc il ne peut pas être vérifié). Voir
docs/ac_wire_format.md §11-§12.version < 9 sur des captures réelles — le constructeur TX n'émet jamais que la version 9, donc les chemins de version inférieure (nom non balisé, champs de fin manquants) sont implémentés d'après la spécification mais non vérifiés sur le fil.unknown_rest) — actuellement opaque.pcap.FrameMeta : canal, RSSI, débit. Actuellement, seuls l'horodatage, les MAC et le BSSID sont renseignés.tests/samples/ — actuellement :
airmax_ac_beacon.pcap (1 trame, beacon) et
airmax_m_probe_response.pcap (1 trame, réponse de probe). D'autres variantes
(assoc req/resp, captures multi-trames) sont toujours les bienvenues.scan (force-association active) et emulate injectent via scapy (les extras [scan] / [emulate]). Voir
scan.py / emulate.py.Le paquet est fourni avec une CLI basée sur des sous-commandes. Lancez-la avec
python -m pyrmax COMMAND ....```
usage: python -m pyrmax [-h] [--version] COMMAND ...
COMMAND parse Print each AirMAX frame in detail. discover Summarize devices observed in the capture. scan Live-scan for AirMAX devices and flag vulnerable firmware. emulate Emulate AirMAX AC/M devices (fake targets for scanners).
`parse` et `discover` acceptent **soit** un fichier pcap/pcapng (positionnel)
**soit** une interface sans fil en direct via `-i / --iface IFACE`. Les deux sont
mutuellement exclusifs. `scan` accepte les mêmes options de source (plus un
mode `--active` qui ne s'applique qu'à une interface en direct) ; `emulate` est
uniquement en direct.```sh
python -m pyrmax parse capture.pcap # offline
python -m pyrmax discover capture.pcap
sudo python -m pyrmax parse -i wlan0mon # live (needs root)
sudo python -m pyrmax discover -i wlan0mon
La capture en direct suppose que l'interface est déjà en mode monitor sur le
canal d'intérêt — pyrmax ne le configure pas non plus. Elle nécessite
l'extension optionnelle [live] (pip install pyrmax[live]) qui installe
pcapy-ng. Appuyez sur Ctrl-C pour arrêter : parse indique combien de trames
ont été diffusées ; discover affiche le résumé agrégé des appareils à la sortie.
Codes de sortie (partagés par les deux commandes et les deux modes de source) : 0 en
cas de succès (y compris « aucune trame AirMAX » — un résultat valide), 1 pour
les erreurs de format de capture (mauvais type de liaison, fichier corrompu, impossible d'ouvrir
l'interface), 2 pour fichier manquant ou arguments de source invalides.
parse — vidage trame par trame```shpython -m pyrmax parse capture.pcap
Chaque trame de gestion 802.11 transportant un IE de fournisseur AirMAX produit un
bloc : paquet AC, paquet M, et tout IE compagnon Routerboard.com trouvé dans la même trame.```
Found 1 AirMAX frame(s) in capture.pcap: 0 AC, 1 M (1 with Routerboard companion).
=== Frame #0 [M] ts=1765494974.955358 ===
802.11 src=04:18:d6:0e:0c:42 dst=24:a4:3c:88:d8:22 bssid=04:18:d6:0e:0c:42
AirMAX M
version 15
msg_type BEACON (raw=1)
src_mac 04:18:d6:0e:0c:42
enable 1
unknown_rest b700000000000000000000040418d60e0c420000000000 (23B)
Routerboard.com IE
oui_type 0
unknown 0000
device_name 'AP Sur HY1315'
sub_ie subtype=1 (30B) 040000001f660902ff0f4150205375722048593133313500000000000000
Le même analyseur en une seule passe est exposé programmatiquement via pyrmax.pcap.iter_airmax(path) — il génère un AirmaxRecord(meta, ac, m, routerboard) pour chaque trame contenant de l'AirMAX, vous n'avez donc pas besoin de corréler à la main les IE M et Routerboard.
discover — résumé de l'appareil```shpython -m pyrmax discover capture.pcap
Les trames sont regroupées par MAC source 802.11, les pairs s'accumulent, et les observations AC payload / M payload / nom d'appareil Routerboard se regroupent en un seul bloc par appareil.```
2 device(s) observed in capture.pcap across 12 AirMAX frame(s).
24:5a:4c:44:57:fd (AC)
radioname 'LB1'
ssid 'labalUBI2'
ac_msg_types BEACON
ac_version 9
cap_flags 0x0000003e
mixed_mode 0
frames 8
first seen 1767046123.708745
last seen 1767046129.012448
peers (broadcast only)
04:18:d6:0e:0c:42 (M)
device_name 'AP Sur HY1315'
msg_types BEACON
m_version 15
m_enable 1
frames 4
first seen 1765494974.955358
last seen 1765494980.341110
peers 24:a4:3c:88:d8:22
La même agrégation est aussi une fonction publique :```python from pyrmax.devices import summarize from pyrmax.pcap import iter_airmax
devices = summarize(iter_airmax("capture.pcap")) for mac, dev in devices.items(): print(mac.hex(":"), dev.device_name or dev.radioname, dev.peers)
## Utilisation
### Décoder un seul message AirMAX AC
Le décodeur attend les octets de l’IE Vendor Specific 802.11 **en partant de
l’OUI** — l’enveloppe IE (Element ID `0xDD` + Longueur) doit déjà être
retirée. `src_mac` / `dst_mac` sont extraits du SA / DA de la trame 802.11
externe.```python
from pyrmax import ac
packet = ac.decode(
data, # bytes starting at b"\x00\x27\x22"
src_mac="aa:bb:cc:dd:ee:ff", # accepts str ("aa:bb:..." or "aa-bb-..."
# or "aabbcc...") and raw 6-byte bytes
dst_mac="ff:ff:ff:ff:ff:ff", # optional — defaults to broadcast
)