Skip to content

mqueue object spray

Seçilmiş uzunlukta POSIX message-queue mesajları göndererek kernel'in attacker-sized, attacker-filled struct msg_msg (ve posix_msg_tree_node) objelerini allocate etmesini sağla; bu objeler mesaj receive edilene kadar yaşar — kontrol edilebilir bir heap-spray ve UAF-reclaim primitive'i.

Mechanism

POSIX message queue'ları (mq_open/mq_timedsend/mq_timedreceive) ve System V queue'ları (msgsnd/msgrcv), queue'daki bir mesajı aynı kernel objesiyle — struct msg_msg artı opsiyonel overflow segment'leriyle — destekler. Mesaj uzunluğu tamamen attacker tarafından seçilir, dolayısıyla allocation'ın size class'ı da seçilmiş olur; mesaj body'si attacker tarafından seçilir, dolayısıyla içerik de seçilir; ve obje, mesaj explicit olarak dequeue edilene kadar kernel'de yaşar. Bu üç özellik tam da bir heap spray'in ihtiyaç duyduğu şeydir.

Note

The header object is laid out as:

struct msg_msg {
    struct list_head m_list;   /* next / prev */
    long             m_type;
    size_t           m_ts;     /* message text size */
    struct msg_msgseg *next;   /* overflow segment chain */
    void            *security;
    /* payload bytes follow inline */
};

