Kernel keyrings (keyctl) attack surface¶
keyctl/add_key/request_keyetrafındaki keyrings subsystem'i, hem tekrar eden bir bug source (join_session_keyring UAF class'ı gibi refcount/type-confusion hataları) hem deuser_key_payloadsayesinde 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.
- 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");
- 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 */
-
Free-yet-referenced state'i yakala.
usagesıfıra sarınca keyring nesnesi free edilir; process hâlâ ona bir handle tutar → UAF penceresi açılır. -
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);
- 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ındanKEYCTL_READise reclaim/leak imzasıdır. auditd-S keyctl -S add_key -S request_keykuralları bu çağrıları yakalar. - Key population.
/proc/keysve/proc/key-usersanormal 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.
usagerefcount kontrolleri için build edilmiş kernel'lardarefcount_tsaturationWARN'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_keyringreference dengesinin düzeltilmesiyle kapatıldı; genel savunma, keyrings CVE'lerini içeren stable kernel'ı çalıştırmaktır. - refcount hardening.
CONFIG_REFCOUNT_FULL/ modernrefcount_tAPI'siusagesayaç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(veroot_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_keysyscall'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_RANDOMveRANDOM_KMALLOC_CACHES, reclaim adjacency'sinin güvenilirliğini düşürerek genel user_key_payload spray etkinliğini azaltır.