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