Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
pyrmax — 用于 Ubiquiti AirMAX 无线协议帧的解码器/编码器;解析 pcap/实时捕获,发现设备,扫描存在漏洞的固件,构造畸形数据包,并模拟 AirMAX AC/M 目标。 | Kitploit
工具/GitHubGitHub/infobyte/pyrmax
漏洞扫描器逆向工程模糊测试网络安全无线安全
GitHubinfobyte/pyrmax

pyrmax

用于 Ubiquiti AirMAX 无线协议帧的解码器/编码器;解析 pcap/实时捕获,发现设备,扫描存在漏洞的固件,构造畸形数据包,并模拟 AirMAX AC/M 目标。

查看仓库
313天前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

pyrmax

用于 Ubiquiti AirMAX 线协议帧的解码器和编码器,涵盖 AC(WA 固件)和 M(XW/XM 固件)系列。

  • 解码:明文外部头由 ksy/ 下的 Kaitai Struct 模式驱动。AC 载荷(按版本门控,依据 msg_type 切换,带 deauth XOR 去掩码)由 ac.py 手写实现——该逻辑不适合纯解析式 Kaitai。
  • 编码:手写实现,与解码路径逐字段对应。

Status

VariantDecodeEncodepcap iteration
AC✅ 全部 5 种 msg 类型(beacon、assoc req/resp、probe req、deauth)✅ 字节级精确逆操作 + 构建器 + seal✅
M部分(9 个已记录的字节;其余为 unknown_rest)✅ 可往返已记录头部 + unknown_rest✅
Routerboard.com IE(M 的伴随项)✅ 设备名 + 子 IE 列表n/a✅

AC 线格式遵循 docs/ac_wire_format.md (已针对 ubnt_poll_host.ko 进行 [P] 确认)。所有 AC 多字节整数均为 大端序。

TODO

  • pyrmax.ac.encode(AcPacket) -> bytes — 手写的 decode() 字节级精确逆操作(外加 seal()、to_ie() 和 build_* 构造器)。
  • pyrmax.m.encode(MPacket) -> bytes — M 同理(外加 build_m、seal、to_ie)。
  • 确定 [open] AC 字段 — cap_flags 精确位图、mixed_mode、/(assoc_req)、/(assoc_resp),以及 deauth (可提取,但其密钥尚未被逆向工程,因此无法验证)。参见 §11-§12。

命令行工具

该包附带一个子命令驱动的 CLI。按如下方式运行: `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).

root@kitploit:~
`parse` 和 `discover` **要么**接受 pcap/pcapng 文件(位置参数)
**要么**通过 `-i / --iface IFACE` 接受实时无线接口。这两者是
互斥的。`scan` 接受相同的源选项(外加一个
仅适用于实时接口的 `--active` 模式);`emulate` 只能
用于实时接口。```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

实时捕获假定接口已经处于监听模式,并且位于目标 信道上——pyrmax 不会对这两者进行配置。它需要可选的 [live] 附加组件(pip install pyrmax[live]),该组件会引入 pcapy-ng。按 Ctrl-C 停止:parse 报告它流式处理了多少帧, 而 discover 在退出时打印聚合的设备摘要。

退出码(两个命令和两种源模式共用):0 表示 成功(包括“没有 AirMAX 帧”——这也是一种有效结果),1 表示 捕获格式错误(链路类型错误、文件损坏、无法打开 接口),2 表示文件缺失或源参数无效。

parse — 逐帧转储```sh

python -m pyrmax parse capture.pcap

root@kitploit:~
每个携带 AirMAX 厂商 IE 的 802.11 管理帧都会生成一个
块:AC 数据包、M 数据包,以及在同一帧中找到的任何 Routerboard.com
配套 IE。```
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

同一个单遍遍历器也以编程方式暴露为 pyrmax.pcap.iter_airmax(path) — 它为每个携带 AirMAX 的帧生成一个 AirmaxRecord(meta, ac, m, routerboard),因此 你无需手动关联 M 和 Routerboard IE。

discover — 设备摘要```sh

python -m pyrmax discover capture.pcap

root@kitploit:~
帧按 802.11 源 MAC 分组,对等节点累积,AC 载荷 / M 载荷 / Routerboard 设备名观测结果汇总为每个设备单个块。```
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

