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:
-
Four Bytes of Power: Exploiting CVE-2021-26708 in the Linux kernel - Achieving arbitrary read: overwrite next and
size_t m_ts
achieve arbitrary read. -
CVE-2021-22555: Turning \x00\x00 into 10000$ - Exploring struct msg_msg: out-of-bound read.
-
Linux Kernel Exploit Development: 1day case study: out-of-bound read.
Usable to achieve arbitrary free
-
Four Bytes of Power: Exploiting CVE-2021-26708 in the Linux kernel - Four bytes of power: Corrupted (struct msg_msg)::security can be used to do arbitrary free.
-
CVE-2021-22555: Turning \x00\x00 into 10000$ - Achieving a better use-after-free: Corrupted mlist.next can be used to do arbitrary free. Note that the code path to free msg in this example is the original code path of the kernel to free msg.
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:
- Mar 19, 2020: xfrm_poc_RE_challenge Vitaly Nikolenko
- Mar 1, 2022: Exploiting CVE-2021-26708 (Linux kernel) with sshd by HardenedVault
- Aug 13, 2022: HardenedLinux’s tweet
- Aug 16, 2022: Reviving Exploits Against Cred Structs - Six Byte Cross Cache Overflow to Leakless Data-Oriented Kernel Pwnage
- Sep 8, 2022: Vitaly Nikolenko’s tweet
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.