Skip to content

refcount memory management flaw (CVE-2021-26708)

net/vmw_vsock/af_vsock.c'deki yanlış locking'ten kaynaklanan birden çok AF_VSOCK race condition'ı: lock_sock()'tan önce cache'lenen stale bir vsk->transport pointer, free edilmiş belleğe 4-byte'lık bir write verir — Alexander Popov'un "Four Bytes of Power"ı bunu, SMEP/SMAP yenilmiş halde local root'a çevirdi.

Mechanism

Note

Neden çalışır: multi-transport desteğiyle, bir AF_VSOCK socket'inin struct vsock_sock'u backend'i (loopback, virtio, vmci...) seçen bir transport pointer taşır. Transport yeniden atanabilir (örn. connect()/setsockopt()'ta). Birkaç fonksiyon lock_sock(sk) almadan önce transport = vsk->transport;'ı bir local'e okur, sonra lock'u edindikten sonra local'i kullanır. Unlocked read ile lock arasında, eşzamanlı bir thread vsk->transport'u değiştirebilir, böylece cache'lenen pointer stale olur — yıkılmış/free edilmiş bir transport objesine (ve onun ilişkili per-socket state'ine) işaret eder. Bu, beş ayrı race noktasında use-after-free olarak tezahür eden bir improper-locking bug'ıdır (CWE-667). Module'ler, ayrıcalıksız bir kullanıcı yalnızca bir AF_VSOCK socket'i yarattığında otomatik yüklendiği için, surface ayrıcalık olmadan ulaşılabilirdir.

Exploit açısından ilgili primitive: stale transport virtio_transport_notify_buffer_size()'a götürür ve bu da vvs->buf_alloc = *val;'ı çalıştırır — free edilmiş bir struct virtio_vsock_sock'un offset 40'ında kontrollü 4-byte'lık (u32) bir write, burada *val doğrudan setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)'tan gelir. İşte "Four Bytes of Power" budur.

Walkthrough

struct virtio_vsock_sock 64 byte'tır → kmalloc-64. Plan: onu free et, security-critical field'ı offset 40'ta duran spray edilmiş bir obje ile reclaim et, sonra 4-byte'lık write'ı uygula.

/* 1. race connect()/setsockopt() on an AF_VSOCK socket so vsk->transport
 *    is reassigned/freed while a sibling op holds the stale local copy   */
int s = socket(AF_VSOCK, SOCK_STREAM, 0);
/* ... thread A: connect()  |  thread B: setsockopt(SO_VM_SOCKETS_BUFFER_SIZE) ... */
/* 2. the stale-transport path reaches: */
vvs->buf_alloc = *val;      /* 4-byte write @ off 40 of FREED virtio_vsock_sock */
Popov'un tam zinciri (Fedora 33 Server, x86_64)
  1. Free + reclaim: kmalloc-64 virtio_vsock_sock'u free et, slot'u reclaim etmek için struct msg_msg (System V message queue'lar, 16-byte'lık mtext) spray et. 4-byte'lık write, msg_msg.security'nin (offset 40) düşük byte'larına düşer.
  2. Timing: setxattr() + userfaultfd() (Vitaly Nikolenko'nun tekniği), overwrite'ın hemen öncesinde copy_from_user()'ı bir page sınırında stall'lar ve race'i deterministik kılar.
  3. Arbitrary read: msg_msg'i (m_ts, next) seçilen bir kernel adresine işaret edecek şekilde corrupt et, sonra dequeue etmeden kernel belleğini userspace'e kopyalamak için MSG_COPY ile msgrcv() (CONFIG_CHECKPOINT_RESTORE gerektirir).
  4. Arbitrary write: ~2800-byte'lık UDP paketleri spray et → kmalloc-4k'da struct sk_buff; skb_shared_info.destructor_arg'ı sahte bir ubuf_info'ya corrupt ederek destruction callback'ini hijack et.
  5. KASLR: sk_write_space pointer'ını (== sock_def_write_space) oku ve slide'ı geri elde etmek için onun no-ASLR adresini çıkar.
  6. SMEP/SMAP bypass: kernel-only ROP — mov rdx,[rdi+8]; mov [rdx+rcx*8],rsi; ret gadget'ı yalnızca kernel pointer'larını dereference ederek kernel space'te çalışır, credential field'larını sıfırlamak için kullanılır → uid/gid 0.

Warning

CVE beş race noktasını kapsar; public exploit bir tanesini (setsockopt/connect transport-reassignment UAF) silahlandırır. Yukarıdaki sayısal offset'ler writeup özetinden alınmıştır — onlara güvenmeden önce Popov'un orijinal sayfasıyla yeniden kontrol et.

Detection

  • net/vmw_vsock/af_vsock.c / virtio_transport_*'te KASAN use-after-free.
  • Ayrıcalıksız kullanıcılardan gelen AF_VSOCK socket aktivitesi ile hızlı connect()/setsockopt(SO_VM_SOCKETS_BUFFER_SIZE) çalkalanmasının birleşimi.
  • userfaultfd + setxattr race pattern'leri ve msgrcv(MSG_COPY) kullanımı bu exploit ailesinde yaygındır.

Mitigation

  • Linux 5.10.13'te commit c518adafa39f "vsock: fix the race conditions in multi-transport support" (Alexander Popov; committer Jakub Kicinski) ile düzeltildi. Fix, her transport = vsk->transport; read'ini lock_sock(sk)'tan sonraya taşır:
-  const struct vsock_transport *transport = vsk->transport;
+  const struct vsock_transport *transport;
+
   lock_sock(sk);
+
+  transport = vsk->transport;
  • c0cfa2d8a788 ("vsock: add multi-transports support") ve 6a2c0962105a ile tanıtıldı, v5.5-rc1'den itibaren mevcut. Etkilenen: 5.5 – 5.10.12.
  • Defense-in-depth: gerekmediği yerde vsock/vmw_vsock_virtio_transport module'lerini blacklist'e al; userfaultfd'yi kısıtla (vm.unprivileged_userfaultfd=0) ve MSG_COPY arbitrary read'i kırmak için CONFIG_CHECKPOINT_RESTORE'u devre dışı bırak.
  • Ek savunmalar (Popov writeup'ında öne çıkanlar): SLAB_QUARANTINE ile slot reuse'u geciktir; MODHARDEN ile otomatik vsock module autoload'unu engelleyerek reachability'yi köreltir; kernel.dmesg_restrict=1 (/dev/kmsg infoleak adımını kapatır); CFI ROP adımını boşa çıkarır (tek bir gadget ile SMEP/SMAP bypass edilmişti).

References