相同的聚合操作也是一个公共函数:```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)

root@kitploit:~
## 使用

### 解码单条 AirMAX AC 消息

解码器期望的是 802.11 厂商特定 IE 的字节,**从
OUI 开始** —— IE 包装(元素 ID `0xDD` + 长度)必须已经
剥离。`src_mac` / `dst_mac` 取自外层 802.11 帧的
SA / DA。```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
)

packet.msg_type          # <MsgType.BEACON: 1>
packet.version           # 9 (the wire-format epoch / version gate)
packet.src_mac           # b'\xaa\xbb\xcc\xdd\xee\xff'  (integrity-checked)
packet.radioname         # "lab-rx-1"   (convenience prop, delegates to body)
packet.ssid              # "NetA"
packet.cap_flags         # 0x3e  (None for msg types that have no cap_flags)

# The per-message-type fields live on packet.body, one of:
#   BeaconBody | AssocReqBody | AssocRespBody | ProbeReqBody | DeauthBody
body = packet.body
if isinstance(body, ac.BeaconBody):
    body.mac_0c          # the radio's own MAC / BSSID
    body.cap_flags       # u32 capability bitfield (§11)
    body.mixed_mode      # u32 [open]

msg_type 是 BEACON / ASSOC_REQ / ASSOC_RESP / PROBE_REQ / DEAUTH 之一。当帧的 version 低于其阈值时,受版本门控的尾部字段(field_9c、rssi、fwname、txpower、 …)为 None。Deauth 在完整性检查之前将 src_mac 与 jiffies nonce 进行异或解算, 并将 16 字节的 enc_token 作为不透明字节呈现(其密钥尚未被逆向, 因此无法验证)。

遍历 AC TLV 列表

携带名称 TLV 的报文主体(beacon、assoc_req)会在 body.tlvs 上逐字保留该流——包括末尾的 Padding 条目。radioname、ssid 和 fwname 作为便捷属性暴露在报文上;其余内容保持原始状态。```python body = packet.body for tlv in getattr(body, "tlvs", ()): if tlv.tag == ac.TlvTag.PADDING: continue print(f"{tlv.tag.name:<10} ({len(tlv.data)} bytes): {tlv.data!r}")

root@kitploit:~
### 解码单条 AirMAX M 消息

形状相同,表面更小——M 载荷中仅有 9 个字节有文档记录;
其余部分原样保留在 `unknown_rest` 中。**注意:** M 载荷
本身没有 SSID 字段——为此,请查看外层信标或探测响应中的标准 802.11 SSID IE
(通过 `pyrmax.pcap` 迭代时会以
`FrameMeta.ssid` 形式呈现)。```python
from pyrmax import m

packet = m.decode(data, src_mac="aa:bb:cc:dd:ee:ff")

packet.version           # 1
packet.msg_type          # <MsgType.BEACON: 1>
packet.src_mac           # b'\xaa\xbb\xcc\xdd\xee\xff'
packet.enable            # 1
packet.unknown_rest      # b'\xde\xad\xbe\xef...'   # opaque, RE pending

构造消息(encode)

encode(packet, dst_mac=…) 是 decode 的字节级精确逆操作 — 对于格式正确的帧,encode(decode(x)) == x。它将主体序列化 (重新应用版本门控,对于 deauth 还重新应用 src_mac XOR 掩码),填充到 AES 块大小,使用由 packet.src_mac 派生的密钥进行加密,并输出 从 OUI 开始的字节。用 to_ie() 包装即可得到完整的 0xDD 厂商 IE。

build_* 构造函数可免去你手工组装嵌套主体的麻烦:```python from pyrmax import ac

A beacon (mac_0c defaults to src_mac; broadcast key, as beacons use)

pkt = ac.build_beacon(src_mac="24:5a:4c:44:57:fd", radioname="LB1", ssid="labalUBI2", cap_flags=0x3e) ie = ac.to_ie(ac.encode(pkt)) # full 802.11 vendor IE, ready to embed

An authenticated deauth — the encoder XOR-masks the src with the jiffies nonce

deauth = ac.build_deauth(src_mac="24:5a:4c:44:57:fd", jiffies_nonce=0xdeadbeef) raw = ac.encode(deauth, dst_mac="24:a4:3c:88:d8:22")

Others: ac.build_assoc_req / build_assoc_resp / build_probe_req, and m.build_m

root@kitploit:~
对于 fuzzing / PoCs,`seal()` 会加密**任意**明文并构建外部头部——因此你可以构造故意格式错误的载荷(伪造的 `msg_type`、错误的长度、子块主体),而结构化编码器永远不会生成这些内容:```python
frame = ac.seal(b"\xde\xad\xbe\xef", src_mac="aa:bb:cc:dd:ee:ff",
                dst_mac="ff:ff:ff:ff:ff:ff", msg_type=0xEE)  # zero-padded to 16

