Skip to content

AF_PACKET TPACKET_V3 rx_owner_map double-free (CVE-2021-22600)

packet_set_ring() içinde bir double-free: packet-ring teardown'u rx_owner_map'i eşleşen bir pg_vec olmadan free ettiğinde ortaya çıkar ve local privilege escalation için exploit edilebilir.

Mechanism

Root cause: koşulsuz bir bitmap free, koşullu bir ring free ile eşleşiyor

AF_PACKET socket'leri kendi TPACKET mmap ring'lerini bir page-vector (pg_vec) ile destekler. TPACKET_V3 için kernel, frame başına ownership'i ayrıca bir bitmap (rx_owner_map) ile takip eder. Kodun korumesı gereken invariant şudur: rx_owner_map yalnızca bir pg_vec ile birlikte allocate edilir ve ikisi tek bir birim olarak ring'e birlikte takılıp birlikte çıkarılır.

Bug bu invariant'ı teardown path'inde kırar: rx_owner_map koşulsuz olarak free edilirken free_pg_vec() bir if (pg_vec) ile korunuyordu. Bir kullanıcı hiç ring allocate etmeyen bir PACKET_RX_RING isteği gönderdiğinde (tp_block_nr == 0, yani pg_vec == NULL) — örneğin versiyonlar arasında geçiş yaparken — swap, ardından bir sonraki işlemde tekrar free edilen stale bir rx_owner_map pointer'ı bırakır. Sonuç, boyutu ve içeriği attacker'ın kısmen kontrol ettiği bir kernel heap object'inin double-free'sidir (CWE-415); bu da use-after-free tarzı bir heap-grooming escalation'ın klasik ön koşuludur.

Walkthrough

Açıklı kod net/packet/af_packet.c içindeki packet_set_ring() fonksiyonunda yer alır. Yeni ve eski ring state'i swap edildikten sonra eski kaynaklar serbest bırakılır. Patch öncesi cleanup, bir page-vector var olup olmadığına bakmaksızın bitmap'i free ediyordu:

/* vulnerable: rx_owner_map freed even when pg_vec == NULL */
bitmap_free(rx_owner_map);
if (pg_vec)
        free_pg_vec(pg_vec, order, req->tp_block_nr);

rx_owner_map ve pg_vec birlikte swap edildiği için, NULL bir pg_vec bu path'te bu ring ile hiçbir bitmap'in eşleştirilmediği anlamına gelir — yine de bitmap_free() yine çalışır ve hâlâ canlı bir ring'e ait olan bir pointer'ı serbest bırakır (ya da onu ikinci kez serbest bırakır). Upstream commit ec6af094ea28 ("net/packet: rx_owner_map depends on pg_vec") iki free'yi birbirine bağlar; böylece bitmap yalnızca ait olduğu ring de free edilirken free edilir:

packet_set_ring() içindeki patch'lenmiş teardown
/* fixed: bitmap freed only alongside its page-vector */
if (pg_vec) {
        bitmap_free(rx_owner_map);
        free_pg_vec(pg_vec, order, req->tp_block_nr);
}

Herhangi bir ayrıcalıksız local kullanıcı tarafından erişilebilir

Eski CAP_NET_RAW ile kapılanan AF_PACKET bug'larının aksine, bu path bir local kullanıcının sıradan setsockopt(PACKET_RX_RING) dizileriyle erişilebilir; bu yüzden CISA'nın Known Exploited Vulnerabilities kataloğuna eklendi. Patch'lenmemiş bir kernel'de gözlemlenebilir davranış, bir KASAN double-free splat'ından, takip eden bir spray'in controlled write'a dönüştürdüğü sessiz heap corruption'a kadar değişir.

Detection

  • KASAN / SLUB_DEBUG: free stack'i af_packet.c'yi gösteren bir BUG: KASAN: double-free or invalid-free in bitmap_free (ya da packet_set_ring) splat'ı, debug bir kernel'de en yüksek doğruluktaki sinyaldir.
  • Syscall telemetry (auditd/EDR): socket(AF_PACKET, …) açan ve ardından PACKET_VERSION'ı (TPACKET_V3'e/dan geçiş yaparak) ve tp_block_nr == 0 olan PACKET_RX_RING'i sıfır olmayan isteklerle iç içe geçirerek hızlıca döngüye sokan setsockopt çağrıları yapan process'leri arayın. Tek bir packet socket üzerinde tekrar eden ring-setup/teardown döngüleri normal uygulamalar için anormaldir.
  • Heap-spray companions: corruption genellikle komşu object spray'leriyle weaponize edilir (bkz. sk-buff spray ve universal heap spray); bir EDR, free'nin kendisinden çok spray pattern'ini (toplu sendmsg/msgsnd/keyring allocation'ları) daha kolay gözlemleyebilir.
  • Crash signature: production kernel'lerde tetiklenen bir double-free çoğu zaman SLUB allocator içinde bir general protection fault ya da list-corruption oops'u olarak yüzeye çıkar.

Mitigation

  • Upstream fix: commit ec6af094ea28f0f2dda1a6a33b14cd57e36a9755 — stable tree'ler genelinde backport edildi. Distro'nuzun patch'lenmiş kernel'ini uygulayın.
  • Attack-surface reduction: packet socket oluşturmayı kısıtlayın. Bir workload raw packet erişimine ihtiyaç duymuyorsa, socket() syscall'unu seccomp ile filtreleyerek (domain == AF_PACKET'i bloklayarak), bir LSM ile ya da systemd'nin RestrictAddressFamilies= direktifiyle AF_PACKET'i reddedin.
  • Defense-in-depth: freelist corruption ve double-free exploitation'ını zorlaştırmak için CONFIG_SLAB_FREELIST_HARDENED ve CONFIG_INIT_ON_FREE_DEFAULT_ON'ı etkinleştirin; aynı duruşun parçası olarak kernel.unprivileged_bpf_disabled / genel LPE hardening'i de uygulayın.

References