Skip to content

Kernel keyrings (keyctl) attack surface

keyctl/add_key/request_key etrafındaki keyrings subsystem'i, hem tekrar eden bir bug source (join_session_keyring UAF class'ı gibi refcount/type-confusion hataları) hem de user_key_payload sayesinde disiplinli bir heap-grooming primitive'idir.

Mechanism

Keyrings subsystem'i, unprivileged bir task'a add_key(2), request_key(2) ve keyctl(2) üzerinden kernel içinde persistent nesneler yaratma, referanslama ve free etme yeteneği verir. İki ayrı sebepten dolayı saldırı yüzeyidir: hem kendi kodu bug barındırır, hem de barındırdığı nesneler başka bug'ları exploit etmek için kullanılır.

Invariant: reachable refcount + attacker-sized payload

Boundary iki yerde crossed olur. Birincisi lifetime/refcount tarafı: bir struct key (ve onun keyring container'ı) usage refcount'u ile yönetilir; unprivileged syscall'lar bu count'u increment/decrement edebildiği için, bir path count'u yanlış dengeleyip nesneyi ya erken free eder (UAF) ya da usage alanını taşırıp (overflow) sahte bir sıfıra düşürerek premature free tetikler. İkincisi payload tarafı: "user" key tipinde saldırgan hem allocation size'ını (sizeof(struct user_key_payload) + datalen) hem de header'ın ötesindeki içeriği tamamen kontrol eder; nesne unlink/revoke edilene kadar persistent kalır. Bu iki özellik keyrings'i aynı anda bir bug source ve bir grooming tool yapar.

Kanonik bug class, join_session_keyring() içindeki refcount hatasıdır (CVE-2016-0728): fonksiyon istenen keyring'e bir reference tutar, fakat o keyring zaten process'in mevcut session keyring'i ise keyring->usage return'den önce decrement edilmez. Bu leak tekrarlanabilir olduğu için 32-bit usage alanı overflow edilebilir; count sıfıra sarınca keyring free edilir ama hâlâ referanslanabilir durumdadır — klasik use-after-free. Aynı aile içinde type-confusion (yanlış key type destructor'ı) ve garbage-collector ile yarışan RCU-free path'leri de yer alır.

Walkthrough

Aşağıdaki adımlar CVE-2016-0728 için public writeup'ların conceptual seviyesidir; ne offset ne de tam exploit içerir.

  1. Reference leak'i tetikle. Aynı session keyring'e tekrar tekrar join et; her çağrı usage'ı dengelenmeden bir artırır:
/* conceptual — leaks one reference per call */
keyctl(KEYCTL_JOIN_SESSION_KEYRING, "attacker-ring");
  1. Overflow'a kadar döndür. Count'u 32-bit'in tamamı boyunca sürmek çok sayıda iterasyon ister; bu, tekniğin gürültülü (noisy) ve zaman alıcı imzasıdır.
for (unsigned long i = 0; i < LOTS; i++)
        keyctl(KEYCTL_JOIN_SESSION_KEYRING, name);   /* usage wraps to 0 */
  1. Free-yet-referenced state'i yakala. usage sıfıra sarınca keyring nesnesi free edilir; process hâlâ ona bir handle tutar → UAF penceresi açılır.

  2. Freed slot'u groom ile reclaim et. Boşalan chunk'ı saldırgan-kontrollü içerikle doldurmak için aynı subsystem'in spray nesnesi kullanılır — bkz. user_key_payload spray:

/* reclaim the freed object with attacker-controlled bytes */
add_key("user", "reclaim", payload, len, KEY_SPEC_PROCESS_KEYRING);
  1. Sahte nesneyi cred manipülasyonuna çevir. Reclaim edilen içerik bir function pointer/type ops taşırsa, dangling handle üzerinden yapılan bir operation kontrol akışını saptırır; nihai hedef genellikle bir cred struct overwrite veya commit_creds çağrısıdır.

Neden bu bir grooming primitive

user_key_payload free path'i controllable (KEYCTL_UNLINK/KEYCTL_REVOKE), size'ı controllable ve content'i controllable olduğundan, keyrings bug'ı keyrings'te olmayan exploit'lerde de reclaim/spray objesi olarak görünür. Yani subsystem, kendi CVE'leri kadar başkalarının exploit chain'lerinde de attack surface'tir.

Talep üzerine free ve reclaim penceresi

KEYCTL_REVOKE + gc_delay etkileşimi, RCU garbage-collector free'sinin ne zaman gerçekleşeceğini yaklaşık olarak zamanlamayı sağlar; bu, cross-cache veya free-then-reuse kurulumlarında pencereyi hizalamak için kullanılır.

Detection

  • Syscall pattern. Unprivileged bir process'ten KEYCTL_JOIN_SESSION_KEYRING'in yüksek frekanslı, uzun süreli loop'u refcount-overflow imzasıdır; add_key("user", ...) patlaması + hemen ardından KEYCTL_READ ise reclaim/leak imzasıdır. auditd -S keyctl -S add_key -S request_key kuralları bu çağrıları yakalar.
  • Key population. /proc/keys ve /proc/key-users anormal key sayısı, quota tüketimi ve olağandışı payload-length dağılımını açığa çıkarır. Kısa sürede maxkeys/maxbytes kotasına dayanan bir UID grooming şüphelisidir.
  • Kernel signalleri. usage refcount kontrolleri için build edilmiş kernel'larda refcount_t saturation WARN'ları (dmesg'de "refcount_t overflow"/"saturated") doğrudan bu bug class'ının exploitation denemesine işaret eder. KASAN/UAF splat'ları test/kernelctf ortamlarında birincil sinyaldir.
  • State flag'leri. /proc/keys'te beklenmedik "R" (revoked) veya "N" (negatively instantiated) yoğunlaşması free-timing manipülasyonuyla korelasyon gösterir.

Mitigation

  • Patch. CVE-2016-0728 upstream'de join_session_keyring reference dengesinin düzeltilmesiyle kapatıldı; genel savunma, keyrings CVE'lerini içeren stable kernel'ı çalıştırmaktır.
  • refcount hardening. CONFIG_REFCOUNT_FULL / modern refcount_t API'si usage sayaçlarının sessizce overflow etmesini engelleyip saturate ederek bu UAF class'ının tamamını neutralize eder.
  • Quota'lar. /proc/sys/kernel/keys/maxkeys, maxbytes (ve root_maxkeys/root_maxbytes) UID başına spray'lenebilecek key/byte miktarını sınırlar; düşük kotalar grooming'i kısıtlar. gc_delay'i kısaltmak reclaim penceresini daraltır.
  • Attack surface reduction. İhtiyaç yoksa CONFIG_KEYS'e bağımlı olmayan iş yüklerinde subsystem'e erişimi seccomp ile kısıtla; keyctl, add_key, request_key syscall'larını unprivileged container profillerinde bloklamak (seccomp/SCMP_ACT_ERRNO) hem bug source'u hem grooming primitive'ini kapatır.
  • Slab hardening. CONFIG_SLAB_FREELIST_RANDOM ve RANDOM_KMALLOC_CACHES, reclaim adjacency'sinin güvenilirliğini düşürerek genel user_key_payload spray etkinliğini azaltır.

References