结构化encode对任何无法上线传输的内容(TLV值 > 255字节、密文 ≥ 0x101)会抛出EncodeError;seal则有意保持宽松。

从pcap或pcapng流式读取数据包

pyrmax.pcap会在802.11管理帧中查找AirMAX厂商IE,从外层802.11头部提取MAC地址,并将所有内容送入正确的解码器。.pcap和.pcapng均会被自动识别。解码失败的帧(密钥错误、内容损坏、无关的厂商IE)会被静默跳过——迭代仅在EOF时停止。```python from pyrmax import pcap

for meta, packet in pcap.iter_ac("capture.pcap"): print( f"{meta.timestamp:.3f} " f"{meta.src_mac.hex(':')} → {meta.dst_mac.hex(':')} " f"{packet.msg_type.name:<10} " f"radio={packet.radioname!r} ssid={packet.ssid!r}" )

for meta, packet in pcap.iter_m("capture.pcapng"): print( f"{meta.timestamp:.3f} " f"{packet.msg_type.name:<10} " f"src={packet.src_mac.hex(':')} enable={packet.enable}" )

root@kitploit:~
`meta` 是 `FrameMeta(timestamp, src_mac, dst_mac, bssid)`。从 radiotap 提取信道/RSSI 已在待办清单中。

### 从 Routerboard.com IE 恢复设备名称

AirMAX M 帧几乎总是伴随同一个 802.11 管理帧中的 Mikrotik / Routerboard.com 厂商 IE(OUI `00:0C:42`)。其 subtype-1 子 IE 携带设备名称。```python
from pyrmax import pcap

for meta, packet in pcap.iter_routerboard("capture.pcap"):
    print(f"{meta.src_mac.hex(':')}  →  {packet.device_name!r}")
    # "AP Sur HY1315"

Routerboard 解码器也可单独使用——传入从 OUI 开始的 IE 数据:```python from pyrmax import routerboard

packet = routerboard.decode(ie_data) packet.device_name # "AP Sur HY1315" packet.sub_ies # tuple of SubIe(subtype, data)

root@kitploit:~
将同一捕获中的 Routerboard 数据包与 M 数据包关联起来,
通过匹配 `meta.timestamp` + `meta.src_mac`。

### `scan` — 发现易受攻击的设备

在监控模式接口(或 pcap)上嗅探 AirMAX 设备,并标记
哪些设备易受攻击。需要 `[scan]` 附加组件(`scapy`),对于实时
捕获,还需要 root 权限。```sh
# offline — scan a capture (no root)
python -m pyrmax scan capture.pcap

# live, passive — read versions only from traffic that happens to fly
sudo python -m pyrmax scan -i wlan0mon --channel 36

# live, active — force AirMAX AC APs to disclose their firmware
sudo python -m pyrmax scan -i wlan0mon --channel 36 --active

版本对不同变体的漏洞影响各不相同。涉及两个版本号:线格式协议版本(wire-format protocol version),携带于每个 AirMAX IE(包括信标)中;以及AC 固件版本(fwname),它仅在关联交换中传递。``` AirMAX AC ──> proto < 9 ? ──yes──────────────┐ │no ├──> VULNERABLE ▼ │ fw <= 8.7.20 ? ──yes───────────┘ ├──no───────> PATCHED └──unknown──> UNDETERMINED

AirMAX M ───> proto < 15 ? ──yes──> VULNERABLE └──no───────────> UNDETERMINED

root@kitploit:~
由于信标中包含 `proto`,**旧**设备(AC epoch < 9,M
version < 15)可被**被动**检测到。要对 AC 得出 `PATCHED` 判定,需要固件版本,因此需要捕获的关联
过程或 `--active`(主动握手 — auth → assoc → 读取 assoc-resp
`fwname` — 即一次完整的、逐字节保真的 Ubiquiti 站点交换)。

标志:`--channel N`(锁定单个信道,否则跳频),`--seconds N`,
`--cutoff X.Y.Z`(如果 `fw <= cutoff` 则 AC 存在漏洞,默认 `8.7.20`),
`--src MAC`(主动探测的源地址,例如 PTP 对端),`--vuln-only`,
`--no-set-channel`。发现任意易受攻击设备时退出码为 **3**
(便于脚本使用),否则为 `0`。```
AirMAX: 5 device(s) (3 AC, 2 M), 2 vulnerable (AC fw <= 8.7.20 or protocol version below the fixed epoch).

  1c:6a:1b:00:00:01  AC  VulnAC            ch36   v8.7.19    VULNERABLE     rssi=-40dBm  peers=0
  1c:6a:1b:00:00:04  M   VulnM             ch36   v14        VULNERABLE     rssi=-42dBm  peers=0
  1c:6a:1b:00:00:03  AC  PatchedAC         ch36   v8.7.24    patched        rssi=-41dBm  peers=0
  1c:6a:1b:00:00:02  AC  UndetAC           ch36   epoch9     undetermined   rssi=-41dBm  peers=0
  1c:6a:1b:00:00:05  M   UndetM            ch36   v15        undetermined   rssi=-43dBm  peers=0

