
Lua 기반 Wireshark 포스트디섹터로 Ubiquiti AirMAX/RouterBoard 802.11 벤더 IE를 복호화하고 파싱하여 필터링 가능한 필드로 변환합니다.
Ubiquiti AirMAX(AC + M) 및 관련 Mikrotik / RouterBoard 벤더 정보 요소(Information Elements)용 Wireshark/tshark 디섹터(dissector)로, Lua로 작성되었습니다.
이는 pyrmax의 와이어상 대응물입니다. 디코드 로직은 해당 Python 패키지를 필드 단위로 그대로 반영합니다. AC 패킷 형식은 AirMAX AC 펌웨어 바이너리에서 리버스 엔지니어링되었으며, M 및 RouterBoard 레이아웃은 이전 AirMAX-M 벤더 IE에 대한 기존 공개 노트에서 비롯되었습니다.
AirMAX는 802.11 벤더별 IE(태그 221) 안에 실려 있습니다. Wireshark는 이미 802.11 프레임을 파싱하고 벤더 IE를 불투명한 blob으로 표시합니다. wiremax는 그 blob을 가져와 복호화(AES-128-ECB, 키는 프레임 MAC에서 파생)한 뒤 파싱되고 필터링 가능한 하위 트리로 렌더링합니다.
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)
복호화된 레이아웃은 Message Type에 따라 달라집니다. 비콘(beacon)은 mac_0c / cap_flags / mixed_mode + 이름 TLV를 담고(위 참조), assoc req/resp는 chainmasks, cap_flags 및 버전에 따라 달라지는 꼬리 필드(field_9c, rssi, fwname, txpower)를 담습니다. probe req는 헤더만 존재합니다. deauth는 jiffies nonce와 불투명한 인증 토큰을 담고 있으며(그리고 해당 소스 MAC은 XOR 마스킹되어 있는데, wiremax가 이를 해제합니다).
| Variant | Coverage |
|---|---|
| AirMAX AC | 외부 헤더 + 복호화된 공통 헤드(version + src_mac) + 메시지 유형별, 버전 게이트 본문(beacon, assoc req/resp, probe req, deauth) + 이름 TLV |
| AirMAX M | 외부 셸 + 복호화된 문서화된 9바이트(version, msg_type, src_mac, enable) + 나머지 raw |
| RouterBoard (Mikrotik) | OUI/type + 하위 IE + 장치 이름(평문) |
문서화되지 않은 바이트 영역은 raw로 표시되며(Unknown [n:len]), 절대 임의로 만들지 않습니다. 이는 pyrmax 및 ac/ 지식 기반과 같은 원칙입니다.
**포스트디섹터(postdissector)**입니다(내장 802.11 디섹터를 대체하지 않고 그 후에 실행됩니다). 각 프레임에 대해 다음을 수행합니다.
Field 추출기를 통해 모든 wlan.tag.oui 및 wlan.sa / wlan.da를 읽습니다.00:27:22), M(00:15:6d) 또는 RouterBoard(00:0c:42) OUI를 매칭합니다. AC/M의 경우 OUI 유형 FF FF FF도 요구하는데, 00:15:6d가 Mikrotik 및 동일한 프레임에 나타나는 다른 Ubiquiti IE와 공유되기 때문입니다.AES-128-ECB로 페이로드를 복호화합니다. 키는 HMAC-SHA1(dst_mac, src_mac)[:16]이며, 캡처된 802.11 목적지를 먼저 시도하고 브로드캐스트로 폴백합니다(identity 프레임은 유니캐스트일 때도 브로드캐스트 키를 사용합니다). 승자는 복호화된 페이로드에 포함된 소스 MAC이 802.11 소스와 일치하는 것입니다.암호화 관련 기능(SHA-1, HMAC-SHA1, AES-128 복호화)은 자체 포함된 순수 Lua입니다. Wireshark는 Lua에 암호화 API를 노출하지 않습니다. 페이로드는 16바이트 블록 몇 개에 불과하므로 성능은 문제가 되지 않습니다. 키는 공개된 MAC에서 파생되므로 이는 보안이 아니라 난독화입니다.
일회성 실행(설치 없음):
tshark -X lua_script:airmax.lua -r capture.pcap -V
wireshark -X lua_script:airmax.lua capture.pcap
영구 설치(Wireshark GUI + tshark): wiremax/ 폴더 전체를 Personal Lua Plugins 디렉터리에 넣으세요. 정확한 경로는 Wireshark의 Help ▸ About Wireshark ▸ Folders ▸ Personal Lua Plugins에 표시됩니다. 기본값은 다음과 같습니다.
| OS | 기본 Personal Lua Plugins 경로 |
|---|---|
| Linux | ~/.local/lib/wireshark/plugins/ |
| macOS | ~/.local/lib/wireshark/plugins/ (또는 ~/.config/wireshark/plugins/) |
| Windows | %APPDATA%\Wireshark\plugins\ |
반복 작업 중에는 심볼릭 링크가 이상적입니다. 편집 내용은 복사 단계 없이 리로드(Ctrl+Shift+L) 시 반영됩니다.
ln -s "$PWD" ~/.local/lib/wireshark/plugins/wiremax
Wireshark는 플러그인 폴더 아래의 모든 .lua를 재귀적으로 로드합니다. wsairmax/ 아래의 하위 모듈은 일반 모듈이며(단독으로 로드해도 무해함), test/selftest.lua는 Wireshark 내부에서 아무 작업도 하지 않습니다.
예시:
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
캡처는 802.11 모니터 모드(radiotap)여야 합니다. IE를 나르는 관리 프레임(beacon / probe response / assoc)은 802.11 계층에서 암호화되지 않습니다. IE 내부의 AirMAX 페이로드만 AES로 감싸져 있으며 wiremax가 이를 풀어냅니다.
test/selftest.lua는 독립 실행형 Lua 5.4 인터프리터(Wireshark 없음)에서 암호화 및 디코더를 실행하며, 기준(ground-truth) 벡터와 표준 SHA-1 / RFC-2202 HMAC 벡터를 대조합니다.
lua test/selftest.lua # from this directory
실제 캡처에 대한 종단 간 테스트(pyrmax는 tests/samples/ 아래에 샘플 pcap을 포함합니다):
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**은 이전 AirMAX-M 노트에 따라 1 = Beacon으로 표시하지만, probe response에서도 값 1이 관찰됩니다. M 메시지 유형 공간이 완전히 매핑되지 않았으므로 원시 숫자를 신뢰하세요.enc_len으로, u16 빅엔디언(09b §3)입니다. 이전의 1바이트 읽기는 256 미만 페이로드에서만 우연히 정확했습니다.version < 9 디코더 역시 검증되지 않았습니다(TX는 9만 방출). test/selftest.lua의 합성 벡터를 참조하세요.pyrmax(src/pyrmax/ac.py 또는 해당 ksy/*.ksy 스키마)에서 필드가 추가/이름 변경되면 일치하는 wsairmax/*.lua 디코더에도 반영하고, 사용자에게 보이는 필드라면 airmax.lua에 ProtoField를 추가하세요. test/selftest.lua의 기준 벡터는 pyrmax 샘플 pcap에서 비롯되므로 차이가 발생하면 그곳에서 드러납니다.
| 필터 | 의미 |
|---|
airmax.ac.msg_type | 메시지 유형: 1 Beacon, 2 Assoc Req, 3 Assoc Resp, 4 Probe Req, 0xC Deauth |
airmax.ac.flags.encrypted | 암호화 비트(0x02) |
airmax.ac.version | 복호화된 형식 버전(게이트 역할, TX는 9를 방출) |
airmax.ac.src_mac | 복호화된 헤드의 소스 MAC(deauth는 마스크 해제됨) |
airmax.ac.mac_0c | 비콘 라디오 MAC(BSSID) |
airmax.ac.cap_flags + airmax.ac.cap_flags.chanbw/.high_density/.auth_deauth/.compat_11ax | capability 비트필드(§11) |
airmax.ac.radioname, airmax.ac.ssid, airmax.ac.fwname | AC 이름/펌웨어 문자열 |
airmax.ac.jiffies_nonce, airmax.ac.enc_token | deauth nonce + 불투명 인증 토큰 |
airmax.ac.tlv.tag / .len / .data | raw TLV 탐색 |
airmax.m.version, airmax.m.msg_type, airmax.m.src_mac, airmax.m.enable | M 문서화된 필드 |
airmax.m.unknown_rest | 문서화되지 않은 M 꼬리 |
airmax.rb.device_name | RouterBoard 장치 이름 |
airmax.ac.key / airmax.m.key | 파생된 AES 키(검증용으로 생성됨) |