Stack Buffer Overflow in zigbee decrypt_data with controlled write bytes
# Stack Buffer Overflow in decrypt_data with controlled write bytes
## Summary
A stack buffer overflow in `decrypt_data()` in the ZigBee Direct dissector. The function allocates a fixed-size `decrypted_data[264]` buffer on the stack (line 851) but passes it as the output buffer to `zbee_sec_ccm_decrypt()` without checking the input length. The AES-128-CTR decryption at `packet-zbee-security.c:1002` writes `l_m = *len - 8` bytes to this buffer, where `*len` is attacker-controlled (up to 65535).
The overflow occurs during CTR decryption (Step 1) **before** CCM authentication (Step 3), so no knowledge of the correct key is required. The overflow bytes are fully attacker-controlled (AES-CTR: `output = ciphertext XOR keystream`, attacker knows key+nonce+ciphertext).
I've test and confirmed: SIGSEGV crash (exit 139) on system tshark, stack-buffer-overflow on ASAN build. The corrupted `len` pointer on the stack demonstrates attacker-controlled write-what-where.
- **Type:** CWE-121 Stack-based Buffer Overflow
- **Platform:** Tested on macOS 15.7.5
- **Affected versions:** 4.6.4
- **Severity:** High. No user configuration required, attacker controlled stack corruption, attacker controlled stack corruption, only require user open pcap files.
## Steps to reproduce
```bash
# 1. Generate PoC pcapng (or use attached exploit_ws_zbee_direct_overflow.pcapng)
python3 exploit_zbee_direct.py
# 2. Reproduce crash (no special options needed):
tshark -2 -r exploit_ws_zbee_direct_overflow.pcapng
# 3. Expected: exit code 139 (SIGSEGV) or ASAN stack-buffer-overflow
```
### With ASAN build:
```bash
WIRESHARK_DEBUG_WMEM_OVERRIDE=simple \
ASAN_OPTIONS=detect_leaks=0:halt_on_error=1 \
tshark -2 -r exploit_ws_zbee_direct_overflow.pcapng
```
## What is the current bug behavior?
### ASAN output:
```
==39052==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x00016cee6898
READ of size 1 at 0x00016cee6898 thread T0
#0 zbee_sec_ccm_decrypt packet-zbee-security.c:1109
#1 dissect_zb_direct_common packet-zbee-direct.c:947
#2 dissect_zb_direct_formation packet-zbee-direct.c:1250
#3 call_dissector_through_handle packet.c:930
#4 dissector_try_string_with_data packet.c:2052
#5 dissect_attribute_value packet-btatt.c:4987
#6 dissect_btatt packet-btatt.c:11535
#7 dissect_btl2cap packet-btl2cap.c:3054
#8 dissect_bthci_acl packet-bthci_acl.c
#9 dissect_hci_h4 packet-hci_h4.c:97
[1008, 1272) 'decrypted_data.i.i.i' (line 851) <== Memory access at offset 1272 overflows this variable
```
### Non-ASAN output (lldb):
```
stop reason = EXC_BAD_ACCESS (code=1, address=0x8e47ea46e3700c38)
frame #0: decrypt_data at packet-zbee-direct.c:894
→ *len = 0; // 'len' pointer corrupted by overflow to 0x8e47ea46e3700c38
```
### Attacker byte control:
Running with two different ciphertext byte patterns:
| Ciphertext fill | Corrupted `len` pointer |
|-----------------|-------------------------|
| `0x42` | `0x8e47ea46e3700c38` |
| `0xFF` | `0x33fa57fb5ecdb185` |
XOR of the two crash addresses:
```
0x8e47ea46e3700c38 XOR 0x33fa57fb5ecdb185 = 0xBDBDBDBDBDBDBDBD
```
**`0xBD = 0x42 XOR 0xFF`** - exact match. So this means: `corrupted_ptr = ciphertext XOR AES_CTR_keystream`.
Since the attacker injects the key (via Dump Info CCM_KEY_SET packet in the same pcapng), they know the keystream and can produce **any desired byte** at any stack position.
## Root cause
**`epan/dissectors/packet-zbee-direct.c`:**
```c
// line 851:
uint8_t decrypted_data[ZIGBEE_DIRECT_MAX_ATT_SIZE + 16]; // = 248 + 16 = 264 bytes
// line 856:
uint16_t encrypted_data_len = *len - sizeof(uint32_t); // = *len - 4
// line 872: only checks lower bound, no upper bound check
if (*len < 8) return false;
// line 876-883:
success = zbee_sec_ccm_decrypt(key, nonce, auth_str,
encrypted_data,
decrypted_data, // <-- 264-byte stack buffer passed as output
sizeof(auth_str), // l_a = 34
encrypted_data_len - ZB_CCM_M, // l_m = *len - 8 (can be up to 65527)
ZB_CCM_M); // M = 4
```
**`epan/dissectors/packet-zbee-security.c`, line 1002:**
```c
// Step 1: CTR decryption — writes l_m bytes to m with NO bounds check
gcry_cipher_encrypt(cipher_hd, m, l_m, c, l_m); // m = decrypted_data[264]
// Step 3: Authentication — happens AFTER the overflow at line 1013+
```
The PoC uses `*len = 2000`, producing `l_m = 1992`. The buffer is 264 bytes. **Overflow: 1728 bytes** of attacker-controlled data written past the stack buffer.
## What is the expected correct behavior?
`decrypt_data()` should validate that `*len <= ZIGBEE_DIRECT_MAX_ATT_SIZE + 8` before calling `zbee_sec_ccm_decrypt()`.
### Suggested fix (packet-zbee-direct.c, before line 876):
```c
if (*len < 8 || *len > ZIGBEE_DIRECT_MAX_ATT_SIZE + 8)
return false;
```
## Sample capture file
**Attached:**
| File | Description |
|------|-------------|
| [wireshark_zbee_overflow.pcapng](/uploads/57c6f084356f6075552010c1c7541e78/wireshark_zbee_overflow.pcapng) | Self-contained PoC (4 packets: FindInfo + KeySet + EncryptionEnable + oversized Form payload) |
| [gen_zbee_direct_pcap.py](/uploads/09be4d6212ac19f2ab85aa467161cd8f/gen_zbee_direct_pcap.py) | Python generator script |
## Relevant logs and/or screenshots
[ASAN_TSHARK_PROOF.txt](/uploads/8c115b861c23cb3034dabf0382523e98/ASAN_TSHARK_PROOF.txt)
Full ASAN trace saved in `ASAN_TSHARK_PROOF.txt`.
Crash analysis via lldb showing the `len` pointer corrupted to attacker-derived value `0x8e47ea46e3700c38`, causing write-to-arbitrary-address at line 894: `*len = 0`.
## Build information
```
TShark (Wireshark) 4.6.4 (Git commit f7c4a74874d9).
Copyright 1998-2026 Gerald Combs <gerald@wireshark.org> and contributors.
Licensed under the terms of the GNU General Public License (version 2 or later).
Compile-time info:
Bit width: 64-bit
Compiler: Clang 17.0.0 (clang-1700.0.13.5)
GLib: 2.88.0
With:
+brotli +nghttp2 1.68.1
+Gcrypt 1.12.1 +nghttp3 1.15.0
+GnuTLS 3.8.12 and PKCS#11 +PCRE2 10.47 2025-10-21
+Kerberos (MIT) +Snappy 1.2.2
+libpcap +xxhash 0.8.3
+libxml2 2.9.13 +zlib 1.2.12
+LZ4 1.10.0 +Zstandard 1.5.7
Without:
-libsmi -MaxMind -zlib-ng
-Lua -POSIX capabilities
Runtime info:
OS: macOS 15.7.5, build 24G624 (Darwin 24.6.0)
CPU: Apple M1 Max
Memory: 32768 MB of physical memory
GLib: 2.88.0
Locale: LC_TYPE=en_US.UTF-8
Plugins: supported, 0 loaded
With:
+brotli 1.2.0 +libpcap 1.10.1 +PCRE2 10.47 2025-10-21
+c-ares 1.34.6 +LZ4 1.10.0 +xxhash 803
+Gcrypt 1.12.1 +nghttp2 1.68.1 +zlib 1.2.12
+GnuTLS 3.8.12 +nghttp3 1.15.0 +Zstandard 1.5.7
```
issue