emulate — 伪造 AirMAX 目标

作为一个或多个伪造的 AirMAX AC/M 设备发送 Beacon,并对 AC 设备应答发现握手,使主动扫描器读取到模拟的固件版本。需要 [emulate] 附加组件(scapy)、监听模式接口和 root 权限。适用于在没有真实硬件的情况下测试 scan。```sh sudo python -m pyrmax emulate -i wlan1mon --channel 36
-d ac/8.7.19/VulnAC -d ac/9/UndetAC -d ac/8.7.24/PatchedAC
-d m/14/VulnM -d m/15/UndetM

root@kitploit:~
每个 `-d`(可重复)都是 `TYPE/VERSION[/SSID[/MAC]]`,以 `/` 分隔,因此
MAC 的冒号是安全的:

- `ac/8.7.19` — 现代 AC,固件 `8.7.19`(epoch 9,泄露 `fwname`)
- `ac/9` — 现代 AC epoch,**无**固件字符串 → 扫描器看到
  `undetermined`
- `ac/7` — **旧版** AC,线格式 epoch 7(< 9)→ 易受攻击,从信标捕获
- `m/14` — AirMAX M,版本 14(< 15)→ 易受攻击
- `m/15` — AirMAX M 在已修复的 epoch → undetermined

如果不带 `-d`,则模拟一个演示机群。`--no-respond` 仅发送信标(AC
固件随后不会泄露)。AC 固件版本仅存在于
assoc-resp 中,这就是响应器存在的原因。

### 在同一台机器上同时测试两者

`scripts/hwsim_testbed.sh` 通过 `mac80211_hwsim` 创建两个虚拟无线电,
这样你可以在一个上运行 `emulate`,在另一个上运行 `scan`,无需硬件:```sh
sudo ./scripts/hwsim_testbed.sh up 36    # prints EMU_IFACE / SCAN_IFACE
# ...run emulate on EMU_IFACE and scan on SCAN_IFACE (two terminals)...
sudo ./scripts/hwsim_testbed.sh down

scripts/demo_5_devices.sh 端到端地完成整个流程——启动无线电,模拟上述 5 台设备组成的设备群,运行 scan --active,然后拆除:```sh sudo ./scripts/demo_5_devices.sh 36

root@kitploit:~
### 错误处理

模式不匹配和完整性失败会引发 `pyrmax.DecodeError`。最常见的原因
是密钥错误(为当前帧提供给 `decode()` 的 `src_mac` / `dst_mac`
不正确)。```python
from pyrmax import ac, DecodeError

try:
    packet = ac.decode(data, src_mac=src, dst_mac=dst)
except DecodeError as exc:
    print(f"skipping frame: {exc}")

可选附加项

  • pip install pyrmax[pcap] (或 uv sync --extra pcap) — 引入 dpkt 使 pyrmax.pcap.iter_ac(path) / iter_m(path) 能够从 .pcap 或 .pcapng 抓包中流式读取数据包 (链路类型 DLT_IEEE802_11_RADIO)。 文件格式根据文件的魔数自动检测。
  • pip install pyrmax[live] — 添加 pcapy-ng,用于从 监听模式的无线接口进行实时捕获。供 CLI 的 -i / --iface 标志 以及编程式 pyrmax.pcap.iter_airmax_live(iface) 生成器使用。
  • pip install pyrmax[scan] — 添加 scapy 以支持 scan 命令 (实时嗅探/解码 + 主动强制关联握手)。
  • pip install pyrmax[emulate] — 添加 以支持 命令 (注入信标 + 作为伪设备应答发现握手)。

