The exploit recon 'msg_msg' and its mitigation in VED November 13, 2022 | 6 min Read

The exploit recon 'msg_msg' and its mitigation in VED

Why msg_msg?

The size of structure is control by userspace

Firstly, the length of the msg_msg struct can be indirectly controlled from userspace, which means that msg can overlap the cache of the specified types. When any object with size from sizeof(struct msg_msg) to PAGE_SIZE has a UAF/double free vulnerability, it can be exploited to achieve the heap spray via allocating many msg_msg overlapping the same cache. Here is the prototype of msgsnd():

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

msgsz would be passed to alloc_msg(), and finally allocated upon the targeted cache in kmalloc():

static struct msg_msg *alloc_msg(size_t len)
{
    struct msg_msg *msg;
    ...
    size_t alen;

    alen = min(len, DATALEN_MSG);
    msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_ACCOUNT); // alloc msg
    if (msg == NULL)
        return NULL;

    ...

    len -= alen;
    ...
    while (len > 0) {
        struct msg_msgseg *seg;
        ...

        alen = min(len, DATALEN_SEG);
        seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT); // alloc msg_seg
        if (seg == NULL)
            goto out_err;
        ...
    }

    return msg;
    ...
}

Therefore, it’s easy to calculate the size of the msg_msg so that it is allocated near the object with the UAF/double free vulnerability, and further completes the heap spray/memory corruption.

Achieve arbitrary read

The kernel will slice the buffer for msgsnd() to process (const void *msgp) to shorter segments when needed, and link them with a next pointer:

struct msg_msg {
    struct list_head m_list;
    long m_type;
    size_t 
    
    ;       /* message text size */
    struct msg_msgseg *next;
    void *security;
    /* the actual message follows immediately */
};

struct msg_msgseg {
    struct msg_msgseg *next;
    /* the next part of the message follows immediately */
};

To read the msg buffer, a loop is implemented in load_msg() to read the segment one by one:

struct msg_msg *load_msg(const void __user *src, size_t len)
{
    ...
    if (copy_from_user(msg + 1, src, alen))
        goto out_err;

    /* read segments one by one */
    for (seg = msg->next; seg != NULL; seg = seg->next) {
        len -= alen;
        src = (char __user *)src + alen;
        alen = min(len, DATALEN_SEG);
        if (copy_from_user(seg + 1, src, alen))
            goto out_err;
    }

    ...

    return msg;
    ...
}

The buffer length is not fixed, and the read size was totally determined by size_t m_ts, once size_t m_ts and struct msg_msgseg *next was corrupted, msgrcv() can be used to achieve arbitary read. Even when only m_ts was corrupted, out-of-bound read can be achieved. So it can be used to do infoleak.

Here is some example use msg_msg to achieve infoleak:

Usable to achieve arbitrary free

Arbitrary free is very close to privilege escalation. It prompts to achieve UAF in arbitrary object, for example an object with function pointer, after corrupted, may be used to achieve control flow hijacking to perform ROP.

More directly, recently, lots of researchers find heap spray of cred_jar cache can be used to perform privilege escalation. Using arbitrary free to free the ‘cred’ pointer of the current thread, and triggering an privileged thread to allocate ‘cred’ objects, for example: sshd, su, sudo, once one of them overlaps where the current thread’s ‘struct cred’ points, the privilege escalation could be done.

The story about DirtyCred

cred_jar heap spray now is called as “DirtyCred” by some researchers, the author announces that it is “a new approach”, but actually it is already used and published by several researchers before that. In this article, we have exposed how the author collected information from syzkaller community and Harbian-QA, reimplemented or reproduced them, then renamed them and wrote paper to announce that it was totally and independently devised by them. Similarly, before “DirtyCred”, security researchers published their works in ‘cred’ spray.

Here is a timeline:

Incomplete mitigations

Integrity check of struct msg_msg

VED maintains a list of msg_msg, calculates the checksum of msg_msg content, reading it with msgrcv(). Corrupted struct msg_msgseg *next, size_t m_ts, void *security can be detected. Our checksum does not include struct list_head m_list since it’s maintained in msg_queue. That also means if a msg_msg is corrupted by using msgrcv() to free it and perform UAF, VED can’t detect it. CVE-2021-22555: Turning \x00\x00 into 10000$ - Exploring struct msg_msg is an example. It uses corrupted .m_list.next to free other msgs.

Out-of-bound read check

VED adds a boundary check of read target. If the size of object is not equal to size to read, VED would detect the corruption. The corruption of ‘.m_ts’ is easy to achieve out-of-bound read to leak information, in order to bypass KALSR or heap address. VED adds a target object length check while reading msg’s buffers. It’s effective in detecting out-of-bound read but carefully crafted msg may bypass it. If the size of an object vulnerable to UAF/double free, msg_msg object, and the target object to used in infoleak are identical, it would still bypass the check. Or the size of where the struct msg_msgseg *next points to is equal to the target object wants to leak.

Although these mitigations are bypassable. Since VED is currently based on LKRG, we should consider more about the balance between security and performance. Complete msg integrity is possible. But it would need more kprobe points and calculations. Although, both of these two mitigation could be bypassed independently, the exploit would be a little difficult to bypass both in the mean time, because these two mitigations has checks of the data in different way and different locations. To bypass these checks, exploit should be more dependent on “the same type of object spray”. But we believe it would be not so difficult for the outstanding hackers. VED will continue to explore other efficient ways to solve the problem.

Countermeasure effectiveness

Currently, public exploits have not tried to bypass these types of check, VED can easily detect them, especially in the early infoleak phase, it would efficiently detect and prevent such exploits and the certain mitigation against exploits:

1:CVE-2022-25636: integrity checks.

2:CVE-2021-26708: integrity checks.

3:CVE-2021-22555: out-of-bound read checks.

4:CVE-2021-22555: out-of-bound read checks.

Demo

alter-text