Send sırasında load_msg() / alloc_msg() payload'ı böler: header chunk'ı inline olarak DATALEN_MSG = PAGE_SIZE - sizeof(struct msg_msg) byte'a kadar tutar, geri kalan ise her biri DATALEN_SEG = PAGE_SIZE - sizeof(struct msg_msgseg) byte tutan struct msg_msgseg node'larından oluşan bir chain'e taşar. Header allocation boyutu sizeof(struct msg_msg) + min(msg_len, DATALEN_MSG) olduğundan:

  • kısa bir mesaj, msg_msg header'ını küçük bir kmalloc-* cache'ine yerleştirir (örn. kmalloc-64, kmalloc-96, ... kmalloc-4k'ya kadar);
  • büyük bir mesaj, bir kmalloc-4k header'ı artı bir veya daha fazla order-0 segment chunk'ı zorlar.

Attacker böylece msg_len'i seçerek target cache'i ayarlar. Body byte'ları header field'larından sonra aynen yazılır, dolayısıyla spray, freed-then-reused slot içinde seçilen offset'lere seçilen 4-byte/8-byte pattern'leri yerleştirmene imkân tanır — dangling bir objenin field'larını overwrite etmek için ideal (bkz. refcount-imbalance-uaf, mq-notify-netlink-sock-double-sock-put-uaf). Mesaj msgrcv/mq_timedreceive'e kadar copy out edilmediğinden, spray persistent ve tek tek free edilebilir: hem allocation'ı hem de free'nin tam anını sen kontrol edersin. POSIX mqueue ek olarak her priority level başına kmalloc-64 içinde bir struct posix_msg_tree_node (bir rb_node içeren) allocate eder, bu da ikinci, sabit boyutlu bir spray objesi verir. Persistence ile content control'ü farklı şekilde takas eden sk-buff-spray ve setxattr-userfaultfd-universal-heap-spray ile karşılaştır.

Warning

msg_msg, sabit offset'lerde bir security pointer'ı ve bir next pointer'ı taşır; obje gerçek bir msg_msg olarak yorumlandığında (örn. daha sonra onu msgrcv ettiğinde) spray içeriğin bunlarla çakışırsa, bozuk bir next/security crash'e veya leak'e yol açabilir. msg_msg'i sırf farklı bir victim tipini overlay etmek için kullanırken bunun önemi yoktur; ama onu arbitrary read için tekrar bir msg_msg olarak kullanırken (corCTF tekniği) field layout kritik öneme sahiptir.

Walkthrough

System V varyantı (sürmesi en kolay olanı). Target cache'e düşen bir boyut seç, sonra spray'le.

#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

struct spray { long mtype; char buf[N]; };  /* N tunes the kmalloc class */

int qid[512];

void spray(int count, int n, char fill) {
    struct spray *m = malloc(sizeof(long) + n);
    m->mtype = 1;
    memset(m->buf, fill, n);                 /* attacker-controlled contents */
    for (int i = 0; i < count; i++) {
        qid[i] = msgget(IPC_PRIVATE, 0644 | IPC_CREAT);
        /* MSG_COPY-free send: object stays resident until msgrcv */
        msgsnd(qid[i], m, n, 0);
    }
}

n'i, sizeof(struct msg_msg) + n istediğin cache'e düşecek şekilde seç. kmalloc-96 için, 64-bit bir kernel'de sizeof(struct msg_msg) 0x30'dur, dolayısıyla 0x30–0x60 civarında bir n, header'ı kmalloc-96'da tutar.

POSIX mqueue varyantı (ayrıca kmalloc-64 tree node'unu da allocate eder):

#include <mqueue.h>
#include <fcntl.h>

struct mq_attr attr = { .mq_maxmsg = 10, .mq_msgsize = 0x60 };
mqd_t mq = mq_open("/spray", O_CREAT | O_RDWR, 0644, &attr);

char body[0x60];
memset(body, 0x41, sizeof(body));
for (int prio = 0; prio < 8; prio++)
    mq_timedsend(mq, body, sizeof(body), prio, NULL);  /* msg_msg + posix_msg_tree_node */

Bir UAF'a karşı tipik kullanım:

  1. Victim objeyi (the bug) free et ki slab slot'u freelist'e düşsün.
  2. Header boyutu victim'in cache'iyle eşleşen mesajları hemen spray()'le; bunlardan biri dangling slot'u reclaim eder ve onun body'si victim'in adresine yerleşir.
  3. Dangling reference'ı sür; read/write'lar artık senin sprayled byte'larına çarpar.
  4. Belleği deterministik şekilde serbest bırakmak veya yeniden şekillendirmek için yalnızca free etmen gereken mesajları (msgrcv/mq_timedreceive ile) free et.
$ ./poc
[*] sprayed 256 msg_msg objects of size 0x60 (kmalloc-96)
[*] dangling object now overlaps spray slot @ ffff8881...
[+] arbitrary read primitive established

Warning

Spray, resource limit'leriyle sınırlıdır: RLIMIT_MSGQUEUE, /proc/sys/kernel/msgmax (varsayılan 8192 — tek bir SysV mesajını sınırlar), msgmnb ve per-queue mq_maxmsg/mq_msgsize. Bunları tüketmek spray'in ortasında EAGAIN/ENOMEM döndürür. Mesajlar yerleşmeden önce queue header allocation'larının kendilerinin slab'ı perturbe ettiğini de hesaba kat.

Detection

  • Unprivileged bir process'ten gelen, eşleşen bir msgrcv olmayan ani msgsnd/mq_timedsend patlamaları — yani iletilmek yerine tutulan mesajlar — bir spray imzasıdır.
  • Tek bir task'a atfedilebilen yüksek msg_msg / kmalloc-* slab doluluğu; /proc/slabinfo delta'ları veya alloc_msg/load_msg üzerindeki BPF ile görülebilir.
  • Dangling obje ile sprayled msg_msg tip konusunda anlaşamadığında KASAN overlay'i yakalar.

Mitigation

  • CONFIG_SLAB_FREELIST_RANDOM ve init_on_alloc/init_on_free, determinizmi azaltır ve reused slot'ları sıfırlar, güvenilir reclaim'i köreltir.
  • Dedicated/randomize edilmiş cache'ler (CONFIG_RANDOM_KMALLOC_CACHES, cgroup-accounted allocation'lar için kmalloc-cg-*), msg_msg'i birçok victim'den ayırır ve attacker'ları cross-cache-attack'e iter.
  • Per-user RLIMIT_MSGQUEUE ve sıkılaştırılmış msgmax/msgmnb/mqueue sysctl'leri spray hacmini sınırlar.

References