可以一次安装多个,例如 uv sync --extra scan --extra emulate。

目录结构```

pyrmax/ ├── ksy/ # Kaitai Struct source schemas │ ├── airmax_ac.ksy # AC cleartext outer header (payload decode is hand-written in ac.py) │ ├── airmax_m.ksy # M outer (OUI marker + encrypted blob) │ ├── airmax_m_payload.ksy # M decrypted payload (9 documented bytes) │ └── routerboard.ksy # Mikrotik / Routerboard.com vendor IE ├── src/pyrmax/ │ ├── init.py │ ├── ac.py # AC decode/encode API + AcPacket dataclass │ ├── m.py # M decode/encode API + MPacket dataclass │ ├── routerboard.py # Routerboard IE decoder + RouterboardPacket │ ├── pcap.py # iter_ac / iter_m / iter_routerboard / iter_airmax / iter_airmax_live │ ├── devices.py # summarize() — per-device aggregation │ ├── vuln.py # firmware-version parse + is_vulnerable() │ ├── scan.py # Scanner — live/pcap discovery + active handshake + vuln verdict │ ├── emulate.py # Emulator — fake AC/M targets (scapy) │ ├── main.py # python -m pyrmax CLI │ ├── exceptions.py │ ├── _crypto.py # AES-128-ECB + HMAC-SHA1 KDF (internal) │ └── _generated/ # kaitai-struct-compiler output (committed) ├── scripts/ │ ├── hwsim_testbed.sh # two virtual radios (mac80211_hwsim) for scan<->emulate │ └── demo_5_devices.sh # end-to-end 5-device emulate + scan demo └── tests/ ├── samples/ # raw frame captures (currently empty) ├── test_ac.py ├── test_m.py ├── test_crypto.py ├── test_pcap.py ├── test_routerboard.py ├── test_devices.py ├── test_cli.py └── test_integration.py # real-capture round-trips

root@kitploit:~
## 开发```sh
uv sync                    # create .venv and install runtime + dev deps
uv run pytest              # run tests
uv run ruff check          # lint
uv run pyright             # static type check

Configuration lives in pyproject.toml ([tool.pyright]):

  • typeCheckingMode = "basic" — 捕获结构性问题,而无需与 dpkt / kaitaistruct / pycryptodome 边界对抗(这些包不附带类型存根)。
  • src/pyrmax/_generated/ 被排除 — Kaitai 生成的文件已经带有 # type: ignore,并且在每次 kaitai-struct-compiler 运行时都会被覆盖。
  • 涉及动态属性(例如 MGMT_Frame.src)的 dpkt 边界,通过在局部绑定上标注 Any 来跨越,而不是散布 ignore 注释。

重新生成 Kaitai 解析器

src/pyrmax/_generated/ 下的生成 Python 文件已提交,因此无需 Kaitai 工具链即可安装该包。要在编辑 .ksy 后重新生成:```sh kaitai-struct-compiler -t python --outdir src/pyrmax/_generated/ ksy/*.ksy

root@kitploit:~
下载工具
field_14
field_9c
sta_field_68
ic_6b8
enc_token
docs/ac_wire_format.md
  • 对照真实抓包验证 version < 9 的 AC 解码器 — TX 构建器只会发出 version 9,因此较低版本(无标签名称、缺少尾部字段)的路径是根据规范实现的,但未在线验证。
  • 逆向工程 M 载荷的其余部分(unknown_rest 中第 9 字节及之后)——目前仍不透明。
  • 在 pcap.FrameMeta 上呈现 radiotap 元数据:channel、RSSI、rate。目前仅填充了 timestamp/MAC/BSSID。
  • tests/samples/ 中的真实抓包固定样本 — 当前有:airmax_ac_beacon.pcap(1 帧,beacon)和 airmax_m_probe_response.pcap(1 帧,probe response)。仍欢迎更多变体(assoc req/resp、多帧抓包)。
  • 数据包发送 — scan(主动强制关联)和 emulate 命令通过 scapy 注入([scan] / [emulate] 附加特性)。参见 scan.py / emulate.py。
  • scapy
    emulate