Skip to content

Unprivileged user namespace attack surface

Unprivileged bir user namespace (clone(CLONE_NEWUSER)), çağıran process'e namespace içinde full capability set verir — böylece eskiden root-only olan kernel kod path'leri (overlayfs, nftables/netfilter, birçok net/fs subsystem) unprivileged local attacker'a açılır ve bu tek başına en büyük LPE enabler'larından biri olur.

Mechanism

Invariant: capability namespace-scoped'tur, ama code path aynıdır

User namespace'in tasarım invariant'ı şudur: bir capability yalnızca onu grant eden namespace ve child'ları içinde geçerlidir; parent namespace'te hiçbir capability'ye dönüşmez. man7'nin ifadesiyle, CLONE_NEWUSER ile yaratılan child "a complete set of capabilities in the new user namespace" ile başlar. uid_map/gid_map sayesinde bir process, namespace dışında normal unprivileged bir uid'e sahipken içeride uid 0 + CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW vb. taşıyabilir.

Güvenlik açığı, bu capability'lerin hangi kod'u reachable yaptığındadır. Capability check'i (ns_capable()) namespace-aware'dir, ama check'in arkasındaki subsystem — nftables parser'ı, overlayfs copy-up path'i, bir net scheduler qdisc'i — çoğunlukla namespace-naive, yıllarca yalnızca gerçek root tarafından fuzz'lanmış eski C kodudur. Böylece boundary şöyle crossed olur: attacker gerçek privilege kazanmaz; bunun yerine daha önce yalnızca root'un dokunabildiği geniş bir kernel attack surface'e unprivileged erişim kazanır. Tek bir memory-safety bug'ı (UAF, OOB, refcount, race) o surface içinde host root'a pivot etmek için yeterlidir.

Neden en büyük LPE enabler'lardan biri

Public ölçümler bunu net yapıyor: systemshardening analizi unprivileged user namespace'leri açmanın reachable kernel attack surface'i yaklaşık 3.4x büyüttüğünü, Google'ın exploit telemetry'sinde ise gözlenen exploit'lerin %44'ünün zincirlerinin parçası olarak unprivileged user namespace gerektirdiğini raporluyor. nf_tables, overlayfs ve net/sched ailesindeki onlarca LPE (bkz. bu KB'deki nf-tables ve overlayfs not'ları) tetikleyici ön koşul olarak tam olarak bu tek unshare primitive'ine dayanır.

Walkthrough

Kavramsal (weaponize edilmemiş) reachability adımları — bir attacker'ın formerly-root subsystem'e nasıl ulaştığını, defender'ın anlaması için:

  1. Unprivileged process yeni bir user namespace açar ve içeride full capability set'e sahip olur:
/* tek syscall, hiçbir privilege gerektirmez */
unshare(CLONE_NEWUSER | CLONE_NEWNET);   /* +CAP_SYS_ADMIN, +CAP_NET_ADMIN */
  1. uid/gid mapping kurulur ki içeride "root" görünülsün (bazı distro helper'ları bunu newuidmap/unshare -r ile yapar):
# kavramsal: outer-unprivileged uid -> inner uid 0
echo "0 $(id -u) 1" > /proc/self/uid_map    # sadeleştirilmiş
  1. Artık ns_capable() gate'li subsystem'ler unprivileged'a reachable:
# normalde CAP_NET_ADMIN ister; şimdi namespace içinde geçer
nft add table inet t
# overlayfs mount, net scheduler qdisc, vb. de aynı şekilde açılır
  1. Attacker bu geniş surface içinde zaten disclosed/patched bir bug'ı tetikler (ör. bir nf_tables UAF ya da bir overlayfs copy-up flaw). Namespace içinde grant edilen capability + subsystem bug'ı, host'a etki eden bir memory-corruption primitive'ine dönüşür.
Neden fix her bug'ı ayrı yamalamakla bitmiyor

Surface o kadar geniştir ki (net, fs, crypto, keyring alt sistemleri) tek tek bug'ları kapatmak whack-a-mole olur. Bu yüzden hardening, bug'ı değil, unprivileged'ın user namespace açma yeteneğini hedefler — tüm surface'i tek hamlede kapatır.

Detection

  • Telemetri: unprivileged uid'lerden gelen unshare(2)/clone(2) çağrılarında CLONE_NEWUSER flag'ini izleyin; ardından hızlıca CLONE_NEWNET/CLONE_NEWNS gelmesi container-benzeri bir setup değilse anormaldir.
  • Bir user namespace açtıktan hemen sonra nft/iptables, mount -t overlay, ya da tc qdisc add gibi eskiden-root işlemlerini yapan process'ler yüksek değerli sinyaldir; audit'te -a always,exit -F arch=b64 -S unshare -S clone ile yakalayın.
  • Host-level: bir process'in /proc/self/uid_map'e yazıp hemen kernel subsystem'lerine dokunması; KASAN/dmesg'te use-after-free/slab-out-of-bounds imzalarının unprivileged bir uid ile korele olması.
  • Runtime EDR/eBPF: user_namespace create eventlerini per-uid sayın; baseline dışı spike'lar (özellikle browser/sandbox olmayan binary'lerden) triage edin.

Mitigation

  • En temiz kontrol: unprivileged user namespace yaratmayı kapatın. Debian/Ubuntu türevlerinde sysctl -w kernel.unprivileged_userns_clone=0; RHEL/Fedora türevlerinde sysctl -w user.max_user_namespaces=0. Ubuntu'da her ikisini de set etmek önerilir çünkü ilkini set edip user.max_user_namespaces'i pozitif bırakmak bazı kernel'lerde residual bir path bırakabilir.
  • Granular alternatif (Ubuntu 23.10+): binary on/off yerine AppArmor tabanlı kontrol. sysctl -w kernel.apparmor_restrict_unprivileged_userns=1 (ve kernel.apparmor_restrict_unprivileged_unconfined=1) ile yalnızca profilinde userns, rule'u olan confined uygulamalar user namespace açabilir; geri kalan unprivileged process'lere kapalıdır. Bu, browser sandbox'ları gibi meşru kullanıcıları bozmadan surface'i daraltır.
  • Kernel limit: nesting zaten 32 seviyeyle sınırlıdır (aşımda EUSERS), ama bu bir security kontrolü değildir; asıl kaldıraç yukarıdaki sysctl'lerdir.
  • Derinlik: seccomp ile container/sandbox içinden unshare/clone yeni-userns path'ini engelleyin; distro'nun subsystem yamalarını güncel tutun; user namespace'e ihtiyaç duymayan workload'larda systemd unit'lerinde RestrictNamespaces= kullanın.

References