Skip to content

addr_limit overwrite (set_fs KERNEL_DS) arbitrary R/W

Per-thread addr_limit'i KERNEL_DS'e overwrite et; böylece copy_to/from_user kernel adreslerini kabul eder ve sıradan read()/write() çağrıları arbitrary kernel R/W'ye dönüşür.

Mechanism

Note

set_fs(), per-thread bir değişken olan addr_limit'i ayarlar; bu değişken user ve kernel space arasındaki sınırı işaretler. access_ok(), user erişimlerini addr_limit ile basit bir karşılaştırma yaparak doğrular. addr_limit KERNEL_DS (tüm adres aralığı) olduğunda, copy_from_user() / copy_to_user() kernel adreslerini de kabul eder. Yani bir attacker addr_limit'i corrupt ederse, access_ok kontrolü her zaman geçer ve usercopy helper'ları kernel memory'yi okur/yazar.

x86_64'te bu alan, task_struct içindeki struct thread_struct içinde yaşar ve access_ok() sınırı user_addr_max() üzerinden çözer:

#define user_addr_max() (current->thread.addr_limit.seg)

Walkthrough

Klasik akış: thread_info/task_struct'a ulaşan bir write primitive bul, addr_limit'i overwrite et, sonra attacker'ın kontrol ettiği adreslerde usercopy çağıran bir syscall kullan. iovec tabanlı yollar (writev/readv/recvmsg) idealdir çünkü hem kernel target'ını (iov_base) hem de uzunluğu (iov_len) kontrol edersin:

/* after addr_limit has been corrupted to ~0 */
struct iovec iov[1];
iov[0].iov_base = (void *)KERNEL_TARGET_ADDR;   /* kernel address */
iov[0].iov_len  = LEN;
writev(pipe_fd[1], iov, 1);                      /* write into kernel memory */
/* recvmsg(... MSG_WAITALL ...) is used to block/synchronize the corruption */

Warning

addr_limit'i 0xFFFFFFFFFFFFFFFE yap, literal KERNEL_DS (0xFFFFFFFFFFFFFFFF / -1) değil. ARM64'te do_page_fault(), if (regs->orig_addr_limit == KERNEL_DS) die() kontrolünü yapar, dolayısıyla tam olarak -1 değeri kernel'i panic'e sokar. 0xFFFFFFFFFFFFFFFE ise tüm kernel adreslerini kapsayacak kadar büyüktür.

Mitigation

  • set_fs() kaldırılması (kernel ~5.10+): addr_limit mekanizmasının tamamı kaldırıldı ve iov_iter arayüzüyle değiştirildi, böylece helper'lar user/kernel sınırı hakkında asla "yalan söylemek" zorunda kalmıyor — bu, attack vector'ı tamamen ortadan kaldırır. Asıl motivasyon, kernel'in eşleşmiş set_fs(KERNEL_DS) / set_fs(old_fs) çağrıları arasında oops verip kernel memory'yi user-writable bıraktığı bir bug sınıfıydı (örn. CVE-2010-4258).
  • PAN / UAO (ARM64) ve SMAP (x86), addr_limit == KERNEL_DS iken user memory'ye yapılan privileged erişimi trap'ler ve set_fs kaldırılmadan önce bile tekniği körleştirir.

References