Skip to content

AF_PACKET TPACKET_V3 ring buffer heap OOB write (CVE-2017-7308)

packet_set_ring()'in block-size validation'ındaki bir signedness error, attacker'ın oversized bir tp_sizeof_priv geçirmesine izin vererek controlled-offset, controlled-size bir heap out-of-bounds write üretir.

Mechanism

Root cause: unsigned underflow'u gizleyen bir signed cast

Bir TPACKET_V3 ring, kullanıcının verdiği tpacket_req3 field'larıyla tanımlanır: tp_block_size, tp_frame_size ve tp_sizeof_priv (block başına private area). packet_set_ring(), her block'un kendi private region'ı artı block descriptor header'ı tutabilecek kadar büyük olması invariant'ını garanti etmek zorunda — yani tp_block_size >= BLK_PLUS_PRIV(tp_sizeof_priv).

Orijinal check, tp_block_size - BLK_PLUS_PRIV(tp_sizeof_priv) ifadesini unsigned aritmetikte hesaplıyor, sonra <= 0 karşılaştırması için sonucu (int)'e cast ediyordu. Eğer tp_sizeof_priv'in high bit'i set ise (ör. 0x80001000), subtraction 0x7fffffd0 gibi büyük bir unsigned değere underflow eder; bu da signed int olarak pozitiftir — yani guard geçer. Oversized değer daha sonra blk_sizeof_priv'e saklanırken 16 bit'e truncate edilir, böylece nxt_offset (bir block içindeki write hedefi) ve max_frame_len artık gerçek block bounds'larıyla eşleşmez. Attacker bu sayede kernel heap üzerinde bir out-of-bounds write için controlled offset ve controlled size elde eder (CWE-681 → CWE-787).

Walkthrough

Andrey Konovalov'un Project Zero analizi ("Exploiting the Linux kernel via packet sockets"), net/packet/af_packet.c içindeki packet_set_ring()'de bulunan tam validation flaw'ını belgeler. Vulnerable guard:

/* vulnerable: unsigned underflow hidden behind an (int) cast */
if (po->tp_version >= TPACKET_V3 &&
    (int)(req->tp_block_size -
          BLK_PLUS_PRIV(req_u->req3.tp_sizeof_priv)) <= 0)
        goto out;

Fix, BLK_PLUS_PRIV aritmetiğini 64-bit'te yapar ve kayıplı signed cast olmadan karşılaştırır, böylece kocaman bir tp_sizeof_priv artık block size'ın altına wrap edemez:

/* fixed: compute in u64, no signedness trick */
if (po->tp_version >= TPACKET_V3 &&
    req->tp_block_size <=
          BLK_PLUS_PRIV((u64)req_u->req3.tp_sizeof_priv))
        goto out;
Bozuk değer write'a nasıl yayılır

Guard bypass edildiğinde, tp_sizeof_priv init_prb_bdqc() içinde unsigned short olan blk_sizeof_priv'e truncate edilir. Bu truncate edilmiş değer, bir frame ring'e commit edildiğinde prb_fill_curr_block() / tpacket_rcv tarafından kullanılan BLK_HDR_LEN/nxt_offset hesaplamalarını besler. Kernel'in inandığı private-area boyutu artık gerçekte reserve edilen byte'ları yansıtmadığı için, frame copy block'un ötesine düşer — offset'i (block içinde ve ötesinde) attacker'ın seçtiği bir heap overflow. Konovalov'un writeup'ı bunu packet-socket heap grooming (packet_sock/pgv page allocation'ları) ve bir timer_list/function-pointer overwrite ile zincirleyerek kernel code execution'a ulaşır.

CAP_NET_RAW ile gated, ama user namespace'ler üzerinden erişilebilir

Packet socket oluşturmak CAP_NET_RAW gerektirir. Unprivileged user namespace'lere izin veren distro'larda, sıradan bir kullanıcı bu capability'yi bir userns içinde elde edip bug'a ulaşır — bunu 4.10.6'ya kadarki kernel'lerde kayda değer bir LPE haline getiren pratik attack surface.

Detection

  • KASAN: write access ile birlikte BUG: KASAN: slab-out-of-bounds … in packet_set_ring / prb_fill_curr_block (veya tpacket_rcv) — kanonik debug splat'ı.
  • Syscall telemetry: socket(AF_PACKET, …)'in ardından setsockopt(PACKET_VERSION, TPACKET_V3) ve tp_sizeof_priv'i mantıksız derecede büyük (high bit set / 0x80000000+ gibi değerler) olan bir PACKET_RX_RING request'i tespit et. Hiçbir legitimate uygulama multi-gigabyte bir block başına private area set etmez.
  • Userns correlation: unprivileged bir process tarafından AF_PACKET kullanımından hemen önce gelen bir unshare(CLONE_NEWUSER|CLONE_NEWNET), audit log'larında güçlü bir precursor sinyalidir.
  • Crash signature: production kernel'lerde, ring setup'tan sonra SLUB redzone/list corruption ya da bir GPF; EDR'ler bunun yerine corruption sonrası commit_creds etkisini (aniden beliren uid-0 child) yakalayabilir.

Mitigation

  • Upstream fix: commit 2b6867c2ce76 ("net/packet: fix overflow in check for priv area size"), v4.11'de yayımlandı ve stable'a backport edildi (≤ 4.10.6 etkilendi). Kernel'i güncelle.
  • Unprivileged user namespace'leri devre dışı bırak ki CAP_NET_RAW'a giden unprivileged yol kalksın: sysctl kernel.unprivileged_userns_clone=0 veya user.max_user_namespaces=0.
  • Attack-surface reduction: raw socket gerektirmeyen servisler için seccomp / systemd RestrictAddressFamilies= ile AF_PACKET'i reddet.
  • Defense-in-depth: CONFIG_SLAB_FREELIST_HARDENED, structure layout randomization ve KASLR, controlled write için exploitation maliyetini yükseltir.

References