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 birvsk->transportpointer, 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)
- Free + reclaim:
kmalloc-64virtio_vsock_sock'u free et, slot'u reclaim etmek içinstruct msg_msg(System V message queue'lar, 16-byte'lıkmtext) spray et. 4-byte'lık write,msg_msg.security'nin (offset 40) düşük byte'larına düşer. - Timing:
setxattr()+userfaultfd()(Vitaly Nikolenko'nun tekniği), overwrite'ın hemen öncesindecopy_from_user()'ı bir page sınırında stall'lar ve race'i deterministik kılar. - 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çinMSG_COPYilemsgrcv()(CONFIG_CHECKPOINT_RESTOREgerektirir). - Arbitrary write: ~2800-byte'lık UDP paketleri spray et → kmalloc-4k'da
struct sk_buff;skb_shared_info.destructor_arg'ı sahte birubuf_info'ya corrupt ederek destructioncallback'ini hijack et. - KASLR:
sk_write_spacepointer'ını (==sock_def_write_space) oku ve slide'ı geri elde etmek için onun no-ASLR adresini çıkar. - SMEP/SMAP bypass: kernel-only ROP —
mov rdx,[rdi+8]; mov [rdx+rcx*8],rsi; retgadget'ı 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_VSOCKsocket aktivitesi ile hızlıconnect()/setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)çalkalanmasının birleşimi. userfaultfd+setxattrrace pattern'leri vemsgrcv(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, hertransport = vsk->transport;read'inilock_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") ve6a2c0962105aile 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_transportmodule'lerini blacklist'e al;userfaultfd'yi kısıtla (vm.unprivileged_userfaultfd=0) veMSG_COPYarbitrary read'i kırmak içinCONFIG_CHECKPOINT_RESTORE'u devre dışı bırak. - Ek savunmalar (Popov writeup'ında öne çıkanlar): SLAB_QUARANTINE ile slot reuse'u geciktir; MODHARDEN ile otomatik
vsockmodule autoload'unu engelleyerek reachability'yi köreltir;kernel.dmesg_restrict=1(/dev/kmsginfoleak adımını kapatır); CFI ROP adımını boşa çıkarır (tek bir gadget ile SMEP/SMAP bypass edilmişti).