Skip to content

CAN ISOTP socket LPE (CVE-2021-32606)

net/can/isotp.c içindeki isotp_setsockopt() ile isotp_bind() arasındaki bir race, freed bir socket üzerinde kayıtlı kalan bir CAN receiver bırakır; bu da isotp_rcv() içinde use-after-free ve local root sağlar.

Mechanism

Bind sonrası imkansız olması gereken bir socket option set etmek

CAN ISO-TP protokolü (net/can/isotp.c, ISO 15765-2), socket bir CAN interface'ine bind edildiğinde socket başına bir receive callback kaydeder. Bu callback'in bookkeeping'ini tutarlı tutmak için isotp_setsockopt(), socket bir kez bind edildikten sonra option değişikliğini reddeder — if (so->bound) kontrolünü yapıp reddeder.

Asıl açık, bu kontrol ile başka bir thread'de çalışan isotp_bind() arasındaki bir time-of-check/time-of-use race'tir:

  1. isotp_setsockopt(), socket hâlâ unbound iken so->bound değerini okur, dolayısıyla guard geçer.
  2. isotp_bind() çalışır, bir CAN receiver kaydeder ve so->bound = 1 yapar.
  3. isotp_setsockopt() ardından copy_from_sockptr() çağrısını tamamlar ve artık bound olan socket'in flag'lerine CAN_ISOTP_SF_BROADCAST yazar.

Bu, imkansız bir state üretir: kayıtlı bir CAN receiver'ı olan ama CAN_ISOTP_SF_BROADCAST flag'i olmaması gerektiğini söyleyen bir socket. Unregister path'inin dayandığı invariant kırılır. isotp_release() içinde cleanup, if (so->bound && !(so->opt.flags & CAN_ISOTP_SF_BROADCAST)) koşuluna bağlıdır; bozulmuş flag bu koşulu false yapar, böylece struct isotp_sock freed olsa bile receiver hiç unregister edilmez. Artık dangling bir callback freed memory'ye işaret eder.

Race'in kök nedeni, isotp_setsockopt()'in lock_sock() olmadan çalışmasıdır; bu yüzden so->bound okuması ile so->opt.flags yazması arasına paralel bir isotp_bind() girebilir. Fix ("can: isotp: prevent race between isotp_bind() and isotp_setsockopt()") kodu lock_sock() altında isotp_setsockopt_locked()'a refactor eder ve isotp_bind() içinde SF_BROADCAST kontrolünü lock altına alır.

Bu bug, CAN_ISOTP_SF_BROADCAST desteğini ekleyen 921ca574cd38 commit'i ile 5.11-rc1'de tanıtıldı ve 5.11'den 5.12.2'ye kadar olan kernel'leri etkiler.

Walkthrough

UAF'i tetiklemek

Race, kayıtlı-ama-freed bir socket bıraktıktan sonra, eşleşen herhangi bir CAN frame soft-IRQ context'inden isotp_rcv()'a ulaşır ve freed struct isotp_sock üzerinde çalışır:

  • isotp_rcv() frame'i validate eder ve check_pad() çağırır; bozuk padding onu sk_error_report(sk) çağırmaya iter.
  • Attacker, freed allocation'ı, sk_error_report function pointer'ının overwrite edildiği bir struct isotp_sock kopyası ile spray ederek reclaim etmiştir.
  • Hijack edilmiş pointer, freed struct'ın adresini tutan RDI üzerinden bir stack pivot gerçekleştirir ve aynı kontrol edilen allocation'a gömülü bir ROP chain'e iner.
  • ROP chain modprobe_path'i overwrite eder, böylece bad-magic bir dosyanın ardından gelen bir execve, attacker binary'sini root olarak çalıştırır.
Thread A: setsockopt(..., CAN_ISOTP_SF_BROADCAST)  // passes !bound check
Thread B: bind(...)                                // registers rcv, sets bound=1
Thread A: copy_from_sockptr() writes BROADCAST flag // corrupts bound socket
          close()  -> isotp_release() skips unregister (flag is set)
                   -> struct isotp_sock freed, receiver still live
incoming CAN frame -> isotp_rcv() on freed sock -> UAF -> hijack sk_error_report

Modern mitigation'lar kapsamda, ama engel değil

Yayınlanan exploitation, data-only/ROP kalarak SMEP/SMAP/KPTI'yi tolere eder; AF_CAN'a ulaşmak için unprivileged bir user namespace (CONFIG_USER_NS), bir dmesg/WARN tabanlı KASLR infoleak ve güvenilirlik için race window'unu genişletmek üzere FUSE kullanır.

Detection

  • KASAN, freed isotp_sock üzerinde isotp_rcv() içindeki use-after-free'i işaretler.
  • Unprivileged namespace'lerden beklenmedik AF_CAN/ISOTP socket kullanımı, otomotiv olmayan sistemlerde güçlü bir anomalidir.
  • modprobe_path integrity'sini ve beklenmedik modprobe çağrılarını izleyin.

Mitigation

  • setsockopt/bind sıralamasını düzelten 5.12.3 veya sonrası kernel'e güncelleyin.
  • Kullanılmayan yerlerde CONFIG_CAN_ISOTP=n ayarlayın ya da can-isotp modülünü blacklist'leyin.
  • CAN stack'ine unprivileged erişimi kaldırmak için unprivileged user namespace'leri devre dışı bırakın (kernel.unprivileged_userns_clone=0).

References