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 birtp_sizeof_privgeç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(veyatpacket_rcv) — kanonik debug splat'ı. - Syscall telemetry:
socket(AF_PACKET, …)'in ardındansetsockopt(PACKET_VERSION, TPACKET_V3)vetp_sizeof_priv'i mantıksız derecede büyük (high bit set /0x80000000+ gibi değerler) olan birPACKET_RX_RINGrequest'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_PACKETkullanımından hemen önce gelen birunshare(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=0veyauser.max_user_namespaces=0. - Attack-surface reduction: raw socket gerektirmeyen servisler için seccomp /
systemd
RestrictAddressFamilies=ileAF_PACKET'i reddet. - Defense-in-depth:
CONFIG_SLAB_FREELIST_HARDENED, structure layout randomization ve KASLR, controlled write için exploitation maliyetini yükseltir.